diff --git a/CHANGELOG.md b/CHANGELOG.md index 4656ee724..47a617803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ All notable changes to this project will be documented in this file. - Collection: ask to rename/replace/skip when converting items with name conflict - Export: bulk converting motion photos to still images +### Fixed + +- switching to PiP when changing device orientation on Android >=13 + ## [v1.11.3] - 2024-06-17 ### Added diff --git a/lib/theme/durations.dart b/lib/theme/durations.dart index 9ba4ab37c..5bd83f525 100644 --- a/lib/theme/durations.dart +++ b/lib/theme/durations.dart @@ -60,7 +60,7 @@ class ADurations { static const highlightJumpDelay = Duration(milliseconds: 400); static const highlightScrollInitDelay = Duration(milliseconds: 800); static const motionPhotoAutoPlayDelay = Duration(milliseconds: 700); - static const videoPauseAppInactiveDelay = Duration(milliseconds: 300); + static const appInactiveReactionDelay = Duration(milliseconds: 300); static const videoOverlayHideDelay = Duration(milliseconds: 500); static const videoProgressTimerInterval = Duration(milliseconds: 300); static const doubleBackTimerDelay = Duration(milliseconds: 1000); diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index a6bdfd4db..116637eb3 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -79,7 +79,7 @@ class _EntryViewerStackState extends State with EntryViewContr late VideoActionDelegate _videoActionDelegate; final ValueNotifier _heroInfoNotifier = ValueNotifier(null); bool _isEntryTracked = true; - Timer? _overlayHidingTimer, _videoPauseTimer; + Timer? _overlayHidingTimer, _appInactiveReactionTimer; @override bool get isViewingImage => _currentVerticalPage.value == imagePage; @@ -203,7 +203,7 @@ class _EntryViewerStackState extends State with EntryViewContr _verticalScrollNotifier.dispose(); _heroInfoNotifier.dispose(); _stopOverlayHidingTimer(); - _stopVideoPauseTimer(); + _stopAppInactiveTimer(); AvesApp.lifecycleStateNotifier.removeListener(_onAppLifecycleStateChanged); _unregisterWidget(widget); super.dispose(); @@ -334,42 +334,43 @@ class _EntryViewerStackState extends State with EntryViewContr switch (AvesApp.lifecycleStateNotifier.value) { case AppLifecycleState.inactive: // inactive: when losing focus - _onAppInactive(); + // also triggered when app is rotated on Android API >=33 + _startAppInactiveTimer(); case AppLifecycleState.paused: case AppLifecycleState.detached: // paused: when switching to another app // detached: when app is without a view viewerController.autopilot = false; - _stopVideoPauseTimer(); + _stopAppInactiveTimer(); pauseVideoControllers(); case AppLifecycleState.resumed: - _stopVideoPauseTimer(); + _stopAppInactiveTimer(); case AppLifecycleState.hidden: // hidden: transient state between `inactive` and `paused` break; } } - Future _onAppInactive() async { - final playingController = context.read().getPlayingController(); + Future _onAppInactive(AvesVideoController? playingController) async { bool enabledPip = false; if (settings.videoBackgroundMode == VideoBackgroundMode.pip) { - enabledPip |= await _enablePictureInPicture(); + enabledPip |= await _enablePictureInPicture(playingController); } if (enabledPip) { // ensure playback, in case lifecycle paused/resumed events happened when switching to PiP await playingController?.play(); } else { - _startVideoPauseTimer(); + await pauseVideoControllers(); } } - void _startVideoPauseTimer() { - _stopVideoPauseTimer(); - _videoPauseTimer = Timer(ADurations.videoPauseAppInactiveDelay, pauseVideoControllers); + void _startAppInactiveTimer() { + _stopAppInactiveTimer(); + final playingController = context.read().getPlayingController(); + _appInactiveReactionTimer = Timer(ADurations.appInactiveReactionDelay, () => _onAppInactive(playingController)); } - void _stopVideoPauseTimer() => _videoPauseTimer?.cancel(); + void _stopAppInactiveTimer() => _appInactiveReactionTimer?.cancel(); Widget _decorateOverlay(Widget overlay) { return ValueListenableBuilder( @@ -920,10 +921,9 @@ class _EntryViewerStackState extends State with EntryViewContr } } - Future _enablePictureInPicture() async { - final videoController = context.read().getPlayingController(); - if (videoController != null) { - final entrySize = videoController.entry.displaySize; + Future _enablePictureInPicture(AvesVideoController? playingController) async { + if (playingController != null) { + final entrySize = playingController.entry.displaySize; final aspectRatio = Rational(entrySize.width.round(), entrySize.height.round()); final viewSize = MediaQuery.sizeOf(context) * MediaQuery.devicePixelRatioOf(context);