Merge branch 'develop'
This commit is contained in:
commit
1d7deac84d
62 changed files with 357 additions and 124 deletions
4
.github/workflows/dependency-review.yml
vendored
4
.github/workflows/dependency-review.yml
vendored
|
@ -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
|
||||
|
|
8
.github/workflows/quality-check.yml
vendored
8
.github/workflows/quality-check.yml
vendored
|
@ -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}}"
|
||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -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
|
||||
|
||||
|
|
4
.github/workflows/scorecards.yml
vendored
4
.github/workflows/scorecards.yml
vendored
|
@ -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
|
||||
|
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
## <a id="v1.11.19"></a>[v1.11.19] - 2024-11-24
|
||||
|
||||
### Added
|
||||
|
||||
- integrate with OS app language settings on Android >=14
|
||||
|
||||
### Changed
|
||||
|
||||
- remember title filter visibility by page
|
||||
|
||||
## <a id="v1.11.18"></a>[v1.11.18] - 2024-11-18
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -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<List<String>>("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
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
<string name="analysis_channel_name">मीडिया जाँचे</string>
|
||||
<string name="app_name">ऐवीज</string>
|
||||
<string name="videos_shortcut_short_label">वीडियो</string>
|
||||
<string name="map_shortcut_short_label">मैप</string>
|
||||
</resources>
|
5
fastlane/metadata/android/en-US/changelogs/138.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/138.txt
Normal file
|
@ -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
|
5
fastlane/metadata/android/en-US/changelogs/13801.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/13801.txt
Normal file
|
@ -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
|
|
@ -1589,5 +1589,7 @@
|
|||
"videoActionShowPreviousFrame": "𐑖𐑴 𐑐𐑮𐑰𐑝𐑾𐑕 𐑓𐑮𐑱𐑥",
|
||||
"@videoActionShowPreviousFrame": {},
|
||||
"videoActionShowNextFrame": "𐑖𐑴 𐑯𐑧𐑒𐑕𐑑 𐑓𐑮𐑱𐑥",
|
||||
"@videoActionShowNextFrame": {}
|
||||
"@videoActionShowNextFrame": {},
|
||||
"newAlbumDialogAlbumAlreadyExistsHelper": "𐑨𐑤𐑚𐑩𐑥 𐑷𐑤𐑮𐑧𐑛𐑦 𐑦𐑜𐑟𐑦𐑕𐑑𐑕",
|
||||
"@newAlbumDialogAlbumAlreadyExistsHelper": {}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ mixin FilterGridsSettings on SettingsAccess {
|
|||
|
||||
set pinnedFilters(Set<CollectionFilter> 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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ abstract class DeviceService {
|
|||
|
||||
Future<Map<String, dynamic>> getCapabilities();
|
||||
|
||||
Future<int?> getDefaultTimeZoneRawOffsetMillis();
|
||||
|
||||
Future<List<Locale>> getLocales();
|
||||
|
||||
Future<void> setLocaleConfig(List<Locale> locales);
|
||||
|
||||
Future<int> getPerformanceClass();
|
||||
|
||||
Future<bool> isLocked();
|
||||
|
@ -50,16 +50,6 @@ class PlatformDeviceService implements DeviceService {
|
|||
return {};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int?> getDefaultTimeZoneRawOffsetMillis() async {
|
||||
try {
|
||||
return await _platform.invokeMethod('getDefaultTimeZoneRawOffsetMillis');
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Locale>> getLocales() async {
|
||||
try {
|
||||
|
@ -80,6 +70,17 @@ class PlatformDeviceService implements DeviceService {
|
|||
return [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setLocaleConfig(List<Locale> locales) async {
|
||||
try {
|
||||
await _platform.invokeMethod('setLocaleConfig', <String, dynamic>{
|
||||
'locales': locales.map((v) => v.toLanguageTag()).toList(),
|
||||
});
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getPerformanceClass() async {
|
||||
try {
|
||||
|
|
|
@ -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<Map<String, dynamic>> 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<void> mp4LargeMoov(CustomPlatformException e) {
|
||||
debugPrint('mp4LargeMoov $e');
|
||||
return reportService.recordError(e, _currentStack());
|
||||
return reportService.recordError(e);
|
||||
}
|
||||
|
||||
Future<void> mp4LargeOther(CustomPlatformException e) {
|
||||
debugPrint('mp4LargeOther $e');
|
||||
return reportService.recordError(e, _currentStack());
|
||||
return reportService.recordError(e);
|
||||
}
|
||||
|
||||
Future<void> fileNotFound(CustomPlatformException e) {
|
||||
debugPrint('fileNotFound $e');
|
||||
return reportService.recordError(e, _currentStack());
|
||||
return reportService.recordError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -423,13 +423,6 @@ class _AvesAppState extends State<AvesApp> 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<AvesApp> with WidgetsBindingObserver {
|
|||
_monitorSettings();
|
||||
videoControllerFactory.init();
|
||||
|
||||
unawaited(deviceService.setLocaleConfig(AvesApp.supportedLocales));
|
||||
unawaited(storageService.deleteTempDirectory());
|
||||
unawaited(_setupErrorReporting());
|
||||
|
||||
|
@ -692,7 +686,7 @@ class _AvesAppState extends State<AvesApp> 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;
|
||||
|
|
|
@ -399,7 +399,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
(action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection),
|
||||
);
|
||||
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return [
|
||||
...quickActionButtons,
|
||||
PopupMenuButton<EntrySetAction>(
|
||||
|
|
|
@ -88,7 +88,7 @@ class _CollectionGridState extends State<CollectionGrid> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final spacing = context.select<Settings, double>((s) => s.getTileLayout(settingsRouteKey) == TileLayout.mosaic ? CollectionGrid.mosaicLayoutSpacing : CollectionGrid.fixedExtentLayoutSpacing);
|
||||
final spacing = context.select<Settings, double>((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<ValueNotifier<AppMode>, bool>((v) => v.value.canSelectMedia);
|
||||
final settingsRouteKey = context.read<TileExtentController>().settingsRouteKey;
|
||||
final tileLayout = context.select<Settings, TileLayout>((s) => s.getTileLayout(settingsRouteKey));
|
||||
final tileLayout = context.select<Settings, TileLayout>((v) => v.getTileLayout(settingsRouteKey));
|
||||
return Consumer<CollectionLens>(
|
||||
builder: (context, collection, child) {
|
||||
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||
|
|
|
@ -91,6 +91,7 @@ class _CollectionPageState extends State<CollectionPage> {
|
|||
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) {
|
||||
|
|
|
@ -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<Query>().toggle();
|
||||
case EntrySetAction.addShortcut:
|
||||
_addShortcut(context);
|
||||
|
|
|
@ -14,7 +14,7 @@ class TvEdgeFocus extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final useTvLayout = context.select<Settings, bool>((s) => s.useTvLayout);
|
||||
final useTvLayout = context.select<Settings, bool>((v) => v.useTvLayout);
|
||||
return useTvLayout
|
||||
? Focus(
|
||||
focusNode: focusNode,
|
||||
|
|
|
@ -20,7 +20,7 @@ class DoubleBackPopHandler extends PopHandler {
|
|||
|
||||
@override
|
||||
bool canPop(BuildContext context) {
|
||||
if (context.select<Settings, bool>((s) => !s.mustBackTwiceToExit)) return true;
|
||||
if (context.select<Settings, bool>((v) => !v.mustBackTwiceToExit)) return true;
|
||||
if (Navigator.canPop(context)) return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class TvNavigationPopHandler implements PopHandler {
|
|||
|
||||
@override
|
||||
bool canPop(BuildContext context) {
|
||||
if (context.select<Settings, bool>((s) => !s.useTvLayout)) return true;
|
||||
if (context.select<Settings, bool>((v) => !v.useTvLayout)) return true;
|
||||
if (_isHome(context)) return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -260,7 +260,7 @@ class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
|
|||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: _isBlurAllowedNotifier,
|
||||
builder: (context, isBlurAllowed, child) {
|
||||
final blurred = isBlurAllowed && context.select<Settings, bool>((s) => s.enableBlurEffect);
|
||||
final blurred = isBlurAllowed && context.select<Settings, bool>((v) => v.enableBlurEffect);
|
||||
return Container(
|
||||
foregroundDecoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
|
|
|
@ -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<Query>(
|
||||
create: (context) => Query(
|
||||
enabled: enabled,
|
||||
enabled: startEnabled,
|
||||
initialValue: initialQuery,
|
||||
),
|
||||
child: child,
|
||||
|
|
|
@ -37,7 +37,7 @@ class AppDebugPage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: AvesScaffold(
|
||||
|
|
|
@ -55,7 +55,7 @@ class _DebugSettingsSectionState extends State<DebugSettingsSection> 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)}',
|
||||
|
|
|
@ -71,7 +71,7 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
|
|||
final l10n = context.l10n;
|
||||
final textScaler = MediaQuery.textScalerOf(context);
|
||||
final effectiveThumbnailExtent = max(thumbnailExtent, textScaler.scale(thumbnailExtent));
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return AvesScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.renameEntrySetPageTitle),
|
||||
|
|
|
@ -152,7 +152,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
|||
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<TagEditorPage> {
|
|||
}
|
||||
|
||||
void _addCustomTag(String newTag) {
|
||||
newTag = newTag.trim();
|
||||
if (newTag.isNotEmpty) {
|
||||
_addTag(TagFilter(newTag));
|
||||
}
|
||||
|
|
|
@ -114,13 +114,13 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
|||
final gridItems = AlbumListPage.getAlbumGridItems(context, source);
|
||||
return SelectionProvider<FilterGridItem<AlbumFilter>>(
|
||||
child: QueryProvider(
|
||||
enabled: settings.showAlbumPickQuery,
|
||||
startEnabled: settings.getShowTitleQuery(context.currentRouteName!),
|
||||
child: FilterGridPage<AlbumFilter>(
|
||||
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<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((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<AlbumFilter>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ItemPickPage> {
|
|||
child: AvesScaffold(
|
||||
body: SelectionProvider<AvesEntry>(
|
||||
child: QueryProvider(
|
||||
startEnabled: settings.getShowTitleQuery(context.currentRouteName!),
|
||||
initialQuery: liveFilter?.query,
|
||||
child: GestureAreaProtectorStack(
|
||||
child: SafeArea(
|
||||
|
|
|
@ -104,7 +104,7 @@ class _ExplorerAppBarState extends State<ExplorerAppBar> with WidgetsBindingObse
|
|||
}
|
||||
|
||||
List<Widget> _buildActions(BuildContext context, double maxWidth) {
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return [
|
||||
IconButton(
|
||||
icon: const Icon(AIcons.search),
|
||||
|
|
|
@ -179,6 +179,8 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
|||
case ChipSetAction.search:
|
||||
_goToSearch(context);
|
||||
case ChipSetAction.toggleTitleSearch:
|
||||
final routeName = context.currentRouteName!;
|
||||
settings.setShowTitleQuery(routeName, !settings.getShowTitleQuery(routeName));
|
||||
context.read<Query>().toggle();
|
||||
case ChipSetAction.createAlbum:
|
||||
case ChipSetAction.createVault:
|
||||
|
|
|
@ -329,7 +329,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
|||
(action) => _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)),
|
||||
);
|
||||
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return [
|
||||
...quickActionButtons,
|
||||
PopupMenuButton<ChipSetAction>(
|
||||
|
|
|
@ -286,7 +286,7 @@ class _FilterGridContentState<T extends CollectionFilter> extends State<_FilterG
|
|||
Widget build(BuildContext context) {
|
||||
final source = context.read<CollectionSource>();
|
||||
final settingsRouteKey = context.read<TileExtentController>().settingsRouteKey;
|
||||
final tileLayout = context.select<Settings, TileLayout>((s) => s.getTileLayout(settingsRouteKey));
|
||||
final tileLayout = context.select<Settings, TileLayout>((v) => v.getTileLayout(settingsRouteKey));
|
||||
return Selector<Query, bool>(
|
||||
selector: (context, query) => query.enabled,
|
||||
builder: (context, queryEnabled, child) {
|
||||
|
|
|
@ -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<T extends CollectionFilter, CSAD extends ChipSe
|
|||
return SelectionProvider<FilterGridItem<T>>(
|
||||
child: Builder(
|
||||
builder: (context) => QueryProvider(
|
||||
startEnabled: settings.getShowTitleQuery(context.currentRouteName!),
|
||||
child: FilterGridPage<T>(
|
||||
appBar: FilterGridAppBar<T, CSAD>(
|
||||
source: widget.source,
|
||||
|
|
|
@ -66,7 +66,7 @@ class _AppBottomNavBarState extends State<AppBottomNavBar> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final showVideo = context.select<Settings, bool>((s) => !s.hiddenFilters.contains(MimeFilter.video));
|
||||
final showVideo = context.select<Settings, bool>((v) => !v.hiddenFilters.contains(MimeFilter.video));
|
||||
|
||||
final items = [
|
||||
const AvesBottomNavItem(route: CollectionPage.routeName),
|
||||
|
@ -91,6 +91,7 @@ class _AppBottomNavBarState extends State<AppBottomNavBar> {
|
|||
.toList(),
|
||||
onTap: (index) => _goTo(context, items, index),
|
||||
currentIndex: _getCurrentIndex(context, items),
|
||||
elevation: 0,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: backgroundColor,
|
||||
showSelectedLabels: false,
|
||||
|
|
|
@ -59,7 +59,7 @@ class _FilePickerPageState extends State<FilePickerPage> {
|
|||
return !isHidden;
|
||||
}
|
||||
}).toList();
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return PopScope(
|
||||
canPop: _directory.relativeDir.isEmpty,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
|
|
|
@ -46,7 +46,7 @@ class _SettingsMobilePageState extends State<SettingsMobilePage> with FeedbackMi
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return AvesScaffold(
|
||||
appBar: AppBar(
|
||||
title: InteractiveAppBarTitle(
|
||||
|
|
|
@ -143,7 +143,7 @@ class SettingsTileThumbnailLocationIcon extends SettingsTile {
|
|||
);
|
||||
|
||||
Widget _buildTrailing(BuildContext context) {
|
||||
final iconType = context.select<Settings, ThumbnailOverlayLocationIcon>((s) => s.thumbnailLocationIcon);
|
||||
final iconType = context.select<Settings, ThumbnailOverlayLocationIcon>((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<Settings, ThumbnailOverlayTagIcon>((s) => s.thumbnailTagIcon);
|
||||
final iconType = context.select<Settings, ThumbnailOverlayTagIcon>((v) => v.thumbnailTagIcon);
|
||||
return ThumbnailOverlayPage.buildTrailingIcon(
|
||||
context: context,
|
||||
key: iconType,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return SliverAppBar(
|
||||
leading: useTvLayout
|
||||
? null
|
||||
|
|
|
@ -252,7 +252,7 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
|||
final exportActions = widget.exportActions;
|
||||
final videoActions = widget.videoActions;
|
||||
final hasOverflowMenu = pageEntry.canRotate || pageEntry.canFlip || topLevelActions.isNotEmpty || exportActions.isNotEmpty || videoActions.isNotEmpty;
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||
return Selector<VideoConductor, AvesVideoController?>(
|
||||
selector: (context, vc) => vc.getController(pageEntry),
|
||||
builder: (context, videoController, child) {
|
||||
|
|
|
@ -3,7 +3,7 @@ class SettingKeys {
|
|||
|
||||
static const Set<String> _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';
|
||||
|
|
|
@ -15,7 +15,7 @@ abstract class ReportService {
|
|||
|
||||
Future<void> setCustomKeys(Map<String, Object> map);
|
||||
|
||||
Future<void> recordError(dynamic exception, StackTrace? stack);
|
||||
Future<void> recordError(dynamic exception, [StackTrace? stack]);
|
||||
|
||||
Future<void> recordFlutterError(FlutterErrorDetails flutterErrorDetails);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ class PlatformReportService extends ReportService {
|
|||
Future<void> log(String message) async => debugPrint('Report log with message=$message');
|
||||
|
||||
@override
|
||||
Future<void> recordError(dynamic exception, StackTrace? stack) async => debugPrint('Report error with exception=$exception, stack=$stack');
|
||||
Future<void> recordError(dynamic exception, [StackTrace? stack]) async => debugPrint('Report error with exception=$exception, stack=$stack');
|
||||
|
||||
@override
|
||||
Future<void> recordFlutterError(FlutterErrorDetails flutterErrorDetails) async => debugPrint('Report Flutter error with details=$flutterErrorDetails');
|
||||
|
|
|
@ -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<void> recordError(dynamic exception, StackTrace? stack) async {
|
||||
Future<void> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ packages:
|
|||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -3,9 +3,6 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:test/fake.dart';
|
||||
|
||||
class FakeDeviceService extends Fake implements DeviceService {
|
||||
@override
|
||||
Future<int> getDefaultTimeZoneRawOffsetMillis() => SynchronousFuture(3600000);
|
||||
|
||||
@override
|
||||
Future<int> getAvailableHeapSize() => SynchronousFuture(0x7fffffff);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class FakeReportService extends ReportService {
|
|||
Future<void> setCustomKeys(Map<String, Object> map) => SynchronousFuture(null);
|
||||
|
||||
@override
|
||||
Future<void> recordError(dynamic exception, StackTrace? stack) => SynchronousFuture(null);
|
||||
Future<void> recordError(dynamic exception, [StackTrace? stack]) => SynchronousFuture(null);
|
||||
|
||||
@override
|
||||
Future<void> recordFlutterError(FlutterErrorDetails flutterErrorDetails) => SynchronousFuture(null);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue