diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index eef9da963..7e9f4f8e5 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -643,6 +643,7 @@ "settingsGesturesTile": "Gestures", "settingsGesturesTitle": "Gestures", + "settingsVideoGestureDoubleTapTogglePlay": "Double tap to play/pause", "settingsVideoGestureSideDoubleTapSeek": "Double tap on screen edges to seek backward/forward", "settingsSectionPrivacy": "Privacy", diff --git a/lib/model/settings/defaults.dart b/lib/model/settings/defaults.dart index 06d0d0b38..e07bb0adf 100644 --- a/lib/model/settings/defaults.dart +++ b/lib/model/settings/defaults.dart @@ -82,6 +82,7 @@ class SettingsDefaults { static const enableVideoAutoPlay = false; static const videoLoopMode = VideoLoopMode.shortOnly; static const videoShowRawTimedText = false; + static const videoGestureDoubleTapTogglePlay = false; static const videoGestureSideDoubleTapSeek = true; // subtitles diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index 760999c95..9eea828f4 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -95,6 +95,7 @@ class Settings extends ChangeNotifier { static const enableVideoAutoPlayKey = 'video_auto_play'; static const videoLoopModeKey = 'video_loop'; static const videoShowRawTimedTextKey = 'video_show_raw_timed_text'; + static const videoGestureDoubleTapTogglePlayKey = 'video_gesture_double_tap_toggle_play'; static const videoGestureSideDoubleTapSeekKey = 'video_gesture_side_double_tap_skip'; // subtitles @@ -437,6 +438,10 @@ class Settings extends ChangeNotifier { set videoShowRawTimedText(bool newValue) => setAndNotify(videoShowRawTimedTextKey, newValue); + bool get videoGestureDoubleTapTogglePlay => getBoolOrDefault(videoGestureDoubleTapTogglePlayKey, SettingsDefaults.videoGestureDoubleTapTogglePlay); + + set videoGestureDoubleTapTogglePlay(bool newValue) => setAndNotify(videoGestureDoubleTapTogglePlayKey, newValue); + bool get videoGestureSideDoubleTapSeek => getBoolOrDefault(videoGestureSideDoubleTapSeekKey, SettingsDefaults.videoGestureSideDoubleTapSeek); set videoGestureSideDoubleTapSeek(bool newValue) => setAndNotify(videoGestureSideDoubleTapSeekKey, newValue); @@ -659,6 +664,7 @@ class Settings extends ChangeNotifier { case enableMotionPhotoAutoPlayKey: case enableVideoHardwareAccelerationKey: case enableVideoAutoPlayKey: + case videoGestureDoubleTapTogglePlayKey: case videoGestureSideDoubleTapSeekKey: case subtitleShowOutlineKey: case saveSearchHistoryKey: diff --git a/lib/theme/durations.dart b/lib/theme/durations.dart index 96fdca5c8..da4010ca9 100644 --- a/lib/theme/durations.dart +++ b/lib/theme/durations.dart @@ -39,7 +39,7 @@ class Durations { static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200); static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150); static const viewerVideoPlayerTransition = Duration(milliseconds: 500); - static const viewerActionFeedbackAnimation = Duration(milliseconds: 800); + static const viewerActionFeedbackAnimation = Duration(milliseconds: 600); // info animations static const mapStyleSwitchAnimation = Duration(milliseconds: 300); diff --git a/lib/widgets/settings/video/gestures.dart b/lib/widgets/settings/video/gestures.dart index 80bb864a7..18184118c 100644 --- a/lib/widgets/settings/video/gestures.dart +++ b/lib/widgets/settings/video/gestures.dart @@ -37,6 +37,14 @@ class VideoGesturesPage extends StatelessWidget { body: SafeArea( child: ListView( children: [ + Selector( + selector: (context, s) => s.videoGestureDoubleTapTogglePlay, + builder: (context, current, child) => SwitchListTile( + value: current, + onChanged: (v) => settings.videoGestureDoubleTapTogglePlay = v, + title: Text(context.l10n.settingsVideoGestureDoubleTapTogglePlay), + ), + ), Selector( selector: (context, s) => s.videoGestureSideDoubleTapSeek, builder: (context, current, child) => SwitchListTile( diff --git a/lib/widgets/viewer/visual/entry_page_view.dart b/lib/widgets/viewer/visual/entry_page_view.dart index deedfaf74..36fc98ac3 100644 --- a/lib/widgets/viewer/visual/entry_page_view.dart +++ b/lib/widgets/viewer/visual/entry_page_view.dart @@ -6,7 +6,7 @@ import 'package:aves/model/entry_images.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; -import 'package:aves/utils/constants.dart'; +import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/magnifier/controller/controller.dart'; import 'package:aves/widgets/common/magnifier/controller/state.dart'; @@ -193,17 +193,27 @@ class _EntryPageViewState extends State { final videoController = context.read().getController(entry); if (videoController == null) return const SizedBox(); - Positioned _buildDoubleTapDetector(AlignmentGeometry alignment, VideoAction action) { + Positioned _buildDoubleTapDetector( + VideoAction action, { + double widthFactor = 1, + AlignmentGeometry alignment = Alignment.center, + IconData? Function()? icon, + }) { return Positioned.fill( child: FractionallySizedBox( alignment: alignment, - widthFactor: .25, + widthFactor: widthFactor, child: GestureDetector( onDoubleTap: () { _actionFeedbackChildNotifier.value = DecoratedIcon( - action.getIconData(), - shadows: Constants.embossShadows, + icon?.call() ?? action.getIconData(), size: 48, + shadows: const [ + Shadow( + color: Colors.black, + blurRadius: 4, + ) + ], ); VideoGestureNotification( controller: videoController, @@ -219,6 +229,9 @@ class _EntryPageViewState extends State { valueListenable: videoController.sarNotifier, builder: (context, sar, child) { final videoDisplaySize = entry.videoDisplaySize(sar); + final playGesture = settings.videoGestureDoubleTapTogglePlay; + final seekGesture = settings.videoGestureSideDoubleTapSeek; + final useActionGesture = playGesture || seekGesture; return Stack( fit: StackFit.expand, children: [ @@ -241,16 +254,22 @@ class _EntryPageViewState extends State { viewStateNotifier: _viewStateNotifier, debugMode: true, ), - if (settings.videoGestureSideDoubleTapSeek) ...[ - _buildDoubleTapDetector(Alignment.centerLeft, VideoAction.replay10), - _buildDoubleTapDetector(Alignment.centerRight, VideoAction.skip10), + if (playGesture) + _buildDoubleTapDetector( + VideoAction.togglePlay, + icon: () => videoController.isPlaying ? AIcons.pause : AIcons.play, + ), + if (seekGesture) ...[ + _buildDoubleTapDetector(VideoAction.replay10, widthFactor: .25, alignment: Alignment.centerLeft), + _buildDoubleTapDetector(VideoAction.skip10, widthFactor: .25, alignment: Alignment.centerRight), + ], + if (useActionGesture) ValueListenableBuilder( valueListenable: _actionFeedbackChildNotifier, builder: (context, feedbackChild, child) => ActionFeedback( child: feedbackChild, ), ), - ], ], ), _buildVideoCover(videoController, videoDisplaySize), diff --git a/untranslated.json b/untranslated.json index 518979511..e90c29dc4 100644 --- a/untranslated.json +++ b/untranslated.json @@ -4,6 +4,7 @@ "settingsViewerShowOverlayThumbnails", "settingsGesturesTile", "settingsGesturesTitle", + "settingsVideoGestureDoubleTapTogglePlay", "settingsVideoGestureSideDoubleTapSeek" ], @@ -11,6 +12,7 @@ "settingsViewerShowOverlayThumbnails", "settingsGesturesTile", "settingsGesturesTitle", + "settingsVideoGestureDoubleTapTogglePlay", "settingsVideoGestureSideDoubleTapSeek" ], @@ -18,6 +20,7 @@ "settingsViewerShowOverlayThumbnails", "settingsGesturesTile", "settingsGesturesTitle", + "settingsVideoGestureDoubleTapTogglePlay", "settingsVideoGestureSideDoubleTapSeek" ], @@ -25,6 +28,7 @@ "settingsViewerShowOverlayThumbnails", "settingsGesturesTile", "settingsGesturesTitle", + "settingsVideoGestureDoubleTapTogglePlay", "settingsVideoGestureSideDoubleTapSeek" ], @@ -32,6 +36,7 @@ "settingsViewerShowOverlayThumbnails", "settingsGesturesTile", "settingsGesturesTitle", + "settingsVideoGestureDoubleTapTogglePlay", "settingsVideoGestureSideDoubleTapSeek" ], @@ -39,6 +44,7 @@ "settingsViewerShowOverlayThumbnails", "settingsGesturesTile", "settingsGesturesTitle", + "settingsVideoGestureDoubleTapTogglePlay", "settingsVideoGestureSideDoubleTapSeek" ], @@ -47,6 +53,7 @@ "settingsViewerShowOverlayThumbnails", "settingsGesturesTile", "settingsGesturesTitle", + "settingsVideoGestureDoubleTapTogglePlay", "settingsVideoGestureSideDoubleTapSeek" ] }