#249 Pick: allow selecting multiple items according to request intent

This commit is contained in:
Thibault Deckers 2022-05-16 11:48:49 +09:00
parent 406e99b13f
commit 16c0ba90f9
17 changed files with 143 additions and 96 deletions

View file

@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
- Collection: thumbnail overlay tag icon - Collection: thumbnail overlay tag icon
- Collection: fast-scrolling shows breadcrumbs from groups - Collection: fast-scrolling shows breadcrumbs from groups
- Settings: search - Settings: search
- Pick: allow selecting multiple items according to request intent
- `huawei` app flavor (Petal Maps, no Crashlytics) - `huawei` app flavor (Petal Maps, no Crashlytics)
### Changed ### Changed

View file

@ -177,12 +177,12 @@ dependencies {
android.productFlavors.each { flavor -> android.productFlavors.each { flavor ->
def tasks = gradle.startParameter.taskRequests.toString().toLowerCase() def tasks = gradle.startParameter.taskRequests.toString().toLowerCase()
if (tasks.contains(flavor.name) && flavor.ext.useCrashlytics) { if (tasks.contains(flavor.name) && flavor.ext.useCrashlytics) {
println("Building flavor with Crashlytics [${flavor.name}] - applying plugin") println("Building flavor [${flavor.name}] with Crashlytics plugin")
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'com.google.firebase.crashlytics'
} }
if (tasks.contains(flavor.name) && flavor.ext.useHMS) { if (tasks.contains(flavor.name) && flavor.ext.useHMS) {
println("Building flavor with HMS [${flavor.name}] - applying plugin") println("Building flavor [${flavor.name}] with HMS plugin")
apply plugin: 'com.huawei.agconnect' apply plugin: 'com.huawei.agconnect'
} }
} }

View file

@ -2,6 +2,7 @@ package deckers.thibault.aves
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.SearchManager import android.app.SearchManager
import android.content.ClipData
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -16,7 +17,6 @@ import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.calls.* import deckers.thibault.aves.channel.calls.*
import deckers.thibault.aves.channel.streams.* import deckers.thibault.aves.channel.streams.*
import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.PermissionManager
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodCall
@ -222,6 +222,7 @@ class MainActivity : FlutterActivity() {
return hashMapOf( return hashMapOf(
INTENT_DATA_KEY_ACTION to INTENT_ACTION_PICK, INTENT_DATA_KEY_ACTION to INTENT_ACTION_PICK,
INTENT_DATA_KEY_MIME_TYPE to intent.type, INTENT_DATA_KEY_MIME_TYPE to intent.type,
INTENT_DATA_KEY_ALLOW_MULTIPLE to (intent.extras?.getBoolean(Intent.EXTRA_ALLOW_MULTIPLE) ?: false),
) )
} }
Intent.ACTION_SEARCH -> { Intent.ACTION_SEARCH -> {
@ -246,10 +247,20 @@ class MainActivity : FlutterActivity() {
} }
private fun pick(call: MethodCall) { private fun pick(call: MethodCall) {
val pickedUri = call.argument<String>("uri") val pickedUris = call.argument<List<String>>("uris")
if (pickedUri != null) { if (pickedUris != null && pickedUris.isNotEmpty()) {
val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(context, Uri.parse(uriString)) }
val intent = Intent().apply { val intent = Intent().apply {
data = Uri.parse(pickedUri) val firstUri = toUri(pickedUris.first())
if (pickedUris.size == 1) {
data = firstUri
} else {
clipData = ClipData.newUri(contentResolver, null, firstUri).apply {
pickedUris.drop(1).forEach {
addItem(ClipData.Item(toUri(it)))
}
}
}
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
} }
setResult(RESULT_OK, intent) setResult(RESULT_OK, intent)
@ -307,6 +318,7 @@ class MainActivity : FlutterActivity() {
const val INTENT_DATA_KEY_ACTION = "action" const val INTENT_DATA_KEY_ACTION = "action"
const val INTENT_DATA_KEY_FILTERS = "filters" const val INTENT_DATA_KEY_FILTERS = "filters"
const val INTENT_DATA_KEY_MIME_TYPE = "mimeType" const val INTENT_DATA_KEY_MIME_TYPE = "mimeType"
const val INTENT_DATA_KEY_ALLOW_MULTIPLE = "allowMultiple"
const val INTENT_DATA_KEY_PAGE = "page" const val INTENT_DATA_KEY_PAGE = "page"
const val INTENT_DATA_KEY_URI = "uri" const val INTENT_DATA_KEY_URI = "uri"
const val INTENT_DATA_KEY_QUERY = "query" const val INTENT_DATA_KEY_QUERY = "query"

View file

@ -216,7 +216,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
try { try {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
if (clipboard != null) { if (clipboard != null) {
val clip = ClipData.newUri(context.contentResolver, label, getShareableUri(uri)) val clip = ClipData.newUri(context.contentResolver, label, getShareableUri(context, uri))
clipboard.setPrimaryClip(clip) clipboard.setPrimaryClip(clip)
result.success(true) result.success(true)
} else { } else {
@ -239,7 +239,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_EDIT) val intent = Intent(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.setDataAndType(getShareableUri(uri), mimeType) .setDataAndType(getShareableUri(context, uri), mimeType)
val started = safeStartActivityChooser(title, intent) val started = safeStartActivityChooser(title, intent)
result.success(started) result.success(started)
@ -256,7 +256,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.setDataAndType(getShareableUri(uri), mimeType) .setDataAndType(getShareableUri(context, uri), mimeType)
val started = safeStartActivityChooser(title, intent) val started = safeStartActivityChooser(title, intent)
result.success(started) result.success(started)
@ -286,7 +286,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_ATTACH_DATA) val intent = Intent(Intent.ACTION_ATTACH_DATA)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.setDataAndType(getShareableUri(uri), mimeType) .setDataAndType(getShareableUri(context, uri), mimeType)
val started = safeStartActivityChooser(title, intent) val started = safeStartActivityChooser(title, intent)
result.success(started) result.success(started)
@ -311,7 +311,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_SEND) val intent = Intent(Intent.ACTION_SEND)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.setType(mimeType) .setType(mimeType)
.putExtra(Intent.EXTRA_STREAM, getShareableUri(uri)) .putExtra(Intent.EXTRA_STREAM, getShareableUri(context, uri))
safeStartActivityChooser(title, intent) safeStartActivityChooser(title, intent)
} else { } else {
var mimeType = "*/*" var mimeType = "*/*"
@ -368,18 +368,6 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
return false return false
} }
private fun getShareableUri(uri: Uri): Uri? {
return when (uri.scheme?.lowercase(Locale.ROOT)) {
ContentResolver.SCHEME_FILE -> {
uri.path?.let { path ->
val authority = "${context.applicationContext.packageName}.file_provider"
FileProvider.getUriForFile(context, authority, File(path))
}
}
else -> uri
}
}
// shortcuts // shortcuts
private fun pinShortcut(call: MethodCall, result: MethodChannel.Result) { private fun pinShortcut(call: MethodCall, result: MethodChannel.Result) {
@ -443,5 +431,17 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
companion object { companion object {
private val LOG_TAG = LogUtils.createTag<AppAdapterHandler>() private val LOG_TAG = LogUtils.createTag<AppAdapterHandler>()
const val CHANNEL = "deckers.thibault/aves/app" const val CHANNEL = "deckers.thibault/aves/app"
fun getShareableUri(context: Context, uri: Uri): Uri? {
return when (uri.scheme?.lowercase(Locale.ROOT)) {
ContentResolver.SCHEME_FILE -> {
uri.path?.let { path ->
val authority = "${context.applicationContext.packageName}.file_provider"
FileProvider.getUriForFile(context, authority, File(path))
}
}
else -> uri
}
}
} }
} }

View file

@ -1,11 +1,13 @@
enum AppMode { main, pickMediaExternal, pickMediaInternal, pickFilterInternal, view } enum AppMode { main, pickSingleMediaExternal, pickMultipleMediaExternal, pickMediaInternal, pickFilterInternal, view }
extension ExtraAppMode on AppMode { extension ExtraAppMode on AppMode {
bool get canSearch => this == AppMode.main || this == AppMode.pickMediaExternal; bool get canSearch => this == AppMode.main || this == AppMode.pickSingleMediaExternal || this == AppMode.pickMultipleMediaExternal;
bool get canSelect => this == AppMode.main; bool get canSelectMedia => this == AppMode.main || this == AppMode.pickMultipleMediaExternal;
bool get hasDrawer => this == AppMode.main || this == AppMode.pickMediaExternal; bool get canSelectFilter => this == AppMode.main;
bool get isPickingMedia => this == AppMode.pickMediaExternal || this == AppMode.pickMediaInternal; bool get hasDrawer => this == AppMode.main || this == AppMode.pickSingleMediaExternal || this == AppMode.pickMultipleMediaExternal;
bool get isPickingMedia => this == AppMode.pickSingleMediaExternal || this == AppMode.pickMultipleMediaExternal || this == AppMode.pickMediaInternal;
} }

View file

@ -15,10 +15,10 @@ class ViewerService {
return {}; return {};
} }
static Future<void> pick(String uri) async { static Future<void> pick(List<String> uris) async {
try { try {
await platform.invokeMethod('pick', <String, dynamic>{ await platform.invokeMethod('pick', <String, dynamic>{
'uri': uri, 'uris': uris,
}); });
} on PlatformException catch (e, stack) { } on PlatformException catch (e, stack) {
await reportService.recordError(e, stack); await reportService.recordError(e, stack);

View file

@ -10,6 +10,7 @@ class AIcons {
static const IconData accessibility = Icons.accessibility_new_outlined; static const IconData accessibility = Icons.accessibility_new_outlined;
static const IconData android = Icons.android; static const IconData android = Icons.android;
static const IconData app = Icons.apps_outlined; static const IconData app = Icons.apps_outlined;
static const IconData apply = Icons.done_outlined;
static const IconData bin = Icons.delete_outlined; static const IconData bin = Icons.delete_outlined;
static const IconData broken = Icons.broken_image_outlined; static const IconData broken = Icons.broken_image_outlined;
static const IconData checked = Icons.done_outlined; static const IconData checked = Icons.done_outlined;

View file

@ -184,7 +184,8 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
case AppLifecycleState.inactive: case AppLifecycleState.inactive:
switch (appModeNotifier.value) { switch (appModeNotifier.value) {
case AppMode.main: case AppMode.main:
case AppMode.pickMediaExternal: case AppMode.pickSingleMediaExternal:
case AppMode.pickMultipleMediaExternal:
_saveTopEntries(); _saveTopEntries();
break; break;
case AppMode.pickMediaInternal: case AppMode.pickMediaInternal:

View file

@ -250,7 +250,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
...(isSelecting ? selectionMenuActions : browsingMenuActions).where(isVisible).map( ...(isSelecting ? selectionMenuActions : browsingMenuActions).where(isVisible).map(
(action) => _toMenuItem(action, enabled: canApply(action), selection: selection), (action) => _toMenuItem(action, enabled: canApply(action), selection: selection),
), ),
if (isSelecting && !isTrash) if (isSelecting && !isTrash && appMode == AppMode.main)
PopupMenuItem<EntrySetAction>( PopupMenuItem<EntrySetAction>(
enabled: canApplyEditActions, enabled: canApplyEditActions,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,

View file

@ -216,10 +216,9 @@ class _CollectionSectionedContentState extends State<_CollectionSectionedContent
child: scrollView, child: scrollView,
); );
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
final selector = GridSelectionGestureDetector( final selector = GridSelectionGestureDetector(
scrollableKey: scrollableKey, scrollableKey: scrollableKey,
selectable: isMainMode, selectable: context.select<ValueNotifier<AppMode>, bool>((v) => v.value.canSelectMedia),
items: collection.sortedEntries, items: collection.sortedEntries,
scrollController: scrollController, scrollController: scrollController,
appBarHeightNotifier: appBarHeightNotifier, appBarHeightNotifier: appBarHeightNotifier,

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/app_mode.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/query.dart';
@ -9,11 +10,14 @@ import 'package:aves/model/selection.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/services/viewer_service.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/collection/collection_grid.dart'; import 'package:aves/widgets/collection/collection_grid.dart';
import 'package:aves/widgets/common/basic/draggable_scrollbar.dart'; import 'package:aves/widgets/common/basic/draggable_scrollbar.dart';
import 'package:aves/widgets/common/basic/insets.dart'; import 'package:aves/widgets/common/basic/insets.dart';
import 'package:aves/widgets/common/behaviour/double_back_pop.dart'; import 'package:aves/widgets/common/behaviour/double_back_pop.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/common/providers/query_provider.dart'; import 'package:aves/widgets/common/providers/query_provider.dart';
import 'package:aves/widgets/common/providers/selection_provider.dart'; import 'package:aves/widgets/common/providers/selection_provider.dart';
@ -75,61 +79,78 @@ class _CollectionPageState extends State<CollectionPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appMode = context.watch<ValueNotifier<AppMode>>().value;
final liveFilter = _collection.filters.firstWhereOrNull((v) => v is QueryFilter && v.live) as QueryFilter?; final liveFilter = _collection.filters.firstWhereOrNull((v) => v is QueryFilter && v.live) as QueryFilter?;
return MediaQueryDataProvider( return MediaQueryDataProvider(
child: Selector<Settings, bool>( child: SelectionProvider<AvesEntry>(
selector: (context, s) => s.showBottomNavigationBar, child: Selector<Selection<AvesEntry>, bool>(
builder: (context, showBottomNavigationBar, child) { selector: (context, selection) => selection.selectedItems.isNotEmpty,
return NotificationListener<DraggableScrollBarNotification>( builder: (context, hasSelection, child) {
onNotification: (notification) { return Selector<Settings, bool>(
_draggableScrollBarEventStreamController.add(notification.event); selector: (context, s) => s.showBottomNavigationBar,
return false; builder: (context, showBottomNavigationBar, child) {
}, return NotificationListener<DraggableScrollBarNotification>(
child: Scaffold( onNotification: (notification) {
body: SelectionProvider<AvesEntry>( _draggableScrollBarEventStreamController.add(notification.event);
child: QueryProvider( return false;
initialQuery: liveFilter?.query, },
child: Builder( child: Scaffold(
builder: (context) => WillPopScope( body: QueryProvider(
onWillPop: () { initialQuery: liveFilter?.query,
final selection = context.read<Selection<AvesEntry>>(); child: Builder(
if (selection.isSelecting) { builder: (context) => WillPopScope(
selection.browse(); onWillPop: () {
return SynchronousFuture(false); final selection = context.read<Selection<AvesEntry>>();
} if (selection.isSelecting) {
return SynchronousFuture(true); selection.browse();
}, return SynchronousFuture(false);
child: DoubleBackPopScope( }
child: GestureAreaProtectorStack( return SynchronousFuture(true);
child: SafeArea( },
bottom: false, child: DoubleBackPopScope(
child: ChangeNotifierProvider<CollectionLens>.value( child: GestureAreaProtectorStack(
value: _collection, child: SafeArea(
child: const CollectionGrid( bottom: false,
// key is expected by test driver child: ChangeNotifierProvider<CollectionLens>.value(
key: Key('collection-grid'), value: _collection,
settingsRouteKey: CollectionPage.routeName, child: const CollectionGrid(
// key is expected by test driver
key: Key('collection-grid'),
settingsRouteKey: CollectionPage.routeName,
),
),
), ),
), ),
), ),
), ),
), ),
), ),
floatingActionButton: appMode == AppMode.pickMultipleMediaExternal && hasSelection
? FloatingActionButton(
tooltip: context.l10n.collectionPickPageTitle,
onPressed: () {
final items = context.read<Selection<AvesEntry>>().selectedItems;
final uris = items.map((entry) => entry.uri).toList();
ViewerService.pick(uris);
},
child: const Icon(AIcons.apply),
)
: null,
drawer: AppDrawer(currentCollection: _collection),
bottomNavigationBar: showBottomNavigationBar
? AppBottomNavBar(
events: _draggableScrollBarEventStreamController.stream,
currentCollection: _collection,
)
: null,
resizeToAvoidBottomInset: false,
extendBody: true,
), ),
), );
), },
drawer: AppDrawer(currentCollection: _collection), );
bottomNavigationBar: showBottomNavigationBar },
? AppBottomNavBar( ),
events: _draggableScrollBarEventStreamController.stream,
currentCollection: _collection,
)
: null,
resizeToAvoidBottomInset: false,
extendBody: true,
),
);
},
), ),
); );
} }

View file

@ -56,7 +56,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
case EntrySetAction.configureView: case EntrySetAction.configureView:
return true; return true;
case EntrySetAction.select: case EntrySetAction.select:
return appMode.canSelect && !isSelecting; return appMode.canSelectMedia && !isSelecting;
case EntrySetAction.selectAll: case EntrySetAction.selectAll:
return isSelecting && selectedItemCount < itemCount; return isSelecting && selectedItemCount < itemCount;
case EntrySetAction.selectNone: case EntrySetAction.selectNone:
@ -69,7 +69,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
case EntrySetAction.addShortcut: case EntrySetAction.addShortcut:
return appMode == AppMode.main && !isSelecting && device.canPinShortcut && !isTrash; return appMode == AppMode.main && !isSelecting && device.canPinShortcut && !isTrash;
case EntrySetAction.emptyBin: case EntrySetAction.emptyBin:
return isTrash; return appMode == AppMode.main && isTrash;
// browsing or selecting // browsing or selecting
case EntrySetAction.map: case EntrySetAction.map:
case EntrySetAction.stats: case EntrySetAction.stats:

View file

@ -43,8 +43,12 @@ class InteractiveTile extends StatelessWidget {
_goToViewer(context); _goToViewer(context);
} }
break; break;
case AppMode.pickMediaExternal: case AppMode.pickSingleMediaExternal:
ViewerService.pick(entry.uri); ViewerService.pick([entry.uri]);
break;
case AppMode.pickMultipleMediaExternal:
final selection = context.read<Selection<AvesEntry>>();
selection.toggleSelection(entry);
break; break;
case AppMode.pickMediaInternal: case AppMode.pickMediaInternal:
Navigator.pop(context, entry); Navigator.pop(context, entry);

View file

@ -52,7 +52,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
case ChipSetAction.configureView: case ChipSetAction.configureView:
return true; return true;
case ChipSetAction.select: case ChipSetAction.select:
return appMode.canSelect && !isSelecting; return appMode.canSelectFilter && !isSelecting;
case ChipSetAction.selectAll: case ChipSetAction.selectAll:
return isSelecting && selectedItemCount < itemCount; return isSelecting && selectedItemCount < itemCount;
case ChipSetAction.selectNone: case ChipSetAction.selectNone:

View file

@ -401,10 +401,9 @@ class _FilterSectionedContentState<T extends CollectionFilter> extends State<_Fi
child: scrollView, child: scrollView,
); );
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
final selector = GridSelectionGestureDetector<FilterGridItem<T>>( final selector = GridSelectionGestureDetector<FilterGridItem<T>>(
scrollableKey: scrollableKey, scrollableKey: scrollableKey,
selectable: isMainMode && widget.selectable, selectable: context.select<ValueNotifier<AppMode>, bool>((v) => v.value.canSelectFilter) && widget.selectable,
items: visibleSections.values.expand((v) => v).toList(), items: visibleSections.values.expand((v) => v).toList(),
scrollController: scrollController, scrollController: scrollController,
appBarHeightNotifier: appBarHeightNotifier, appBarHeightNotifier: appBarHeightNotifier,

View file

@ -50,7 +50,8 @@ class _InteractiveFilterTileState<T extends CollectionFilter> extends State<Inte
final appMode = context.read<ValueNotifier<AppMode>>().value; final appMode = context.read<ValueNotifier<AppMode>>().value;
switch (appMode) { switch (appMode) {
case AppMode.main: case AppMode.main:
case AppMode.pickMediaExternal: case AppMode.pickSingleMediaExternal:
case AppMode.pickMultipleMediaExternal:
final selection = context.read<Selection<FilterGridItem<T>>>(); final selection = context.read<Selection<FilterGridItem<T>>>();
if (selection.isSelecting) { if (selection.isSelecting) {
selection.toggleSelection(gridItem); selection.toggleSelection(gridItem);

View file

@ -95,11 +95,12 @@ class _HomePageState extends State<HomePage> {
} }
break; break;
case 'pick': case 'pick':
appMode = AppMode.pickMediaExternal;
// TODO TLAD apply pick mimetype(s) // TODO TLAD apply pick mimetype(s)
// some apps define multiple types, separated by a space (maybe other signs too, like `,` `;`?) // some apps define multiple types, separated by a space (maybe other signs too, like `,` `;`?)
String? pickMimeTypes = intentData['mimeType']; String? pickMimeTypes = intentData['mimeType'];
debugPrint('pick mimeType=$pickMimeTypes'); final multiple = intentData['allowMultiple'] ?? false;
debugPrint('pick mimeType=$pickMimeTypes multiple=$multiple');
appMode = multiple ? AppMode.pickMultipleMediaExternal : AppMode.pickSingleMediaExternal;
break; break;
case 'search': case 'search':
_shortcutRouteName = SearchPage.routeName; _shortcutRouteName = SearchPage.routeName;
@ -121,7 +122,8 @@ class _HomePageState extends State<HomePage> {
switch (appMode) { switch (appMode) {
case AppMode.main: case AppMode.main:
case AppMode.pickMediaExternal: case AppMode.pickSingleMediaExternal:
case AppMode.pickMultipleMediaExternal:
unawaited(GlobalSearch.registerCallback()); unawaited(GlobalSearch.registerCallback());
unawaited(AnalysisService.registerCallback()); unawaited(AnalysisService.registerCallback());
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
@ -226,11 +228,15 @@ class _HomePageState extends State<HomePage> {
String routeName; String routeName;
Set<CollectionFilter?>? filters; Set<CollectionFilter?>? filters;
if (appMode == AppMode.pickMediaExternal) { switch (appMode) {
routeName = CollectionPage.routeName; case AppMode.pickSingleMediaExternal:
} else { case AppMode.pickMultipleMediaExternal:
routeName = _shortcutRouteName ?? settings.homePage.routeName; routeName = CollectionPage.routeName;
filters = (_shortcutFilters ?? {}).map(CollectionFilter.fromJson).toSet(); break;
default:
routeName = _shortcutRouteName ?? settings.homePage.routeName;
filters = (_shortcutFilters ?? {}).map(CollectionFilter.fromJson).toSet();
break;
} }
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
switch (routeName) { switch (routeName) {