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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: 'Checkout Repository'
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: 'Dependency Review'
|
- 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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ jobs:
|
||||||
build-mode: manual
|
build-mode: manual
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ jobs:
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
|
uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
build-mode: ${{ matrix.build-mode }}
|
build-mode: ${{ matrix.build-mode }}
|
||||||
|
@ -83,6 +83,6 @@ jobs:
|
||||||
./flutterw build apk --profile -t lib/main_play.dart --flavor play
|
./flutterw build apk --profile -t lib/main_play.dart --flavor play
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
|
uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|
4
.github/workflows/scorecards.yml
vendored
4
.github/workflows/scorecards.yml
vendored
|
@ -31,7 +31,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
@ -71,6 +71,6 @@ jobs:
|
||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- 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:
|
with:
|
||||||
sarif_file: results.sarif
|
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="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
|
## <a id="v1.11.18"></a>[v1.11.18] - 2024-11-18
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package deckers.thibault.aves.channel.calls
|
package deckers.thibault.aves.channel.calls
|
||||||
|
|
||||||
|
import android.app.LocaleConfig
|
||||||
|
import android.app.LocaleManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.location.Geocoder
|
import android.location.Geocoder
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.LocaleList
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
|
@ -30,8 +33,8 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"canManageMedia" -> safe(call, result, ::canManageMedia)
|
"canManageMedia" -> safe(call, result, ::canManageMedia)
|
||||||
"getCapabilities" -> defaultScope.launch { safe(call, result, ::getCapabilities) }
|
"getCapabilities" -> defaultScope.launch { safe(call, result, ::getCapabilities) }
|
||||||
"getDefaultTimeZoneRawOffsetMillis" -> safe(call, result, ::getDefaultTimeZoneRawOffsetMillis)
|
|
||||||
"getLocales" -> safe(call, result, ::getLocales)
|
"getLocales" -> safe(call, result, ::getLocales)
|
||||||
|
"setLocaleConfig" -> safe(call, result, ::setLocaleConfig)
|
||||||
"getPerformanceClass" -> safe(call, result, ::getPerformanceClass)
|
"getPerformanceClass" -> safe(call, result, ::getPerformanceClass)
|
||||||
"isLocked" -> safe(call, result, ::isLocked)
|
"isLocked" -> safe(call, result, ::isLocked)
|
||||||
"isSystemFilePickerEnabled" -> safe(call, result, ::isSystemFilePickerEnabled)
|
"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) {
|
private fun getLocales(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||||
fun toMap(locale: Locale): FieldMap = hashMapOf(
|
fun toMap(locale: Locale): FieldMap = hashMapOf(
|
||||||
"language" to locale.language,
|
"language" to locale.language,
|
||||||
|
@ -88,6 +87,21 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
||||||
result.success(locales)
|
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) {
|
private fun getPerformanceClass(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
val performanceClass = Build.VERSION.MEDIA_PERFORMANCE_CLASS
|
val performanceClass = Build.VERSION.MEDIA_PERFORMANCE_CLASS
|
||||||
|
|
|
@ -8,4 +8,5 @@
|
||||||
<string name="analysis_channel_name">मीडिया जाँचे</string>
|
<string name="analysis_channel_name">मीडिया जाँचे</string>
|
||||||
<string name="app_name">ऐवीज</string>
|
<string name="app_name">ऐवीज</string>
|
||||||
<string name="videos_shortcut_short_label">वीडियो</string>
|
<string name="videos_shortcut_short_label">वीडियो</string>
|
||||||
|
<string name="map_shortcut_short_label">मैप</string>
|
||||||
</resources>
|
</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": "𐑖𐑴 𐑐𐑮𐑰𐑝𐑾𐑕 𐑓𐑮𐑱𐑥",
|
||||||
"@videoActionShowPreviousFrame": {},
|
"@videoActionShowPreviousFrame": {},
|
||||||
"videoActionShowNextFrame": "𐑖𐑴 𐑯𐑧𐑒𐑕𐑑 𐑓𐑮𐑱𐑥",
|
"videoActionShowNextFrame": "𐑖𐑴 𐑯𐑧𐑒𐑕𐑑 𐑓𐑮𐑱𐑥",
|
||||||
"@videoActionShowNextFrame": {}
|
"@videoActionShowNextFrame": {},
|
||||||
|
"newAlbumDialogAlbumAlreadyExistsHelper": "𐑨𐑤𐑚𐑩𐑥 𐑷𐑤𐑮𐑧𐑛𐑦 𐑦𐑜𐑟𐑦𐑕𐑑𐑕",
|
||||||
|
"@newAlbumDialogAlbumAlreadyExistsHelper": {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -464,5 +464,178 @@
|
||||||
"videoLoopModeAlways": "Alati",
|
"videoLoopModeAlways": "Alati",
|
||||||
"@videoLoopModeAlways": {},
|
"@videoLoopModeAlways": {},
|
||||||
"unitSystemMetric": "Meetermõõdustik",
|
"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": "इमेजेस",
|
||||||
"@drawerCollectionImages": {},
|
"@drawerCollectionImages": {},
|
||||||
"aboutDataUsageMisc": "विविध",
|
"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": "OSM Liberty",
|
||||||
"@mapStyleOsmLiberty": {},
|
"@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": "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);
|
final appliedModifier = await _applyDateModifierToEntry(userModifier);
|
||||||
if (appliedModifier == null) {
|
if (appliedModifier == null) {
|
||||||
if (isValid && userModifier.action != DateEditAction.copyField) {
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
|
||||||
final shiftedDate = date.add(Duration(seconds: appliedModifier.shiftSeconds!));
|
final shiftedDate = date.add(Duration(seconds: appliedModifier.shiftSeconds!));
|
||||||
editCreateDateXmp(descriptions, shiftedDate);
|
editCreateDateXmp(descriptions, shiftedDate);
|
||||||
} else {
|
} else {
|
||||||
reportService.recordError('failed to parse XMP date=$xmpDate', null);
|
reportService.recordError('failed to parse XMP date=$xmpDate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case DateEditAction.remove:
|
case DateEditAction.remove:
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:aves/model/entry/entry.dart';
|
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/channel_layouts.dart';
|
||||||
import 'package:aves/model/media/video/codecs.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/aac.dart';
|
||||||
import 'package:aves/model/media/video/profiles/h264.dart';
|
import 'package:aves/model/media/video/profiles/h264.dart';
|
||||||
import 'package:aves/model/media/video/profiles/hevc.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/languages.dart';
|
||||||
import 'package:aves/ref/locales.dart';
|
import 'package:aves/ref/locales.dart';
|
||||||
import 'package:aves/ref/mime_types.dart';
|
import 'package:aves/ref/mime_types.dart';
|
||||||
|
@ -99,7 +99,7 @@ class VideoMetadataFormatter {
|
||||||
if (isDefined(dateString)) {
|
if (isDefined(dateString)) {
|
||||||
dateMillis = parseVideoDate(dateString);
|
dateMillis = parseVideoDate(dateString);
|
||||||
if (dateMillis == null && !isAmbiguousDate(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);
|
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;
|
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());
|
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 {
|
} else {
|
||||||
debugPrint('failed to import key=$key, value=$newValue is not a string');
|
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 {
|
} else {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SettingKeys.subtitleTextColorKey:
|
case SettingKeys.subtitleTextColorKey:
|
||||||
|
@ -404,7 +410,6 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings
|
||||||
case SettingKeys.stateSortReverseKey:
|
case SettingKeys.stateSortReverseKey:
|
||||||
case SettingKeys.placeSortReverseKey:
|
case SettingKeys.placeSortReverseKey:
|
||||||
case SettingKeys.tagSortReverseKey:
|
case SettingKeys.tagSortReverseKey:
|
||||||
case SettingKeys.showAlbumPickQueryKey:
|
|
||||||
case SettingKeys.showOverlayOnOpeningKey:
|
case SettingKeys.showOverlayOnOpeningKey:
|
||||||
case SettingKeys.showOverlayMinimapKey:
|
case SettingKeys.showOverlayMinimapKey:
|
||||||
case SettingKeys.showOverlayInfoKey:
|
case SettingKeys.showOverlayInfoKey:
|
||||||
|
|
|
@ -59,15 +59,13 @@ class MediaStoreSource extends CollectionSource {
|
||||||
await vaults.init();
|
await vaults.init();
|
||||||
await favourites.init();
|
await favourites.init();
|
||||||
await covers.init();
|
await covers.init();
|
||||||
final currentTimeZoneOffset = await deviceService.getDefaultTimeZoneRawOffsetMillis();
|
final currentTimeZoneOffset = DateTime.now().timeZoneOffset.inMilliseconds;
|
||||||
if (currentTimeZoneOffset != null) {
|
final catalogTimeZoneOffset = settings.catalogTimeZoneOffsetMillis;
|
||||||
final catalogTimeZoneOffset = settings.catalogTimeZoneRawOffsetMillis;
|
if (currentTimeZoneOffset != catalogTimeZoneOffset) {
|
||||||
if (currentTimeZoneOffset != catalogTimeZoneOffset) {
|
unawaited(reportService.recordError('Time zone offset change: $currentTimeZoneOffset -> $catalogTimeZoneOffset. Clear catalog metadata to get correct date/times.'));
|
||||||
unawaited(reportService.recordError('Time zone offset change: $currentTimeZoneOffset -> $catalogTimeZoneOffset. Clear catalog metadata to get correct date/times.', null));
|
await localMediaDb.clearDates();
|
||||||
await localMediaDb.clearDates();
|
await localMediaDb.clearCatalogMetadata();
|
||||||
await localMediaDb.clearCatalogMetadata();
|
settings.catalogTimeZoneOffsetMillis = currentTimeZoneOffset;
|
||||||
settings.catalogTimeZoneRawOffsetMillis = currentTimeZoneOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await loadDates();
|
await loadDates();
|
||||||
debugPrint('$runtimeType load essentials complete in ${stopwatch.elapsed.inMilliseconds}ms');
|
debugPrint('$runtimeType load essentials complete in ${stopwatch.elapsed.inMilliseconds}ms');
|
||||||
|
@ -214,7 +212,7 @@ class MediaStoreSource extends CollectionSource {
|
||||||
// TODO TLAD find duplication cause
|
// TODO TLAD find duplication cause
|
||||||
final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries);
|
final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries);
|
||||||
if (duplicates.isNotEmpty) {
|
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
|
// post-error cleanup
|
||||||
await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet());
|
await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet());
|
||||||
for (final duplicate in duplicates) {
|
for (final duplicate in duplicates) {
|
||||||
|
@ -327,7 +325,7 @@ class MediaStoreSource extends CollectionSource {
|
||||||
// TODO TLAD find duplication cause
|
// TODO TLAD find duplication cause
|
||||||
final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries);
|
final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries);
|
||||||
if (duplicates.isNotEmpty) {
|
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
|
// post-error cleanup
|
||||||
await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet());
|
await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet());
|
||||||
for (final duplicate in duplicates) {
|
for (final duplicate in duplicates) {
|
||||||
|
|
|
@ -76,7 +76,7 @@ mixin TrashMixin on SourceBase {
|
||||||
sourceEntry.trashDetails = _buildTrashDetails(id);
|
sourceEntry.trashDetails = _buildTrashDetails(id);
|
||||||
newEntries.add(sourceEntry);
|
newEntries.add(sourceEntry);
|
||||||
} else {
|
} 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;
|
sourceEntry.origin = EntryOrigins.vault;
|
||||||
newEntries.add(sourceEntry);
|
newEntries.add(sourceEntry);
|
||||||
} else {
|
} 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<Map<String, dynamic>> getCapabilities();
|
||||||
|
|
||||||
Future<int?> getDefaultTimeZoneRawOffsetMillis();
|
|
||||||
|
|
||||||
Future<List<Locale>> getLocales();
|
Future<List<Locale>> getLocales();
|
||||||
|
|
||||||
|
Future<void> setLocaleConfig(List<Locale> locales);
|
||||||
|
|
||||||
Future<int> getPerformanceClass();
|
Future<int> getPerformanceClass();
|
||||||
|
|
||||||
Future<bool> isLocked();
|
Future<bool> isLocked();
|
||||||
|
@ -50,16 +50,6 @@ class PlatformDeviceService implements DeviceService {
|
||||||
return {};
|
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
|
@override
|
||||||
Future<List<Locale>> getLocales() async {
|
Future<List<Locale>> getLocales() async {
|
||||||
try {
|
try {
|
||||||
|
@ -80,6 +70,17 @@ class PlatformDeviceService implements DeviceService {
|
||||||
return [];
|
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
|
@override
|
||||||
Future<int> getPerformanceClass() async {
|
Future<int> getPerformanceClass() async {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -6,10 +6,8 @@ import 'package:aves/model/entry/extensions/props.dart';
|
||||||
import 'package:aves/model/metadata/date_modifier.dart';
|
import 'package:aves/model/metadata/date_modifier.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves_model/aves_model.dart';
|
import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:aves_report/aves_report.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:stack_trace/stack_trace.dart';
|
|
||||||
|
|
||||||
abstract class MetadataEditService {
|
abstract class MetadataEditService {
|
||||||
Future<Map<String, dynamic>> rotate(AvesEntry entry, {required bool clockwise});
|
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
|
// 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.
|
// 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) {
|
Future<void> mp4LargeMoov(CustomPlatformException e) {
|
||||||
debugPrint('mp4LargeMoov $e');
|
debugPrint('mp4LargeMoov $e');
|
||||||
return reportService.recordError(e, _currentStack());
|
return reportService.recordError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> mp4LargeOther(CustomPlatformException e) {
|
Future<void> mp4LargeOther(CustomPlatformException e) {
|
||||||
debugPrint('mp4LargeOther $e');
|
debugPrint('mp4LargeOther $e');
|
||||||
return reportService.recordError(e, _currentStack());
|
return reportService.recordError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fileNotFound(CustomPlatformException e) {
|
Future<void> fileNotFound(CustomPlatformException e) {
|
||||||
debugPrint('fileNotFound $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
|
@override
|
||||||
void didChangeMetrics() => _updateCutoutInsets();
|
void didChangeMetrics() => _updateCutoutInsets();
|
||||||
|
|
||||||
|
@ -502,6 +495,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
_monitorSettings();
|
_monitorSettings();
|
||||||
videoControllerFactory.init();
|
videoControllerFactory.init();
|
||||||
|
|
||||||
|
unawaited(deviceService.setLocaleConfig(AvesApp.supportedLocales));
|
||||||
unawaited(storageService.deleteTempDirectory());
|
unawaited(storageService.deleteTempDirectory());
|
||||||
unawaited(_setupErrorReporting());
|
unawaited(_setupErrorReporting());
|
||||||
|
|
||||||
|
@ -692,7 +686,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
_mediaStoreSource.updateDerivedFilters();
|
_mediaStoreSource.updateDerivedFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onError(String? error) => reportService.recordError(error, null);
|
void _onError(String? error) => reportService.recordError(error);
|
||||||
|
|
||||||
void _onAppModeChanged() {
|
void _onAppModeChanged() {
|
||||||
final appMode = _appModeNotifier.value;
|
final appMode = _appModeNotifier.value;
|
||||||
|
|
|
@ -399,7 +399,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
(action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection),
|
(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 [
|
return [
|
||||||
...quickActionButtons,
|
...quickActionButtons,
|
||||||
PopupMenuButton<EntrySetAction>(
|
PopupMenuButton<EntrySetAction>(
|
||||||
|
|
|
@ -88,7 +88,7 @@ class _CollectionGridState extends State<CollectionGrid> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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) {
|
if (_tileExtentController?.spacing != spacing) {
|
||||||
_tileExtentController = TileExtentController(
|
_tileExtentController = TileExtentController(
|
||||||
settingsRouteKey: settingsRouteKey,
|
settingsRouteKey: settingsRouteKey,
|
||||||
|
@ -136,7 +136,7 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectable = context.select<ValueNotifier<AppMode>, bool>((v) => v.value.canSelectMedia);
|
final selectable = context.select<ValueNotifier<AppMode>, bool>((v) => v.value.canSelectMedia);
|
||||||
final settingsRouteKey = context.read<TileExtentController>().settingsRouteKey;
|
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>(
|
return Consumer<CollectionLens>(
|
||||||
builder: (context, collection, child) {
|
builder: (context, collection, child) {
|
||||||
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||||
|
|
|
@ -91,6 +91,7 @@ class _CollectionPageState extends State<CollectionPage> {
|
||||||
selector: (context, selection) => selection.selectedItems.isNotEmpty,
|
selector: (context, selection) => selection.selectedItems.isNotEmpty,
|
||||||
builder: (context, hasSelection, child) {
|
builder: (context, hasSelection, child) {
|
||||||
final body = QueryProvider(
|
final body = QueryProvider(
|
||||||
|
startEnabled: settings.getShowTitleQuery(context.currentRouteName!),
|
||||||
initialQuery: liveFilter?.query,
|
initialQuery: liveFilter?.query,
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
|
|
@ -181,6 +181,8 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
case EntrySetAction.searchCollection:
|
case EntrySetAction.searchCollection:
|
||||||
_goToSearch(context);
|
_goToSearch(context);
|
||||||
case EntrySetAction.toggleTitleSearch:
|
case EntrySetAction.toggleTitleSearch:
|
||||||
|
final routeName = context.currentRouteName!;
|
||||||
|
settings.setShowTitleQuery(routeName, !settings.getShowTitleQuery(routeName));
|
||||||
context.read<Query>().toggle();
|
context.read<Query>().toggle();
|
||||||
case EntrySetAction.addShortcut:
|
case EntrySetAction.addShortcut:
|
||||||
_addShortcut(context);
|
_addShortcut(context);
|
||||||
|
|
|
@ -14,7 +14,7 @@ class TvEdgeFocus extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final useTvLayout = context.select<Settings, bool>((s) => s.useTvLayout);
|
final useTvLayout = context.select<Settings, bool>((v) => v.useTvLayout);
|
||||||
return useTvLayout
|
return useTvLayout
|
||||||
? Focus(
|
? Focus(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
|
|
@ -20,7 +20,7 @@ class DoubleBackPopHandler extends PopHandler {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool canPop(BuildContext context) {
|
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;
|
if (Navigator.canPop(context)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ class TvNavigationPopHandler implements PopHandler {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool canPop(BuildContext context) {
|
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;
|
if (_isHome(context)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,7 +260,7 @@ class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
|
||||||
return ValueListenableBuilder<bool>(
|
return ValueListenableBuilder<bool>(
|
||||||
valueListenable: _isBlurAllowedNotifier,
|
valueListenable: _isBlurAllowedNotifier,
|
||||||
builder: (context, isBlurAllowed, child) {
|
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(
|
return Container(
|
||||||
foregroundDecoration: BoxDecoration(
|
foregroundDecoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
|
|
|
@ -3,13 +3,13 @@ import 'package:flutter/widgets.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class QueryProvider extends StatelessWidget {
|
class QueryProvider extends StatelessWidget {
|
||||||
final bool enabled;
|
final bool startEnabled;
|
||||||
final String? initialQuery;
|
final String? initialQuery;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const QueryProvider({
|
const QueryProvider({
|
||||||
super.key,
|
super.key,
|
||||||
this.enabled = false,
|
this.startEnabled = false,
|
||||||
this.initialQuery,
|
this.initialQuery,
|
||||||
required this.child,
|
required this.child,
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,7 @@ class QueryProvider extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ChangeNotifierProvider<Query>(
|
return ChangeNotifierProvider<Query>(
|
||||||
create: (context) => Query(
|
create: (context) => Query(
|
||||||
enabled: enabled,
|
enabled: startEnabled,
|
||||||
initialValue: initialQuery,
|
initialValue: initialQuery,
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
|
|
|
@ -37,7 +37,7 @@ class AppDebugPage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||||
return Directionality(
|
return Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: AvesScaffold(
|
child: AvesScaffold(
|
||||||
|
|
|
@ -55,7 +55,7 @@ class _DebugSettingsSectionState extends State<DebugSettingsSection> with Automa
|
||||||
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
|
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
|
||||||
child: InfoRowGroup(
|
child: InfoRowGroup(
|
||||||
info: {
|
info: {
|
||||||
'catalogTimeZoneRawOffsetMillis': '${settings.catalogTimeZoneRawOffsetMillis}',
|
'catalogTimeZoneRawOffsetMillis': '${settings.catalogTimeZoneOffsetMillis}',
|
||||||
'tileExtent - Collection': '${settings.getTileExtent(CollectionPage.routeName)}',
|
'tileExtent - Collection': '${settings.getTileExtent(CollectionPage.routeName)}',
|
||||||
'tileExtent - Albums': '${settings.getTileExtent(AlbumListPage.routeName)}',
|
'tileExtent - Albums': '${settings.getTileExtent(AlbumListPage.routeName)}',
|
||||||
'tileExtent - Countries': '${settings.getTileExtent(CountryListPage.routeName)}',
|
'tileExtent - Countries': '${settings.getTileExtent(CountryListPage.routeName)}',
|
||||||
|
|
|
@ -71,7 +71,7 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final textScaler = MediaQuery.textScalerOf(context);
|
final textScaler = MediaQuery.textScalerOf(context);
|
||||||
final effectiveThumbnailExtent = max(thumbnailExtent, textScaler.scale(thumbnailExtent));
|
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(
|
return AvesScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(l10n.renameEntrySetPageTitle),
|
title: Text(l10n.renameEntrySetPageTitle),
|
||||||
|
|
|
@ -152,7 +152,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
icon: const Icon(AIcons.add),
|
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,
|
tooltip: l10n.tagEditorPageAddTagTooltip,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -296,6 +296,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addCustomTag(String newTag) {
|
void _addCustomTag(String newTag) {
|
||||||
|
newTag = newTag.trim();
|
||||||
if (newTag.isNotEmpty) {
|
if (newTag.isNotEmpty) {
|
||||||
_addTag(TagFilter(newTag));
|
_addTag(TagFilter(newTag));
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,13 +114,13 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
||||||
final gridItems = AlbumListPage.getAlbumGridItems(context, source);
|
final gridItems = AlbumListPage.getAlbumGridItems(context, source);
|
||||||
return SelectionProvider<FilterGridItem<AlbumFilter>>(
|
return SelectionProvider<FilterGridItem<AlbumFilter>>(
|
||||||
child: QueryProvider(
|
child: QueryProvider(
|
||||||
enabled: settings.showAlbumPickQuery,
|
startEnabled: settings.getShowTitleQuery(context.currentRouteName!),
|
||||||
child: FilterGridPage<AlbumFilter>(
|
child: FilterGridPage<AlbumFilter>(
|
||||||
settingsRouteKey: AlbumListPage.routeName,
|
settingsRouteKey: AlbumListPage.routeName,
|
||||||
appBar: FilterGridAppBar(
|
appBar: FilterGridAppBar(
|
||||||
source: source,
|
source: source,
|
||||||
title: title,
|
title: title,
|
||||||
actionDelegate: _AlbumChipSetPickActionDelegate(gridItems),
|
actionDelegate: AlbumChipSetActionDelegate(gridItems),
|
||||||
actionsBuilder: _buildActions,
|
actionsBuilder: _buildActions,
|
||||||
isEmpty: false,
|
isEmpty: false,
|
||||||
appBarHeightNotifier: _appBarHeightNotifier,
|
appBarHeightNotifier: _appBarHeightNotifier,
|
||||||
|
@ -211,7 +211,7 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
||||||
required bool Function(ChipSetAction action) isVisible,
|
required bool Function(ChipSetAction action) isVisible,
|
||||||
required void Function(ChipSetAction action) onActionSelected,
|
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 [
|
return [
|
||||||
if (widget.moveType != null)
|
if (widget.moveType != null)
|
||||||
..._quickActions.where(isVisible).map(
|
..._quickActions.where(isVisible).map(
|
||||||
|
@ -287,15 +287,3 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
||||||
Navigator.maybeOf(context)?.pop<AlbumFilter>(filter);
|
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/app_mode.dart';
|
||||||
import 'package:aves/model/entry/entry.dart';
|
import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/model/filters/query.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/model/source/collection_lens.dart';
|
||||||
import 'package:aves/widgets/collection/collection_grid.dart';
|
import 'package:aves/widgets/collection/collection_grid.dart';
|
||||||
import 'package:aves/widgets/collection/collection_page.dart';
|
import 'package:aves/widgets/collection/collection_page.dart';
|
||||||
import 'package:aves/widgets/common/basic/insets.dart';
|
import 'package:aves/widgets/common/basic/insets.dart';
|
||||||
import 'package:aves/widgets/common/basic/scaffold.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/query_provider.dart';
|
||||||
import 'package:aves/widgets/common/providers/selection_provider.dart';
|
import 'package:aves/widgets/common/providers/selection_provider.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
@ -49,6 +51,7 @@ class _ItemPickPageState extends State<ItemPickPage> {
|
||||||
child: AvesScaffold(
|
child: AvesScaffold(
|
||||||
body: SelectionProvider<AvesEntry>(
|
body: SelectionProvider<AvesEntry>(
|
||||||
child: QueryProvider(
|
child: QueryProvider(
|
||||||
|
startEnabled: settings.getShowTitleQuery(context.currentRouteName!),
|
||||||
initialQuery: liveFilter?.query,
|
initialQuery: liveFilter?.query,
|
||||||
child: GestureAreaProtectorStack(
|
child: GestureAreaProtectorStack(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
|
|
|
@ -104,7 +104,7 @@ class _ExplorerAppBarState extends State<ExplorerAppBar> with WidgetsBindingObse
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildActions(BuildContext context, double maxWidth) {
|
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 [
|
return [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(AIcons.search),
|
icon: const Icon(AIcons.search),
|
||||||
|
|
|
@ -179,6 +179,8 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
case ChipSetAction.search:
|
case ChipSetAction.search:
|
||||||
_goToSearch(context);
|
_goToSearch(context);
|
||||||
case ChipSetAction.toggleTitleSearch:
|
case ChipSetAction.toggleTitleSearch:
|
||||||
|
final routeName = context.currentRouteName!;
|
||||||
|
settings.setShowTitleQuery(routeName, !settings.getShowTitleQuery(routeName));
|
||||||
context.read<Query>().toggle();
|
context.read<Query>().toggle();
|
||||||
case ChipSetAction.createAlbum:
|
case ChipSetAction.createAlbum:
|
||||||
case ChipSetAction.createVault:
|
case ChipSetAction.createVault:
|
||||||
|
|
|
@ -329,7 +329,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
||||||
(action) => _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)),
|
(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 [
|
return [
|
||||||
...quickActionButtons,
|
...quickActionButtons,
|
||||||
PopupMenuButton<ChipSetAction>(
|
PopupMenuButton<ChipSetAction>(
|
||||||
|
|
|
@ -286,7 +286,7 @@ class _FilterGridContentState<T extends CollectionFilter> extends State<_FilterG
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
final settingsRouteKey = context.read<TileExtentController>().settingsRouteKey;
|
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>(
|
return Selector<Query, bool>(
|
||||||
selector: (context, query) => query.enabled,
|
selector: (context, query) => query.enabled,
|
||||||
builder: (context, queryEnabled, child) {
|
builder: (context, queryEnabled, child) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:aves/model/filters/filters.dart';
|
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/model/source/collection_source.dart';
|
||||||
import 'package:aves/utils/time_utils.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/identity/aves_filter_chip.dart';
|
||||||
import 'package:aves/widgets/common/providers/query_provider.dart';
|
import 'package:aves/widgets/common/providers/query_provider.dart';
|
||||||
import 'package:aves/widgets/common/providers/selection_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>>(
|
return SelectionProvider<FilterGridItem<T>>(
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => QueryProvider(
|
builder: (context) => QueryProvider(
|
||||||
|
startEnabled: settings.getShowTitleQuery(context.currentRouteName!),
|
||||||
child: FilterGridPage<T>(
|
child: FilterGridPage<T>(
|
||||||
appBar: FilterGridAppBar<T, CSAD>(
|
appBar: FilterGridAppBar<T, CSAD>(
|
||||||
source: widget.source,
|
source: widget.source,
|
||||||
|
|
|
@ -66,7 +66,7 @@ class _AppBottomNavBarState extends State<AppBottomNavBar> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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 = [
|
final items = [
|
||||||
const AvesBottomNavItem(route: CollectionPage.routeName),
|
const AvesBottomNavItem(route: CollectionPage.routeName),
|
||||||
|
@ -91,6 +91,7 @@ class _AppBottomNavBarState extends State<AppBottomNavBar> {
|
||||||
.toList(),
|
.toList(),
|
||||||
onTap: (index) => _goTo(context, items, index),
|
onTap: (index) => _goTo(context, items, index),
|
||||||
currentIndex: _getCurrentIndex(context, items),
|
currentIndex: _getCurrentIndex(context, items),
|
||||||
|
elevation: 0,
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
showSelectedLabels: false,
|
showSelectedLabels: false,
|
||||||
|
|
|
@ -59,7 +59,7 @@ class _FilePickerPageState extends State<FilePickerPage> {
|
||||||
return !isHidden;
|
return !isHidden;
|
||||||
}
|
}
|
||||||
}).toList();
|
}).toList();
|
||||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: _directory.relativeDir.isEmpty,
|
canPop: _directory.relativeDir.isEmpty,
|
||||||
onPopInvokedWithResult: (didPop, result) {
|
onPopInvokedWithResult: (didPop, result) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ class _SettingsMobilePageState extends State<SettingsMobilePage> with FeedbackMi
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||||
return AvesScaffold(
|
return AvesScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: InteractiveAppBarTitle(
|
title: InteractiveAppBarTitle(
|
||||||
|
|
|
@ -143,7 +143,7 @@ class SettingsTileThumbnailLocationIcon extends SettingsTile {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTrailing(BuildContext context) {
|
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(
|
return ThumbnailOverlayPage.buildTrailingIcon(
|
||||||
context: context,
|
context: context,
|
||||||
key: iconType,
|
key: iconType,
|
||||||
|
@ -168,7 +168,7 @@ class SettingsTileThumbnailTagIcon extends SettingsTile {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTrailing(BuildContext context) {
|
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(
|
return ThumbnailOverlayPage.buildTrailingIcon(
|
||||||
context: context,
|
context: context,
|
||||||
key: iconType,
|
key: iconType,
|
||||||
|
|
|
@ -61,8 +61,8 @@ mixin SingleEntryEditorMixin on FeedbackMixin, PermissionAwareMixin {
|
||||||
} else {
|
} else {
|
||||||
showFeedback(context, FeedbackType.warn, l10n.genericFailureFeedback);
|
showFeedback(context, FeedbackType.warn, l10n.genericFailureFeedback);
|
||||||
}
|
}
|
||||||
} catch (error, stack) {
|
} catch (e, stack) {
|
||||||
await reportService.recordError(error, stack);
|
await reportService.recordError(e, stack);
|
||||||
}
|
}
|
||||||
source?.resumeMonitoring();
|
source?.resumeMonitoring();
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,8 +96,8 @@ mixin CastMixin {
|
||||||
);
|
);
|
||||||
debugPrint('cast: play entry=$entry');
|
debugPrint('cast: play entry=$entry');
|
||||||
unawaited(renderer.play());
|
unawaited(renderer.play());
|
||||||
} catch (error, stack) {
|
} catch (e, stack) {
|
||||||
await reportService.recordError(error, stack);
|
await reportService.recordError(e, stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ class InfoAppBar extends StatelessWidget {
|
||||||
final commonActions = EntryActions.commonMetadataActions.where(isVisible);
|
final commonActions = EntryActions.commonMetadataActions.where(isVisible);
|
||||||
final formatSpecificActions = EntryActions.formatSpecificMetadataActions.where(isVisible);
|
final formatSpecificActions = EntryActions.formatSpecificMetadataActions.where(isVisible);
|
||||||
final useTvLayout = settings.useTvLayout;
|
final useTvLayout = settings.useTvLayout;
|
||||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
final animations = context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);
|
||||||
return SliverAppBar(
|
return SliverAppBar(
|
||||||
leading: useTvLayout
|
leading: useTvLayout
|
||||||
? null
|
? null
|
||||||
|
|
|
@ -252,7 +252,7 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
||||||
final exportActions = widget.exportActions;
|
final exportActions = widget.exportActions;
|
||||||
final videoActions = widget.videoActions;
|
final videoActions = widget.videoActions;
|
||||||
final hasOverflowMenu = pageEntry.canRotate || pageEntry.canFlip || topLevelActions.isNotEmpty || exportActions.isNotEmpty || videoActions.isNotEmpty;
|
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?>(
|
return Selector<VideoConductor, AvesVideoController?>(
|
||||||
selector: (context, vc) => vc.getController(pageEntry),
|
selector: (context, vc) => vc.getController(pageEntry),
|
||||||
builder: (context, videoController, child) {
|
builder: (context, videoController, child) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ class SettingKeys {
|
||||||
|
|
||||||
static const Set<String> _internalKeys = {
|
static const Set<String> _internalKeys = {
|
||||||
hasAcceptedTermsKey,
|
hasAcceptedTermsKey,
|
||||||
catalogTimeZoneRawOffsetMillisKey,
|
catalogTimeZoneOffsetMillisKey,
|
||||||
searchHistoryKey,
|
searchHistoryKey,
|
||||||
platformAccelerometerRotationKey,
|
platformAccelerometerRotationKey,
|
||||||
platformTransitionAnimationScaleKey,
|
platformTransitionAnimationScaleKey,
|
||||||
|
@ -21,7 +21,7 @@ class SettingKeys {
|
||||||
static const isErrorReportingAllowedKey = 'is_crashlytics_enabled';
|
static const isErrorReportingAllowedKey = 'is_crashlytics_enabled';
|
||||||
static const localeKey = 'locale';
|
static const localeKey = 'locale';
|
||||||
static const forceWesternArabicNumeralsKey = 'force_western_arabic_numerals';
|
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 tileExtentPrefixKey = 'tile_extent_';
|
||||||
static const tileLayoutPrefixKey = 'tile_layout_';
|
static const tileLayoutPrefixKey = 'tile_layout_';
|
||||||
static const entryRenamingPatternKey = 'entry_renaming_pattern';
|
static const entryRenamingPatternKey = 'entry_renaming_pattern';
|
||||||
|
@ -86,7 +86,7 @@ class SettingKeys {
|
||||||
static const pinnedFiltersKey = 'pinned_filters';
|
static const pinnedFiltersKey = 'pinned_filters';
|
||||||
static const hiddenFiltersKey = 'hidden_filters';
|
static const hiddenFiltersKey = 'hidden_filters';
|
||||||
static const deactivatedHiddenFiltersKey = 'deactivated_hidden_filters';
|
static const deactivatedHiddenFiltersKey = 'deactivated_hidden_filters';
|
||||||
static const showAlbumPickQueryKey = 'show_album_pick_query';
|
static const showTitleQueryPrefixKey = 'show_title_query_';
|
||||||
|
|
||||||
// viewer
|
// viewer
|
||||||
static const viewerQuickActionsKey = 'viewer_quick_actions';
|
static const viewerQuickActionsKey = 'viewer_quick_actions';
|
||||||
|
|
|
@ -15,7 +15,7 @@ abstract class ReportService {
|
||||||
|
|
||||||
Future<void> setCustomKeys(Map<String, Object> map);
|
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);
|
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');
|
Future<void> log(String message) async => debugPrint('Report log with message=$message');
|
||||||
|
|
||||||
@override
|
@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
|
@override
|
||||||
Future<void> recordFlutterError(FlutterErrorDetails flutterErrorDetails) async => debugPrint('Report Flutter error with details=$flutterErrorDetails');
|
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:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:stack_trace/stack_trace.dart';
|
||||||
|
|
||||||
class PlatformReportService extends ReportService {
|
class PlatformReportService extends ReportService {
|
||||||
FirebaseCrashlytics? get _instance {
|
FirebaseCrashlytics? get _instance {
|
||||||
|
@ -65,11 +66,12 @@ class PlatformReportService extends ReportService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> recordError(dynamic exception, StackTrace? stack) async {
|
Future<void> recordError(dynamic exception, [StackTrace? stack]) async {
|
||||||
if (exception is PlatformException && stack != null) {
|
if (exception is PlatformException && stack != null) {
|
||||||
stack = ReportService.buildReportStack(stack, level: 2);
|
stack = ReportService.buildReportStack(stack, level: 2);
|
||||||
}
|
}
|
||||||
if (exception is! UnreportedStateError) {
|
if (exception is! UnreportedStateError) {
|
||||||
|
stack ??= ReportService.buildReportStack(Trace.current(), level: 1);
|
||||||
return _instance?.recordError(exception, stack);
|
return _instance?.recordError(exception, stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,7 +213,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
|
|
|
@ -14,6 +14,7 @@ dependencies:
|
||||||
# so that the transitive `path` gets upgraded to v1.8.3
|
# so that the transitive `path` gets upgraded to v1.8.3
|
||||||
firebase_core: ">=2.10.0"
|
firebase_core: ">=2.10.0"
|
||||||
firebase_crashlytics:
|
firebase_crashlytics:
|
||||||
|
stack_trace:
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
|
|
|
@ -7,7 +7,7 @@ repository: https://github.com/deckerst/aves
|
||||||
# - play changelog: /whatsnew/whatsnew-en-US
|
# - play changelog: /whatsnew/whatsnew-en-US
|
||||||
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt
|
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt
|
||||||
# - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt
|
# - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt
|
||||||
version: 1.11.18+137
|
version: 1.11.19+138
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -3,9 +3,6 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:test/fake.dart';
|
import 'package:test/fake.dart';
|
||||||
|
|
||||||
class FakeDeviceService extends Fake implements DeviceService {
|
class FakeDeviceService extends Fake implements DeviceService {
|
||||||
@override
|
|
||||||
Future<int> getDefaultTimeZoneRawOffsetMillis() => SynchronousFuture(3600000);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> getAvailableHeapSize() => SynchronousFuture(0x7fffffff);
|
Future<int> getAvailableHeapSize() => SynchronousFuture(0x7fffffff);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class FakeReportService extends ReportService {
|
||||||
Future<void> setCustomKeys(Map<String, Object> map) => SynchronousFuture(null);
|
Future<void> setCustomKeys(Map<String, Object> map) => SynchronousFuture(null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> recordError(dynamic exception, StackTrace? stack) => SynchronousFuture(null);
|
Future<void> recordError(dynamic exception, [StackTrace? stack]) => SynchronousFuture(null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> recordFlutterError(FlutterErrorDetails flutterErrorDetails) => SynchronousFuture(null);
|
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
|
- peruse your videos frame by frame
|
||||||
- create map shortcuts to filtered collections
|
- create map shortcuts to filtered collections
|
||||||
- enjoy the app in Shavian
|
- enjoy the app in Shavian
|
||||||
|
|
Loading…
Reference in a new issue