diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt index 7736d8058..cd30bf1a7 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt @@ -96,7 +96,7 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any val granted = PermissionManager.requestMediaFileAccess(activity, uris, mimeTypes) success(granted) } catch (e: Exception) { - error("requestMediaFileAccess-request", "failed to request access to uris=$uris", e.message) + error("requestMediaFileAccess-request", "failed to request access to ${uris.size} uris=$uris", e.message) } endOfStream() } diff --git a/lib/ref/mime_types.dart b/lib/ref/mime_types.dart index ef07f9959..9df697fc2 100644 --- a/lib/ref/mime_types.dart +++ b/lib/ref/mime_types.dart @@ -52,7 +52,9 @@ class MimeTypes { static const v3gpp = 'video/3gpp'; static const asf = 'video/x-ms-asf'; static const avi = 'video/avi'; + static const aviMSVideo = 'video/msvideo'; static const aviVnd = 'video/vnd.avi'; + static const aviXMSVideo = 'video/x-msvideo'; static const flv = 'video/flv'; static const flvX = 'video/x-flv'; static const mkv = 'video/mkv'; @@ -87,7 +89,7 @@ class MimeTypes { static const Set _knownOpaqueImages = {jpeg}; - static const Set _knownVideos = {v3gpp, asf, avi, aviVnd, flv, flvX, mkv, mkvX, mov, mp2p, mp2t, mp2ts, mp4, mpeg, ogv, realVideo, webm, wmv}; + static const Set _knownVideos = {v3gpp, asf, avi, aviMSVideo, aviVnd, aviXMSVideo, flv, flvX, mkv, mkvX, mov, mp2p, mp2t, mp2ts, mp4, mpeg, ogv, realVideo, webm, wmv}; static final Set knownMediaTypes = { anyImage, @@ -108,7 +110,9 @@ class MimeTypes { static bool refersToSameType(String a, b) { switch (a) { case avi: + case aviMSVideo: case aviVnd: + case aviXMSVideo: return [avi, aviVnd].contains(b); case bmp: case bmpX: diff --git a/lib/widgets/viewer/visual/entry_page_view.dart b/lib/widgets/viewer/visual/entry_page_view.dart index 362aa1424..c263cad74 100644 --- a/lib/widgets/viewer/visual/entry_page_view.dart +++ b/lib/widgets/viewer/visual/entry_page_view.dart @@ -113,8 +113,11 @@ class _EntryPageViewState extends State with SingleTickerProvider viewerController.startAutopilotAnimation( vsync: this, onUpdate: ({required scaleLevel}) { - final scale = _magnifierController.scaleBoundaries.scaleForLevel(scaleLevel); - _magnifierController.update(scale: scale, source: ChangeSource.animation); + final boundaries = _magnifierController.scaleBoundaries; + if (boundaries != null) { + final scale = boundaries.scaleForLevel(scaleLevel); + _magnifierController.update(scale: scale, source: ChangeSource.animation); + } }); } @@ -318,11 +321,14 @@ class _EntryPageViewState extends State with SingleTickerProvider // while cover is fading out, the same controller is used for both the cover and the video, // and both fire scale boundaries events, so we make sure that in the end // the scale boundaries from the video are used after the cover is gone - _magnifierController.setScaleBoundaries( - _magnifierController.scaleBoundaries.copyWith( - childSize: videoDisplaySize, - ), - ); + final boundaries = _magnifierController.scaleBoundaries; + if (boundaries != null) { + _magnifierController.setScaleBoundaries( + boundaries.copyWith( + childSize: videoDisplaySize, + ), + ); + } }, child: ValueListenableBuilder( valueListenable: _videoCoverInfoNotifier, diff --git a/plugins/aves_magnifier/lib/src/controller/controller.dart b/plugins/aves_magnifier/lib/src/controller/controller.dart index bf13e0084..3e1695375 100644 --- a/plugins/aves_magnifier/lib/src/controller/controller.dart +++ b/plugins/aves_magnifier/lib/src/controller/controller.dart @@ -48,7 +48,7 @@ class AvesMagnifierController { double? get scale => currentState.scale; - ScaleBoundaries get scaleBoundaries => _scaleBoundaries!; + ScaleBoundaries? get scaleBoundaries => _scaleBoundaries; ScaleStateChange get scaleState => _currentScaleState; @@ -118,17 +118,20 @@ class AvesMagnifierController { } double? getScaleForScaleState(ScaleState scaleState) { - double _clamp(double scale) => scale.clamp(scaleBoundaries.minScale, scaleBoundaries.maxScale); + final boundaries = scaleBoundaries; + if (boundaries == null) return null; + + double _clamp(double scale) => scale.clamp(boundaries.minScale, boundaries.maxScale); switch (scaleState) { case ScaleState.initial: case ScaleState.zoomedIn: case ScaleState.zoomedOut: - return _clamp(scaleBoundaries.initialScale); + return _clamp(boundaries.initialScale); case ScaleState.covering: - return _clamp(ScaleLevel.scaleForCovering(scaleBoundaries.viewportSize, scaleBoundaries.childSize)); + return _clamp(ScaleLevel.scaleForCovering(boundaries.viewportSize, boundaries.childSize)); case ScaleState.originalSize: - return _clamp(scaleBoundaries.originalScale); + return _clamp(boundaries.originalScale); default: return null; } diff --git a/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart b/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart index 9ef3a4fae..93a47d0f1 100644 --- a/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart +++ b/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart @@ -13,11 +13,7 @@ import 'package:flutter/widgets.dart'; mixin AvesMagnifierControllerDelegate on State { AvesMagnifierController get controller => widget.controller; - ScaleBoundaries get scaleBoundaries => controller.scaleBoundaries; - - Size get childSize => scaleBoundaries.childSize; - - Size get viewportSize => scaleBoundaries.viewportSize; + ScaleBoundaries? get scaleBoundaries => controller.scaleBoundaries; ScaleStateCycle get scaleStateCycle => widget.scaleStateCycle; @@ -56,8 +52,9 @@ mixin AvesMagnifierControllerDelegate on State { var nextPosition = Offset.zero; if (nextScaleState == ScaleState.covering || nextScaleState == ScaleState.originalSize) { final childFocalPoint = scaleStateChange.childFocalPoint; - if (childFocalPoint != null) { - nextPosition = scaleBoundaries.childToStatePosition(nextScale!, childFocalPoint); + final boundaries = scaleBoundaries; + if (childFocalPoint != null && boundaries != null) { + nextPosition = boundaries.childToStatePosition(nextScale!, childFocalPoint); } } @@ -70,11 +67,14 @@ mixin AvesMagnifierControllerDelegate on State { } void _onMagnifierStateChange(MagnifierState state) { + final boundaries = scaleBoundaries; + if (boundaries == null) return; + controller.update(position: clampPosition(), source: state.source); if (controller.scale == controller.previousState.scale) return; if (state.source == ChangeSource.internal || state.source == ChangeSource.animation) return; - final newScaleState = (scale! > scaleBoundaries.initialScale) ? ScaleState.zoomedIn : ScaleState.zoomedOut; + final newScaleState = (scale! > boundaries.initialScale) ? ScaleState.zoomedIn : ScaleState.zoomedOut; controller.setScaleState(newScaleState, state.source); } @@ -104,9 +104,12 @@ mixin AvesMagnifierControllerDelegate on State { } void updateScaleStateFromNewScale(double newScale, ChangeSource source) { + final boundaries = scaleBoundaries; + if (boundaries == null) return; + var newScaleState = ScaleState.initial; - if (scale != scaleBoundaries.initialScale) { - newScaleState = (newScale > scaleBoundaries.initialScale) ? ScaleState.zoomedIn : ScaleState.zoomedOut; + if (scale != boundaries.initialScale) { + newScaleState = (newScale > boundaries.initialScale) ? ScaleState.zoomedIn : ScaleState.zoomedOut; } controller.setScaleState(newScaleState, source); } @@ -136,10 +139,13 @@ mixin AvesMagnifierControllerDelegate on State { } CornersRange cornersX({double? scale}) { + final boundaries = scaleBoundaries; + if (boundaries == null) return const CornersRange(0, 0); + final _scale = scale ?? this.scale!; - final computedWidth = childSize.width * _scale; - final screenWidth = viewportSize.width; + final computedWidth = boundaries.childSize.width * _scale; + final screenWidth = boundaries.viewportSize.width; final positionX = basePosition.x; final widthDiff = computedWidth - screenWidth; @@ -150,10 +156,13 @@ mixin AvesMagnifierControllerDelegate on State { } CornersRange cornersY({double? scale}) { + final boundaries = scaleBoundaries; + if (boundaries == null) return const CornersRange(0, 0); + final _scale = scale ?? this.scale!; - final computedHeight = childSize.height * _scale; - final screenHeight = viewportSize.height; + final computedHeight = boundaries.childSize.height * _scale; + final screenHeight = boundaries.viewportSize.height; final positionY = basePosition.y; final heightDiff = computedHeight - screenHeight; @@ -164,14 +173,17 @@ mixin AvesMagnifierControllerDelegate on State { } Offset clampPosition({Offset? position, double? scale}) { + final boundaries = scaleBoundaries; + if (boundaries == null) return Offset.zero; + final _scale = scale ?? this.scale!; final _position = position ?? this.position; - final computedWidth = childSize.width * _scale; - final computedHeight = childSize.height * _scale; + final computedWidth = boundaries.childSize.width * _scale; + final computedHeight = boundaries.childSize.height * _scale; - final screenWidth = viewportSize.width; - final screenHeight = viewportSize.height; + final screenWidth = boundaries.viewportSize.width; + final screenHeight = boundaries.viewportSize.height; var finalX = 0.0; if (screenWidth < computedWidth) { diff --git a/plugins/aves_magnifier/lib/src/core/core.dart b/plugins/aves_magnifier/lib/src/core/core.dart index 47a762758..d8512c37c 100644 --- a/plugins/aves_magnifier/lib/src/core/core.dart +++ b/plugins/aves_magnifier/lib/src/core/core.dart @@ -112,6 +112,9 @@ class _MagnifierCoreState extends State with TickerProviderStateM } void onScaleUpdate(ScaleUpdateDetails details) { + final boundaries = scaleBoundaries; + if (boundaries == null) return; + double newScale; if (_doubleTap) { // quick scale, aka one finger zoom @@ -131,7 +134,7 @@ class _MagnifierCoreState extends State with TickerProviderStateM final scaleFocalPoint = _doubleTap ? _startFocalPoint! : details.focalPoint; final panPositionDelta = scaleFocalPoint - _lastViewportFocalPosition!; - final scalePositionDelta = scaleBoundaries.viewportToStatePosition(controller, scaleFocalPoint) * (scale! / newScale - 1); + final scalePositionDelta = boundaries.viewportToStatePosition(controller, scaleFocalPoint) * (scale! / newScale - 1); final newPosition = position + panPositionDelta + scalePositionDelta; updateScaleStateFromNewScale(newScale, ChangeSource.gesture); @@ -145,10 +148,13 @@ class _MagnifierCoreState extends State with TickerProviderStateM } void onScaleEnd(ScaleEndDetails details) { + final boundaries = scaleBoundaries; + if (boundaries == null) return; + final _position = controller.position; final _scale = controller.scale!; - final maxScale = scaleBoundaries.maxScale; - final minScale = scaleBoundaries.minScale; + final maxScale = boundaries.maxScale; + final minScale = boundaries.minScale; // animate back to min/max scale if gesture yielded a scale exceeding them if (_scale > maxScale || _scale < minScale) { @@ -202,21 +208,30 @@ class _MagnifierCoreState extends State with TickerProviderStateM final onTap = widget.onTap; if (onTap == null) return; + final boundaries = scaleBoundaries; + if (boundaries == null) return; + final viewportTapPosition = details.localPosition; + final viewportSize = boundaries.viewportSize; final alignment = Alignment(viewportTapPosition.dx / viewportSize.width, viewportTapPosition.dy / viewportSize.height); - final childTapPosition = scaleBoundaries.viewportToChildPosition(controller, viewportTapPosition); + final childTapPosition = boundaries.viewportToChildPosition(controller, viewportTapPosition); + onTap(context, controller.currentState, alignment, childTapPosition); } void onDoubleTap(TapDownDetails details) { + final boundaries = scaleBoundaries; + if (boundaries == null) return; + final viewportTapPosition = details.localPosition; - if (widget.onDoubleTap != null) { - final viewportSize = scaleBoundaries.viewportSize; + final onDoubleTap = widget.onDoubleTap; + if (onDoubleTap != null) { + final viewportSize = boundaries.viewportSize; final alignment = Alignment(viewportTapPosition.dx / viewportSize.width, viewportTapPosition.dy / viewportSize.height); - if (widget.onDoubleTap?.call(alignment) == true) return; + if (onDoubleTap.call(alignment) == true) return; } - final childTapPosition = scaleBoundaries.viewportToChildPosition(controller, viewportTapPosition); + final childTapPosition = boundaries.viewportToChildPosition(controller, viewportTapPosition); nextScaleState(ChangeSource.gesture, childFocalPoint: childTapPosition); } @@ -245,7 +260,7 @@ class _MagnifierCoreState extends State with TickerProviderStateM /// Check if scale is equal to initial after scale animation update void onAnimationStatusCompleted() { - if (controller.scaleState.state != ScaleState.initial && scale == scaleBoundaries.initialScale) { + if (controller.scaleState.state != ScaleState.initial && scale == scaleBoundaries?.initialScale) { controller.setScaleState(ScaleState.initial, ChangeSource.animation); } } @@ -264,42 +279,44 @@ class _MagnifierCoreState extends State with TickerProviderStateM } return StreamBuilder( - stream: controller.stateStream, - initialData: controller.previousState, - builder: (context, snapshot) { - if (!snapshot.hasData) return Container(); + stream: controller.stateStream, + initialData: controller.previousState, + builder: (context, snapshot) { + final boundaries = scaleBoundaries; + if (!snapshot.hasData || boundaries == null) return const SizedBox(); - final magnifierState = snapshot.data!; - final position = magnifierState.position; - final applyScale = widget.applyScale; + final magnifierState = snapshot.data!; + final position = magnifierState.position; + final applyScale = widget.applyScale; - Widget child = CustomSingleChildLayout( - delegate: _CenterWithOriginalSizeDelegate( - scaleBoundaries.childSize, - basePosition, - applyScale, - ), - child: widget.child, - ); + Widget child = CustomSingleChildLayout( + delegate: _CenterWithOriginalSizeDelegate( + boundaries.childSize, + basePosition, + applyScale, + ), + child: widget.child, + ); - child = Transform( - transform: Matrix4.identity() - ..translate(position.dx, position.dy) - ..scale(applyScale ? scale : 1.0), - alignment: basePosition, - child: child, - ); + child = Transform( + transform: Matrix4.identity() + ..translate(position.dx, position.dy) + ..scale(applyScale ? scale : 1.0), + alignment: basePosition, + child: child, + ); - return MagnifierGestureDetector( - onDoubleTap: onDoubleTap, - onScaleStart: onScaleStart, - onScaleUpdate: onScaleUpdate, - onScaleEnd: onScaleEnd, - hitDetector: this, - onTapUp: widget.onTap == null ? null : onTap, - child: child, - ); - }); + return MagnifierGestureDetector( + onDoubleTap: onDoubleTap, + onScaleStart: onScaleStart, + onScaleUpdate: onScaleUpdate, + onScaleEnd: onScaleEnd, + hitDetector: this, + onTapUp: widget.onTap == null ? null : onTap, + child: child, + ); + }, + ); } } diff --git a/plugins/aves_magnifier/lib/src/pan/corner_hit_detector.dart b/plugins/aves_magnifier/lib/src/pan/corner_hit_detector.dart index bf80edbea..8faa9e8a4 100644 --- a/plugins/aves_magnifier/lib/src/pan/corner_hit_detector.dart +++ b/plugins/aves_magnifier/lib/src/pan/corner_hit_detector.dart @@ -9,8 +9,11 @@ mixin CornerHitDetector on AvesMagnifierControllerDelegate { // so be sure to compare with `precisionErrorTolerance` _CornerHit _hitCornersX() { - final childWidth = scaleBoundaries.childSize.width * scale!; - final viewportWidth = scaleBoundaries.viewportSize.width; + final boundaries = scaleBoundaries; + if (boundaries == null) return const _CornerHit(false, false); + + final childWidth = boundaries.childSize.width * scale!; + final viewportWidth = boundaries.viewportSize.width; if (viewportWidth + precisionErrorTolerance >= childWidth) { return const _CornerHit(true, true); } @@ -20,8 +23,11 @@ mixin CornerHitDetector on AvesMagnifierControllerDelegate { } _CornerHit _hitCornersY() { - final childHeight = scaleBoundaries.childSize.height * scale!; - final viewportHeight = scaleBoundaries.viewportSize.height; + final boundaries = scaleBoundaries; + if (boundaries == null) return const _CornerHit(false, false); + + final childHeight = boundaries.childSize.height * scale!; + final viewportHeight = boundaries.viewportSize.height; if (viewportHeight + precisionErrorTolerance >= childHeight) { return const _CornerHit(true, true); }