#180 video: double tap play gesture
This commit is contained in:
parent
3a91314a5d
commit
6b62806ddb
7 changed files with 52 additions and 10 deletions
|
@ -643,6 +643,7 @@
|
||||||
|
|
||||||
"settingsGesturesTile": "Gestures",
|
"settingsGesturesTile": "Gestures",
|
||||||
"settingsGesturesTitle": "Gestures",
|
"settingsGesturesTitle": "Gestures",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay": "Double tap to play/pause",
|
||||||
"settingsVideoGestureSideDoubleTapSeek": "Double tap on screen edges to seek backward/forward",
|
"settingsVideoGestureSideDoubleTapSeek": "Double tap on screen edges to seek backward/forward",
|
||||||
|
|
||||||
"settingsSectionPrivacy": "Privacy",
|
"settingsSectionPrivacy": "Privacy",
|
||||||
|
|
|
@ -82,6 +82,7 @@ class SettingsDefaults {
|
||||||
static const enableVideoAutoPlay = false;
|
static const enableVideoAutoPlay = false;
|
||||||
static const videoLoopMode = VideoLoopMode.shortOnly;
|
static const videoLoopMode = VideoLoopMode.shortOnly;
|
||||||
static const videoShowRawTimedText = false;
|
static const videoShowRawTimedText = false;
|
||||||
|
static const videoGestureDoubleTapTogglePlay = false;
|
||||||
static const videoGestureSideDoubleTapSeek = true;
|
static const videoGestureSideDoubleTapSeek = true;
|
||||||
|
|
||||||
// subtitles
|
// subtitles
|
||||||
|
|
|
@ -95,6 +95,7 @@ class Settings extends ChangeNotifier {
|
||||||
static const enableVideoAutoPlayKey = 'video_auto_play';
|
static const enableVideoAutoPlayKey = 'video_auto_play';
|
||||||
static const videoLoopModeKey = 'video_loop';
|
static const videoLoopModeKey = 'video_loop';
|
||||||
static const videoShowRawTimedTextKey = 'video_show_raw_timed_text';
|
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';
|
static const videoGestureSideDoubleTapSeekKey = 'video_gesture_side_double_tap_skip';
|
||||||
|
|
||||||
// subtitles
|
// subtitles
|
||||||
|
@ -437,6 +438,10 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set videoShowRawTimedText(bool newValue) => setAndNotify(videoShowRawTimedTextKey, newValue);
|
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);
|
bool get videoGestureSideDoubleTapSeek => getBoolOrDefault(videoGestureSideDoubleTapSeekKey, SettingsDefaults.videoGestureSideDoubleTapSeek);
|
||||||
|
|
||||||
set videoGestureSideDoubleTapSeek(bool newValue) => setAndNotify(videoGestureSideDoubleTapSeekKey, newValue);
|
set videoGestureSideDoubleTapSeek(bool newValue) => setAndNotify(videoGestureSideDoubleTapSeekKey, newValue);
|
||||||
|
@ -659,6 +664,7 @@ class Settings extends ChangeNotifier {
|
||||||
case enableMotionPhotoAutoPlayKey:
|
case enableMotionPhotoAutoPlayKey:
|
||||||
case enableVideoHardwareAccelerationKey:
|
case enableVideoHardwareAccelerationKey:
|
||||||
case enableVideoAutoPlayKey:
|
case enableVideoAutoPlayKey:
|
||||||
|
case videoGestureDoubleTapTogglePlayKey:
|
||||||
case videoGestureSideDoubleTapSeekKey:
|
case videoGestureSideDoubleTapSeekKey:
|
||||||
case subtitleShowOutlineKey:
|
case subtitleShowOutlineKey:
|
||||||
case saveSearchHistoryKey:
|
case saveSearchHistoryKey:
|
||||||
|
|
|
@ -39,7 +39,7 @@ class Durations {
|
||||||
static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200);
|
static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200);
|
||||||
static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150);
|
static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150);
|
||||||
static const viewerVideoPlayerTransition = Duration(milliseconds: 500);
|
static const viewerVideoPlayerTransition = Duration(milliseconds: 500);
|
||||||
static const viewerActionFeedbackAnimation = Duration(milliseconds: 800);
|
static const viewerActionFeedbackAnimation = Duration(milliseconds: 600);
|
||||||
|
|
||||||
// info animations
|
// info animations
|
||||||
static const mapStyleSwitchAnimation = Duration(milliseconds: 300);
|
static const mapStyleSwitchAnimation = Duration(milliseconds: 300);
|
||||||
|
|
|
@ -37,6 +37,14 @@ class VideoGesturesPage extends StatelessWidget {
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
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<Settings, bool>(
|
||||||
selector: (context, s) => s.videoGestureSideDoubleTapSeek,
|
selector: (context, s) => s.videoGestureSideDoubleTapSeek,
|
||||||
builder: (context, current, child) => SwitchListTile(
|
builder: (context, current, child) => SwitchListTile(
|
||||||
|
|
|
@ -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/enums/accessibility_animations.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/durations.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/action_mixins/feedback.dart';
|
||||||
import 'package:aves/widgets/common/magnifier/controller/controller.dart';
|
import 'package:aves/widgets/common/magnifier/controller/controller.dart';
|
||||||
import 'package:aves/widgets/common/magnifier/controller/state.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);
|
final videoController = context.read<VideoConductor>().getController(entry);
|
||||||
if (videoController == null) return const SizedBox();
|
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(
|
return Positioned.fill(
|
||||||
child: FractionallySizedBox(
|
child: FractionallySizedBox(
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
widthFactor: .25,
|
widthFactor: widthFactor,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onDoubleTap: () {
|
onDoubleTap: () {
|
||||||
_actionFeedbackChildNotifier.value = DecoratedIcon(
|
_actionFeedbackChildNotifier.value = DecoratedIcon(
|
||||||
action.getIconData(),
|
icon?.call() ?? action.getIconData(),
|
||||||
shadows: Constants.embossShadows,
|
|
||||||
size: 48,
|
size: 48,
|
||||||
|
shadows: const [
|
||||||
|
Shadow(
|
||||||
|
color: Colors.black,
|
||||||
|
blurRadius: 4,
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
VideoGestureNotification(
|
VideoGestureNotification(
|
||||||
controller: videoController,
|
controller: videoController,
|
||||||
|
@ -219,6 +229,9 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
valueListenable: videoController.sarNotifier,
|
valueListenable: videoController.sarNotifier,
|
||||||
builder: (context, sar, child) {
|
builder: (context, sar, child) {
|
||||||
final videoDisplaySize = entry.videoDisplaySize(sar);
|
final videoDisplaySize = entry.videoDisplaySize(sar);
|
||||||
|
final playGesture = settings.videoGestureDoubleTapTogglePlay;
|
||||||
|
final seekGesture = settings.videoGestureSideDoubleTapSeek;
|
||||||
|
final useActionGesture = playGesture || seekGesture;
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -241,9 +254,16 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
viewStateNotifier: _viewStateNotifier,
|
viewStateNotifier: _viewStateNotifier,
|
||||||
debugMode: true,
|
debugMode: true,
|
||||||
),
|
),
|
||||||
if (settings.videoGestureSideDoubleTapSeek) ...[
|
if (playGesture)
|
||||||
_buildDoubleTapDetector(Alignment.centerLeft, VideoAction.replay10),
|
_buildDoubleTapDetector(
|
||||||
_buildDoubleTapDetector(Alignment.centerRight, VideoAction.skip10),
|
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?>(
|
ValueListenableBuilder<Widget?>(
|
||||||
valueListenable: _actionFeedbackChildNotifier,
|
valueListenable: _actionFeedbackChildNotifier,
|
||||||
builder: (context, feedbackChild, child) => ActionFeedback(
|
builder: (context, feedbackChild, child) => ActionFeedback(
|
||||||
|
@ -251,7 +271,6 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
|
||||||
),
|
),
|
||||||
_buildVideoCover(videoController, videoDisplaySize),
|
_buildVideoCover(videoController, videoDisplaySize),
|
||||||
],
|
],
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsGesturesTile",
|
"settingsGesturesTile",
|
||||||
"settingsGesturesTitle",
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay",
|
||||||
"settingsVideoGestureSideDoubleTapSeek"
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsGesturesTile",
|
"settingsGesturesTile",
|
||||||
"settingsGesturesTitle",
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay",
|
||||||
"settingsVideoGestureSideDoubleTapSeek"
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -18,6 +20,7 @@
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsGesturesTile",
|
"settingsGesturesTile",
|
||||||
"settingsGesturesTitle",
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay",
|
||||||
"settingsVideoGestureSideDoubleTapSeek"
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -25,6 +28,7 @@
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsGesturesTile",
|
"settingsGesturesTile",
|
||||||
"settingsGesturesTitle",
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay",
|
||||||
"settingsVideoGestureSideDoubleTapSeek"
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -32,6 +36,7 @@
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsGesturesTile",
|
"settingsGesturesTile",
|
||||||
"settingsGesturesTitle",
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay",
|
||||||
"settingsVideoGestureSideDoubleTapSeek"
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -39,6 +44,7 @@
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsGesturesTile",
|
"settingsGesturesTile",
|
||||||
"settingsGesturesTitle",
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay",
|
||||||
"settingsVideoGestureSideDoubleTapSeek"
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -47,6 +53,7 @@
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsGesturesTile",
|
"settingsGesturesTile",
|
||||||
"settingsGesturesTitle",
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureDoubleTapTogglePlay",
|
||||||
"settingsVideoGestureSideDoubleTapSeek"
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue