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);