浏览代码

Long press to play and let go to stop motion photo

Marty Fuhry 2 年之前
父节点
当前提交
dd7dadc694

+ 22 - 3
mobile/lib/modules/asset_viewer/views/gallery_viewer.dart

@@ -244,7 +244,7 @@ class GalleryViewerPage extends HookConsumerWidget {
       );
     }
 
-    void handleSwipeUpDown(DragUpdateDetails details) {
+    void handleDragUpdate(Asset asset, DragUpdateDetails details) {
       int sensitivity = 15;
       int dxThreshold = 50;
 
@@ -494,7 +494,14 @@ class GalleryViewerPage extends HookConsumerWidget {
                     onDragStart: (_, details, __) =>
                         localPosition = details.localPosition,
                     onDragUpdate: (_, details, __) =>
-                        handleSwipeUpDown(details),
+                        handleDragUpdate(assetList[index], details),
+                    onLongPressStart: (_, __, ___) {
+                      // If we are a motion video and we are not playing, start playing
+                      if (asset.livePhotoVideoId != null &&
+                          !isPlayingMotionVideo.value) {
+                        isPlayingMotionVideo.value = true;
+                      }
+                    },
                     onTapDown: (_, __, ___) =>
                         showAppBar.value = !showAppBar.value,
                     imageProvider: provider,
@@ -514,7 +521,7 @@ class GalleryViewerPage extends HookConsumerWidget {
                     onDragStart: (_, details, __) =>
                         localPosition = details.localPosition,
                     onDragUpdate: (_, details, __) =>
-                        handleSwipeUpDown(details),
+                        handleDragUpdate(assetList[index], details),
                     heroAttributes: PhotoViewHeroAttributes(
                       tag: assetList[index].id,
                     ),
@@ -544,6 +551,18 @@ class GalleryViewerPage extends HookConsumerWidget {
                 }
               },
             ),
+            Listener(
+              behavior: HitTestBehavior.translucent,
+              onPointerUp: (_) {
+                // Stop playing the motion video if this is a video and we are
+                // playing
+                if (assetList[indexOfAsset.value].livePhotoVideoId != null &&
+                    isPlayingMotionVideo.value) {
+                  isPlayingMotionVideo.value = false;
+                }
+              },
+              child: const SizedBox.expand(),
+            ),
             Positioned(
               top: 0,
               left: 0,

+ 29 - 1
mobile/lib/shared/ui/photo_view/photo_view.dart

@@ -253,6 +253,8 @@ class PhotoView extends StatefulWidget {
     this.onTapDown,
     this.onDragStart,
     this.onDragEnd,
+    this.onLongPressStart,
+    this.onLongPressEnd,
     this.onDragUpdate,
     this.onScaleEnd,
     this.customSize,
@@ -293,6 +295,8 @@ class PhotoView extends StatefulWidget {
     this.onDragStart,
     this.onDragEnd,
     this.onDragUpdate,
+    this.onLongPressStart,
+    this.onLongPressEnd,
     this.onScaleEnd,
     this.customSize,
     this.gestureDetectorBehavior,
@@ -397,6 +401,12 @@ class PhotoView extends StatefulWidget {
   /// location.
   final PhotoViewImageDragUpdateCallback? onDragUpdate;
 
+  /// A long press start event
+  final PhotoViewImageLongPressStartCallback? onLongPressStart;
+
+  /// A long press end event
+  final PhotoViewImageLongPressEndCallback? onLongPressEnd;
+
   /// A pointer that will trigger a scale has stopped contacting the screen at a
   /// particular location.
   final PhotoViewImageScaleEndCallback? onScaleEnd;
@@ -534,6 +544,8 @@ class _PhotoViewState extends State<PhotoView>
                 onDragStart: widget.onDragStart,
                 onDragEnd: widget.onDragEnd,
                 onDragUpdate: widget.onDragUpdate,
+                onLongPressStart: widget.onLongPressStart,
+                onLongPressEnd: widget.onLongPressEnd,
                 onScaleEnd: widget.onScaleEnd,
                 outerSize: computedOuterSize,
                 gestureDetectorBehavior: widget.gestureDetectorBehavior,
@@ -563,6 +575,8 @@ class _PhotoViewState extends State<PhotoView>
                 onDragStart: widget.onDragStart,
                 onDragEnd: widget.onDragEnd,
                 onDragUpdate: widget.onDragUpdate,
+                onLongPressStart: widget.onLongPressStart,
+                onLongPressEnd: widget.onLongPressEnd,
                 onScaleEnd: widget.onScaleEnd,
                 outerSize: computedOuterSize,
                 gestureDetectorBehavior: widget.gestureDetectorBehavior,
@@ -625,7 +639,7 @@ typedef PhotoViewImageDragStartCallback = Function(
   PhotoViewControllerValue controllerValue,
 );
 
-/// A type definition for a callback when the user drags 
+/// A type definition for a callback when the user drags
 typedef PhotoViewImageDragUpdateCallback = Function(
   BuildContext context,
   DragUpdateDetails details,
@@ -646,6 +660,20 @@ typedef PhotoViewImageScaleEndCallback = Function(
   PhotoViewControllerValue controllerValue,
 );
 
+/// A type definition for a callback when the user starts a long press
+typedef PhotoViewImageLongPressStartCallback = Function(
+  BuildContext context,
+  LongPressStartDetails details,
+  PhotoViewControllerValue controllerValue,
+);
+
+/// A type definition for a callback when the user starts a long press
+typedef PhotoViewImageLongPressEndCallback = Function(
+  BuildContext context,
+  LongPressEndDetails details,
+  PhotoViewControllerValue controllerValue,
+);
+
 /// A type definition for a callback to show a widget while the image is loading, a [ImageChunkEvent] is passed to inform progress
 typedef LoadingBuilder = Widget Function(
   BuildContext context,

+ 17 - 12
mobile/lib/shared/ui/photo_view/photo_view_gallery.dart

@@ -3,16 +3,7 @@ library photo_view_gallery;
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
 import 'package:immich_mobile/shared/ui/photo_view/photo_view.dart'
-    show
-        LoadingBuilder,
-        PhotoView,
-        PhotoViewImageTapDownCallback,
-        PhotoViewImageTapUpCallback,
-        PhotoViewImageDragStartCallback,
-        PhotoViewImageDragEndCallback,
-        PhotoViewImageDragUpdateCallback,
-        PhotoViewImageScaleEndCallback,
-        ScaleStateCycle;
+    show LoadingBuilder, PhotoView, PhotoViewImageDragEndCallback, PhotoViewImageDragStartCallback, PhotoViewImageDragUpdateCallback, PhotoViewImageLongPressEndCallback, PhotoViewImageLongPressStartCallback, PhotoViewImageScaleEndCallback, PhotoViewImageTapDownCallback, PhotoViewImageTapUpCallback, ScaleStateCycle;
 
 import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_controller.dart';
 import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_scalestate_controller.dart';
@@ -25,7 +16,7 @@ typedef PhotoViewGalleryPageChangedCallback = void Function(int index);
 
 /// A type definition for a [Function] that defines a page in [PhotoViewGallery.build]
 typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function(
-  BuildContext context, 
+  BuildContext context,
   int index,
 );
 
@@ -270,6 +261,8 @@ class _PhotoViewGalleryState extends State<PhotoViewGallery> {
             onDragStart: pageOption.onDragStart,
             onDragEnd: pageOption.onDragEnd,
             onDragUpdate: pageOption.onDragUpdate,
+            onLongPressStart: pageOption.onLongPressStart,
+            onLongPressEnd: pageOption.onLongPressEnd,
             onScaleEnd: pageOption.onScaleEnd,
             gestureDetectorBehavior: pageOption.gestureDetectorBehavior,
             tightMode: pageOption.tightMode,
@@ -299,6 +292,8 @@ class _PhotoViewGalleryState extends State<PhotoViewGallery> {
             onDragStart: pageOption.onDragStart,
             onDragEnd: pageOption.onDragEnd,
             onDragUpdate: pageOption.onDragUpdate,
+            onLongPressStart: pageOption.onLongPressStart,
+            onLongPressEnd: pageOption.onLongPressEnd,
             onScaleEnd: pageOption.onScaleEnd,
             gestureDetectorBehavior: pageOption.gestureDetectorBehavior,
             tightMode: pageOption.tightMode,
@@ -355,6 +350,8 @@ class PhotoViewGalleryPageOptions {
     this.onDragStart,
     this.onDragEnd,
     this.onDragUpdate,
+    this.onLongPressStart,
+    this.onLongPressEnd,
     this.onScaleEnd,
     this.gestureDetectorBehavior,
     this.tightMode,
@@ -381,6 +378,8 @@ class PhotoViewGalleryPageOptions {
     this.onDragStart,
     this.onDragEnd,
     this.onDragUpdate,
+    this.onLongPressStart,
+    this.onLongPressEnd,
     this.onScaleEnd,
     this.gestureDetectorBehavior,
     this.tightMode,
@@ -431,9 +430,15 @@ class PhotoViewGalleryPageOptions {
   /// Mirror to [PhotoView.onDragDown]
   final PhotoViewImageDragEndCallback? onDragEnd;
 
-  /// Mirror to [PhotoView.onDraUpdate]
+  /// Mirror to [PhotoView.onDragUpdate]
   final PhotoViewImageDragUpdateCallback? onDragUpdate;
 
+  /// Mirror to [PhotoView.onLongPressStart]
+  final PhotoViewImageLongPressStartCallback? onLongPressStart;
+
+  /// Mirror to [PhotoView.onLongPressStart]
+  final PhotoViewImageLongPressEndCallback? onLongPressEnd;
+
   /// Mirror to [PhotoView.onTapDown]
   final PhotoViewImageTapDownCallback? onTapDown;
 

+ 17 - 13
mobile/lib/shared/ui/photo_view/src/core/photo_view_core.dart

@@ -1,15 +1,6 @@
 import 'package:flutter/widgets.dart';
 import 'package:immich_mobile/shared/ui/photo_view/photo_view.dart'
-    show
-        PhotoViewScaleState,
-        PhotoViewHeroAttributes,
-        PhotoViewImageTapDownCallback,
-        PhotoViewImageTapUpCallback,
-        PhotoViewImageScaleEndCallback,
-        PhotoViewImageDragEndCallback,
-        PhotoViewImageDragStartCallback,
-        PhotoViewImageDragUpdateCallback,
-        ScaleStateCycle;
+    show PhotoViewHeroAttributes, PhotoViewImageDragEndCallback, PhotoViewImageDragStartCallback, PhotoViewImageDragUpdateCallback, PhotoViewImageLongPressEndCallback, PhotoViewImageLongPressStartCallback, PhotoViewImageScaleEndCallback, PhotoViewImageTapDownCallback, PhotoViewImageTapUpCallback, PhotoViewScaleState, ScaleStateCycle;
 import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_controller.dart';
 import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_controller_delegate.dart';
 import 'package:immich_mobile/shared/ui/photo_view/src/controller/photo_view_scalestate_controller.dart';
@@ -36,6 +27,8 @@ class PhotoViewCore extends StatefulWidget {
     required this.onDragStart,
     required this.onDragEnd,
     required this.onDragUpdate,
+    required this.onLongPressStart,
+    required this.onLongPressEnd,
     required this.onScaleEnd,
     required this.gestureDetectorBehavior,
     required this.controller,
@@ -61,6 +54,8 @@ class PhotoViewCore extends StatefulWidget {
     this.onDragStart,
     this.onDragEnd,
     this.onDragUpdate,
+    this.onLongPressStart,
+    this.onLongPressEnd,
     this.onScaleEnd,
     this.gestureDetectorBehavior,
     required this.controller,
@@ -97,6 +92,9 @@ class PhotoViewCore extends StatefulWidget {
   final PhotoViewImageDragEndCallback? onDragEnd;
   final PhotoViewImageDragUpdateCallback? onDragUpdate;
 
+  final PhotoViewImageLongPressStartCallback? onLongPressStart;
+  final PhotoViewImageLongPressEndCallback? onLongPressEnd;
+
   final HitTestBehavior? gestureDetectorBehavior;
   final bool tightMode;
   final bool disableGestures;
@@ -359,13 +357,13 @@ class PhotoViewCoreState extends State<PhotoViewCore>
             onScaleStart: onScaleStart,
             onScaleUpdate: onScaleUpdate,
             onScaleEnd: onScaleEnd,
-            onDragStart:  widget.onDragStart != null 
+            onDragStart:  widget.onDragStart != null
                ? (details) => widget.onDragStart!(context, details, value)
                : null,
-            onDragEnd:  widget.onDragEnd != null 
+            onDragEnd:  widget.onDragEnd != null
                ? (details) => widget.onDragEnd!(context, details, value)
                : null,
-            onDragUpdate: widget.onDragUpdate != null 
+            onDragUpdate: widget.onDragUpdate != null
                ? (details) => widget.onDragUpdate!(context, details, value)
                : null,
             hitDetector: this,
@@ -375,6 +373,12 @@ class PhotoViewCoreState extends State<PhotoViewCore>
             onTapDown: widget.onTapDown != null
                 ? (details) => widget.onTapDown!(context, details, value)
                 : null,
+            onLongPressStart: widget.onLongPressStart != null
+                ? (details) => widget.onLongPressStart!(context, details, value)
+                : null,
+            onLongPressEnd: widget.onLongPressEnd != null
+                ? (details) => widget.onLongPressEnd!(context, details, value)
+                : null,
             child: child,
           );
         } else {

+ 21 - 3
mobile/lib/shared/ui/photo_view/src/core/photo_view_gesture_detector.dart

@@ -16,6 +16,8 @@ class PhotoViewGestureDetector extends StatelessWidget {
     this.onDragStart,
     this.onDragEnd,
     this.onDragUpdate,
+    this.onLongPressStart,
+    this.onLongPressEnd,
     this.child,
     this.onTapUp,
     this.onTapDown,
@@ -36,6 +38,9 @@ class PhotoViewGestureDetector extends StatelessWidget {
   final GestureTapUpCallback? onTapUp;
   final GestureTapDownCallback? onTapDown;
 
+  final GestureLongPressStartCallback? onLongPressStart;
+  final GestureLongPressEndCallback? onLongPressEnd;
+
   final Widget? child;
 
   final HitTestBehavior? behavior;
@@ -63,7 +68,7 @@ class PhotoViewGestureDetector extends StatelessWidget {
     }
 
     if (onDragStart != null || onDragEnd != null || onDragUpdate != null) {
-      gestures[VerticalDragGestureRecognizer] = 
+      gestures[VerticalDragGestureRecognizer] =
           GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
         () => VerticalDragGestureRecognizer(debugOwner: this),
         (VerticalDragGestureRecognizer instance) {
@@ -75,6 +80,19 @@ class PhotoViewGestureDetector extends StatelessWidget {
       );
     }
 
+    if (onLongPressStart != null || onLongPressEnd != null) {
+      print('on long press start added');
+      gestures[LongPressGestureRecognizer] =
+          GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
+        () => LongPressGestureRecognizer(debugOwner: this),
+        (LongPressGestureRecognizer instance) {
+          instance
+              ..onLongPressStart = onLongPressStart
+              ..onLongPressEnd = onLongPressEnd;
+        },
+      );
+    }
+
     gestures[DoubleTapGestureRecognizer] =
         GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
       () => DoubleTapGestureRecognizer(debugOwner: this),
@@ -236,7 +254,7 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer {
 /// ```
 class PhotoViewGestureDetectorScope extends InheritedWidget {
   const PhotoViewGestureDetectorScope({
-    super.key, 
+    super.key,
     this.axis,
     this.touchSlopFactor = .2,
     required Widget child,
@@ -254,7 +272,7 @@ class PhotoViewGestureDetectorScope extends InheritedWidget {
   // 0: most reactive but will not let tap recognizers accept gestures
   // <1: less reactive but gives the most leeway to other recognizers
   // 1: will not be able to compete with a `HorizontalDragGestureRecognizer` up the widget tree
-  final double touchSlopFactor;  
+  final double touchSlopFactor;
 
   @override
   bool updateShouldNotify(PhotoViewGestureDetectorScope oldWidget) {

+ 12 - 0
mobile/lib/shared/ui/photo_view/src/photo_view_wrappers.dart

@@ -27,6 +27,8 @@ class ImageWrapper extends StatefulWidget {
     required this.onDragStart,
     required this.onDragEnd,
     required this.onDragUpdate,
+    required this.onLongPressStart,
+    required this.onLongPressEnd,
     required this.onScaleEnd,
     required this.outerSize,
     required this.gestureDetectorBehavior,
@@ -57,6 +59,8 @@ class ImageWrapper extends StatefulWidget {
   final PhotoViewImageDragStartCallback? onDragStart;
   final PhotoViewImageDragEndCallback? onDragEnd;
   final PhotoViewImageDragUpdateCallback? onDragUpdate;
+  final PhotoViewImageLongPressStartCallback? onLongPressStart;
+  final PhotoViewImageLongPressEndCallback? onLongPressEnd;
   final PhotoViewImageScaleEndCallback? onScaleEnd;
   final Size outerSize;
   final HitTestBehavior? gestureDetectorBehavior;
@@ -201,6 +205,8 @@ class _ImageWrapperState extends State<ImageWrapper> {
       onDragStart: widget.onDragStart,
       onDragEnd: widget.onDragEnd,
       onDragUpdate: widget.onDragUpdate,
+      onLongPressStart: widget.onLongPressStart,
+      onLongPressEnd: widget.onLongPressEnd,
       onScaleEnd: widget.onScaleEnd,
       gestureDetectorBehavior: widget.gestureDetectorBehavior,
       tightMode: widget.tightMode ?? false,
@@ -253,6 +259,8 @@ class CustomChildWrapper extends StatelessWidget {
     this.onDragStart,
     this.onDragEnd,
     this.onDragUpdate,
+    this.onLongPressStart,
+    this.onLongPressEnd,
     this.onScaleEnd,
     required this.outerSize,
     this.gestureDetectorBehavior,
@@ -283,6 +291,8 @@ class CustomChildWrapper extends StatelessWidget {
   final PhotoViewImageDragStartCallback? onDragStart;
   final PhotoViewImageDragEndCallback? onDragEnd;
   final PhotoViewImageDragUpdateCallback? onDragUpdate;
+  final PhotoViewImageLongPressStartCallback? onLongPressStart;
+  final PhotoViewImageLongPressEndCallback? onLongPressEnd;
   final PhotoViewImageScaleEndCallback? onScaleEnd;
   final Size outerSize;
   final HitTestBehavior? gestureDetectorBehavior;
@@ -316,6 +326,8 @@ class CustomChildWrapper extends StatelessWidget {
       onDragStart: onDragStart,
       onDragEnd: onDragEnd,
       onDragUpdate: onDragUpdate,
+      onLongPressEnd: onLongPressEnd,
+      onLongPressStart: onLongPressStart,
       onScaleEnd: onScaleEnd,
       gestureDetectorBehavior: gestureDetectorBehavior,
       tightMode: tightMode ?? false,