#249 Pick: allow selecting multiple items according to request intent
This commit is contained in:
parent
406e99b13f
commit
16c0ba90f9
17 changed files with 143 additions and 96 deletions
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,9 +79,14 @@ 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>(
|
||||||
|
child: Selector<Selection<AvesEntry>, bool>(
|
||||||
|
selector: (context, selection) => selection.selectedItems.isNotEmpty,
|
||||||
|
builder: (context, hasSelection, child) {
|
||||||
|
return Selector<Settings, bool>(
|
||||||
selector: (context, s) => s.showBottomNavigationBar,
|
selector: (context, s) => s.showBottomNavigationBar,
|
||||||
builder: (context, showBottomNavigationBar, child) {
|
builder: (context, showBottomNavigationBar, child) {
|
||||||
return NotificationListener<DraggableScrollBarNotification>(
|
return NotificationListener<DraggableScrollBarNotification>(
|
||||||
|
@ -86,8 +95,7 @@ class _CollectionPageState extends State<CollectionPage> {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: SelectionProvider<AvesEntry>(
|
body: QueryProvider(
|
||||||
child: QueryProvider(
|
|
||||||
initialQuery: liveFilter?.query,
|
initialQuery: liveFilter?.query,
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => WillPopScope(
|
builder: (context) => WillPopScope(
|
||||||
|
@ -117,7 +125,17 @@ class _CollectionPageState extends State<CollectionPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
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),
|
drawer: AppDrawer(currentCollection: _collection),
|
||||||
bottomNavigationBar: showBottomNavigationBar
|
bottomNavigationBar: showBottomNavigationBar
|
||||||
? AppBottomNavBar(
|
? AppBottomNavBar(
|
||||||
|
@ -130,6 +148,9 @@ class _CollectionPageState extends State<CollectionPage> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
case AppMode.pickSingleMediaExternal:
|
||||||
|
case AppMode.pickMultipleMediaExternal:
|
||||||
routeName = CollectionPage.routeName;
|
routeName = CollectionPage.routeName;
|
||||||
} else {
|
break;
|
||||||
|
default:
|
||||||
routeName = _shortcutRouteName ?? settings.homePage.routeName;
|
routeName = _shortcutRouteName ?? settings.homePage.routeName;
|
||||||
filters = (_shortcutFilters ?? {}).map(CollectionFilter.fromJson).toSet();
|
filters = (_shortcutFilters ?? {}).map(CollectionFilter.fromJson).toSet();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
switch (routeName) {
|
switch (routeName) {
|
||||||
|
|
Loading…
Reference in a new issue