#180 video: double tap seek gesture
This commit is contained in:
parent
064f20bb3a
commit
7e54b91aa4
17 changed files with 231 additions and 30 deletions
|
@ -92,8 +92,8 @@
|
||||||
"entryActionSetAs": "Set as",
|
"entryActionSetAs": "Set as",
|
||||||
"entryActionOpenMap": "Show in map app",
|
"entryActionOpenMap": "Show in map app",
|
||||||
"entryActionRotateScreen": "Rotate screen",
|
"entryActionRotateScreen": "Rotate screen",
|
||||||
"entryActionAddFavourite": "Add to favourites",
|
"entryActionAddFavourite": "Add to favorites",
|
||||||
"entryActionRemoveFavourite": "Remove from favourites",
|
"entryActionRemoveFavourite": "Remove from favorites",
|
||||||
|
|
||||||
"videoActionCaptureFrame": "Capture frame",
|
"videoActionCaptureFrame": "Capture frame",
|
||||||
"videoActionPause": "Pause",
|
"videoActionPause": "Pause",
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
"entryInfoActionRemoveMetadata": "Remove metadata",
|
"entryInfoActionRemoveMetadata": "Remove metadata",
|
||||||
|
|
||||||
"filterBinLabel": "Recycle bin",
|
"filterBinLabel": "Recycle bin",
|
||||||
"filterFavouriteLabel": "Favourite",
|
"filterFavouriteLabel": "Favorite",
|
||||||
"filterLocationEmptyLabel": "Unlocated",
|
"filterLocationEmptyLabel": "Unlocated",
|
||||||
"filterTagEmptyLabel": "Untagged",
|
"filterTagEmptyLabel": "Untagged",
|
||||||
"filterRatingUnratedLabel": "Unrated",
|
"filterRatingUnratedLabel": "Unrated",
|
||||||
|
@ -490,7 +490,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"collectionEmptyFavourites": "No favourites",
|
"collectionEmptyFavourites": "No favorites",
|
||||||
"collectionEmptyVideos": "No videos",
|
"collectionEmptyVideos": "No videos",
|
||||||
"collectionEmptyImages": "No images",
|
"collectionEmptyImages": "No images",
|
||||||
|
|
||||||
|
@ -498,7 +498,7 @@
|
||||||
"collectionDeselectSectionTooltip": "Deselect section",
|
"collectionDeselectSectionTooltip": "Deselect section",
|
||||||
|
|
||||||
"drawerCollectionAll": "All collection",
|
"drawerCollectionAll": "All collection",
|
||||||
"drawerCollectionFavourites": "Favourites",
|
"drawerCollectionFavourites": "Favorites",
|
||||||
"drawerCollectionImages": "Images",
|
"drawerCollectionImages": "Images",
|
||||||
"drawerCollectionVideos": "Videos",
|
"drawerCollectionVideos": "Videos",
|
||||||
"drawerCollectionAnimated": "Animated",
|
"drawerCollectionAnimated": "Animated",
|
||||||
|
@ -556,7 +556,7 @@
|
||||||
"settingsActionImport": "Import",
|
"settingsActionImport": "Import",
|
||||||
|
|
||||||
"appExportCovers": "Covers",
|
"appExportCovers": "Covers",
|
||||||
"appExportFavourites": "Favourites",
|
"appExportFavourites": "Favorites",
|
||||||
"appExportSettings": "Settings",
|
"appExportSettings": "Settings",
|
||||||
|
|
||||||
"settingsSectionNavigation": "Navigation",
|
"settingsSectionNavigation": "Navigation",
|
||||||
|
@ -579,7 +579,7 @@
|
||||||
"settingsNavigationDrawerAddAlbum": "Add album",
|
"settingsNavigationDrawerAddAlbum": "Add album",
|
||||||
|
|
||||||
"settingsSectionThumbnails": "Thumbnails",
|
"settingsSectionThumbnails": "Thumbnails",
|
||||||
"settingsThumbnailShowFavouriteIcon": "Show favourite icon",
|
"settingsThumbnailShowFavouriteIcon": "Show favorite icon",
|
||||||
"settingsThumbnailShowLocationIcon": "Show location icon",
|
"settingsThumbnailShowLocationIcon": "Show location icon",
|
||||||
"settingsThumbnailShowMotionPhotoIcon": "Show motion photo icon",
|
"settingsThumbnailShowMotionPhotoIcon": "Show motion photo icon",
|
||||||
"settingsThumbnailShowRating": "Show rating",
|
"settingsThumbnailShowRating": "Show rating",
|
||||||
|
@ -641,6 +641,10 @@
|
||||||
"settingsSubtitleThemeTextAlignmentCenter": "Center",
|
"settingsSubtitleThemeTextAlignmentCenter": "Center",
|
||||||
"settingsSubtitleThemeTextAlignmentRight": "Right",
|
"settingsSubtitleThemeTextAlignmentRight": "Right",
|
||||||
|
|
||||||
|
"settingsGesturesTile": "Gestures",
|
||||||
|
"settingsGesturesTitle": "Gestures",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek": "Double tap on screen edges to seek backward/forward",
|
||||||
|
|
||||||
"settingsSectionPrivacy": "Privacy",
|
"settingsSectionPrivacy": "Privacy",
|
||||||
"settingsAllowInstalledAppAccess": "Allow access to app inventory",
|
"settingsAllowInstalledAppAccess": "Allow access to app inventory",
|
||||||
"settingsAllowInstalledAppAccessSubtitle": "Used to improve album display",
|
"settingsAllowInstalledAppAccessSubtitle": "Used to improve album display",
|
||||||
|
|
|
@ -23,9 +23,7 @@ extension ExtraChipAction on ChipAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getIcon() {
|
Widget getIcon() => Icon(_getIconData());
|
||||||
return Icon(_getIconData());
|
|
||||||
}
|
|
||||||
|
|
||||||
IconData _getIconData() {
|
IconData _getIconData() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
|
|
@ -90,9 +90,7 @@ extension ExtraChipSetAction on ChipSetAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getIcon() {
|
Widget getIcon() => Icon(_getIconData());
|
||||||
return Icon(_getIconData());
|
|
||||||
}
|
|
||||||
|
|
||||||
IconData _getIconData() {
|
IconData _getIconData() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
|
|
@ -159,9 +159,7 @@ extension ExtraEntrySetAction on EntrySetAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getIcon() {
|
Widget getIcon() => Icon(_getIconData());
|
||||||
return Icon(_getIconData());
|
|
||||||
}
|
|
||||||
|
|
||||||
IconData _getIconData() {
|
IconData _getIconData() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
|
|
@ -50,11 +50,9 @@ extension ExtraVideoAction on VideoAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getIcon() {
|
Widget getIcon() => Icon(getIconData());
|
||||||
return Icon(_getIconData());
|
|
||||||
}
|
|
||||||
|
|
||||||
IconData _getIconData() {
|
IconData getIconData() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case VideoAction.captureFrame:
|
case VideoAction.captureFrame:
|
||||||
return AIcons.captureFrame;
|
return AIcons.captureFrame;
|
||||||
|
|
|
@ -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 videoGestureSideDoubleTapSeek = true;
|
||||||
|
|
||||||
// subtitles
|
// subtitles
|
||||||
static const subtitleFontSize = 20.0;
|
static const subtitleFontSize = 20.0;
|
||||||
|
|
|
@ -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 videoGestureSideDoubleTapSeekKey = 'video_gesture_side_double_tap_skip';
|
||||||
|
|
||||||
// subtitles
|
// subtitles
|
||||||
static const subtitleFontSizeKey = 'subtitle_font_size';
|
static const subtitleFontSizeKey = 'subtitle_font_size';
|
||||||
|
@ -436,6 +437,10 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set videoShowRawTimedText(bool newValue) => setAndNotify(videoShowRawTimedTextKey, newValue);
|
set videoShowRawTimedText(bool newValue) => setAndNotify(videoShowRawTimedTextKey, newValue);
|
||||||
|
|
||||||
|
bool get videoGestureSideDoubleTapSeek => getBoolOrDefault(videoGestureSideDoubleTapSeekKey, SettingsDefaults.videoGestureSideDoubleTapSeek);
|
||||||
|
|
||||||
|
set videoGestureSideDoubleTapSeek(bool newValue) => setAndNotify(videoGestureSideDoubleTapSeekKey, newValue);
|
||||||
|
|
||||||
// subtitles
|
// subtitles
|
||||||
|
|
||||||
double get subtitleFontSize => getDouble(subtitleFontSizeKey) ?? SettingsDefaults.subtitleFontSize;
|
double get subtitleFontSize => getDouble(subtitleFontSizeKey) ?? SettingsDefaults.subtitleFontSize;
|
||||||
|
@ -654,6 +659,7 @@ class Settings extends ChangeNotifier {
|
||||||
case enableMotionPhotoAutoPlayKey:
|
case enableMotionPhotoAutoPlayKey:
|
||||||
case enableVideoHardwareAccelerationKey:
|
case enableVideoHardwareAccelerationKey:
|
||||||
case enableVideoAutoPlayKey:
|
case enableVideoAutoPlayKey:
|
||||||
|
case videoGestureSideDoubleTapSeekKey:
|
||||||
case subtitleShowOutlineKey:
|
case subtitleShowOutlineKey:
|
||||||
case saveSearchHistoryKey:
|
case saveSearchHistoryKey:
|
||||||
case filePickerShowHiddenFilesKey:
|
case filePickerShowHiddenFilesKey:
|
||||||
|
|
|
@ -39,6 +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);
|
||||||
|
|
||||||
// info animations
|
// info animations
|
||||||
static const mapStyleSwitchAnimation = Duration(milliseconds: 300);
|
static const mapStyleSwitchAnimation = Duration(milliseconds: 300);
|
||||||
|
|
|
@ -294,3 +294,70 @@ class _FeedbackMessageState extends State<_FeedbackMessage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ActionFeedback extends StatefulWidget {
|
||||||
|
final Widget? child;
|
||||||
|
|
||||||
|
const ActionFeedback({
|
||||||
|
Key? key,
|
||||||
|
required this.child,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ActionFeedbackState createState() => _ActionFeedbackState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ActionFeedbackState extends State<ActionFeedback> with SingleTickerProviderStateMixin {
|
||||||
|
late final AnimationController _animationController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationController = AnimationController(
|
||||||
|
duration: Durations.viewerActionFeedbackAnimation,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant ActionFeedback oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (oldWidget.child != widget.child) {
|
||||||
|
_animationController.reset();
|
||||||
|
if (widget.child != null) {
|
||||||
|
_animationController.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return IgnorePointer(
|
||||||
|
child: Center(
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _animationController,
|
||||||
|
builder: (context, child) {
|
||||||
|
final t = _animationController.value;
|
||||||
|
final opacity = Curves.easeOutQuad.transform(t > .5 ? (1 - t) * 2 : t * 2);
|
||||||
|
final scale = Curves.slowMiddle.transform(t) * 2;
|
||||||
|
return Opacity(
|
||||||
|
opacity: opacity,
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: scale,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
53
lib/widgets/settings/video/gestures.dart
Normal file
53
lib/widgets/settings/video/gestures.dart
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class VideoGesturesTile extends StatelessWidget {
|
||||||
|
const VideoGesturesTile({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(context.l10n.settingsGesturesTile),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
settings: const RouteSettings(name: VideoGesturesPage.routeName),
|
||||||
|
builder: (context) => const VideoGesturesPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoGesturesPage extends StatelessWidget {
|
||||||
|
static const routeName = '/settings/video/gestures';
|
||||||
|
|
||||||
|
const VideoGesturesPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(context.l10n.settingsGesturesTitle),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Selector<Settings, bool>(
|
||||||
|
selector: (context, s) => s.videoGestureSideDoubleTapSeek,
|
||||||
|
builder: (context, current, child) => SwitchListTile(
|
||||||
|
value: current,
|
||||||
|
onChanged: (v) => settings.videoGestureSideDoubleTapSeek = v,
|
||||||
|
title: Text(context.l10n.settingsVideoGestureSideDoubleTapSeek),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ class SubtitleThemeTile extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubtitleThemePage extends StatelessWidget {
|
class SubtitleThemePage extends StatelessWidget {
|
||||||
static const routeName = '/settings/subtitle_theme';
|
static const routeName = '/settings/video/subtitle_theme';
|
||||||
|
|
||||||
static const textAlignOptions = [TextAlign.left, TextAlign.center, TextAlign.right];
|
static const textAlignOptions = [TextAlign.left, TextAlign.center, TextAlign.right];
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
|
||||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
||||||
import 'package:aves/widgets/settings/common/tile_leading.dart';
|
import 'package:aves/widgets/settings/common/tile_leading.dart';
|
||||||
|
import 'package:aves/widgets/settings/video/gestures.dart';
|
||||||
import 'package:aves/widgets/settings/video/subtitle_theme.dart';
|
import 'package:aves/widgets/settings/video/subtitle_theme.dart';
|
||||||
import 'package:aves/widgets/settings/video/video_actions_editor.dart';
|
import 'package:aves/widgets/settings/video/video_actions_editor.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -73,6 +74,7 @@ class VideoSection extends StatelessWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const VideoGesturesTile(),
|
||||||
const SubtitleThemeTile(),
|
const SubtitleThemeTile(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -209,6 +209,10 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
|
||||||
if (_currentHorizontalPage != index) {
|
if (_currentHorizontalPage != index) {
|
||||||
_horizontalPager.jumpToPage(index);
|
_horizontalPager.jumpToPage(index);
|
||||||
}
|
}
|
||||||
|
} else if (notification is VideoGestureNotification) {
|
||||||
|
final controller = notification.controller;
|
||||||
|
final action = notification.action;
|
||||||
|
_videoActionDelegate.onActionSelected(context, controller, action);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:aves/model/actions/video_actions.dart';
|
||||||
|
import 'package:aves/widgets/viewer/video/controller.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
|
@ -13,3 +15,14 @@ class ViewEntryNotification extends Notification {
|
||||||
|
|
||||||
const ViewEntryNotification({required this.index});
|
const ViewEntryNotification({required this.index});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class VideoGestureNotification extends Notification {
|
||||||
|
final AvesVideoController controller;
|
||||||
|
final VideoAction action;
|
||||||
|
|
||||||
|
const VideoGestureNotification({
|
||||||
|
required this.controller,
|
||||||
|
required this.action,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
|
@ -301,7 +300,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> seekTo(int targetMillis) async {
|
Future<void> seekTo(int targetMillis) async {
|
||||||
targetMillis = max(0, targetMillis);
|
targetMillis = targetMillis.clamp(0, duration);
|
||||||
if (isReady) {
|
if (isReady) {
|
||||||
await _instance.seekTo(targetMillis);
|
await _instance.seekTo(targetMillis);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:aves/model/actions/video_actions.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/entry_images.dart';
|
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/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';
|
||||||
import 'package:aves/widgets/common/magnifier/magnifier.dart';
|
import 'package:aves/widgets/common/magnifier/magnifier.dart';
|
||||||
|
@ -24,6 +27,7 @@ import 'package:aves/widgets/viewer/visual/subtitle/subtitle.dart';
|
||||||
import 'package:aves/widgets/viewer/visual/vector.dart';
|
import 'package:aves/widgets/viewer/visual/vector.dart';
|
||||||
import 'package:aves/widgets/viewer/visual/video.dart';
|
import 'package:aves/widgets/viewer/visual/video.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -51,6 +55,7 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
ImageStream? _videoCoverStream;
|
ImageStream? _videoCoverStream;
|
||||||
late ImageStreamListener _videoCoverStreamListener;
|
late ImageStreamListener _videoCoverStreamListener;
|
||||||
final ValueNotifier<ImageInfo?> _videoCoverInfoNotifier = ValueNotifier(null);
|
final ValueNotifier<ImageInfo?> _videoCoverInfoNotifier = ValueNotifier(null);
|
||||||
|
final ValueNotifier<Widget?> _actionFeedbackChildNotifier = ValueNotifier(null);
|
||||||
|
|
||||||
MagnifierController? _dismissedCoverMagnifierController;
|
MagnifierController? _dismissedCoverMagnifierController;
|
||||||
|
|
||||||
|
@ -187,6 +192,29 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
Widget _buildVideoView() {
|
Widget _buildVideoView() {
|
||||||
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) {
|
||||||
|
return Positioned.fill(
|
||||||
|
child: FractionallySizedBox(
|
||||||
|
alignment: alignment,
|
||||||
|
widthFactor: .25,
|
||||||
|
child: GestureDetector(
|
||||||
|
onDoubleTap: () {
|
||||||
|
_actionFeedbackChildNotifier.value = DecoratedIcon(
|
||||||
|
action.getIconData(),
|
||||||
|
shadows: Constants.embossShadows,
|
||||||
|
size: 48,
|
||||||
|
);
|
||||||
|
VideoGestureNotification(
|
||||||
|
controller: videoController,
|
||||||
|
action: action,
|
||||||
|
).dispatch(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return ValueListenableBuilder<double>(
|
return ValueListenableBuilder<double>(
|
||||||
valueListenable: videoController.sarNotifier,
|
valueListenable: videoController.sarNotifier,
|
||||||
builder: (context, sar, child) {
|
builder: (context, sar, child) {
|
||||||
|
@ -213,6 +241,16 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
viewStateNotifier: _viewStateNotifier,
|
viewStateNotifier: _viewStateNotifier,
|
||||||
debugMode: true,
|
debugMode: true,
|
||||||
),
|
),
|
||||||
|
if (settings.videoGestureSideDoubleTapSeek) ...[
|
||||||
|
_buildDoubleTapDetector(Alignment.centerLeft, VideoAction.replay10),
|
||||||
|
_buildDoubleTapDetector(Alignment.centerRight, VideoAction.skip10),
|
||||||
|
ValueListenableBuilder<Widget?>(
|
||||||
|
valueListenable: _actionFeedbackChildNotifier,
|
||||||
|
builder: (context, feedbackChild, child) => ActionFeedback(
|
||||||
|
child: feedbackChild,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
_buildVideoCover(videoController, videoDisplaySize),
|
_buildVideoCover(videoController, videoDisplaySize),
|
||||||
|
|
|
@ -1,31 +1,52 @@
|
||||||
{
|
{
|
||||||
"de": [
|
"de": [
|
||||||
"entryActionConvert",
|
"entryActionConvert",
|
||||||
"settingsViewerShowOverlayThumbnails"
|
"settingsViewerShowOverlayThumbnails",
|
||||||
|
"settingsGesturesTile",
|
||||||
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
"settingsViewerShowOverlayThumbnails"
|
"settingsViewerShowOverlayThumbnails",
|
||||||
|
"settingsGesturesTile",
|
||||||
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
"settingsViewerShowOverlayThumbnails"
|
"settingsViewerShowOverlayThumbnails",
|
||||||
|
"settingsGesturesTile",
|
||||||
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
"id": [
|
"id": [
|
||||||
"settingsViewerShowOverlayThumbnails"
|
"settingsViewerShowOverlayThumbnails",
|
||||||
|
"settingsGesturesTile",
|
||||||
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ko": [
|
"ko": [
|
||||||
"settingsViewerShowOverlayThumbnails"
|
"settingsViewerShowOverlayThumbnails",
|
||||||
|
"settingsGesturesTile",
|
||||||
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
"settingsViewerShowOverlayThumbnails"
|
"settingsViewerShowOverlayThumbnails",
|
||||||
|
"settingsGesturesTile",
|
||||||
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
"entryActionConvert",
|
"entryActionConvert",
|
||||||
"settingsViewerShowOverlayThumbnails"
|
"settingsViewerShowOverlayThumbnails",
|
||||||
|
"settingsGesturesTile",
|
||||||
|
"settingsGesturesTitle",
|
||||||
|
"settingsVideoGestureSideDoubleTapSeek"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue