#180 video: double tap play gesture

This commit is contained in:
Thibault Deckers 2022-03-02 09:09:14 +09:00
parent 3a91314a5d
commit 6b62806ddb
7 changed files with 52 additions and 10 deletions

View file

@ -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",

View file

@ -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

View file

@ -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:

View file

@ -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);

View file

@ -37,6 +37,14 @@ class VideoGesturesPage extends StatelessWidget {
body: SafeArea(
child: ListView(
children: [
Selector<Settings, bool>(
selector: (context, s) => s.videoGestureDoubleTapTogglePlay,
builder: (context, current, child) => SwitchListTile(
value: current,
onChanged: (v) => settings.videoGestureDoubleTapTogglePlay = v,
title: Text(context.l10n.settingsVideoGestureDoubleTapTogglePlay),
),
),
Selector<Settings, bool>(
selector: (context, s) => s.videoGestureSideDoubleTapSeek,
builder: (context, current, child) => SwitchListTile(

View file

@ -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<EntryPageView> {
final videoController = context.read<VideoConductor>().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<EntryPageView> {
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<EntryPageView> {
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<Widget?>(
valueListenable: _actionFeedbackChildNotifier,
builder: (context, feedbackChild, child) => ActionFeedback(
child: feedbackChild,
),
),
],
],
),
_buildVideoCover(videoController, videoDisplaySize),

View file

@ -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"
]
}