#1013 fixed handling of app inactive event by screen rotation on API33+

This commit is contained in:
Thibault Deckers 2024-06-23 18:23:46 +02:00
parent 978c22dc50
commit 2d56172c96
3 changed files with 22 additions and 18 deletions

View file

@ -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 - Collection: ask to rename/replace/skip when converting items with name conflict
- Export: bulk converting motion photos to still images - Export: bulk converting motion photos to still images
### Fixed
- switching to PiP when changing device orientation on Android >=13
## <a id="v1.11.3"></a>[v1.11.3] - 2024-06-17 ## <a id="v1.11.3"></a>[v1.11.3] - 2024-06-17
### Added ### Added

View file

@ -60,7 +60,7 @@ class ADurations {
static const highlightJumpDelay = Duration(milliseconds: 400); static const highlightJumpDelay = Duration(milliseconds: 400);
static const highlightScrollInitDelay = Duration(milliseconds: 800); static const highlightScrollInitDelay = Duration(milliseconds: 800);
static const motionPhotoAutoPlayDelay = Duration(milliseconds: 700); 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 videoOverlayHideDelay = Duration(milliseconds: 500);
static const videoProgressTimerInterval = Duration(milliseconds: 300); static const videoProgressTimerInterval = Duration(milliseconds: 300);
static const doubleBackTimerDelay = Duration(milliseconds: 1000); static const doubleBackTimerDelay = Duration(milliseconds: 1000);

View file

@ -79,7 +79,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
late VideoActionDelegate _videoActionDelegate; late VideoActionDelegate _videoActionDelegate;
final ValueNotifier<HeroInfo?> _heroInfoNotifier = ValueNotifier(null); final ValueNotifier<HeroInfo?> _heroInfoNotifier = ValueNotifier(null);
bool _isEntryTracked = true; bool _isEntryTracked = true;
Timer? _overlayHidingTimer, _videoPauseTimer; Timer? _overlayHidingTimer, _appInactiveReactionTimer;
@override @override
bool get isViewingImage => _currentVerticalPage.value == imagePage; bool get isViewingImage => _currentVerticalPage.value == imagePage;
@ -203,7 +203,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
_verticalScrollNotifier.dispose(); _verticalScrollNotifier.dispose();
_heroInfoNotifier.dispose(); _heroInfoNotifier.dispose();
_stopOverlayHidingTimer(); _stopOverlayHidingTimer();
_stopVideoPauseTimer(); _stopAppInactiveTimer();
AvesApp.lifecycleStateNotifier.removeListener(_onAppLifecycleStateChanged); AvesApp.lifecycleStateNotifier.removeListener(_onAppLifecycleStateChanged);
_unregisterWidget(widget); _unregisterWidget(widget);
super.dispose(); super.dispose();
@ -334,42 +334,43 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
switch (AvesApp.lifecycleStateNotifier.value) { switch (AvesApp.lifecycleStateNotifier.value) {
case AppLifecycleState.inactive: case AppLifecycleState.inactive:
// inactive: when losing focus // inactive: when losing focus
_onAppInactive(); // also triggered when app is rotated on Android API >=33
_startAppInactiveTimer();
case AppLifecycleState.paused: case AppLifecycleState.paused:
case AppLifecycleState.detached: case AppLifecycleState.detached:
// paused: when switching to another app // paused: when switching to another app
// detached: when app is without a view // detached: when app is without a view
viewerController.autopilot = false; viewerController.autopilot = false;
_stopVideoPauseTimer(); _stopAppInactiveTimer();
pauseVideoControllers(); pauseVideoControllers();
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
_stopVideoPauseTimer(); _stopAppInactiveTimer();
case AppLifecycleState.hidden: case AppLifecycleState.hidden:
// hidden: transient state between `inactive` and `paused` // hidden: transient state between `inactive` and `paused`
break; break;
} }
} }
Future<void> _onAppInactive() async { Future<void> _onAppInactive(AvesVideoController? playingController) async {
final playingController = context.read<VideoConductor>().getPlayingController();
bool enabledPip = false; bool enabledPip = false;
if (settings.videoBackgroundMode == VideoBackgroundMode.pip) { if (settings.videoBackgroundMode == VideoBackgroundMode.pip) {
enabledPip |= await _enablePictureInPicture(); enabledPip |= await _enablePictureInPicture(playingController);
} }
if (enabledPip) { if (enabledPip) {
// ensure playback, in case lifecycle paused/resumed events happened when switching to PiP // ensure playback, in case lifecycle paused/resumed events happened when switching to PiP
await playingController?.play(); await playingController?.play();
} else { } else {
_startVideoPauseTimer(); await pauseVideoControllers();
} }
} }
void _startVideoPauseTimer() { void _startAppInactiveTimer() {
_stopVideoPauseTimer(); _stopAppInactiveTimer();
_videoPauseTimer = Timer(ADurations.videoPauseAppInactiveDelay, pauseVideoControllers); final playingController = context.read<VideoConductor>().getPlayingController();
_appInactiveReactionTimer = Timer(ADurations.appInactiveReactionDelay, () => _onAppInactive(playingController));
} }
void _stopVideoPauseTimer() => _videoPauseTimer?.cancel(); void _stopAppInactiveTimer() => _appInactiveReactionTimer?.cancel();
Widget _decorateOverlay(Widget overlay) { Widget _decorateOverlay(Widget overlay) {
return ValueListenableBuilder<double>( return ValueListenableBuilder<double>(
@ -920,10 +921,9 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
} }
} }
Future<bool> _enablePictureInPicture() async { Future<bool> _enablePictureInPicture(AvesVideoController? playingController) async {
final videoController = context.read<VideoConductor>().getPlayingController(); if (playingController != null) {
if (videoController != null) { final entrySize = playingController.entry.displaySize;
final entrySize = videoController.entry.displaySize;
final aspectRatio = Rational(entrySize.width.round(), entrySize.height.round()); final aspectRatio = Rational(entrySize.width.round(), entrySize.height.round());
final viewSize = MediaQuery.sizeOf(context) * MediaQuery.devicePixelRatioOf(context); final viewSize = MediaQuery.sizeOf(context) * MediaQuery.devicePixelRatioOf(context);