diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71011bf79..7bb0e41f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [v1.10.2] - 2023-12-24
+
+### Changed
+
+- Viewer: keep controls in the lower right corner even with RTL locales
+
+### Fixed
+
+- crash when loading SVG defined with large dimensions
+
## [v1.10.1] - 2023-12-21
- Cataloguing: detect/filter `Ultra HDR`
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt
index 2c96888b6..232fa09cd 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt
@@ -89,6 +89,7 @@ class MediaFetchBytesHandler(private val context: Context) : MethodCallHandler {
MimeTypes.SVG -> SvgRegionFetcher(context).fetch(
uri = uri,
sizeBytes = sizeBytes,
+ scale = sampleSize,
regionRect = regionRect,
imageWidth = imageWidth,
imageHeight = imageHeight,
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt
index 614a87f0e..882844c6e 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt
@@ -25,6 +25,7 @@ class SvgRegionFetcher internal constructor(
suspend fun fetch(
uri: Uri,
sizeBytes: Long?,
+ scale: Int,
regionRect: Rect,
imageWidth: Int,
imageHeight: Int,
@@ -32,7 +33,7 @@ class SvgRegionFetcher internal constructor(
) {
if (!MemoryUtils.canAllocate(sizeBytes)) {
// opening an SVG that large would yield an OOM during parsing from `com.caverock.androidsvg.SVGParser`
- result.error("fetch-read-large", "SVG too large at $sizeBytes bytes, for uri=$uri regionRect=$regionRect", null)
+ result.error("fetch-read-large-file", "SVG too large at $sizeBytes bytes, for uri=$uri regionRect=$regionRect", null)
return
}
@@ -67,8 +68,8 @@ class SvgRegionFetcher internal constructor(
val viewBox = svg.documentViewBox
val svgWidth = viewBox.width()
val svgHeight = viewBox.height()
- val xf = imageWidth / ceil(svgWidth)
- val yf = imageHeight / ceil(svgHeight)
+ val xf = imageWidth / scale / ceil(svgWidth)
+ val yf = imageHeight / scale / ceil(svgHeight)
// some SVG paths do not respect the rendering viewbox and do not reach its edges
// so we render to a slightly larger bitmap, using a slightly larger viewbox,
// and crop that bitmap to the target region size
@@ -87,6 +88,15 @@ class SvgRegionFetcher internal constructor(
val targetBitmapWidth = regionRect.width()
val targetBitmapHeight = regionRect.height()
+
+ // use `Long` as rect size could be unexpectedly large and go beyond `Int` max
+ val targetBitmapSizeBytes: Long = ARGB_8888_BYTE_SIZE.toLong() * targetBitmapWidth * targetBitmapHeight
+ if (!MemoryUtils.canAllocate(targetBitmapSizeBytes)) {
+ // decoding a region that large would yield an OOM when creating the bitmap
+ result.error("fetch-read-large-region", "SVG region too large for uri=$uri regionRect=$regionRect", null)
+ return
+ }
+
var bitmap = Bitmap.createBitmap(
targetBitmapWidth + bleedX * 2,
targetBitmapHeight + bleedY * 2,
@@ -106,4 +116,8 @@ class SvgRegionFetcher internal constructor(
val uri: Uri,
val svg: SVG,
)
+
+ companion object {
+ const val ARGB_8888_BYTE_SIZE = 4
+ }
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
index d68d851b1..9d219a7a2 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
@@ -92,7 +92,7 @@ object MimeTypes {
// as of `metadata-extractor` v2.14.0
fun canReadWithMetadataExtractor(mimeType: String) = when (mimeType) {
- DJVU, WBMP -> false
+ DJVU, SVG, WBMP -> false
MKV, MP2T, MP2TS, OGV, WEBM -> false
else -> true
}
diff --git a/fastlane/metadata/android/ar/full_description.txt b/fastlane/metadata/android/ar/full_description.txt
index 74df1a08d..eb0b4ab29 100644
--- a/fastlane/metadata/android/ar/full_description.txt
+++ b/fastlane/metadata/android/ar/full_description.txt
@@ -1,5 +1,5 @@
-أيفيس يمكنه التعامل مع جميع أنواع الصور ومقاطع الفيديو ، بما في ذلك ملفات JPEG و MP4 النموذجية ، ولكن أيضًا أشياء أكثر غرابة مثل ملفات TIFF و SVG و AVI القديمة متعددة الصفحات والمزيد! يقوم بمسح مجموعة الوسائط الخاصة بك لتحديد الصور المتحركة, الإستعراضات (المعروف أيضًا باسم الصور البانورامية), 360 درجة مقاطع الفيديو, إلى جانب GeoTIFF الملفات.
+Aves يمكنه التعامل مع جميع أنواع الصور ومقاطع الفيديو ، بما في ذلك ملفات JPEG و MP4 النموذجية ، ولكن أيضًا أشياء أكثر غرابة مثل ملفات TIFF و SVG و AVI القديمة متعددة الصفحات والمزيد! يقوم بمسح مجموعة الوسائط الخاصة بك لتحديد الصور المتحركة, الإستعراضات (المعروف أيضًا باسم الصور البانورامية), 360 درجة مقاطع الفيديو, إلى جانب GeoTIFF الملفات.
الملاحة والبحث جزء مهم من Avesالهدف هو أن يكون بإمكان المستخدمين التنقل بسهولة من الألبومات إلى الصور إلى الوسوم إلى الخرائط، وما إلى ذلك..
-Avesتتكامل مع نظام Android (من KitKat إلى Android 14، بما في ذلك Android TV) مع ميزات مثلs الودجتس, اختصارات التطبيق, حافظة الشاشة والبحث العالمي التعامل معه. كما أنه يعمل كـ عارض واختار لوسائط (ملفات الوسائط).
+Avesتتكامل مع نظام Android (من KitKat إلى Android 14، بما في ذلك Android TV) مع ميزات مثلs الودجتس, اختصارات التطبيق, حافظة الشاشة والبحث العالمي التعامل معه. كما أنه يعمل كـ عارض الوسائط ومنتقيها.
diff --git a/fastlane/metadata/android/en-US/changelogs/111.txt b/fastlane/metadata/android/en-US/changelogs/111.txt
new file mode 100644
index 000000000..3278d2a31
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/111.txt
@@ -0,0 +1,4 @@
+In v1.10.2:
+- JPEG MPF support
+- enjoy the app in Arabic & Belarusian
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/11101.txt b/fastlane/metadata/android/en-US/changelogs/11101.txt
new file mode 100644
index 000000000..3278d2a31
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/11101.txt
@@ -0,0 +1,4 @@
+In v1.10.2:
+- JPEG MPF support
+- enjoy the app in Arabic & Belarusian
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb
index 12836dbde..d0392a93d 100644
--- a/lib/l10n/app_ar.arb
+++ b/lib/l10n/app_ar.arb
@@ -51,7 +51,7 @@
"@cancelTooltip": {},
"previousTooltip": "السابق",
"@previousTooltip": {},
- "welcomeMessage": "مرحبا بكم في أيفيس",
+ "welcomeMessage": "مرحبا بكم في Aves",
"@welcomeMessage": {},
"applyButtonLabel": "تقديم",
"@applyButtonLabel": {},
@@ -77,11 +77,11 @@
"@saveCopyButtonLabel": {},
"chipActionFilterOut": "تصفية أو استبعاد",
"@chipActionFilterOut": {},
- "chipActionHide": "يخفي",
+ "chipActionHide": "إخفاء",
"@chipActionHide": {},
"sourceStateCataloguing": "تصنيف",
"@sourceStateCataloguing": {},
- "chipActionDelete": "يمسح",
+ "chipActionDelete": "حَذف",
"@chipActionDelete": {},
"sourceStateLoading": "تحميل",
"@sourceStateLoading": {},
@@ -103,9 +103,9 @@
"@pickTooltip": {},
"chipActionGoToCountryPage": "عرض في الدول",
"@chipActionGoToCountryPage": {},
- "applyTooltip": "يتقدم",
+ "applyTooltip": "تقدم",
"@applyTooltip": {},
- "chipActionUnpin": "إلغاء تثبيت من الأعلى",
+ "chipActionUnpin": "إلغاء التثبيت في الأعلى",
"@chipActionUnpin": {},
"chipActionGoToTagPage": "عرض في الوسوم",
"@chipActionGoToTagPage": {},
@@ -121,7 +121,7 @@
},
"settingsNavigationDrawerAddAlbum": "إضافة ألبوم",
"@settingsNavigationDrawerAddAlbum": {},
- "settingsPrivacySectionTitle": "خصوصية",
+ "settingsPrivacySectionTitle": "الخصوصية",
"@settingsPrivacySectionTitle": {},
"settingsEnableBinSubtitle": "الاحتفاظ بالعناصر المحذوفة لمدة 30 يومًا",
"@settingsEnableBinSubtitle": {},
@@ -141,7 +141,7 @@
"@entryInfoActionExportMetadata": {},
"aboutDataUsageMisc": "منوعات",
"@aboutDataUsageMisc": {},
- "editorActionTransform": "تحول",
+ "editorActionTransform": "تحويل",
"@editorActionTransform": {},
"collectionExportFailureFeedback": "{count, plural, =1{فشل تصدير صفحة واحدة} other{فشل التصدير {count} pages}}",
"@collectionExportFailureFeedback": {
@@ -199,7 +199,7 @@
"@tooManyItemsErrorDialogMessage": {},
"collectionActionEdit": "تحرير",
"@collectionActionEdit": {},
- "viewerInfoSearchSuggestionResolution": "دقة",
+ "viewerInfoSearchSuggestionResolution": "الدقة",
"@viewerInfoSearchSuggestionResolution": {},
"viewerTransitionSlide": "الإنزلاق",
"@viewerTransitionSlide": {},
@@ -207,7 +207,7 @@
"@sortOrderLargestFirst": {},
"viewerTransitionParallax": "تأثير الشبكية",
"@viewerTransitionParallax": {},
- "settingsViewerSectionTitle": "عارض",
+ "settingsViewerSectionTitle": "المعرض",
"@settingsViewerSectionTitle": {},
"mapStyleStamenWatercolor": "ستايمن بالألوان المائية",
"@mapStyleStamenWatercolor": {},
@@ -241,7 +241,7 @@
"@coordinateDmsNorth": {},
"dateYesterday": "أمس",
"@dateYesterday": {},
- "viewerInfoLabelDate": "تاريخ",
+ "viewerInfoLabelDate": "التاريخ",
"@viewerInfoLabelDate": {},
"nameConflictStrategyReplace": "إستبدال",
"@nameConflictStrategyReplace": {},
@@ -263,7 +263,7 @@
"@settingsSubtitleThemeTextColor": {},
"collectionDeselectSectionTooltip": "قم بإلغاء تحديد القسم",
"@collectionDeselectSectionTooltip": {},
- "settingsKeepScreenOnTile": "إبقاء شاشة قيد التشغيل",
+ "settingsKeepScreenOnTile": "إبقاء الشاشة قيد التشغيل",
"@settingsKeepScreenOnTile": {},
"tileLayoutGrid": "شبكة",
"@tileLayoutGrid": {},
@@ -275,7 +275,7 @@
"@aboutCreditsWorldAtlas1": {},
"nameConflictDialogMultipleSourceMessage": "بعض الملفات لها نفس الاسم.",
"@nameConflictDialogMultipleSourceMessage": {},
- "settingsVideoSectionTitle": "فيديو",
+ "settingsVideoSectionTitle": "الفيديو",
"@settingsVideoSectionTitle": {},
"appExportCovers": "أغلفة",
"@appExportCovers": {},
@@ -295,7 +295,7 @@
"@settingsThumbnailOverlayTile": {},
"settingsNavigationDrawerTabPages": "الصفحات",
"@settingsNavigationDrawerTabPages": {},
- "settingsConfirmationDialogTitle": "نوافذ التأكيد الحوار",
+ "settingsConfirmationDialogTitle": "نوافذ تأكيد الحوار",
"@settingsConfirmationDialogTitle": {},
"videoStreamSelectionDialogText": "ترجمات",
"@videoStreamSelectionDialogText": {},
@@ -303,7 +303,7 @@
"@settingsVideoAutoPlay": {},
"settingsVideoEnableHardwareAcceleration": "تسريع الأجهزة",
"@settingsVideoEnableHardwareAcceleration": {},
- "editEntryDateDialogShift": "يحول",
+ "editEntryDateDialogShift": "تغيير",
"@editEntryDateDialogShift": {},
"viewerInfoLabelCoordinates": "الإحداثيات",
"@viewerInfoLabelCoordinates": {},
@@ -421,7 +421,7 @@
"@settingsVideoGestureVerticalDragBrightnessVolume": {},
"settingsAccessibilitySectionTitle": "إمكانية الوصول",
"@settingsAccessibilitySectionTitle": {},
- "settingsActionExport": "يصدّر",
+ "settingsActionExport": "تصدير",
"@settingsActionExport": {},
"aboutBugCopyInfoInstruction": "نسخ معلومات النظام",
"@aboutBugCopyInfoInstruction": {},
@@ -477,9 +477,9 @@
"@drawerAlbumPage": {},
"statsTopCountriesSectionTitle": "أهم الدول",
"@statsTopCountriesSectionTitle": {},
- "settingsActionImport": "يستورد",
+ "settingsActionImport": "إستيراد",
"@settingsActionImport": {},
- "viewerInfoLabelSize": "مقاس",
+ "viewerInfoLabelSize": "المقاس",
"@viewerInfoLabelSize": {},
"locationPickerUseThisLocationButton": "استخدم هذا الموقع",
"@locationPickerUseThisLocationButton": {},
@@ -559,7 +559,7 @@
},
"viewerTransitionZoomIn": "تكبير",
"@viewerTransitionZoomIn": {},
- "drawerCollectionAll": "كل جمع",
+ "drawerCollectionAll": "كافة الوسائط",
"@drawerCollectionAll": {},
"collectionMoveSuccessFeedback": "{count, plural, =1{تم نقل عنصر واحد} other{نقل {count} عناصر}}",
"@collectionMoveSuccessFeedback": {
@@ -595,7 +595,7 @@
"@menuActionMap": {},
"entryInfoActionRemoveMetadata": "إزالة البيانات الوصفية",
"@entryInfoActionRemoveMetadata": {},
- "collectionActionMove": "الانتقال إلى الألبوم",
+ "collectionActionMove": "نقل إلى الألبوم",
"@collectionActionMove": {},
"searchAlbumsSectionTitle": "الألبومات",
"@searchAlbumsSectionTitle": {},
@@ -665,7 +665,7 @@
"@viewerInfoSearchSuggestionDate": {},
"videoPlaybackWithSound": "تشغيل بالصوت",
"@videoPlaybackWithSound": {},
- "viewerInfoLabelTitle": "عنوان",
+ "viewerInfoLabelTitle": "العنوان",
"@viewerInfoLabelTitle": {},
"searchCollectionFieldHint": "البحث في المجموعة",
"@searchCollectionFieldHint": {},
@@ -851,7 +851,7 @@
"@albumTierPinned": {},
"mapStyleDialogTitle": "نمط الخريطة",
"@mapStyleDialogTitle": {},
- "entryActionRotateCCW": "تدوير باتجاه عقارب الساعة",
+ "entryActionRotateCCW": "تدوير عكس عقارب الساعة",
"@entryActionRotateCCW": {},
"settingsVideoBackgroundMode": "وضع الخلفية",
"@settingsVideoBackgroundMode": {},
@@ -869,7 +869,7 @@
"@entryActionShowGeoTiffOnMap": {},
"viewDialogReverseSortOrder": "عكس ترتيب الفرز",
"@viewDialogReverseSortOrder": {},
- "menuActionConfigureView": "رؤية",
+ "menuActionConfigureView": "العرض",
"@menuActionConfigureView": {},
"aboutLicensesDartPackagesSectionTitle": "حزم البرمجة",
"@aboutLicensesDartPackagesSectionTitle": {},
@@ -933,7 +933,7 @@
"@settingsThumbnailShowVideoDuration": {},
"passwordDialogConfirm": "تأكيد كلمة السر",
"@passwordDialogConfirm": {},
- "videoActionReplay10": "تقدم الخلفي لمدة 10 ثوانٍ",
+ "videoActionReplay10": "الرجوع للخلف لمدة 10 ثوانٍ",
"@videoActionReplay10": {},
"settingsCollectionBurstPatternsNone": "لا شيء",
"@settingsCollectionBurstPatternsNone": {},
@@ -989,7 +989,7 @@
"@entryActionOpen": {},
"settingsCoordinateFormatTile": "تنسيق الإحداثيات",
"@settingsCoordinateFormatTile": {},
- "newVaultWarningDialogMessage": "العناصر الموجودة في الخزائن متاحة فقط لهذا التطبيق وليس للتطبيقات الأخرى.\n\nإذا قمت بإلغاء تثبيت هذا التطبيق، أو قمت بمسح بيانات هذا التطبيق، فسوف تفقد كل هذه العناصر.",
+ "newVaultWarningDialogMessage": "العناصر الموجودة في الخزائن متاحة فقط لهذا التطبيق وليس للتطبيقات الأخرى.\n\nإذا قمت بإلغاء تثبيت هذا التطبيق، أو قمت بحذف بيانات هذا التطبيق، فسوف تفقد كل هذه العناصر.",
"@newVaultWarningDialogMessage": {},
"settingsViewerQuickActionEditorBanner": "المس مع الاستمرار لتحريك الأزرار وتحديد الإجراءات التي سيتم عرضها في العارض.",
"@settingsViewerQuickActionEditorBanner": {},
@@ -1139,11 +1139,11 @@
"@settingsStorageAccessEmpty": {},
"settingsRemoveAnimationsTile": "إزالة الرسوم المتحركة",
"@settingsRemoveAnimationsTile": {},
- "settingsStorageAccessBanner": "تتطلب بعض الدلائل منح وصول صريح لتعديل الملفات الموجودة فيها. يمكنك هنا مراجعة الدلائل التي منحتها حق الوصول إليها مسبقًا.",
+ "settingsStorageAccessBanner": "تتطلب بعض المسارات منح وصول صريح لتعديل الملفات الموجودة فيها. يمكنك هنا مراجعة المسارات التي منحتها حق الوصول إليها مسبقًا.",
"@settingsStorageAccessBanner": {},
- "authenticateToConfigureVault": "قم بالمصادقة لتكوين المخزن",
+ "authenticateToConfigureVault": "قم بالمصادقة لتكوين الخزنة",
"@authenticateToConfigureVault": {},
- "settingsActionExportDialogTitle": "يصدّر",
+ "settingsActionExportDialogTitle": "تصدير",
"@settingsActionExportDialogTitle": {},
"genericDangerWarningDialogMessage": "هل أنت متأكد؟",
"@genericDangerWarningDialogMessage": {},
@@ -1197,13 +1197,13 @@
"@settingsViewerShowMinimap": {},
"settingsCollectionBurstPatternsTile": "أنماط الانفجار",
"@settingsCollectionBurstPatternsTile": {},
- "viewerInfoLabelPath": "الطريق",
+ "viewerInfoLabelPath": "المسار",
"@viewerInfoLabelPath": {},
"albumGroupVolume": "حسب حجم التخزين",
"@albumGroupVolume": {},
"collectionGroupMonth": "حسب الشهر",
"@collectionGroupMonth": {},
- "viewerInfoLabelResolution": "دقة",
+ "viewerInfoLabelResolution": "الدقة",
"@viewerInfoLabelResolution": {},
"renameProcessorCounter": "عداد",
"@renameProcessorCounter": {},
@@ -1217,7 +1217,7 @@
"@sortByDate": {},
"statsTopAlbumsSectionTitle": "أهم الألبومات",
"@statsTopAlbumsSectionTitle": {},
- "configureVaultDialogTitle": "تكوين قبو",
+ "configureVaultDialogTitle": "تكوين خزنة",
"@configureVaultDialogTitle": {},
"entryActionRename": "إعادة تسمية",
"@entryActionRename": {},
@@ -1229,7 +1229,7 @@
"@keepScreenOnNever": {},
"statsPageTitle": "احصائيات",
"@statsPageTitle": {},
- "viewerInfoSearchSuggestionDimensions": "أبعاد",
+ "viewerInfoSearchSuggestionDimensions": "الأبعاد",
"@viewerInfoSearchSuggestionDimensions": {},
"mapStyleOsmHot": "خرائط OSM",
"@mapStyleOsmHot": {},
@@ -1255,7 +1255,7 @@
},
"sortBySize": "حسب الحجم",
"@sortBySize": {},
- "viewerInfoSearchSuggestionDescription": "وصف",
+ "viewerInfoSearchSuggestionDescription": "الوصف",
"@viewerInfoSearchSuggestionDescription": {},
"settingsViewerShowOverlayThumbnails": "عرض الصور المصغرة",
"@settingsViewerShowOverlayThumbnails": {},
@@ -1347,7 +1347,7 @@
"@settingsSubtitleThemeBackgroundOpacity": {},
"settingsStorageAccessTile": "الوصول إلى التخزين",
"@settingsStorageAccessTile": {},
- "entryActionDelete": "مسح",
+ "entryActionDelete": "حذف",
"@entryActionDelete": {},
"settingsThemeBrightnessTile": "السمَة",
"@settingsThemeBrightnessTile": {},
@@ -1401,7 +1401,7 @@
},
"videoResumeButtonLabel": "إستئناف",
"@videoResumeButtonLabel": {},
- "entryActionExport": "تصدير",
+ "entryActionExport": "المزيد",
"@entryActionExport": {},
"mapEmptyRegion": "لا توجد صور في هذه المنطقة",
"@mapEmptyRegion": {},
@@ -1421,7 +1421,7 @@
"@entryActionAddFavourite": {},
"entryActionEdit": "تحرير",
"@entryActionEdit": {},
- "newVaultDialogTitle": "قبو جديد",
+ "newVaultDialogTitle": "خزنة جديدة",
"@newVaultDialogTitle": {},
"entryInfoActionEditRating": "تحرير التقييم",
"@entryInfoActionEditRating": {},
@@ -1447,7 +1447,7 @@
"@settingsVideoBackgroundModeDialogTitle": {},
"binPageTitle": "سلة المحذوفات",
"@binPageTitle": {},
- "tagPlaceholderState": "ولاية",
+ "tagPlaceholderState": "الولاية",
"@tagPlaceholderState": {},
"sortByAlbumFileName": "حسب الألبوم واسم الملف",
"@sortByAlbumFileName": {},
@@ -1481,7 +1481,7 @@
"@appPickDialogTitle": {},
"settingsHiddenItemsTabPaths": "المسارات المخفية",
"@settingsHiddenItemsTabPaths": {},
- "viewerInfoSearchSuggestionRights": "حقوق",
+ "viewerInfoSearchSuggestionRights": "الحقوق",
"@viewerInfoSearchSuggestionRights": {},
"mapPointNorthUpTooltip": "نقطة الشمال لأعلى",
"@mapPointNorthUpTooltip": {},
@@ -1497,7 +1497,7 @@
"@entryActionShareImageOnly": {},
"settingsVideoButtonsTile": "أزرار",
"@settingsVideoButtonsTile": {},
- "settingsSubtitleThemeSample": "هذه عينة.",
+ "settingsSubtitleThemeSample": "هذه عينة بسيطة.",
"@settingsSubtitleThemeSample": {},
"albumPickPageTitleMove": "انتقل إلى الألبوم",
"@albumPickPageTitleMove": {},
diff --git a/lib/l10n/app_be.arb b/lib/l10n/app_be.arb
index 2d919b3fd..122e32173 100644
--- a/lib/l10n/app_be.arb
+++ b/lib/l10n/app_be.arb
@@ -222,7 +222,7 @@
"@filterRecentlyAddedLabel": {},
"filterTypeAnimatedLabel": "Аніміраваныя",
"@filterTypeAnimatedLabel": {},
- "filterTypeRawLabel": "Без апрацоўкі",
+ "filterTypeRawLabel": "Raw",
"@filterTypeRawLabel": {},
"filterTypeSphericalVideoLabel": "Відэа 360°",
"@filterTypeSphericalVideoLabel": {},
@@ -257,7 +257,7 @@
"@coordinateFormatDecimal": {},
"subtitlePositionBottom": "Ніз",
"@subtitlePositionBottom": {},
- "videoControlsPlaySeek": "Прайграванне і перамотка назад/уперад",
+ "videoControlsPlaySeek": "Прайграванне і пераход на пазіцыю",
"@videoControlsPlaySeek": {},
"nameConflictStrategyReplace": "Замяніць",
"@nameConflictStrategyReplace": {},
@@ -351,9 +351,9 @@
"@themeBrightnessDark": {},
"themeBrightnessBlack": "Чорная",
"@themeBrightnessBlack": {},
- "unitSystemMetric": "Метрычныя адзінкі вымярэння",
+ "unitSystemMetric": "Метрычныя",
"@unitSystemMetric": {},
- "unitSystemImperial": "Імперская",
+ "unitSystemImperial": "Англійскія",
"@unitSystemImperial": {},
"vaultLockTypePattern": "Шаблон",
"@vaultLockTypePattern": {},
@@ -365,7 +365,7 @@
"@settingsVideoEnablePip": {},
"videoControlsPlayOutside": "Адкрыць у іншым прайгравальніку",
"@videoControlsPlayOutside": {},
- "videoControlsPlay": "Прайграць",
+ "videoControlsPlay": "Прайграванне",
"@videoControlsPlay": {},
"videoLoopModeNever": "Ніколі",
"@videoLoopModeNever": {},
@@ -373,9 +373,9 @@
"@videoLoopModeShortOnly": {},
"videoPlaybackSkip": "Прапусціць",
"@videoPlaybackSkip": {},
- "videoPlaybackMuted": "Гуляць без гука",
+ "videoPlaybackMuted": "Прайграваць без гуку",
"@videoPlaybackMuted": {},
- "videoPlaybackWithSound": "Гуляць з гукам",
+ "videoPlaybackWithSound": "Прайграваць з гукам",
"@videoPlaybackWithSound": {},
"videoResumptionModeNever": "Ніколі",
"@videoResumptionModeNever": {},
@@ -597,7 +597,7 @@
"@viewerInfoSearchSuggestionResolution": {},
"viewerInfoSearchSuggestionDimensions": "Памеры",
"@viewerInfoSearchSuggestionDimensions": {},
- "videoControlsNone": "Без",
+ "videoControlsNone": "Нічога",
"@videoControlsNone": {},
"viewerErrorUnknown": "Ой!",
"@viewerErrorUnknown": {},
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index 4a72170e9..e71487be5 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -1021,7 +1021,7 @@
"@settingsScreenSaverPageTitle": {},
"settingsWidgetPageTitle": "Bilderrahmen",
"@settingsWidgetPageTitle": {},
- "settingsWidgetShowOutline": "Gliederung",
+ "settingsWidgetShowOutline": "Umrandung",
"@settingsWidgetShowOutline": {},
"settingsWidgetOpenPage": "Beim Tippen auf das Widget",
"@settingsWidgetOpenPage": {},
@@ -1331,7 +1331,7 @@
"@cropAspectRatioOriginal": {},
"cropAspectRatioSquare": "Quadrat",
"@cropAspectRatioSquare": {},
- "widgetTapUpdateWidget": "Widget öffnen",
+ "widgetTapUpdateWidget": "Widget aktualisieren",
"@widgetTapUpdateWidget": {},
"aboutDataUsageSectionTitle": "Datennutzung",
"@aboutDataUsageSectionTitle": {},
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index 0e09ed45b..97c61ec99 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -601,7 +601,7 @@
"@collectionEmptyFavourites": {},
"collectionEmptyVideos": "Nenhum video",
"@collectionEmptyVideos": {},
- "collectionEmptyImages": "Nenhuma image",
+ "collectionEmptyImages": "Nenhuma imagem",
"@collectionEmptyImages": {},
"collectionEmptyGrantAccessButtonLabel": "Garantir acesso",
"@collectionEmptyGrantAccessButtonLabel": {},
@@ -1356,5 +1356,9 @@
"settingsViewerShowHistogram": "Mostrar histograma",
"@settingsViewerShowHistogram": {},
"aboutDataUsageClearCache": "Limpar o cache",
- "@aboutDataUsageClearCache": {}
+ "@aboutDataUsageClearCache": {},
+ "entryActionCast": "Transmitir",
+ "@entryActionCast": {},
+ "castDialogTitle": "Dispositivos para Transmitir",
+ "@castDialogTitle": {}
}
diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb
index 9f73f7463..ded1cbf7a 100644
--- a/lib/l10n/app_uk.arb
+++ b/lib/l10n/app_uk.arb
@@ -249,7 +249,7 @@
"@subtitlePositionBottom": {},
"videoPlaybackSkip": "Пропустити",
"@videoPlaybackSkip": {},
- "videoPlaybackMuted": "Відтворити з вимкненим звуком",
+ "videoPlaybackMuted": "Відтворити без звуку",
"@videoPlaybackMuted": {},
"videoPlaybackWithSound": "Відтворити зі звуком",
"@videoPlaybackWithSound": {},
diff --git a/lib/utils/math_utils.dart b/lib/utils/math_utils.dart
index 82be6858d..68a3f1ec9 100644
--- a/lib/utils/math_utils.dart
+++ b/lib/utils/math_utils.dart
@@ -3,7 +3,9 @@ import 'dart:ui';
int highestPowerOf2(num x) => x < 1 ? 0 : pow(2, (log(x) / ln2).floor()).toInt();
-int smallestPowerOf2(num x) => x < 1 ? 1 : pow(2, (log(x) / ln2).ceil()).toInt();
+num smallestPowerOf2(num x, {bool allowNegativePower = false}) {
+ return x < 1 && !allowNegativePower ? 1 : pow(2, (log(x) / ln2).ceil());
+}
double roundToPrecision(final double value, {required final int decimals}) => (value * pow(10, decimals)).round() / pow(10, decimals);
diff --git a/lib/widgets/common/map/geo_map.dart b/lib/widgets/common/map/geo_map.dart
index 2f4cea000..d59476297 100644
--- a/lib/widgets/common/map/geo_map.dart
+++ b/lib/widgets/common/map/geo_map.dart
@@ -439,7 +439,7 @@ class _GeoMapState extends State {
if (points.length != geoEntry.pointsSize) {
// `Fluster.points()` method does not always return all the points contained in a cluster
// the higher `nodeSize` is, the higher the chance to get all the points (i.e. as many as the cluster `pointsSize`)
- _slowMarkerCluster ??= _buildFluster(nodeSize: smallestPowerOf2(entries.length));
+ _slowMarkerCluster ??= _buildFluster(nodeSize: smallestPowerOf2(entries.length).toInt());
points = _slowMarkerCluster!.points(clusterId);
assert(points.length == geoEntry.pointsSize, 'got ${points.length}/${geoEntry.pointsSize} for geoEntry=$geoEntry');
}
diff --git a/lib/widgets/viewer/overlay/bottom.dart b/lib/widgets/viewer/overlay/bottom.dart
index cb9339f2a..14dd28492 100644
--- a/lib/widgets/viewer/overlay/bottom.dart
+++ b/lib/widgets/viewer/overlay/bottom.dart
@@ -186,41 +186,45 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
builder: (context, child) {
final viewInsetsPadding = (widget.viewInsets ?? EdgeInsets.zero) + (widget.viewPadding ?? EdgeInsets.zero);
final selection = context.read?>();
- final viewerButtonRow = (selection?.isSelecting ?? false)
- ? SelectionButton(
- mainEntry: mainEntry,
- scale: _buttonScale,
- )
- : FocusableActionDetector(
- focusNode: _buttonRowFocusScopeNode,
- shortcuts: settings.useTvLayout
- ? const {
- SingleActivator(LogicalKeyboardKey.arrowUp): TvShowLessInfoIntent(),
- }
- : null,
- actions: {
- TvShowLessInfoIntent: CallbackAction(onInvoke: (intent) => TvShowLessInfoNotification().dispatch(context)),
- },
- child: SafeArea(
- top: false,
- bottom: false,
- minimum: EdgeInsets.only(
- left: viewInsetsPadding.left,
- right: viewInsetsPadding.right,
+ final viewerButtonRow = Directionality(
+ // always keep action buttons in the lower right corner, even with RTL locales
+ textDirection: TextDirection.ltr,
+ child: (selection?.isSelecting ?? false)
+ ? SelectionButton(
+ mainEntry: mainEntry,
+ scale: _buttonScale,
+ )
+ : FocusableActionDetector(
+ focusNode: _buttonRowFocusScopeNode,
+ shortcuts: settings.useTvLayout
+ ? const {
+ SingleActivator(LogicalKeyboardKey.arrowUp): TvShowLessInfoIntent(),
+ }
+ : null,
+ actions: {
+ TvShowLessInfoIntent: CallbackAction(onInvoke: (intent) => TvShowLessInfoNotification().dispatch(context)),
+ },
+ child: SafeArea(
+ top: false,
+ bottom: false,
+ minimum: EdgeInsets.only(
+ left: viewInsetsPadding.left,
+ right: viewInsetsPadding.right,
+ ),
+ child: isWallpaperMode
+ ? WallpaperButtons(
+ entry: pageEntry,
+ scale: _buttonScale,
+ )
+ : ViewerButtons(
+ mainEntry: mainEntry,
+ pageEntry: pageEntry,
+ collection: widget.collection,
+ scale: _buttonScale,
+ ),
),
- child: isWallpaperMode
- ? WallpaperButtons(
- entry: pageEntry,
- scale: _buttonScale,
- )
- : ViewerButtons(
- mainEntry: mainEntry,
- pageEntry: pageEntry,
- collection: widget.collection,
- scale: _buttonScale,
- ),
),
- );
+ );
final showMultiPageOverlay = mainEntry.isMultiPage && multiPageController != null;
final collapsedPageScroller = mainEntry.isMotionPhoto;
@@ -247,6 +251,8 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
(showMultiPageOverlay && collapsedPageScroller)
? Row(
crossAxisAlignment: CrossAxisAlignment.center,
+ // always keep action buttons in the lower right corner, even with RTL locales
+ textDirection: TextDirection.ltr,
children: [
SafeArea(
top: false,
diff --git a/lib/widgets/viewer/overlay/video/video.dart b/lib/widgets/viewer/overlay/video/video.dart
index 5272fa414..0ec8cdfb2 100644
--- a/lib/widgets/viewer/overlay/video/video.dart
+++ b/lib/widgets/viewer/overlay/video/video.dart
@@ -40,44 +40,48 @@ class _VideoControlOverlayState extends State with SingleTi
@override
Widget build(BuildContext context) {
- return StreamBuilder(
- stream: statusStream,
- builder: (context, snapshot) {
- // do not use stream snapshot because it is obsolete when switching between videos
- final status = controller?.status ?? VideoStatus.idle;
+ return Directionality(
+ // always keep action buttons in the lower right corner, even with RTL locales
+ textDirection: TextDirection.ltr,
+ child: StreamBuilder(
+ stream: statusStream,
+ builder: (context, snapshot) {
+ // do not use stream snapshot because it is obsolete when switching between videos
+ final status = controller?.status ?? VideoStatus.idle;
- if (status == VideoStatus.error) {
- const action = EntryAction.openVideo;
- return Align(
- alignment: AlignmentDirectional.centerEnd,
- child: OverlayButton(
- scale: scale,
- child: IconButton(
- icon: action.getIcon(),
- onPressed: entry.trashed ? null : () => widget.onActionSelected(action),
- tooltip: action.getText(context),
+ if (status == VideoStatus.error) {
+ const action = EntryAction.openVideo;
+ return Align(
+ alignment: AlignmentDirectional.centerEnd,
+ child: OverlayButton(
+ scale: scale,
+ child: IconButton(
+ icon: action.getIcon(),
+ onPressed: entry.trashed ? null : () => widget.onActionSelected(action),
+ tooltip: action.getText(context),
+ ),
),
- ),
- );
- }
+ );
+ }
- return Row(
- children: [
- Expanded(
- child: VideoProgressBar(
+ return Row(
+ children: [
+ Expanded(
+ child: VideoProgressBar(
+ controller: controller,
+ scale: scale,
+ ),
+ ),
+ VideoControlRow(
+ entry: entry,
controller: controller,
scale: scale,
+ onActionSelected: widget.onActionSelected,
),
- ),
- VideoControlRow(
- entry: entry,
- controller: controller,
- scale: scale,
- onActionSelected: widget.onActionSelected,
- ),
- ],
- );
- },
+ ],
+ );
+ },
+ ),
);
}
}
diff --git a/lib/widgets/viewer/visual/vector.dart b/lib/widgets/viewer/visual/vector.dart
index eae983d37..b76cbb5ab 100644
--- a/lib/widgets/viewer/visual/vector.dart
+++ b/lib/widgets/viewer/visual/vector.dart
@@ -286,7 +286,7 @@ class _VectorImageViewState extends State {
required double scale,
required double devicePixelRatio,
}) =>
- smallestPowerOf2(scale * devicePixelRatio).toDouble();
+ smallestPowerOf2(scale * devicePixelRatio, allowNegativePower: true).toDouble();
}
typedef _BackgroundFrameBuilder = Widget Function(Widget child, int? frame, Rect tileRect);
diff --git a/pubspec.yaml b/pubspec.yaml
index 9685864a0..8da1a8f5e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -7,7 +7,7 @@ repository: https://github.com/deckerst/aves
# - play changelog: /whatsnew/whatsnew-en-US
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt
# - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt
-version: 1.10.1+110
+version: 1.10.2+111
publish_to: none
environment:
diff --git a/test/utils/math_utils_test.dart b/test/utils/math_utils_test.dart
index 0d067bcd6..7d5989d42 100644
--- a/test/utils/math_utils_test.dart
+++ b/test/utils/math_utils_test.dart
@@ -20,6 +20,8 @@ void main() {
expect(smallestPowerOf2(-42), 1);
expect(smallestPowerOf2(.5), 1);
expect(smallestPowerOf2(1.5), 2);
+ expect(smallestPowerOf2(0.5, allowNegativePower: true), 0.5);
+ expect(smallestPowerOf2(0.1, allowNegativePower: true), 0.125);
});
test('rounding to a given precision after the decimal', () {
diff --git a/untranslated.json b/untranslated.json
index 68aae3568..5dad1ffde 100644
--- a/untranslated.json
+++ b/untranslated.json
@@ -6457,11 +6457,6 @@
"filePickerUseThisFolder"
],
- "pt": [
- "entryActionCast",
- "castDialogTitle"
- ],
-
"ro": [
"saveCopyButtonLabel",
"applyTooltip",
diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US
index 69b5f63e7..3278d2a31 100644
--- a/whatsnew/whatsnew-en-US
+++ b/whatsnew/whatsnew-en-US
@@ -1,4 +1,4 @@
-In v1.10.1:
+In v1.10.2:
- JPEG MPF support
- enjoy the app in Arabic & Belarusian
Full changelog available on GitHub
\ No newline at end of file