diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 955b3b3fb..21a469b13 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit - name: 'Checkout Repository' uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: 'Dependency Review' - uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a # v4.4.0 + uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml index 4051df96a..6f573c642 100644 --- a/.github/workflows/quality-check.yml +++ b/.github/workflows/quality-check.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -52,7 +52,7 @@ jobs: build-mode: manual steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -69,7 +69,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4 + uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -83,6 +83,6 @@ jobs: ./flutterw build apk --profile -t lib/main_play.dart --flavor play - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4 + uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 17147aa87..e82037478 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: id-token: write steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -98,7 +98,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index a2291978f..781659b9f 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit @@ -71,6 +71,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4 + uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: sarif_file: results.sarif diff --git a/CHANGELOG.md b/CHANGELOG.md index 741cb66d8..c67508ef8 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.11.19] - 2024-11-24 + +### Added + +- integrate with OS app language settings on Android >=14 + +### Changed + +- remember title filter visibility by page + ## [v1.11.18] - 2024-11-18 ### Changed diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt index 3fe35ae01..15cd66c86 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt @@ -1,11 +1,14 @@ package deckers.thibault.aves.channel.calls +import android.app.LocaleConfig +import android.app.LocaleManager import android.content.Context import android.content.Intent import android.content.res.Resources import android.location.Geocoder import android.net.Uri import android.os.Build +import android.os.LocaleList import android.provider.MediaStore import android.provider.Settings import androidx.core.content.pm.ShortcutManagerCompat @@ -30,8 +33,8 @@ class DeviceHandler(private val context: Context) : MethodCallHandler { when (call.method) { "canManageMedia" -> safe(call, result, ::canManageMedia) "getCapabilities" -> defaultScope.launch { safe(call, result, ::getCapabilities) } - "getDefaultTimeZoneRawOffsetMillis" -> safe(call, result, ::getDefaultTimeZoneRawOffsetMillis) "getLocales" -> safe(call, result, ::getLocales) + "setLocaleConfig" -> safe(call, result, ::setLocaleConfig) "getPerformanceClass" -> safe(call, result, ::getPerformanceClass) "isLocked" -> safe(call, result, ::isLocked) "isSystemFilePickerEnabled" -> safe(call, result, ::isSystemFilePickerEnabled) @@ -63,10 +66,6 @@ class DeviceHandler(private val context: Context) : MethodCallHandler { ) } - private fun getDefaultTimeZoneRawOffsetMillis(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { - result.success(TimeZone.getDefault().rawOffset) - } - private fun getLocales(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { fun toMap(locale: Locale): FieldMap = hashMapOf( "language" to locale.language, @@ -88,6 +87,21 @@ class DeviceHandler(private val context: Context) : MethodCallHandler { result.success(locales) } + private fun setLocaleConfig(call: MethodCall, result: MethodChannel.Result) { + val locales = call.argument>("locales") + if (locales.isNullOrEmpty()) { + result.error("setLocaleConfig-args", "missing arguments", null) + return + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + val lm = context.getSystemService(Context.LOCALE_SERVICE) as? LocaleManager + lm?.overrideLocaleConfig = LocaleConfig(LocaleList.forLanguageTags(locales.joinToString(","))) + } + + result.success(true) + } + private fun getPerformanceClass(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val performanceClass = Build.VERSION.MEDIA_PERFORMANCE_CLASS diff --git a/android/app/src/main/res/values-hi/strings.xml b/android/app/src/main/res/values-hi/strings.xml index 0447c8eb1..9b6676a4d 100644 --- a/android/app/src/main/res/values-hi/strings.xml +++ b/android/app/src/main/res/values-hi/strings.xml @@ -8,4 +8,5 @@ मीडिया जाँचे ऐवीज वीडियो + मैप \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/138.txt b/fastlane/metadata/android/en-US/changelogs/138.txt new file mode 100644 index 000000000..e405b5830 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/138.txt @@ -0,0 +1,5 @@ +In v1.11.19: +- peruse your videos frame by frame +- create map shortcuts to filtered collections +- enjoy the app in Shavian +Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13801.txt b/fastlane/metadata/android/en-US/changelogs/13801.txt new file mode 100644 index 000000000..e405b5830 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/13801.txt @@ -0,0 +1,5 @@ +In v1.11.19: +- peruse your videos frame by frame +- create map shortcuts to filtered collections +- enjoy the app in Shavian +Full changelog available on GitHub \ No newline at end of file diff --git a/lib/l10n/app_en_Shaw.arb b/lib/l10n/app_en_Shaw.arb index 65a149a47..e9451a13a 100644 --- a/lib/l10n/app_en_Shaw.arb +++ b/lib/l10n/app_en_Shaw.arb @@ -1589,5 +1589,7 @@ "videoActionShowPreviousFrame": "𐑖𐑴 𐑐𐑮𐑰𐑝𐑾𐑕 𐑓𐑮𐑱𐑥", "@videoActionShowPreviousFrame": {}, "videoActionShowNextFrame": "𐑖𐑴 𐑯𐑧𐑒𐑕𐑑 𐑓𐑮𐑱𐑥", - "@videoActionShowNextFrame": {} + "@videoActionShowNextFrame": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "𐑨𐑤𐑚𐑩𐑥 𐑷𐑤𐑮𐑧𐑛𐑦 𐑦𐑜𐑟𐑦𐑕𐑑𐑕", + "@newAlbumDialogAlbumAlreadyExistsHelper": {} } diff --git a/lib/l10n/app_et.arb b/lib/l10n/app_et.arb index 9bc9c3e5c..af5471797 100644 --- a/lib/l10n/app_et.arb +++ b/lib/l10n/app_et.arb @@ -464,5 +464,178 @@ "videoLoopModeAlways": "Alati", "@videoLoopModeAlways": {}, "unitSystemMetric": "Meetermõõdustik", - "@unitSystemMetric": {} + "@unitSystemMetric": {}, + "viewerTransitionZoomIn": "Sissesuumimine", + "@viewerTransitionZoomIn": {}, + "wallpaperTargetLock": "Lukustusvaade", + "@wallpaperTargetLock": {}, + "wallpaperTargetHomeLock": "Avaleht ja lukustusvaade", + "@wallpaperTargetHomeLock": {}, + "widgetDisplayedItemRandom": "Juhuslik", + "@widgetDisplayedItemRandom": {}, + "widgetDisplayedItemMostRecent": "Viimane", + "@widgetDisplayedItemMostRecent": {}, + "widgetOpenPageHome": "Mine avalehele", + "@widgetOpenPageHome": {}, + "widgetOpenPageCollection": "Ava kogumik", + "@widgetOpenPageCollection": {}, + "widgetOpenPageViewer": "Ava sirvija", + "@widgetOpenPageViewer": {}, + "widgetTapUpdateWidget": "Värskenda vidinat", + "@widgetTapUpdateWidget": {}, + "storageVolumeDescriptionFallbackPrimary": "Sisemine andmehoidla", + "@storageVolumeDescriptionFallbackPrimary": {}, + "rootDirectoryDescription": "juurkaust", + "@rootDirectoryDescription": {}, + "viewerTransitionNone": "Määratlemata", + "@viewerTransitionNone": {}, + "wallpaperTargetHome": "Avaleht", + "@wallpaperTargetHome": {}, + "storageVolumeDescriptionFallbackNonPrimary": "SD-kaart", + "@storageVolumeDescriptionFallbackNonPrimary": {}, + "unitSystemImperial": "Inglise mõõdustik", + "@unitSystemImperial": {}, + "viewerTransitionSlide": "Äraliuglemine", + "@viewerTransitionSlide": {}, + "viewerTransitionFade": "Hajumine", + "@viewerTransitionFade": {}, + "viewerTransitionParallax": "Parallaks", + "@viewerTransitionParallax": {}, + "otherDirectoryDescription": "„{name}“ kaust", + "@otherDirectoryDescription": { + "placeholders": { + "name": { + "type": "String", + "example": "Pictures", + "description": "the name of a specific directory" + } + } + }, + "notEnoughSpaceDialogMessage": "See tegevus vajab „{volume}“ andmeruumis „{neededSize}“ vaba andmemahtu, kuid alles on vaid „{freeSize}“.", + "@notEnoughSpaceDialogMessage": { + "placeholders": { + "neededSize": { + "type": "String", + "example": "314 MB" + }, + "freeSize": { + "type": "String", + "example": "123 MB" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "missingSystemFilePickerDialogMessage": "Süsteemi failihaldur/failivalija on puudu või kasutuselt eemaldatud. Palun pane ta tööle ja proovi siis uuesti.", + "@missingSystemFilePickerDialogMessage": {}, + "nameConflictDialogSingleSourceMessage": "Mõnedel sihtkausta failidel on sama nimi.", + "@nameConflictDialogSingleSourceMessage": {}, + "videoStartOverButtonLabel": "ALUSTA UUESTI", + "@videoStartOverButtonLabel": {}, + "videoResumeButtonLabel": "JÄTKA", + "@videoResumeButtonLabel": {}, + "setCoverDialogLatest": "Viimane objekt", + "@setCoverDialogLatest": {}, + "setCoverDialogAuto": "Automaatne", + "@setCoverDialogAuto": {}, + "nameConflictDialogMultipleSourceMessage": "Mõnedel failidel on sama nimi.", + "@nameConflictDialogMultipleSourceMessage": {}, + "addShortcutDialogLabel": "Kiirnupu silt", + "@addShortcutDialogLabel": {}, + "addShortcutButtonLabel": "LISA", + "@addShortcutButtonLabel": {}, + "noMatchingAppDialogMessage": "Pole rakendusi, mis oskaks seda kasutada.", + "@noMatchingAppDialogMessage": {}, + "binEntriesConfirmationDialogMessage": "{count, plural, =1{Kas viskame selle objekti prügikasti?} other{Kas viskame need {count} objekti prügikasti?}}", + "@binEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "moveUndatedConfirmationDialogMessage": "Kas enne jätkamist salvestame objekti kuupäevad?", + "@moveUndatedConfirmationDialogMessage": {}, + "moveUndatedConfirmationDialogSetDate": "Salvesta kuupäevad", + "@moveUndatedConfirmationDialogSetDate": {}, + "videoResumeDialogMessage": "Kas sa soovid jätkata esitamist {time} kohalt?", + "@videoResumeDialogMessage": { + "placeholders": { + "time": { + "type": "String", + "example": "13:37" + } + } + }, + "patternDialogEnter": "Sisesta viipemuster", + "@patternDialogEnter": {}, + "patternDialogConfirm": "Korda viipemustrit", + "@patternDialogConfirm": {}, + "pinDialogEnter": "Sisesta PIN-kood", + "@pinDialogEnter": {}, + "pinDialogConfirm": "Korda PIN-koodi", + "@pinDialogConfirm": {}, + "passwordDialogEnter": "Sisesta salasõna", + "@passwordDialogEnter": {}, + "passwordDialogConfirm": "Korda salasõna", + "@passwordDialogConfirm": {}, + "renameAlbumDialogLabel": "Uus nimi", + "@renameAlbumDialogLabel": {}, + "renameAlbumDialogLabelAlreadyExistsHelper": "Selline kaust on juba olemas", + "@renameAlbumDialogLabelAlreadyExistsHelper": {}, + "renameEntrySetPageTitle": "Muuda nime", + "@renameEntrySetPageTitle": {}, + "renameEntrySetPagePatternFieldLabel": "Failide nimemuster", + "@renameEntrySetPagePatternFieldLabel": {}, + "renameEntrySetPageInsertTooltip": "Lisa väli", + "@renameEntrySetPageInsertTooltip": {}, + "renameEntrySetPagePreviewSectionTitle": "Eelvaade", + "@renameEntrySetPagePreviewSectionTitle": {}, + "renameProcessorCounter": "Loendur", + "@renameProcessorCounter": {}, + "renameProcessorHash": "Räsi", + "@renameProcessorHash": {}, + "renameProcessorName": "Nimi", + "@renameProcessorName": {}, + "storageAccessDialogMessage": "Palun anna sellele rakendusele järgmises ekraanivaates õigused „{directory}“ kaustale „{volume}“ andmekogus.", + "@storageAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "restrictedAccessDialogMessage": "Sellel rakendusel pole õigusi muuta faile „{volume}“ andmekogu „{directory}“ kaustas.\n\nPalun kasuta failihaldurit või galeriirakendust failide tõstmiseks muude asukohta.", + "@restrictedAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "setCoverDialogCustom": "Sinu valik", + "@setCoverDialogCustom": {}, + "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Kas kustutame selle objekti?} other{Kas kustutame need {count} objekti?}}", + "@deleteEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + } } diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb index c027bc9ca..bca107eb8 100644 --- a/lib/l10n/app_hi.arb +++ b/lib/l10n/app_hi.arb @@ -964,5 +964,31 @@ "drawerCollectionImages": "इमेजेस", "@drawerCollectionImages": {}, "aboutDataUsageMisc": "विविध", - "@aboutDataUsageMisc": {} + "@aboutDataUsageMisc": {}, + "sortByName": "नाम से", + "@sortByName": {}, + "sortByDate": "दिनांक से", + "@sortByDate": {}, + "videoActionShowPreviousFrame": "पिछला फ्रेम दिखाए", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "अगला फ्रेम दिखाए", + "@videoActionShowNextFrame": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "एल्बम पहले से उपलब्ध हैं", + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "drawerCountryPage": "देश", + "@drawerCountryPage": {}, + "collectionPickPageTitle": "चुने", + "@collectionPickPageTitle": {}, + "drawerCollectionPanoramas": "पैनोरामा", + "@drawerCollectionPanoramas": {}, + "drawerCollectionRaws": "रॉ फ़ोटो", + "@drawerCollectionRaws": {}, + "drawerAlbumPage": "एल्बम", + "@drawerAlbumPage": {}, + "drawerCollectionSphericalVideos": "360⁰ वीडियो", + "@drawerCollectionSphericalVideos": {}, + "drawerPlacePage": "स्थान", + "@drawerPlacePage": {}, + "drawerTagPage": "टैग्स", + "@drawerTagPage": {} } diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index ce3230d0f..546ee4d6d 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -1390,5 +1390,7 @@ "mapStyleOsmLiberty": "OSM Liberty", "@mapStyleOsmLiberty": {}, "mapAttributionOsmLiberty": "Tiles by [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Hosted by [OSM Americana](https://tile.ourmap.us)", - "@mapAttributionOsmLiberty": {} + "@mapAttributionOsmLiberty": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "Альбом уже существует", + "@newAlbumDialogAlbumAlreadyExistsHelper": {} } diff --git a/lib/model/entry/extensions/metadata_edition.dart b/lib/model/entry/extensions/metadata_edition.dart index 6ad3b2dce..5e4228279 100644 --- a/lib/model/entry/extensions/metadata_edition.dart +++ b/lib/model/entry/extensions/metadata_edition.dart @@ -32,7 +32,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { final appliedModifier = await _applyDateModifierToEntry(userModifier); if (appliedModifier == null) { if (isValid && userModifier.action != DateEditAction.copyField) { - await reportService.recordError('failed to get date for modifier=$userModifier, entry=$this', null); + await reportService.recordError('failed to get date for modifier=$userModifier, entry=$this'); } return {}; } @@ -65,7 +65,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { final shiftedDate = date.add(Duration(seconds: appliedModifier.shiftSeconds!)); editCreateDateXmp(descriptions, shiftedDate); } else { - reportService.recordError('failed to parse XMP date=$xmpDate', null); + reportService.recordError('failed to parse XMP date=$xmpDate'); } } case DateEditAction.remove: diff --git a/lib/model/media/video/metadata.dart b/lib/model/media/video/metadata.dart index 3349e202f..972f227ba 100644 --- a/lib/model/media/video/metadata.dart +++ b/lib/model/media/video/metadata.dart @@ -1,12 +1,12 @@ import 'dart:async'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/media/video/channel_layouts.dart'; import 'package:aves/model/media/video/codecs.dart'; import 'package:aves/model/media/video/profiles/aac.dart'; import 'package:aves/model/media/video/profiles/h264.dart'; import 'package:aves/model/media/video/profiles/hevc.dart'; +import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/ref/languages.dart'; import 'package:aves/ref/locales.dart'; import 'package:aves/ref/mime_types.dart'; @@ -99,7 +99,7 @@ class VideoMetadataFormatter { if (isDefined(dateString)) { dateMillis = parseVideoDate(dateString); if (dateMillis == null && !isAmbiguousDate(dateString)) { - await reportService.recordError('getCatalogMetadata failed to parse date=$dateString for mimeType=${entry.mimeType} entry=$entry', null); + await reportService.recordError('getCatalogMetadata failed to parse date=$dateString for mimeType=${entry.mimeType} entry=$entry'); } } diff --git a/lib/model/settings/modules/app.dart b/lib/model/settings/modules/app.dart index 2b9caa141..4c3538be5 100644 --- a/lib/model/settings/modules/app.dart +++ b/lib/model/settings/modules/app.dart @@ -87,9 +87,9 @@ mixin AppSettings on SettingsAccess { set forceWesternArabicNumerals(bool newValue) => set(SettingKeys.forceWesternArabicNumeralsKey, newValue); - int get catalogTimeZoneRawOffsetMillis => getInt(SettingKeys.catalogTimeZoneRawOffsetMillisKey) ?? 0; + int get catalogTimeZoneOffsetMillis => getInt(SettingKeys.catalogTimeZoneOffsetMillisKey) ?? 0; - set catalogTimeZoneRawOffsetMillis(int newValue) => set(SettingKeys.catalogTimeZoneRawOffsetMillisKey, newValue); + set catalogTimeZoneOffsetMillis(int newValue) => set(SettingKeys.catalogTimeZoneOffsetMillisKey, newValue); double getTileExtent(String routeName) => getDouble(SettingKeys.tileExtentPrefixKey + routeName) ?? 0; diff --git a/lib/model/settings/modules/filter_grids.dart b/lib/model/settings/modules/filter_grids.dart index d2a7064f7..fc5a52275 100644 --- a/lib/model/settings/modules/filter_grids.dart +++ b/lib/model/settings/modules/filter_grids.dart @@ -51,7 +51,7 @@ mixin FilterGridsSettings on SettingsAccess { set pinnedFilters(Set newValue) => set(SettingKeys.pinnedFiltersKey, newValue.map((filter) => filter.toJson()).toList()); - bool get showAlbumPickQuery => getBool(SettingKeys.showAlbumPickQueryKey) ?? false; + bool getShowTitleQuery(String routeName) => getBool(SettingKeys.showTitleQueryPrefixKey + routeName) ?? false; - set showAlbumPickQuery(bool newValue) => set(SettingKeys.showAlbumPickQueryKey, newValue); + void setShowTitleQuery(String routeName, bool newValue) => set(SettingKeys.showTitleQueryPrefixKey + routeName, newValue); } diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index 40804ec4f..e1a52c276 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -360,6 +360,12 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings } else { debugPrint('failed to import key=$key, value=$newValue is not a string'); } + } else if (key.startsWith(SettingKeys.showTitleQueryPrefixKey)) { + if (newValue is bool) { + store.setBool(key, newValue); + } else { + debugPrint('failed to import key=$key, value=$newValue is not a bool'); + } } else { switch (key) { case SettingKeys.subtitleTextColorKey: @@ -404,7 +410,6 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings case SettingKeys.stateSortReverseKey: case SettingKeys.placeSortReverseKey: case SettingKeys.tagSortReverseKey: - case SettingKeys.showAlbumPickQueryKey: case SettingKeys.showOverlayOnOpeningKey: case SettingKeys.showOverlayMinimapKey: case SettingKeys.showOverlayInfoKey: diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index c2a85e28f..9f01ef478 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -59,15 +59,13 @@ class MediaStoreSource extends CollectionSource { await vaults.init(); await favourites.init(); await covers.init(); - final currentTimeZoneOffset = await deviceService.getDefaultTimeZoneRawOffsetMillis(); - if (currentTimeZoneOffset != null) { - final catalogTimeZoneOffset = settings.catalogTimeZoneRawOffsetMillis; - if (currentTimeZoneOffset != catalogTimeZoneOffset) { - unawaited(reportService.recordError('Time zone offset change: $currentTimeZoneOffset -> $catalogTimeZoneOffset. Clear catalog metadata to get correct date/times.', null)); - await localMediaDb.clearDates(); - await localMediaDb.clearCatalogMetadata(); - settings.catalogTimeZoneRawOffsetMillis = currentTimeZoneOffset; - } + final currentTimeZoneOffset = DateTime.now().timeZoneOffset.inMilliseconds; + final catalogTimeZoneOffset = settings.catalogTimeZoneOffsetMillis; + if (currentTimeZoneOffset != catalogTimeZoneOffset) { + unawaited(reportService.recordError('Time zone offset change: $currentTimeZoneOffset -> $catalogTimeZoneOffset. Clear catalog metadata to get correct date/times.')); + await localMediaDb.clearDates(); + await localMediaDb.clearCatalogMetadata(); + settings.catalogTimeZoneOffsetMillis = currentTimeZoneOffset; } await loadDates(); debugPrint('$runtimeType load essentials complete in ${stopwatch.elapsed.inMilliseconds}ms'); @@ -214,7 +212,7 @@ class MediaStoreSource extends CollectionSource { // TODO TLAD find duplication cause final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries); if (duplicates.isNotEmpty) { - unawaited(reportService.recordError(Exception('Loading entries yielded duplicates=${duplicates.join(', ')}'), StackTrace.current)); + unawaited(reportService.recordError(Exception('Loading entries yielded duplicates=${duplicates.join(', ')}'))); // post-error cleanup await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet()); for (final duplicate in duplicates) { @@ -327,7 +325,7 @@ class MediaStoreSource extends CollectionSource { // TODO TLAD find duplication cause final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries); if (duplicates.isNotEmpty) { - unawaited(reportService.recordError(Exception('Refreshing entries yielded duplicates=${duplicates.join(', ')}'), StackTrace.current)); + unawaited(reportService.recordError(Exception('Refreshing entries yielded duplicates=${duplicates.join(', ')}'))); // post-error cleanup await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet()); for (final duplicate in duplicates) { diff --git a/lib/model/source/trash.dart b/lib/model/source/trash.dart index f3107cbf5..6afbce6be 100644 --- a/lib/model/source/trash.dart +++ b/lib/model/source/trash.dart @@ -76,7 +76,7 @@ mixin TrashMixin on SourceBase { sourceEntry.trashDetails = _buildTrashDetails(id); newEntries.add(sourceEntry); } else { - await reportService.recordError('Failed to recover untracked bin item at uri=$uri', null); + await reportService.recordError('Failed to recover untracked bin item at uri=$uri'); } } }); diff --git a/lib/model/vaults/vaults.dart b/lib/model/vaults/vaults.dart index a621c2952..7f80c563a 100644 --- a/lib/model/vaults/vaults.dart +++ b/lib/model/vaults/vaults.dart @@ -175,7 +175,7 @@ class Vaults extends ChangeNotifier { sourceEntry.origin = EntryOrigins.vault; newEntries.add(sourceEntry); } else { - await reportService.recordError('Failed to recover untracked vault item at uri=$uri', null); + await reportService.recordError('Failed to recover untracked vault item at uri=$uri'); } }); } diff --git a/lib/services/device_service.dart b/lib/services/device_service.dart index 0eac24328..eeceda1fc 100644 --- a/lib/services/device_service.dart +++ b/lib/services/device_service.dart @@ -8,10 +8,10 @@ abstract class DeviceService { Future> getCapabilities(); - Future getDefaultTimeZoneRawOffsetMillis(); - Future> getLocales(); + Future setLocaleConfig(List locales); + Future getPerformanceClass(); Future isLocked(); @@ -50,16 +50,6 @@ class PlatformDeviceService implements DeviceService { return {}; } - @override - Future getDefaultTimeZoneRawOffsetMillis() async { - try { - return await _platform.invokeMethod('getDefaultTimeZoneRawOffsetMillis'); - } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); - } - return null; - } - @override Future> getLocales() async { try { @@ -80,6 +70,17 @@ class PlatformDeviceService implements DeviceService { return []; } + @override + Future setLocaleConfig(List locales) async { + try { + await _platform.invokeMethod('setLocaleConfig', { + 'locales': locales.map((v) => v.toLanguageTag()).toList(), + }); + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + } + @override Future getPerformanceClass() async { try { diff --git a/lib/services/metadata/metadata_edit_service.dart b/lib/services/metadata/metadata_edit_service.dart index 7317e55e8..611b4cbc1 100644 --- a/lib/services/metadata/metadata_edit_service.dart +++ b/lib/services/metadata/metadata_edit_service.dart @@ -6,10 +6,8 @@ import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:aves_report/aves_report.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:stack_trace/stack_trace.dart'; abstract class MetadataEditService { Future> rotate(AvesEntry entry, {required bool clockwise}); @@ -135,24 +133,22 @@ class PlatformMetadataEditService implements MetadataEditService { } } - StackTrace? _currentStack() => ReportService.buildReportStack(Trace.current(), level: 1); - // distinct exceptions to convince Crashlytics to split reports into distinct issues // The distinct debug statement is there to make the body unique, so that the methods are not merged at compile time. Future mp4LargeMoov(CustomPlatformException e) { debugPrint('mp4LargeMoov $e'); - return reportService.recordError(e, _currentStack()); + return reportService.recordError(e); } Future mp4LargeOther(CustomPlatformException e) { debugPrint('mp4LargeOther $e'); - return reportService.recordError(e, _currentStack()); + return reportService.recordError(e); } Future fileNotFound(CustomPlatformException e) { debugPrint('fileNotFound $e'); - return reportService.recordError(e, _currentStack()); + return reportService.recordError(e); } } diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index e75829d11..39620f813 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -423,13 +423,6 @@ class _AvesAppState extends State with WidgetsBindingObserver { } } - @override - void didHaveMemoryPressure() { - super.didHaveMemoryPressure(); - debugPrint('App memory pressure'); - imageCache.clear(); - } - @override void didChangeMetrics() => _updateCutoutInsets(); @@ -502,6 +495,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { _monitorSettings(); videoControllerFactory.init(); + unawaited(deviceService.setLocaleConfig(AvesApp.supportedLocales)); unawaited(storageService.deleteTempDirectory()); unawaited(_setupErrorReporting()); @@ -692,7 +686,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { _mediaStoreSource.updateDerivedFilters(); } - void _onError(String? error) => reportService.recordError(error, null); + void _onError(String? error) => reportService.recordError(error); void _onAppModeChanged() { final appMode = _appModeNotifier.value; diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index aae93bac0..e2f47293f 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -399,7 +399,7 @@ class _CollectionAppBarState extends State with SingleTickerPr (action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection), ); - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return [ ...quickActionButtons, PopupMenuButton( diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index 874f9004a..7199fd7ad 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -88,7 +88,7 @@ class _CollectionGridState extends State { @override Widget build(BuildContext context) { - final spacing = context.select((s) => s.getTileLayout(settingsRouteKey) == TileLayout.mosaic ? CollectionGrid.mosaicLayoutSpacing : CollectionGrid.fixedExtentLayoutSpacing); + final spacing = context.select((v) => v.getTileLayout(settingsRouteKey) == TileLayout.mosaic ? CollectionGrid.mosaicLayoutSpacing : CollectionGrid.fixedExtentLayoutSpacing); if (_tileExtentController?.spacing != spacing) { _tileExtentController = TileExtentController( settingsRouteKey: settingsRouteKey, @@ -136,7 +136,7 @@ class _CollectionGridContentState extends State<_CollectionGridContent> { Widget build(BuildContext context) { final selectable = context.select, bool>((v) => v.value.canSelectMedia); final settingsRouteKey = context.read().settingsRouteKey; - final tileLayout = context.select((s) => s.getTileLayout(settingsRouteKey)); + final tileLayout = context.select((v) => v.getTileLayout(settingsRouteKey)); return Consumer( builder: (context, collection, child) { final sectionedListLayoutProvider = ValueListenableBuilder( diff --git a/lib/widgets/collection/collection_page.dart b/lib/widgets/collection/collection_page.dart index 777a4f262..e2631b6bd 100644 --- a/lib/widgets/collection/collection_page.dart +++ b/lib/widgets/collection/collection_page.dart @@ -91,6 +91,7 @@ class _CollectionPageState extends State { selector: (context, selection) => selection.selectedItems.isNotEmpty, builder: (context, hasSelection, child) { final body = QueryProvider( + startEnabled: settings.getShowTitleQuery(context.currentRouteName!), initialQuery: liveFilter?.query, child: Builder( builder: (context) { diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index fd9956bc4..93f1c5618 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -181,6 +181,8 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware case EntrySetAction.searchCollection: _goToSearch(context); case EntrySetAction.toggleTitleSearch: + final routeName = context.currentRouteName!; + settings.setShowTitleQuery(routeName, !settings.getShowTitleQuery(routeName)); context.read().toggle(); case EntrySetAction.addShortcut: _addShortcut(context); diff --git a/lib/widgets/common/basic/tv_edge_focus.dart b/lib/widgets/common/basic/tv_edge_focus.dart index 9e624a030..190d047e6 100644 --- a/lib/widgets/common/basic/tv_edge_focus.dart +++ b/lib/widgets/common/basic/tv_edge_focus.dart @@ -14,7 +14,7 @@ class TvEdgeFocus extends StatelessWidget { @override Widget build(BuildContext context) { - final useTvLayout = context.select((s) => s.useTvLayout); + final useTvLayout = context.select((v) => v.useTvLayout); return useTvLayout ? Focus( focusNode: focusNode, diff --git a/lib/widgets/common/behaviour/pop/double_back.dart b/lib/widgets/common/behaviour/pop/double_back.dart index b5a2b3729..d73aa5661 100644 --- a/lib/widgets/common/behaviour/pop/double_back.dart +++ b/lib/widgets/common/behaviour/pop/double_back.dart @@ -20,7 +20,7 @@ class DoubleBackPopHandler extends PopHandler { @override bool canPop(BuildContext context) { - if (context.select((s) => !s.mustBackTwiceToExit)) return true; + if (context.select((v) => !v.mustBackTwiceToExit)) return true; if (Navigator.canPop(context)) return true; return false; } diff --git a/lib/widgets/common/behaviour/pop/tv_navigation.dart b/lib/widgets/common/behaviour/pop/tv_navigation.dart index b895aa917..720a397ae 100644 --- a/lib/widgets/common/behaviour/pop/tv_navigation.dart +++ b/lib/widgets/common/behaviour/pop/tv_navigation.dart @@ -20,7 +20,7 @@ class TvNavigationPopHandler implements PopHandler { @override bool canPop(BuildContext context) { - if (context.select((s) => !s.useTvLayout)) return true; + if (context.select((v) => !v.useTvLayout)) return true; if (_isHome(context)) return true; return false; } diff --git a/lib/widgets/common/identity/aves_app_bar.dart b/lib/widgets/common/identity/aves_app_bar.dart index f66653fc2..9089a2bc8 100644 --- a/lib/widgets/common/identity/aves_app_bar.dart +++ b/lib/widgets/common/identity/aves_app_bar.dart @@ -260,7 +260,7 @@ class _AvesFloatingBarState extends State with RouteAware { return ValueListenableBuilder( valueListenable: _isBlurAllowedNotifier, builder: (context, isBlurAllowed, child) { - final blurred = isBlurAllowed && context.select((s) => s.enableBlurEffect); + final blurred = isBlurAllowed && context.select((v) => v.enableBlurEffect); return Container( foregroundDecoration: BoxDecoration( border: Border.all( diff --git a/lib/widgets/common/providers/query_provider.dart b/lib/widgets/common/providers/query_provider.dart index e811e92ad..8c0506112 100644 --- a/lib/widgets/common/providers/query_provider.dart +++ b/lib/widgets/common/providers/query_provider.dart @@ -3,13 +3,13 @@ import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; class QueryProvider extends StatelessWidget { - final bool enabled; + final bool startEnabled; final String? initialQuery; final Widget child; const QueryProvider({ super.key, - this.enabled = false, + this.startEnabled = false, this.initialQuery, required this.child, }); @@ -18,7 +18,7 @@ class QueryProvider extends StatelessWidget { Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => Query( - enabled: enabled, + enabled: startEnabled, initialValue: initialQuery, ), child: child, diff --git a/lib/widgets/debug/app_debug_page.dart b/lib/widgets/debug/app_debug_page.dart index 004d4193f..0eb919a7a 100644 --- a/lib/widgets/debug/app_debug_page.dart +++ b/lib/widgets/debug/app_debug_page.dart @@ -37,7 +37,7 @@ class AppDebugPage extends StatelessWidget { @override Widget build(BuildContext context) { - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return Directionality( textDirection: TextDirection.ltr, child: AvesScaffold( diff --git a/lib/widgets/debug/settings.dart b/lib/widgets/debug/settings.dart index 262b89a22..b8976059a 100644 --- a/lib/widgets/debug/settings.dart +++ b/lib/widgets/debug/settings.dart @@ -55,7 +55,7 @@ class _DebugSettingsSectionState extends State with Automa padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8), child: InfoRowGroup( info: { - 'catalogTimeZoneRawOffsetMillis': '${settings.catalogTimeZoneRawOffsetMillis}', + 'catalogTimeZoneRawOffsetMillis': '${settings.catalogTimeZoneOffsetMillis}', 'tileExtent - Collection': '${settings.getTileExtent(CollectionPage.routeName)}', 'tileExtent - Albums': '${settings.getTileExtent(AlbumListPage.routeName)}', 'tileExtent - Countries': '${settings.getTileExtent(CountryListPage.routeName)}', diff --git a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart index b3962c5fc..85f1d8151 100644 --- a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart +++ b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart @@ -71,7 +71,7 @@ class _RenameEntrySetPageState extends State { final l10n = context.l10n; final textScaler = MediaQuery.textScalerOf(context); final effectiveThumbnailExtent = max(thumbnailExtent, textScaler.scale(thumbnailExtent)); - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return AvesScaffold( appBar: AppBar( title: Text(l10n.renameEntrySetPageTitle), diff --git a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart index 28e9c7159..c0f7b3bc1 100644 --- a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart +++ b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart @@ -152,7 +152,7 @@ class _TagEditorPageState extends State { builder: (context, value, child) { return IconButton( icon: const Icon(AIcons.add), - onPressed: value.text.isEmpty ? null : () => _addCustomTag(_newTagTextController.text), + onPressed: value.text.trim().isEmpty ? null : () => _addCustomTag(_newTagTextController.text), tooltip: l10n.tagEditorPageAddTagTooltip, ); }, @@ -296,6 +296,7 @@ class _TagEditorPageState extends State { } void _addCustomTag(String newTag) { + newTag = newTag.trim(); if (newTag.isNotEmpty) { _addTag(TagFilter(newTag)); } diff --git a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart index 66fd7a2b4..107383a5d 100644 --- a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart @@ -114,13 +114,13 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { final gridItems = AlbumListPage.getAlbumGridItems(context, source); return SelectionProvider>( child: QueryProvider( - enabled: settings.showAlbumPickQuery, + startEnabled: settings.getShowTitleQuery(context.currentRouteName!), child: FilterGridPage( settingsRouteKey: AlbumListPage.routeName, appBar: FilterGridAppBar( source: source, title: title, - actionDelegate: _AlbumChipSetPickActionDelegate(gridItems), + actionDelegate: AlbumChipSetActionDelegate(gridItems), actionsBuilder: _buildActions, isEmpty: false, appBarHeightNotifier: _appBarHeightNotifier, @@ -211,7 +211,7 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { required bool Function(ChipSetAction action) isVisible, required void Function(ChipSetAction action) onActionSelected, }) { - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return [ if (widget.moveType != null) ..._quickActions.where(isVisible).map( @@ -287,15 +287,3 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { Navigator.maybeOf(context)?.pop(filter); } } - -class _AlbumChipSetPickActionDelegate extends AlbumChipSetActionDelegate { - _AlbumChipSetPickActionDelegate(super.items); - - @override - void onActionSelected(BuildContext context, ChipSetAction action) { - if (action == ChipSetAction.toggleTitleSearch) { - settings.showAlbumPickQuery = !settings.showAlbumPickQuery; - } - super.onActionSelected(context, action); - } -} diff --git a/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart index 49fe4266a..f7abf4b8d 100644 --- a/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart @@ -1,11 +1,13 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/query.dart'; +import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/widgets/collection/collection_grid.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/basic/insets.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/providers/query_provider.dart'; import 'package:aves/widgets/common/providers/selection_provider.dart'; import 'package:collection/collection.dart'; @@ -49,6 +51,7 @@ class _ItemPickPageState extends State { child: AvesScaffold( body: SelectionProvider( child: QueryProvider( + startEnabled: settings.getShowTitleQuery(context.currentRouteName!), initialQuery: liveFilter?.query, child: GestureAreaProtectorStack( child: SafeArea( diff --git a/lib/widgets/explorer/app_bar.dart b/lib/widgets/explorer/app_bar.dart index 5a9985f1e..f4883915a 100644 --- a/lib/widgets/explorer/app_bar.dart +++ b/lib/widgets/explorer/app_bar.dart @@ -104,7 +104,7 @@ class _ExplorerAppBarState extends State with WidgetsBindingObse } List _buildActions(BuildContext context, double maxWidth) { - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return [ IconButton( icon: const Icon(AIcons.search), diff --git a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart index a6a6cb814..b6b5537d7 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart @@ -179,6 +179,8 @@ abstract class ChipSetActionDelegate with FeedbackMi case ChipSetAction.search: _goToSearch(context); case ChipSetAction.toggleTitleSearch: + final routeName = context.currentRouteName!; + settings.setShowTitleQuery(routeName, !settings.getShowTitleQuery(routeName)); context.read().toggle(); case ChipSetAction.createAlbum: case ChipSetAction.createVault: diff --git a/lib/widgets/filter_grids/common/app_bar.dart b/lib/widgets/filter_grids/common/app_bar.dart index d75dd86f6..07bb607f2 100644 --- a/lib/widgets/filter_grids/common/app_bar.dart +++ b/lib/widgets/filter_grids/common/app_bar.dart @@ -329,7 +329,7 @@ class _FilterGridAppBarState _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)), ); - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return [ ...quickActionButtons, PopupMenuButton( diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index 9323ceb05..19769884e 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -286,7 +286,7 @@ class _FilterGridContentState extends State<_FilterG Widget build(BuildContext context) { final source = context.read(); final settingsRouteKey = context.read().settingsRouteKey; - final tileLayout = context.select((s) => s.getTileLayout(settingsRouteKey)); + final tileLayout = context.select((v) => v.getTileLayout(settingsRouteKey)); return Selector( selector: (context, query) => query.enabled, builder: (context, queryEnabled, child) { diff --git a/lib/widgets/filter_grids/common/filter_nav_page.dart b/lib/widgets/filter_grids/common/filter_nav_page.dart index 92463e2d2..4b4f95a09 100644 --- a/lib/widgets/filter_grids/common/filter_nav_page.dart +++ b/lib/widgets/filter_grids/common/filter_nav_page.dart @@ -1,6 +1,8 @@ import 'package:aves/model/filters/filters.dart'; +import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/utils/time_utils.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/providers/query_provider.dart'; import 'package:aves/widgets/common/providers/selection_provider.dart'; @@ -110,6 +112,7 @@ class _FilterNavigationPageState>( child: Builder( builder: (context) => QueryProvider( + startEnabled: settings.getShowTitleQuery(context.currentRouteName!), child: FilterGridPage( appBar: FilterGridAppBar( source: widget.source, diff --git a/lib/widgets/navigation/nav_bar/nav_bar.dart b/lib/widgets/navigation/nav_bar/nav_bar.dart index 7e42b2fbe..636a40036 100644 --- a/lib/widgets/navigation/nav_bar/nav_bar.dart +++ b/lib/widgets/navigation/nav_bar/nav_bar.dart @@ -66,7 +66,7 @@ class _AppBottomNavBarState extends State { @override Widget build(BuildContext context) { - final showVideo = context.select((s) => !s.hiddenFilters.contains(MimeFilter.video)); + final showVideo = context.select((v) => !v.hiddenFilters.contains(MimeFilter.video)); final items = [ const AvesBottomNavItem(route: CollectionPage.routeName), @@ -91,6 +91,7 @@ class _AppBottomNavBarState extends State { .toList(), onTap: (index) => _goTo(context, items, index), currentIndex: _getCurrentIndex(context, items), + elevation: 0, type: BottomNavigationBarType.fixed, backgroundColor: backgroundColor, showSelectedLabels: false, diff --git a/lib/widgets/settings/privacy/file_picker/file_picker_page.dart b/lib/widgets/settings/privacy/file_picker/file_picker_page.dart index c9fa121e7..59a7b56cc 100644 --- a/lib/widgets/settings/privacy/file_picker/file_picker_page.dart +++ b/lib/widgets/settings/privacy/file_picker/file_picker_page.dart @@ -59,7 +59,7 @@ class _FilePickerPageState extends State { return !isHidden; } }).toList(); - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return PopScope( canPop: _directory.relativeDir.isEmpty, onPopInvokedWithResult: (didPop, result) { diff --git a/lib/widgets/settings/settings_mobile_page.dart b/lib/widgets/settings/settings_mobile_page.dart index ffb754546..664e1cfc4 100644 --- a/lib/widgets/settings/settings_mobile_page.dart +++ b/lib/widgets/settings/settings_mobile_page.dart @@ -46,7 +46,7 @@ class _SettingsMobilePageState extends State with FeedbackMi @override Widget build(BuildContext context) { - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return AvesScaffold( appBar: AppBar( title: InteractiveAppBarTitle( diff --git a/lib/widgets/settings/thumbnails/overlay.dart b/lib/widgets/settings/thumbnails/overlay.dart index 6a19793c1..108facaf2 100644 --- a/lib/widgets/settings/thumbnails/overlay.dart +++ b/lib/widgets/settings/thumbnails/overlay.dart @@ -143,7 +143,7 @@ class SettingsTileThumbnailLocationIcon extends SettingsTile { ); Widget _buildTrailing(BuildContext context) { - final iconType = context.select((s) => s.thumbnailLocationIcon); + final iconType = context.select((v) => v.thumbnailLocationIcon); return ThumbnailOverlayPage.buildTrailingIcon( context: context, key: iconType, @@ -168,7 +168,7 @@ class SettingsTileThumbnailTagIcon extends SettingsTile { ); Widget _buildTrailing(BuildContext context) { - final iconType = context.select((s) => s.thumbnailTagIcon); + final iconType = context.select((v) => v.thumbnailTagIcon); return ThumbnailOverlayPage.buildTrailingIcon( context: context, key: iconType, diff --git a/lib/widgets/viewer/action/single_entry_editor.dart b/lib/widgets/viewer/action/single_entry_editor.dart index 85acb841f..46adba957 100644 --- a/lib/widgets/viewer/action/single_entry_editor.dart +++ b/lib/widgets/viewer/action/single_entry_editor.dart @@ -61,8 +61,8 @@ mixin SingleEntryEditorMixin on FeedbackMixin, PermissionAwareMixin { } else { showFeedback(context, FeedbackType.warn, l10n.genericFailureFeedback); } - } catch (error, stack) { - await reportService.recordError(error, stack); + } catch (e, stack) { + await reportService.recordError(e, stack); } source?.resumeMonitoring(); } diff --git a/lib/widgets/viewer/controls/cast.dart b/lib/widgets/viewer/controls/cast.dart index 731712a59..377f8714d 100644 --- a/lib/widgets/viewer/controls/cast.dart +++ b/lib/widgets/viewer/controls/cast.dart @@ -96,8 +96,8 @@ mixin CastMixin { ); debugPrint('cast: play entry=$entry'); unawaited(renderer.play()); - } catch (error, stack) { - await reportService.recordError(error, stack); + } catch (e, stack) { + await reportService.recordError(e, stack); } } diff --git a/lib/widgets/viewer/info/info_app_bar.dart b/lib/widgets/viewer/info/info_app_bar.dart index 866b8f4cf..24f111962 100644 --- a/lib/widgets/viewer/info/info_app_bar.dart +++ b/lib/widgets/viewer/info/info_app_bar.dart @@ -50,7 +50,7 @@ class InfoAppBar extends StatelessWidget { final commonActions = EntryActions.commonMetadataActions.where(isVisible); final formatSpecificActions = EntryActions.formatSpecificMetadataActions.where(isVisible); final useTvLayout = settings.useTvLayout; - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return SliverAppBar( leading: useTvLayout ? null diff --git a/lib/widgets/viewer/overlay/viewer_buttons.dart b/lib/widgets/viewer/overlay/viewer_buttons.dart index 32e613aa3..4591d8ae4 100644 --- a/lib/widgets/viewer/overlay/viewer_buttons.dart +++ b/lib/widgets/viewer/overlay/viewer_buttons.dart @@ -252,7 +252,7 @@ class _ViewerButtonRowContentState extends State { final exportActions = widget.exportActions; final videoActions = widget.videoActions; final hasOverflowMenu = pageEntry.canRotate || pageEntry.canFlip || topLevelActions.isNotEmpty || exportActions.isNotEmpty || videoActions.isNotEmpty; - final animations = context.select((s) => s.accessibilityAnimations); + final animations = context.select((v) => v.accessibilityAnimations); return Selector( selector: (context, vc) => vc.getController(pageEntry), builder: (context, videoController, child) { diff --git a/plugins/aves_model/lib/src/settings/keys.dart b/plugins/aves_model/lib/src/settings/keys.dart index 19be577ed..0f44d3b58 100644 --- a/plugins/aves_model/lib/src/settings/keys.dart +++ b/plugins/aves_model/lib/src/settings/keys.dart @@ -3,7 +3,7 @@ class SettingKeys { static const Set _internalKeys = { hasAcceptedTermsKey, - catalogTimeZoneRawOffsetMillisKey, + catalogTimeZoneOffsetMillisKey, searchHistoryKey, platformAccelerometerRotationKey, platformTransitionAnimationScaleKey, @@ -21,7 +21,7 @@ class SettingKeys { static const isErrorReportingAllowedKey = 'is_crashlytics_enabled'; static const localeKey = 'locale'; static const forceWesternArabicNumeralsKey = 'force_western_arabic_numerals'; - static const catalogTimeZoneRawOffsetMillisKey = 'catalog_time_zone_raw_offset_millis'; + static const catalogTimeZoneOffsetMillisKey = 'catalog_time_zone_raw_offset_millis'; static const tileExtentPrefixKey = 'tile_extent_'; static const tileLayoutPrefixKey = 'tile_layout_'; static const entryRenamingPatternKey = 'entry_renaming_pattern'; @@ -86,7 +86,7 @@ class SettingKeys { static const pinnedFiltersKey = 'pinned_filters'; static const hiddenFiltersKey = 'hidden_filters'; static const deactivatedHiddenFiltersKey = 'deactivated_hidden_filters'; - static const showAlbumPickQueryKey = 'show_album_pick_query'; + static const showTitleQueryPrefixKey = 'show_title_query_'; // viewer static const viewerQuickActionsKey = 'viewer_quick_actions'; diff --git a/plugins/aves_report/lib/aves_report.dart b/plugins/aves_report/lib/aves_report.dart index 4d3a3fc5c..609252260 100644 --- a/plugins/aves_report/lib/aves_report.dart +++ b/plugins/aves_report/lib/aves_report.dart @@ -15,7 +15,7 @@ abstract class ReportService { Future setCustomKeys(Map map); - Future recordError(dynamic exception, StackTrace? stack); + Future recordError(dynamic exception, [StackTrace? stack]); Future recordFlutterError(FlutterErrorDetails flutterErrorDetails); diff --git a/plugins/aves_report_console/lib/aves_report_platform.dart b/plugins/aves_report_console/lib/aves_report_platform.dart index e57e70f85..dd1d461ea 100644 --- a/plugins/aves_report_console/lib/aves_report_platform.dart +++ b/plugins/aves_report_console/lib/aves_report_platform.dart @@ -9,7 +9,7 @@ class PlatformReportService extends ReportService { Future log(String message) async => debugPrint('Report log with message=$message'); @override - Future recordError(dynamic exception, StackTrace? stack) async => debugPrint('Report error with exception=$exception, stack=$stack'); + Future recordError(dynamic exception, [StackTrace? stack]) async => debugPrint('Report error with exception=$exception, stack=$stack'); @override Future recordFlutterError(FlutterErrorDetails flutterErrorDetails) async => debugPrint('Report Flutter error with details=$flutterErrorDetails'); diff --git a/plugins/aves_report_crashlytics/lib/aves_report_platform.dart b/plugins/aves_report_crashlytics/lib/aves_report_platform.dart index 1bcd7f567..05a7efbf6 100644 --- a/plugins/aves_report_crashlytics/lib/aves_report_platform.dart +++ b/plugins/aves_report_crashlytics/lib/aves_report_platform.dart @@ -5,6 +5,7 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:stack_trace/stack_trace.dart'; class PlatformReportService extends ReportService { FirebaseCrashlytics? get _instance { @@ -65,11 +66,12 @@ class PlatformReportService extends ReportService { } @override - Future recordError(dynamic exception, StackTrace? stack) async { + Future recordError(dynamic exception, [StackTrace? stack]) async { if (exception is PlatformException && stack != null) { stack = ReportService.buildReportStack(stack, level: 2); } if (exception is! UnreportedStateError) { + stack ??= ReportService.buildReportStack(Trace.current(), level: 1); return _instance?.recordError(exception, stack); } } diff --git a/plugins/aves_report_crashlytics/pubspec.lock b/plugins/aves_report_crashlytics/pubspec.lock index da24c1b41..91fcf4ed0 100644 --- a/plugins/aves_report_crashlytics/pubspec.lock +++ b/plugins/aves_report_crashlytics/pubspec.lock @@ -213,7 +213,7 @@ packages: source: hosted version: "1.10.0" stack_trace: - dependency: transitive + dependency: "direct main" description: name: stack_trace sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" diff --git a/plugins/aves_report_crashlytics/pubspec.yaml b/plugins/aves_report_crashlytics/pubspec.yaml index c94c91d59..5c04ca756 100644 --- a/plugins/aves_report_crashlytics/pubspec.yaml +++ b/plugins/aves_report_crashlytics/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: # so that the transitive `path` gets upgraded to v1.8.3 firebase_core: ">=2.10.0" firebase_crashlytics: + stack_trace: dev_dependencies: flutter_lints: diff --git a/pubspec.yaml b/pubspec.yaml index c19cd905d..2b40001d1 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.11.18+137 +version: 1.11.19+138 publish_to: none environment: diff --git a/test/fake/device_service.dart b/test/fake/device_service.dart index 82f81f5c3..07705e24d 100644 --- a/test/fake/device_service.dart +++ b/test/fake/device_service.dart @@ -3,9 +3,6 @@ import 'package:flutter/foundation.dart'; import 'package:test/fake.dart'; class FakeDeviceService extends Fake implements DeviceService { - @override - Future getDefaultTimeZoneRawOffsetMillis() => SynchronousFuture(3600000); - @override Future getAvailableHeapSize() => SynchronousFuture(0x7fffffff); diff --git a/test/fake/report_service.dart b/test/fake/report_service.dart index 898c9462c..a7a8f99b2 100644 --- a/test/fake/report_service.dart +++ b/test/fake/report_service.dart @@ -21,7 +21,7 @@ class FakeReportService extends ReportService { Future setCustomKeys(Map map) => SynchronousFuture(null); @override - Future recordError(dynamic exception, StackTrace? stack) => SynchronousFuture(null); + Future recordError(dynamic exception, [StackTrace? stack]) => SynchronousFuture(null); @override Future recordFlutterError(FlutterErrorDetails flutterErrorDetails) => SynchronousFuture(null); diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US index 444e6cae9..e405b5830 100644 --- a/whatsnew/whatsnew-en-US +++ b/whatsnew/whatsnew-en-US @@ -1,4 +1,4 @@ -In v1.11.18: +In v1.11.19: - peruse your videos frame by frame - create map shortcuts to filtered collections - enjoy the app in Shavian