Merge branch 'develop'

This commit is contained in:
Thibault Deckers 2024-11-24 23:16:41 +01:00
commit 1d7deac84d
62 changed files with 357 additions and 124 deletions

View file

@ -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

View file

@ -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}}"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>

View 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

View 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

View file

@ -1589,5 +1589,7 @@
"videoActionShowPreviousFrame": "𐑖𐑴 𐑐𐑮𐑰𐑝𐑾𐑕 𐑓𐑮𐑱𐑥",
"@videoActionShowPreviousFrame": {},
"videoActionShowNextFrame": "𐑖𐑴 𐑯𐑧𐑒𐑕𐑑 𐑓𐑮𐑱𐑥",
"@videoActionShowNextFrame": {}
"@videoActionShowNextFrame": {},
"newAlbumDialogAlbumAlreadyExistsHelper": "𐑨𐑤𐑚𐑩𐑥 𐑷𐑤𐑮𐑧𐑛𐑦 𐑦𐑜𐑟𐑦𐑕𐑑𐑕",
"@newAlbumDialogAlbumAlreadyExistsHelper": {}
}

View file

@ -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"
}
}
}
}

View file

@ -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": {}
}

View file

@ -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": {}
}

View file

@ -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:

View file

@ -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');
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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:

View file

@ -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;
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.', null));
unawaited(reportService.recordError('Time zone offset change: $currentTimeZoneOffset -> $catalogTimeZoneOffset. Clear catalog metadata to get correct date/times.'));
await localMediaDb.clearDates();
await localMediaDb.clearCatalogMetadata();
settings.catalogTimeZoneRawOffsetMillis = currentTimeZoneOffset;
}
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) {

View file

@ -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');
}
}
});

View file

@ -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');
}
});
}

View file

@ -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 {

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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>(

View file

@ -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>(

View file

@ -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) {

View file

@ -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);

View file

@ -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,

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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(

View file

@ -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,

View file

@ -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(

View file

@ -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)}',

View file

@ -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),

View file

@ -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));
}

View file

@ -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);
}
}

View file

@ -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(

View file

@ -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),

View file

@ -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:

View file

@ -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>(

View file

@ -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) {

View file

@ -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,

View file

@ -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,

View file

@ -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) {

View file

@ -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(

View file

@ -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,

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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) {

View file

@ -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';

View file

@ -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);

View file

@ -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');

View file

@ -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);
}
}

View file

@ -213,7 +213,7 @@ packages:
source: hosted
version: "1.10.0"
stack_trace:
dependency: transitive
dependency: "direct main"
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"

View file

@ -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:

View file

@ -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:

View file

@ -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);

View file

@ -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);

View file

@ -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