diff --git a/CHANGELOG.md b/CHANGELOG.md
index 64f2a4152..61d67a565 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [v1.6.13] - 2022-08-29
+
+### Changed
+
+- use natural order when sorting by name items, albums, tags
+
+### Fixed
+
+- adding duplicate items during loading in some cases
+- screensaver stopping when device orientation changes
+
## [v1.6.12] - 2022-08-27
### Added
diff --git a/fastlane/metadata/android/en-US/changelogs/1079.txt b/fastlane/metadata/android/en-US/changelogs/1079.txt
new file mode 100644
index 000000000..30000cd00
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/1079.txt
@@ -0,0 +1,5 @@
+In v1.6.13:
+- play your HEIC motion photos
+- find recently downloaded images with the `recently added` filter
+- enjoy the app in Dutch
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index 9ce02959a..e8f129ec7 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -87,6 +87,7 @@
"entryInfoActionEditDate": "Editar data e hora",
"entryInfoActionEditLocation": "Editar localização",
+ "entryInfoActionEditDescription": "Editar descrição",
"entryInfoActionEditRating": "Editar classificação",
"entryInfoActionEditTags": "Editar etiquetas",
"entryInfoActionRemoveMetadata": "Remover metadados",
@@ -96,6 +97,7 @@
"filterLocationEmptyLabel": "Não localizado",
"filterTagEmptyLabel": "Sem etiqueta",
"filterOnThisDayLabel": "Neste dia",
+ "filterRecentlyAddedLabel": "Adicionado recentemente",
"filterRatingUnratedLabel": "Sem classificação",
"filterRatingRejectedLabel": "Rejeitado",
"filterTypeAnimatedLabel": "Animado",
@@ -257,6 +259,8 @@
"locationPickerUseThisLocationButton": "Usar essa localização",
+ "editEntryDescriptionDialogTitle": "Descrição",
+
"editEntryRatingDialogTitle": "Avaliação",
"removeEntryMetadataDialogTitle": "Remoção de metadados",
@@ -451,6 +455,7 @@
"settingsConfirmationDialogDeleteItems": "Pergunte antes de excluir itens para sempre",
"settingsConfirmationDialogMoveToBinItems": "Pergunte antes de mover itens para a lixeira",
"settingsConfirmationDialogMoveUndatedItems": "Pergunte antes de mover itens sem data de metadados",
+ "settingsConfirmationAfterMoveToBinItems": "Mostrar mensagem depois de mover itens para a lixeira",
"settingsNavigationDrawerTile": "Menu de navegação",
"settingsNavigationDrawerEditorTitle": "Menu de navegação",
@@ -479,6 +484,7 @@
"settingsCollectionSelectionQuickActionEditorBanner": "Toque e segure para mover os botões e selecionar quais ações são exibidas ao selecionar itens.",
"settingsSectionViewer": "Visualizador",
+ "settingsViewerGestureSideTapNext": "Toque nas bordas da tela para mostrar anterior/seguinte",
"settingsViewerUseCutout": "Usar área de recorte",
"settingsViewerMaximumBrightness": "Brilho máximo",
"settingsMotionPhotoAutoPlay": "Reprodução automática de fotos em movimento",
diff --git a/lib/model/entry.dart b/lib/model/entry.dart
index 606ac3a09..1a70bf464 100644
--- a/lib/model/entry.dart
+++ b/lib/model/entry.dart
@@ -819,7 +819,7 @@ class AvesEntry {
// 1) title ascending
// 2) extension ascending
static int compareByName(AvesEntry a, AvesEntry b) {
- final c = compareAsciiUpperCase(a.bestTitle ?? '', b.bestTitle ?? '');
+ final c = compareAsciiUpperCaseNatural(a.bestTitle ?? '', b.bestTitle ?? '');
return c != 0 ? c : compareAsciiUpperCase(a.extension ?? '', b.extension ?? '');
}
diff --git a/lib/model/filters/filters.dart b/lib/model/filters/filters.dart
index 4fdd2822a..1c0b7a300 100644
--- a/lib/model/filters/filters.dart
+++ b/lib/model/filters/filters.dart
@@ -118,7 +118,7 @@ abstract class CollectionFilter extends Equatable implements Comparable stateNotifier = ValueNotifier(SourceState.ready);
+ set state(SourceState value) => stateNotifier.value = value;
+
+ SourceState get state => stateNotifier.value;
+
+ bool get isReady => state == SourceState.ready;
+
ValueNotifier progressNotifier = ValueNotifier(const ProgressEvent(done: 0, total: 0));
void setProgress({required int done, required int total}) => progressNotifier.value = ProgressEvent(done: done, total: total);
@@ -430,7 +436,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
updateDerivedFilters(todoEntries);
}
}
- stateNotifier.value = SourceState.ready;
+ state = SourceState.ready;
}
// monitoring
diff --git a/lib/model/source/location.dart b/lib/model/source/location.dart
index 144cb683b..1b29dc43c 100644
--- a/lib/model/source/location.dart
+++ b/lib/model/source/location.dart
@@ -51,7 +51,7 @@ mixin LocationMixin on SourceBase {
final todo = (force ? candidateEntries.where((entry) => entry.hasGps) : candidateEntries.where(locateCountriesTest)).toSet();
if (todo.isEmpty) return;
- stateNotifier.value = SourceState.locatingCountries;
+ state = SourceState.locatingCountries;
var progressDone = 0;
final progressTotal = todo.length;
setProgress(done: progressDone, total: progressTotal);
@@ -106,7 +106,7 @@ mixin LocationMixin on SourceBase {
knownLocations.putIfAbsent(approximateLatLng(entry), () => entry.addressDetails);
});
- stateNotifier.value = SourceState.locatingPlaces;
+ state = SourceState.locatingPlaces;
var progressDone = 0;
final progressTotal = todo.length;
setProgress(done: progressDone, total: progressTotal);
diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart
index 481804577..80318c595 100644
--- a/lib/model/source/media_store_source.dart
+++ b/lib/model/source/media_store_source.dart
@@ -42,7 +42,7 @@ class MediaStoreSource extends CollectionSource {
Future _loadEssentials() async {
final stopwatch = Stopwatch()..start();
- stateNotifier.value = SourceState.loading;
+ state = SourceState.loading;
await metadataDb.init();
await favourites.init();
await covers.init();
@@ -69,7 +69,7 @@ class MediaStoreSource extends CollectionSource {
}) async {
debugPrint('$runtimeType refresh start');
final stopwatch = Stopwatch()..start();
- stateNotifier.value = SourceState.loading;
+ state = SourceState.loading;
clearEntries();
final Set topEntries = {};
@@ -195,7 +195,7 @@ class MediaStoreSource extends CollectionSource {
if (canAnalyze) {
await analyze(analysisController, entries: analysisEntries);
} else {
- stateNotifier.value = SourceState.ready;
+ state = SourceState.ready;
}
// the home page may not reflect the current derived filters
@@ -216,7 +216,7 @@ class MediaStoreSource extends CollectionSource {
// sometimes yields an entry with its temporary path: `/data/sec/camera/!@#$%^..._temp.jpg`
@override
Future> refreshUris(Set changedUris, {AnalysisController? analysisController}) async {
- if (_initState == SourceInitializationState.none || !isMonitoring) return changedUris;
+ if (_initState == SourceInitializationState.none || !isMonitoring || !isReady) return changedUris;
debugPrint('$runtimeType refreshUris ${changedUris.length} uris');
final uriByContentId = Map.fromEntries(changedUris.map((uri) {
diff --git a/lib/model/source/tag.dart b/lib/model/source/tag.dart
index c092600cf..f7b597a18 100644
--- a/lib/model/source/tag.dart
+++ b/lib/model/source/tag.dart
@@ -31,7 +31,7 @@ mixin TagMixin on SourceBase {
final todo = force ? candidateEntries : candidateEntries.where(catalogEntriesTest).toSet();
if (todo.isEmpty) return;
- stateNotifier.value = SourceState.cataloguing;
+ state = SourceState.cataloguing;
var progressDone = 0;
final progressTotal = todo.length;
setProgress(done: progressDone, total: progressTotal);
@@ -64,7 +64,7 @@ mixin TagMixin on SourceBase {
}
void updateTags() {
- final updatedTags = visibleEntries.expand((entry) => entry.tags).toSet().toList()..sort(compareAsciiUpperCase);
+ final updatedTags = visibleEntries.expand((entry) => entry.tags).toSet().toList()..sort(compareAsciiUpperCaseNatural);
if (!listEquals(updatedTags, sortedTags)) {
sortedTags = List.unmodifiable(updatedTags);
invalidateTagFilterSummary();
diff --git a/lib/services/analysis_service.dart b/lib/services/analysis_service.dart
index 10f916ad9..1fb764e07 100644
--- a/lib/services/analysis_service.dart
+++ b/lib/services/analysis_service.dart
@@ -85,7 +85,7 @@ class Analyzer {
bool get isRunning => serviceState == AnalyzerState.running;
- SourceState get sourceState => _source.stateNotifier.value;
+ SourceState get sourceState => _source.state;
static const notificationUpdateInterval = Duration(seconds: 1);
@@ -151,7 +151,7 @@ class Analyzer {
}
void _onSourceStateChanged() {
- if (sourceState == SourceState.ready) {
+ if (_source.isReady) {
_refreshApp();
_serviceStateNotifier.value = AnalyzerState.stopping;
}
diff --git a/lib/widget_common.dart b/lib/widget_common.dart
index 79ad994ef..8a267587d 100644
--- a/lib/widget_common.dart
+++ b/lib/widget_common.dart
@@ -4,7 +4,6 @@ import 'package:aves/app_flavor.dart';
import 'package:aves/model/entry.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
-import 'package:aves/model/source/enums.dart';
import 'package:aves/model/source/media_store_source.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/widgets/home_widget.dart';
@@ -64,7 +63,7 @@ Future _getWidgetEntry(int widgetId, bool reuseEntry) async {
final source = MediaStoreSource();
final readyCompleter = Completer();
source.stateNotifier.addListener(() {
- if (source.stateNotifier.value == SourceState.ready) {
+ if (source.isReady) {
readyCompleter.complete();
}
});
diff --git a/lib/widgets/common/app_bar/app_bar_subtitle.dart b/lib/widgets/common/app_bar/app_bar_subtitle.dart
index 2a1df005d..688ca0ab5 100644
--- a/lib/widgets/common/app_bar/app_bar_subtitle.dart
+++ b/lib/widgets/common/app_bar/app_bar_subtitle.dart
@@ -60,7 +60,7 @@ class SourceStateSubtitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final sourceState = source.stateNotifier.value;
+ final sourceState = source.state;
final subtitle = sourceState.getName(context.l10n);
if (subtitle == null) return const SizedBox();
diff --git a/lib/widgets/dialogs/entry_editors/edit_tags_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_tags_dialog.dart
index 7c39adc58..545daab89 100644
--- a/lib/widgets/dialogs/entry_editors/edit_tags_dialog.dart
+++ b/lib/widgets/dialogs/entry_editors/edit_tags_dialog.dart
@@ -192,7 +192,7 @@ class _TagEditorPageState extends State {
return entryCountByTag.entries.toList()
..sort((kv1, kv2) {
final c = kv2.value.compareTo(kv1.value);
- return c != 0 ? c : compareAsciiUpperCase(kv1.key, kv2.key);
+ return c != 0 ? c : compareAsciiUpperCaseNatural(kv1.key, kv2.key);
});
}
diff --git a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart
index 89312f415..c06f4a3eb 100644
--- a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart
+++ b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart
@@ -48,7 +48,7 @@ class _CreateAlbumDialogState extends State {
final volumeTiles = [];
if (_allVolumes.length > 1) {
final byPrimary = groupBy(_allVolumes, (volume) => volume.isPrimary);
- int compare(StorageVolume a, StorageVolume b) => compareAsciiUpperCase(a.path, b.path);
+ int compare(StorageVolume a, StorageVolume b) => compareAsciiUpperCaseNatural(a.path, b.path);
final primaryVolumes = (byPrimary[true] ?? [])..sort(compare);
final otherVolumes = (byPrimary[false] ?? [])..sort(compare);
volumeTiles.addAll([
diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart
index 3d3da1c88..e26607508 100644
--- a/lib/widgets/home_page.dart
+++ b/lib/widgets/home_page.dart
@@ -266,7 +266,7 @@ class _HomePageState extends State {
// wait for collection to pass the `loading` state
final completer = Completer();
void _onSourceStateChanged() {
- if (source.stateNotifier.value != SourceState.loading) {
+ if (source.state != SourceState.loading) {
source.stateNotifier.removeListener(_onSourceStateChanged);
completer.complete();
}
diff --git a/lib/widgets/settings/privacy/file_picker/file_picker.dart b/lib/widgets/settings/privacy/file_picker/file_picker.dart
index 349ca7ab5..378a167f3 100644
--- a/lib/widgets/settings/privacy/file_picker/file_picker.dart
+++ b/lib/widgets/settings/privacy/file_picker/file_picker.dart
@@ -200,7 +200,7 @@ class _FilePickerState extends State {
contents.add(entity);
}
}, onDone: () {
- _contents = contents..sort((a, b) => compareAsciiUpperCase(pContext.split(a.path).last, pContext.split(b.path).last));
+ _contents = contents..sort((a, b) => compareAsciiUpperCaseNatural(pContext.split(a.path).last, pContext.split(b.path).last));
setState(() {});
});
}
diff --git a/lib/widgets/viewer/info/basic_section.dart b/lib/widgets/viewer/info/basic_section.dart
index 2c949209a..5e599b3a6 100644
--- a/lib/widgets/viewer/info/basic_section.dart
+++ b/lib/widgets/viewer/info/basic_section.dart
@@ -58,7 +58,7 @@ class BasicSection extends StatelessWidget {
}
Widget _buildChips(BuildContext context) {
- final tags = entry.tags.toList()..sort(compareAsciiUpperCase);
+ final tags = entry.tags.toList()..sort(compareAsciiUpperCaseNatural);
final album = entry.directory;
final filters = {
MimeFilter(entry.mimeType),
diff --git a/lib/widgets/viewer/screen_saver_page.dart b/lib/widgets/viewer/screen_saver_page.dart
index 9afc79eac..036f9f179 100644
--- a/lib/widgets/viewer/screen_saver_page.dart
+++ b/lib/widgets/viewer/screen_saver_page.dart
@@ -4,7 +4,6 @@ import 'package:aves/model/settings/enums/slideshow_interval.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
-import 'package:aves/model/source/enums.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/empty.dart';
@@ -29,7 +28,7 @@ class ScreenSaverPage extends StatefulWidget {
State createState() => _ScreenSaverPageState();
}
-class _ScreenSaverPageState extends State {
+class _ScreenSaverPageState extends State with WidgetsBindingObserver {
late final ViewerController _viewerController;
CollectionLens? _slideshowCollection;
@@ -47,24 +46,24 @@ class _ScreenSaverPageState extends State {
);
source.stateNotifier.addListener(_onSourceStateChanged);
_initSlideshowCollection();
- }
-
- void _onSourceStateChanged() {
- if (_slideshowCollection == null) {
- _initSlideshowCollection();
- if (_slideshowCollection != null) {
- setState(() {});
- }
- }
+ WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
source.stateNotifier.removeListener(_onSourceStateChanged);
_viewerController.dispose();
+ WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ if (state == AppLifecycleState.resumed) {
+ _viewerController.autopilot = true;
+ }
+ }
+
@override
Widget build(BuildContext context) {
Widget child;
@@ -102,8 +101,17 @@ class _ScreenSaverPageState extends State {
);
}
+ void _onSourceStateChanged() {
+ if (_slideshowCollection == null) {
+ _initSlideshowCollection();
+ if (_slideshowCollection != null) {
+ setState(() {});
+ }
+ }
+ }
+
void _initSlideshowCollection() {
- if (source.stateNotifier.value != SourceState.ready || _slideshowCollection != null) return;
+ if (!source.isReady || _slideshowCollection != null) return;
final originalCollection = CollectionLens(
source: source,
diff --git a/pubspec.yaml b/pubspec.yaml
index cf390fad3..83374102b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -6,7 +6,7 @@ repository: https://github.com/deckerst/aves
# - github changelog: /CHANGELOG.md
# - play changelog: /whatsnew/whatsnew-en-US
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/1XXX.txt
-version: 1.6.12+78
+version: 1.6.13+79
publish_to: none
environment:
diff --git a/test/fake/android_app_service.dart b/test/fake/android_app_service.dart
index 9ec20dcd7..436d767e9 100644
--- a/test/fake/android_app_service.dart
+++ b/test/fake/android_app_service.dart
@@ -1,7 +1,7 @@
import 'package:aves/services/android_app_service.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeAndroidAppService extends Fake implements AndroidAppService {
@override
diff --git a/test/fake/availability.dart b/test/fake/availability.dart
index cf09187e4..b92e630c3 100644
--- a/test/fake/availability.dart
+++ b/test/fake/availability.dart
@@ -1,6 +1,6 @@
import 'package:aves/model/availability.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeAvesAvailability extends Fake implements AvesAvailability {
@override
diff --git a/test/fake/device_service.dart b/test/fake/device_service.dart
index a2efd54c1..c8afba4ec 100644
--- a/test/fake/device_service.dart
+++ b/test/fake/device_service.dart
@@ -1,6 +1,6 @@
import 'package:aves/services/device_service.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeDeviceService extends Fake implements DeviceService {
@override
diff --git a/test/fake/media_fetch_service.dart b/test/fake/media_fetch_service.dart
new file mode 100644
index 000000000..fa9645ace
--- /dev/null
+++ b/test/fake/media_fetch_service.dart
@@ -0,0 +1,13 @@
+import 'package:aves/model/entry.dart';
+import 'package:aves/services/media/media_fetch_service.dart';
+import 'package:collection/collection.dart';
+import 'package:test/fake.dart';
+
+class FakeMediaFetchService extends Fake implements MediaFetchService {
+ Set entries = {};
+
+ @override
+ Future getEntry(String uri, String? mimeType) async {
+ return entries.firstWhereOrNull((v) => v.uri == uri);
+ }
+}
diff --git a/test/fake/media_store_service.dart b/test/fake/media_store_service.dart
index 1ef2e2bb9..0649476ab 100644
--- a/test/fake/media_store_service.dart
+++ b/test/fake/media_store_service.dart
@@ -2,17 +2,28 @@ import 'package:aves/model/entry.dart';
import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/common/image_op_events.dart';
import 'package:aves/services/media/media_store_service.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeMediaStoreService extends Fake implements MediaStoreService {
- Set entries = {};
+ late Set entries;
+ Duration? latency;
+
+ void reset() {
+ entries = {};
+ latency = null;
+ }
@override
- Future> checkObsoleteContentIds(List knownContentIds) => SynchronousFuture([]);
+ Future> checkObsoleteContentIds(List knownContentIds) async {
+ if (latency != null) await Future.delayed(latency!);
+ return [];
+ }
@override
- Future> checkObsoletePaths(Map knownPathById) => SynchronousFuture([]);
+ Future> checkObsoletePaths(Map knownPathById) async {
+ if (latency != null) await Future.delayed(latency!);
+ return [];
+ }
@override
Stream getEntries(Map knownEntries, {String? directory}) => Stream.fromIterable(entries);
@@ -23,14 +34,15 @@ class FakeMediaStoreService extends Fake implements MediaStoreService {
static int get dateSecs => DateTime.now().millisecondsSinceEpoch ~/ 1000;
- static AvesEntry newImage(String album, String filenameWithoutExtension) {
- final id = nextId;
+ static AvesEntry newImage(String album, String filenameWithoutExtension, {int? id, int? contentId}) {
+ id ??= nextId;
+ contentId ??= id;
final date = dateSecs;
return AvesEntry(
id: id,
- uri: 'content://media/external/images/media/$id',
+ uri: 'content://media/external/images/media/$contentId',
path: '$album/$filenameWithoutExtension.jpg',
- contentId: id,
+ contentId: contentId,
pageId: null,
sourceMimeType: MimeTypes.jpeg,
width: 360,
diff --git a/test/fake/metadata_db.dart b/test/fake/metadata_db.dart
index 665b1bb7b..49e36feba 100644
--- a/test/fake/metadata_db.dart
+++ b/test/fake/metadata_db.dart
@@ -7,7 +7,7 @@ import 'package:aves/model/metadata/address.dart';
import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/metadata/trash.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeMetadataDb extends Fake implements MetadataDb {
static int _lastId = 0;
diff --git a/test/fake/metadata_fetch_service.dart b/test/fake/metadata_fetch_service.dart
index 556a75bf1..1891146ea 100644
--- a/test/fake/metadata_fetch_service.dart
+++ b/test/fake/metadata_fetch_service.dart
@@ -2,7 +2,7 @@ import 'package:aves/model/entry.dart';
import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/services/metadata/metadata_fetch_service.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeMetadataFetchService extends Fake implements MetadataFetchService {
final Map _metaMap = {};
diff --git a/test/fake/report_service.dart b/test/fake/report_service.dart
index c4e8d1701..bead6f09b 100644
--- a/test/fake/report_service.dart
+++ b/test/fake/report_service.dart
@@ -1,6 +1,5 @@
import 'package:aves_report/aves_report.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
class FakeReportService extends ReportService {
@override
diff --git a/test/fake/storage_service.dart b/test/fake/storage_service.dart
index 754860374..49714a4b4 100644
--- a/test/fake/storage_service.dart
+++ b/test/fake/storage_service.dart
@@ -1,7 +1,7 @@
import 'package:aves/services/storage_service.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeStorageService extends Fake implements StorageService {
static const primaryRootAlbum = '/storage/emulated/0';
diff --git a/test/fake/window_service.dart b/test/fake/window_service.dart
index a87e5c2dc..500d275c3 100644
--- a/test/fake/window_service.dart
+++ b/test/fake/window_service.dart
@@ -1,7 +1,7 @@
import 'package:aves/services/window_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
-import 'package:flutter_test/flutter_test.dart';
+import 'package:test/fake.dart';
class FakeWindowService extends Fake implements WindowService {
@override
diff --git a/test/model/collection_source_test.dart b/test/model/collection_source_test.dart
index 029244b25..9858c8114 100644
--- a/test/model/collection_source_test.dart
+++ b/test/model/collection_source_test.dart
@@ -10,11 +10,12 @@ import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/metadata/address.dart';
import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/settings/settings.dart';
-import 'package:aves/model/source/enums.dart';
+import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/media_store_source.dart';
import 'package:aves/services/android_app_service.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/services/device_service.dart';
+import 'package:aves/services/media/media_fetch_service.dart';
import 'package:aves/services/media/media_store_service.dart';
import 'package:aves/services/metadata/metadata_fetch_service.dart';
import 'package:aves/services/storage_service.dart';
@@ -29,6 +30,7 @@ import 'package:path/path.dart' as p;
import '../fake/android_app_service.dart';
import '../fake/availability.dart';
import '../fake/device_service.dart';
+import '../fake/media_fetch_service.dart';
import '../fake/media_store_service.dart';
import '../fake/metadata_db.dart';
import '../fake/metadata_fetch_service.dart';
@@ -49,7 +51,7 @@ void main() {
countryName: 'AUS',
);
- setUp(() async {
+ setUpAll(() async {
// specify Posix style path context for consistent behaviour when running tests on Windows
getIt.registerLazySingleton(() => p.Context(style: p.Style.posix));
getIt.registerLazySingleton(FakeAvesAvailability.new);
@@ -57,6 +59,7 @@ void main() {
getIt.registerLazySingleton(FakeAndroidAppService.new);
getIt.registerLazySingleton(FakeDeviceService.new);
+ getIt.registerLazySingleton(FakeMediaFetchService.new);
getIt.registerLazySingleton(FakeMediaStoreService.new);
getIt.registerLazySingleton(FakeMetadataFetchService.new);
getIt.registerLazySingleton(FakeReportService.new);
@@ -65,9 +68,14 @@ void main() {
await settings.init(monitorPlatformSettings: false);
settings.canUseAnalysisService = false;
+ await androidFileUtils.init();
});
- tearDown(() async {
+ setUp(() async {
+ (getIt() as FakeMediaStoreService).reset();
+ });
+
+ tearDownAll(() async {
await getIt.reset();
});
@@ -75,7 +83,7 @@ void main() {
final source = MediaStoreSource();
final readyCompleter = Completer();
source.stateNotifier.addListener(() {
- if (source.stateNotifier.value == SourceState.ready) {
+ if (source.isReady) {
readyCompleter.complete();
}
});
@@ -84,6 +92,26 @@ void main() {
return source;
}
+ test('initial load v. refresh race condition', () async {
+ const latency = Duration(milliseconds: 100);
+
+ final loadEntry = FakeMediaStoreService.newImage(testAlbum, 'image1', id: -1, contentId: 1);
+ final refreshEntry = FakeMediaStoreService.newImage(testAlbum, 'image1', id: -1, contentId: 1);
+ (mediaStoreService as FakeMediaStoreService)
+ ..entries = {loadEntry}
+ ..latency = latency;
+ (mediaFetchService as FakeMediaFetchService).entries = {refreshEntry};
+
+ final source = MediaStoreSource();
+ unawaited(source.init());
+ await Future.delayed(const Duration(milliseconds: 10));
+ expect(source.initState, SourceInitializationState.full);
+ await source.refreshUris({refreshEntry.uri});
+
+ await Future.delayed(const Duration(seconds: 1));
+ expect(source.allEntries.length, 1);
+ });
+
test('album/country/tag hidden on launch when their items are hidden by entry prop', () async {
settings.hiddenFilters = {const AlbumFilter(testAlbum, 'whatever')};
@@ -336,7 +364,6 @@ void main() {
FakeMediaStoreService.newImage('${FakeStorageService.primaryPath}Pictures/Arendt', '1'),
};
- await androidFileUtils.init();
final source = await _initSource();
await tester.pumpWidget(
Builder(
diff --git a/untranslated.json b/untranslated.json
index bbe31db7f..b1efafdb6 100644
--- a/untranslated.json
+++ b/untranslated.json
@@ -22,14 +22,6 @@
"settingsViewerGestureSideTapNext"
],
- "pt": [
- "entryInfoActionEditDescription",
- "filterRecentlyAddedLabel",
- "editEntryDescriptionDialogTitle",
- "settingsConfirmationAfterMoveToBinItems",
- "settingsViewerGestureSideTapNext"
- ],
-
"ru": [
"entryInfoActionEditDescription",
"filterOnThisDayLabel",
diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US
index 756c775df..30000cd00 100644
--- a/whatsnew/whatsnew-en-US
+++ b/whatsnew/whatsnew-en-US
@@ -1,4 +1,4 @@
-In v1.6.12:
+In v1.6.13:
- play your HEIC motion photos
- find recently downloaded images with the `recently added` filter
- enjoy the app in Dutch