diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt index 339ecbbbf..0166fdff4 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt @@ -10,6 +10,7 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper +import android.os.TransactionTooLargeException import android.provider.MediaStore import android.util.Log import androidx.annotation.RequiresApi @@ -21,6 +22,7 @@ import deckers.thibault.aves.channel.AvesByteSendingMethodCodec import deckers.thibault.aves.channel.calls.AccessibilityHandler import deckers.thibault.aves.channel.calls.AnalysisHandler import deckers.thibault.aves.channel.calls.AppAdapterHandler +import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.channel.calls.DebugHandler import deckers.thibault.aves.channel.calls.DeviceHandler import deckers.thibault.aves.channel.calls.EmbeddedDataHandler @@ -170,7 +172,7 @@ open class MainActivity : FlutterFragmentActivity() { intentDataMap.clear() } - "submitPickedItems" -> submitPickedItems(call) + "submitPickedItems" -> safe(call, result, ::submitPickedItems) "submitPickedCollectionFilters" -> submitPickedCollectionFilters(call) } } @@ -408,28 +410,36 @@ open class MainActivity : FlutterFragmentActivity() { return null } - open fun submitPickedItems(call: MethodCall) { + open fun submitPickedItems(call: MethodCall, result: MethodChannel.Result) { val pickedUris = call.argument>("uris") - if (!pickedUris.isNullOrEmpty()) { - val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(this, Uri.parse(uriString)) } - val intent = Intent().apply { - 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))) + try { + if (!pickedUris.isNullOrEmpty()) { + val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(this, Uri.parse(uriString)) } + val intent = Intent().apply { + 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) + } else { + setResult(RESULT_CANCELED) + } + finish() + } catch (e: Exception) { + if (e is TransactionTooLargeException || e.cause is TransactionTooLargeException) { + result.error("submitPickedItems-large", "transaction too large with ${pickedUris?.size} URIs", e) + } else { + result.error("submitPickedItems-exception", "failed to pick ${pickedUris?.size} URIs", e) } - setResult(RESULT_OK, intent) - } else { - setResult(RESULT_CANCELED) } - finish() } private fun submitPickedCollectionFilters(call: MethodCall) { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt index 9adf417c4..067ee8e34 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt @@ -6,6 +6,7 @@ import deckers.thibault.aves.channel.calls.AppAdapterHandler import deckers.thibault.aves.model.FieldMap import deckers.thibault.aves.utils.getParcelableExtraCompat import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel class WallpaperActivity : MainActivity() { private var originalIntent: String? = null @@ -34,7 +35,7 @@ class WallpaperActivity : MainActivity() { return super.extractIntentData(intent) } - override fun submitPickedItems(call: MethodCall) { + override fun submitPickedItems(call: MethodCall, result: MethodChannel.Result) { if (originalIntent != null) { val pickedUris = call.argument>("uris") if (!pickedUris.isNullOrEmpty()) { @@ -48,7 +49,7 @@ class WallpaperActivity : MainActivity() { finish() } } else { - super.submitPickedItems(call) + super.submitPickedItems(call, result) } } } diff --git a/lib/services/intent_service.dart b/lib/services/intent_service.dart index 47d46e691..51f18940a 100644 --- a/lib/services/intent_service.dart +++ b/lib/services/intent_service.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:aves/model/filters/filters.dart'; +import 'package:aves/services/app_service.dart'; import 'package:aves/services/common/services.dart'; import 'package:collection/collection.dart'; import 'package:flutter/services.dart'; @@ -27,7 +28,11 @@ class IntentService { 'uris': uris, }); } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); + if (e.code == 'submitPickedItems-large') { + throw TooManyItemsException(); + } else { + await reportService.recordError(e, stack); + } } } diff --git a/lib/widgets/collection/collection_page.dart b/lib/widgets/collection/collection_page.dart index 5945d67e3..5a6812e99 100644 --- a/lib/widgets/collection/collection_page.dart +++ b/lib/widgets/collection/collection_page.dart @@ -10,6 +10,7 @@ import 'package:aves/model/selection.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/services/app_service.dart'; import 'package:aves/services/intent_service.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/collection/collection_grid.dart'; @@ -23,6 +24,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_fab.dart'; import 'package:aves/widgets/common/providers/query_provider.dart'; import 'package:aves/widgets/common/providers/selection_provider.dart'; +import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:aves/widgets/navigation/drawer/app_drawer.dart'; import 'package:aves/widgets/navigation/nav_bar/nav_bar.dart'; import 'package:aves/widgets/navigation/tv_rail.dart'; @@ -185,10 +187,21 @@ class _CollectionPageState extends State { return hasSelection ? AvesFab( tooltip: context.l10n.pickTooltip, - onPressed: () { + onPressed: () async { final items = context.read>().selectedItems; final uris = items.map((entry) => entry.uri).toList(); - IntentService.submitPickedItems(uris); + try { + await IntentService.submitPickedItems(uris); + } on TooManyItemsException catch (_) { + await showDialog( + context: context, + builder: (context) => AvesDialog( + content: Text(context.l10n.tooManyItemsErrorDialogMessage), + actions: const [OkButton()], + ), + routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), + ); + } }, ) : null; diff --git a/lib/widgets/viewer/overlay/video/progress_bar.dart b/lib/widgets/viewer/overlay/video/progress_bar.dart index 310b363a2..1c71ad503 100644 --- a/lib/widgets/viewer/overlay/video/progress_bar.dart +++ b/lib/widgets/viewer/overlay/video/progress_bar.dart @@ -61,13 +61,13 @@ class _VideoProgressBarState extends State { }, onHorizontalDragStart: (details) { _playingOnDragStart = isPlaying; - if (_playingOnDragStart) controller!.pause(); + if (_playingOnDragStart) controller?.pause(); }, onHorizontalDragUpdate: (details) { _seekFromTap(details.globalPosition); }, onHorizontalDragEnd: (details) { - if (_playingOnDragStart) controller!.play(); + if (_playingOnDragStart) controller?.play(); }, child: ConstrainedBox( constraints: const BoxConstraints(minHeight: kMinInteractiveDimension),