diff --git a/CHANGELOG.md b/CHANGELOG.md index f6a08502c..e76779da3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Changed + +- Viewer: allow setting default outside video player + ## [v1.7.7] - 2022-11-27 ### Added diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt index 0ac6bf607..bf68b220b 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt @@ -232,7 +232,8 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { val title = call.argument("title") val uri = call.argument("uri")?.let { Uri.parse(it) } val mimeType = call.argument("mimeType") - if (uri == null) { + val forceChooser = call.argument("forceChooser") + if (uri == null || forceChooser == null) { result.error("open-args", "missing arguments", null) return } @@ -240,7 +241,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { val intent = Intent(Intent.ACTION_VIEW) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .setDataAndType(getShareableUri(context, uri), mimeType) - val started = safeStartActivityChooser(title, intent) + val started = if (forceChooser) safeStartActivityChooser(title, intent) else safeStartActivity(intent) result.success(started) } diff --git a/lib/model/actions/entry_actions.dart b/lib/model/actions/entry_actions.dart index cb6dabebb..a03fa7fc7 100644 --- a/lib/model/actions/entry_actions.dart +++ b/lib/model/actions/entry_actions.dart @@ -34,6 +34,7 @@ enum EntryAction { // external edit, open, + openVideo, openMap, setAs, // platform @@ -191,6 +192,7 @@ extension ExtraEntryAction on EntryAction { case EntryAction.edit: return context.l10n.entryActionEdit; case EntryAction.open: + case EntryAction.openVideo: return context.l10n.entryActionOpen; case EntryAction.openMap: return context.l10n.entryActionOpenMap; @@ -302,6 +304,7 @@ extension ExtraEntryAction on EntryAction { case EntryAction.edit: return AIcons.edit; case EntryAction.open: + case EntryAction.openVideo: return AIcons.openOutside; case EntryAction.openMap: return AIcons.map; diff --git a/lib/services/android_app_service.dart b/lib/services/android_app_service.dart index b21f4ee8d..71ba0ca6e 100644 --- a/lib/services/android_app_service.dart +++ b/lib/services/android_app_service.dart @@ -16,7 +16,7 @@ abstract class AndroidAppService { Future edit(String uri, String mimeType); - Future open(String uri, String mimeType); + Future open(String uri, String mimeType, {required bool forceChooser}); Future openMap(LatLng latLng); @@ -101,11 +101,12 @@ class PlatformAndroidAppService implements AndroidAppService { } @override - Future open(String uri, String mimeType) async { + Future open(String uri, String mimeType, {required bool forceChooser}) async { try { final result = await _platform.invokeMethod('open', { 'uri': uri, 'mimeType': mimeType, + 'forceChooser': forceChooser, }); if (result != null) return result as bool; } on PlatformException catch (e, stack) { diff --git a/lib/widgets/dialogs/aves_selection_dialog.dart b/lib/widgets/dialogs/aves_selection_dialog.dart index 9e12b0b02..15dce1fe0 100644 --- a/lib/widgets/dialogs/aves_selection_dialog.dart +++ b/lib/widgets/dialogs/aves_selection_dialog.dart @@ -58,12 +58,13 @@ class _AvesSelectionDialogState extends State> { Widget build(BuildContext context) { final title = widget.title; final message = widget.message; + final verticalPadding = (title == null && message == null) ? AvesDialog.cornerRadius.y / 2 : .0; final confirmationButtonLabel = widget.confirmationButtonLabel; final needConfirmation = confirmationButtonLabel != null; return AvesDialog( title: title, scrollableContent: [ - if (title == null && message == null) SizedBox(height: AvesDialog.cornerRadius.y / 2), + if (verticalPadding != 0) SizedBox(height: verticalPadding), if (message != null) Padding( padding: const EdgeInsets.all(16), @@ -82,6 +83,7 @@ class _AvesSelectionDialogState extends State> { setGroupValue: (v) => setState(() => _selectedValue = v), ); }), + if (verticalPadding != 0) SizedBox(height: verticalPadding), ], actions: [ TextButton( diff --git a/lib/widgets/viewer/action/entry_action_delegate.dart b/lib/widgets/viewer/action/entry_action_delegate.dart index 9311227db..1e20fcf34 100644 --- a/lib/widgets/viewer/action/entry_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_action_delegate.dart @@ -93,6 +93,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.videoTogglePlay: case EntryAction.videoReplay10: case EntryAction.videoSkip10: + case EntryAction.openVideo: return targetEntry.isVideo; case EntryAction.rotateScreen: return settings.isRotationLocked; @@ -225,6 +226,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.videoTogglePlay: case EntryAction.videoReplay10: case EntryAction.videoSkip10: + case EntryAction.openVideo: final controller = context.read().getController(targetEntry); if (controller != null) { VideoActionNotification(controller: controller, action: action).dispatch(context); @@ -236,7 +238,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix }); break; case EntryAction.open: - androidAppService.open(targetEntry.uri, targetEntry.mimeTypeAnySubtype).then((success) { + androidAppService.open(targetEntry.uri, targetEntry.mimeTypeAnySubtype, forceChooser: true).then((success) { if (!success) showNoMatchingAppDialog(context); }); break; diff --git a/lib/widgets/viewer/embedded/embedded_data_opener.dart b/lib/widgets/viewer/embedded/embedded_data_opener.dart index 7349fa289..288a9c256 100644 --- a/lib/widgets/viewer/embedded/embedded_data_opener.dart +++ b/lib/widgets/viewer/embedded/embedded_data_opener.dart @@ -54,7 +54,7 @@ class EmbeddedDataOpener extends StatelessWidget with FeedbackMixin { final uri = fields['uri']!; if (!MimeTypes.isImage(mimeType) && !MimeTypes.isVideo(mimeType)) { // open with another app - unawaited(androidAppService.open(uri, mimeType).then((success) { + unawaited(androidAppService.open(uri, mimeType, forceChooser: true).then((success) { if (!success) { // fallback to sharing, so that the file can be saved somewhere androidAppService.shareSingle(uri, mimeType).then((success) { diff --git a/lib/widgets/viewer/overlay/video/controls.dart b/lib/widgets/viewer/overlay/video/controls.dart index 90f096566..8b06bb2c6 100644 --- a/lib/widgets/viewer/overlay/video/controls.dart +++ b/lib/widgets/viewer/overlay/video/controls.dart @@ -66,7 +66,7 @@ class VideoControlRow extends StatelessWidget { final trashed = controller?.entry.trashed ?? false; return Padding( padding: const EdgeInsetsDirectional.only(start: padding), - child: _buildIconButton(context, EntryAction.open, enabled: !trashed), + child: _buildIconButton(context, EntryAction.openVideo, enabled: !trashed), ); case VideoControls.none: return const SizedBox(); diff --git a/lib/widgets/viewer/overlay/video/video.dart b/lib/widgets/viewer/overlay/video/video.dart index e0a2d9e4b..6a57af95e 100644 --- a/lib/widgets/viewer/overlay/video/video.dart +++ b/lib/widgets/viewer/overlay/video/video.dart @@ -46,7 +46,7 @@ class _VideoControlOverlayState extends State with SingleTi final status = controller?.status ?? VideoStatus.idle; if (status == VideoStatus.error) { - const action = EntryAction.open; + const action = EntryAction.openVideo; return Align( alignment: AlignmentDirectional.centerEnd, child: OverlayButton( diff --git a/lib/widgets/viewer/video_action_delegate.dart b/lib/widgets/viewer/video_action_delegate.dart index 5cb382709..a23e5bee0 100644 --- a/lib/widgets/viewer/video_action_delegate.dart +++ b/lib/widgets/viewer/video_action_delegate.dart @@ -65,9 +65,9 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.videoSkip10: await controller.seekTo(controller.currentPosition + 10000); break; - case EntryAction.open: + case EntryAction.openVideo: final entry = controller.entry; - await androidAppService.open(entry.uri, entry.mimeTypeAnySubtype).then((success) { + await androidAppService.open(entry.uri, entry.mimeTypeAnySubtype, forceChooser: false).then((success) { if (!success) showNoMatchingAppDialog(context); }); break;