diff --git a/lib/screens/channel/channel.dart b/lib/screens/channel/channel.dart index c66dae36..c65c0e4b 100644 --- a/lib/screens/channel/channel.dart +++ b/lib/screens/channel/channel.dart @@ -7,7 +7,6 @@ import 'package:frosty/screens/channel/chat/stores/chat_store.dart'; import 'package:frosty/screens/channel/chat/stores/chat_tabs_store.dart'; import 'package:frosty/screens/channel/chat/widgets/chat_tabs.dart'; import 'package:frosty/screens/channel/video/native_video.dart'; -import 'package:frosty/screens/channel/video/native_video_player_interface.dart'; import 'package:frosty/screens/channel/video/native_video_store.dart'; import 'package:frosty/screens/channel/video/stream_info_bar.dart'; import 'package:frosty/screens/channel/video/video.dart'; @@ -772,12 +771,10 @@ class _VideoChatState extends State // If on Android, use PiPSwitcher to enable PiP functionality. if (Platform.isAndroid) { - void notifyPip(bool isInPip) { - final store = _videoStore; - if (store is NativeVideoPlayerInterface) { - store.handleAndroidPipChanged(isInPip); - } - } + // Both players need this — the WebView store used to be skipped by a + // NativeVideoPlayerInterface type check, so it never learned it was in + // PiP on Android. + void notifyPip(bool isInPip) => _videoStore.handleAndroidPipChanged(isInPip); return PipWidget( pipLayout: PipActionsLayout.mediaOnlyPause, diff --git a/lib/screens/channel/video/native_video_player_interface.dart b/lib/screens/channel/video/native_video_player_interface.dart index 766d5a3c..a01e34ec 100644 --- a/lib/screens/channel/video/native_video_player_interface.dart +++ b/lib/screens/channel/video/native_video_player_interface.dart @@ -5,8 +5,4 @@ import 'package:frosty/screens/channel/video/video_player_interface.dart'; abstract class NativeVideoPlayerInterface extends VideoPlayerInterface { /// Whether the current quality is an HLS audio-only variant. bool get isAudioOnlyMode; - - /// Called from platform PiP callbacks on Android. Lets the store sync - /// its overlay state with the OS PiP window. - void handleAndroidPipChanged(bool isInPip); } diff --git a/lib/screens/channel/video/native_video_store.dart b/lib/screens/channel/video/native_video_store.dart index e0b789bc..e32a276c 100644 --- a/lib/screens/channel/video/native_video_store.dart +++ b/lib/screens/channel/video/native_video_store.dart @@ -499,9 +499,19 @@ abstract class NativeVideoStoreBase }); } + /// Keeps the Android PiP window's play/pause remote action in sync with + /// actual playback. Without this the button is stuck on whatever glyph the + /// `mediaOnlyPause` layout started with, and `onPipAction` — which toggles + /// blind — then does the opposite of what the button shows. + void _syncPipPlaybackState({required bool isPlaying}) { + if (!Platform.isAndroid) return; + _pip.setIsPlaying(isPlaying); + } + void _onPlaying() { _loading = false; _paused = false; + _syncPipPlaybackState(isPlaying: true); _isStalled = false; _hasPlayedOnce = true; _initializing = false; @@ -543,6 +553,7 @@ abstract class NativeVideoStoreBase void _onPlayerError(Map? eventData) { _loading = false; _paused = true; + _syncPipPlaybackState(isPlaying: false); _isStalled = false; _initializing = false; _isQualitySwitching = false; @@ -667,6 +678,7 @@ abstract class NativeVideoStoreBase void _onPaused() { _healthyPlaybackTimer?.cancel(); _paused = true; + _syncPipPlaybackState(isPlaying: false); _isStalled = false; _isQualitySwitching = false; _stallRecoveryTimer?.cancel(); diff --git a/lib/screens/channel/video/video_player_interface.dart b/lib/screens/channel/video/video_player_interface.dart index b2507f1f..9838e7ad 100644 --- a/lib/screens/channel/video/video_player_interface.dart +++ b/lib/screens/channel/video/video_player_interface.dart @@ -39,5 +39,10 @@ abstract class VideoPlayerInterface { Future setStreamQuality(String quality); Future updateStreamInfo({bool forceUpdate}); void handleAppResume(); + + /// Called from the platform PiP callbacks on Android so the store can sync + /// its overlay state, and its playback, with the OS PiP window. + void handleAndroidPipChanged(bool isInPip); + void dispose(); }