Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
deckerst committed Aug 29, 2022
2 parents d0719f4 + c97c1fa commit 369fce9
Show file tree
Hide file tree
Showing 33 changed files with 145 additions and 67 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.

## <a id="unreleased"></a>[Unreleased]

## <a id="v1.6.13"></a>[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

## <a id="v1.6.12"></a>[v1.6.12] - 2022-08-27

### Added
Expand Down
5 changes: 5 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/1079.txt
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions lib/l10n/app_pt.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -257,6 +259,8 @@

"locationPickerUseThisLocationButton": "Usar essa localização",

"editEntryDescriptionDialogTitle": "Descrição",

"editEntryRatingDialogTitle": "Avaliação",

"removeEntryMetadataDialogTitle": "Remoção de metadados",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion lib/model/entry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? '');
}

Expand Down
2 changes: 1 addition & 1 deletion lib/model/filters/filters.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ abstract class CollectionFilter extends Equatable implements Comparable<Collecti
int compareTo(CollectionFilter other) {
final c = displayPriority.compareTo(other.displayPriority);
// assume we compare context-independent labels
return c != 0 ? c : compareAsciiUpperCase(universalLabel, other.universalLabel);
return c != 0 ? c : compareAsciiUpperCaseNatural(universalLabel, other.universalLabel);
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/model/source/album.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ mixin AlbumMixin on SourceBase {
int compareAlbumsByName(String a, String b) {
final ua = getAlbumDisplayName(null, a);
final ub = getAlbumDisplayName(null, b);
final c = compareAsciiUpperCase(ua, ub);
final c = compareAsciiUpperCaseNatural(ua, ub);
if (c != 0) return c;
final va = androidFileUtils.getStorageVolume(a)?.path ?? '';
final vb = androidFileUtils.getStorageVolume(b)?.path ?? '';
return compareAsciiUpperCase(va, vb);
return compareAsciiUpperCaseNatural(va, vb);
}

void notifyAlbumsChanged() {
Expand Down
8 changes: 7 additions & 1 deletion lib/model/source/collection_source.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ mixin SourceBase {

ValueNotifier<SourceState> stateNotifier = ValueNotifier(SourceState.ready);

set state(SourceState value) => stateNotifier.value = value;

SourceState get state => stateNotifier.value;

bool get isReady => state == SourceState.ready;

ValueNotifier<ProgressEvent> progressNotifier = ValueNotifier(const ProgressEvent(done: 0, total: 0));

void setProgress({required int done, required int total}) => progressNotifier.value = ProgressEvent(done: done, total: total);
Expand Down Expand Up @@ -430,7 +436,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
updateDerivedFilters(todoEntries);
}
}
stateNotifier.value = SourceState.ready;
state = SourceState.ready;
}

// monitoring
Expand Down
4 changes: 2 additions & 2 deletions lib/model/source/location.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions lib/model/source/media_store_source.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class MediaStoreSource extends CollectionSource {

Future<void> _loadEssentials() async {
final stopwatch = Stopwatch()..start();
stateNotifier.value = SourceState.loading;
state = SourceState.loading;
await metadataDb.init();
await favourites.init();
await covers.init();
Expand All @@ -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<AvesEntry> topEntries = {};
Expand Down Expand Up @@ -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
Expand All @@ -216,7 +216,7 @@ class MediaStoreSource extends CollectionSource {
// sometimes yields an entry with its temporary path: `/data/sec/camera/!@#$%^..._temp.jpg`
@override
Future<Set<String>> refreshUris(Set<String> 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) {
Expand Down
4 changes: 2 additions & 2 deletions lib/model/source/tag.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions lib/services/analysis_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -151,7 +151,7 @@ class Analyzer {
}

void _onSourceStateChanged() {
if (sourceState == SourceState.ready) {
if (_source.isReady) {
_refreshApp();
_serviceStateNotifier.value = AnalyzerState.stopping;
}
Expand Down
3 changes: 1 addition & 2 deletions lib/widget_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -64,7 +63,7 @@ Future<AvesEntry?> _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();
}
});
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/common/app_bar/app_bar_subtitle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/dialogs/entry_editors/edit_tags_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
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);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class _CreateAlbumDialogState extends State<CreateAlbumDialog> {
final volumeTiles = <Widget>[];
if (_allVolumes.length > 1) {
final byPrimary = groupBy<StorageVolume, bool>(_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([
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ class _HomePageState extends State<HomePage> {
// 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();
}
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/settings/privacy/file_picker/file_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class _FilePickerState extends State<FilePicker> {
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(() {});
});
}
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/viewer/info/basic_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
32 changes: 20 additions & 12 deletions lib/widgets/viewer/screen_saver_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -29,7 +28,7 @@ class ScreenSaverPage extends StatefulWidget {
State<ScreenSaverPage> createState() => _ScreenSaverPageState();
}

class _ScreenSaverPageState extends State<ScreenSaverPage> {
class _ScreenSaverPageState extends State<ScreenSaverPage> with WidgetsBindingObserver {
late final ViewerController _viewerController;
CollectionLens? _slideshowCollection;

Expand All @@ -47,24 +46,24 @@ class _ScreenSaverPageState extends State<ScreenSaverPage> {
);
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;
Expand Down Expand Up @@ -102,8 +101,17 @@ class _ScreenSaverPageState extends State<ScreenSaverPage> {
);
}

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,
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion test/fake/android_app_service.dart
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/fake/availability.dart
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/fake/device_service.dart
Original file line number Diff line number Diff line change
@@ -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
Expand Down
13 changes: 13 additions & 0 deletions test/fake/media_fetch_service.dart
Original file line number Diff line number Diff line change
@@ -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<AvesEntry> entries = {};

@override
Future<AvesEntry?> getEntry(String uri, String? mimeType) async {
return entries.firstWhereOrNull((v) => v.uri == uri);
}
}
Loading

0 comments on commit 369fce9

Please sign in to comment.