diff --git a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageFileHandler.java b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageFileHandler.java index 2b94ab194..3240e3d4a 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageFileHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/ImageFileHandler.java @@ -32,7 +32,11 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler { public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { switch (call.method) { case "getImageEntries": - new Thread(() -> mediaStoreStreamHandler.fetchAll(activity)).start(); + new Thread(() -> { + String sortBy = call.argument("sort"); + String groupBy = call.argument("group"); + mediaStoreStreamHandler.fetchAll(activity, sortBy, groupBy); + }).start(); result.success(null); break; case "getImageEntry": diff --git a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MediaStoreStreamHandler.java b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MediaStoreStreamHandler.java index 6847b59f8..41a51b97e 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MediaStoreStreamHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MediaStoreStreamHandler.java @@ -22,9 +22,9 @@ public class MediaStoreStreamHandler implements EventChannel.StreamHandler { // nothing } - void fetchAll(Activity activity) { + void fetchAll(Activity activity, String sortBy, String groupBy) { Handler handler = new Handler(Looper.getMainLooper()); - new MediaStoreImageProvider().fetchAll(activity, (entry) -> handler.post(() -> eventSink.success(entry))); // 350ms + new MediaStoreImageProvider().fetchAll(activity, sortBy, groupBy, (entry) -> handler.post(() -> eventSink.success(entry))); // 350ms handler.post(() -> eventSink.endOfStream()); } } \ No newline at end of file diff --git a/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java b/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java index aa4fd1ad1..60ac722f0 100644 --- a/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java +++ b/android/app/src/main/java/deckers/thibault/aves/model/provider/MediaStoreImageProvider.java @@ -68,9 +68,39 @@ public class MediaStoreImageProvider extends ImageProvider { MediaStore.Video.Media.ORIENTATION, } : new String[0]).flatMap(Stream::of).toArray(String[]::new); - public void fetchAll(Activity activity, NewEntryHandler newEntryHandler) { - fetchFrom(activity, newEntryHandler, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION); - fetchFrom(activity, newEntryHandler, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, VIDEO_PROJECTION); + public void fetchAll(Activity activity, final String sortBy, final String groupBy, NewEntryHandler newEntryHandler) { + String orderBy; + switch (sortBy) { + case "size": + orderBy = MediaStore.MediaColumns.SIZE + " DESC"; + break; + case "name": + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + orderBy = MediaStore.MediaColumns.RELATIVE_PATH + ", " + MediaStore.MediaColumns.BUCKET_DISPLAY_NAME + ", " + MediaStore.MediaColumns.DISPLAY_NAME; + } else { + orderBy = MediaStore.MediaColumns.DATA; + } + break; + default: + case "date": + switch (groupBy) { + case "album": + // TODO TLAD find album order first + case "month": + case "day": + default: + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + orderBy = MediaStore.MediaColumns.DATE_TAKEN + " DESC"; + } else { + orderBy = MediaStore.MediaColumns.DATE_MODIFIED + " DESC"; + } + break; + } + break; + } + + fetchFrom(activity, newEntryHandler, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION, orderBy); + fetchFrom(activity, newEntryHandler, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, VIDEO_PROJECTION, orderBy); } @Override @@ -83,10 +113,10 @@ public class MediaStoreImageProvider extends ImageProvider { }; if (mimeType.startsWith(MimeTypes.IMAGE)) { Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id); - entryCount = fetchFrom(context, onSuccess, contentUri, IMAGE_PROJECTION); + entryCount = fetchFrom(context, onSuccess, contentUri, IMAGE_PROJECTION, null); } else if (mimeType.startsWith(MimeTypes.VIDEO)) { Uri contentUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id); - entryCount = fetchFrom(context, onSuccess, contentUri, VIDEO_PROJECTION); + entryCount = fetchFrom(context, onSuccess, contentUri, VIDEO_PROJECTION, null); } if (entryCount == 0) { callback.onFailure(new Exception("failed to fetch entry at uri=" + uri)); @@ -94,8 +124,7 @@ public class MediaStoreImageProvider extends ImageProvider { } @SuppressLint("InlinedApi") - private int fetchFrom(final Context context, NewEntryHandler newEntryHandler, final Uri contentUri, String[] projection) { - String orderBy = MediaStore.MediaColumns.DATE_TAKEN + " DESC"; + private int fetchFrom(final Context context, NewEntryHandler newEntryHandler, final Uri contentUri, String[] projection, String orderBy) { int entryCount = 0; final boolean needDuration = projection == VIDEO_PROJECTION; diff --git a/lib/services/image_file_service.dart b/lib/services/image_file_service.dart index bf93a41dd..dfc6228c3 100644 --- a/lib/services/image_file_service.dart +++ b/lib/services/image_file_service.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; +import 'package:aves/model/collection_lens.dart'; import 'package:aves/model/image_entry.dart'; import 'package:aves/services/service_policy.dart'; import 'package:flutter/foundation.dart'; @@ -14,9 +15,12 @@ class ImageFileService { static final StreamsChannel byteChannel = StreamsChannel('deckers.thibault/aves/imagebytestream'); static final StreamsChannel opChannel = StreamsChannel('deckers.thibault/aves/imageopstream'); - static Future getImageEntries() async { + static Future getImageEntries(SortFactor sort, GroupFactor group) async { try { - await platform.invokeMethod('getImageEntries'); + await platform.invokeMethod('getImageEntries', { + 'sort': sort.toString().replaceAll('SortFactor.', ''), + 'group': group.toString().replaceAll('GroupFactor.', ''), + }); } on PlatformException catch (e) { debugPrint('getImageEntries failed with code=${e.code}, exception=${e.message}, details=${e.details}'); } diff --git a/lib/widgets/common/data_providers/media_store_collection_provider.dart b/lib/widgets/common/data_providers/media_store_collection_provider.dart index 6ab7007e3..d43678af1 100644 --- a/lib/widgets/common/data_providers/media_store_collection_provider.dart +++ b/lib/widgets/common/data_providers/media_store_collection_provider.dart @@ -63,6 +63,6 @@ class MediaStoreSource { // TODO split image fetch AND/OR cache fetch across sessions debugPrint('$runtimeType stream start at ${stopwatch.elapsed.inMilliseconds}ms'); - await ImageFileService.getImageEntries(); // 460ms + await ImageFileService.getImageEntries(settings.collectionSortFactor, settings.collectionGroupFactor); // 460ms } }