#1013 fixed handling of app inactive event by screen rotation on API33+
This commit is contained in:
parent
978c22dc50
commit
2d56172c96
3 changed files with 22 additions and 18 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue