fix(tv): stop playback behind menu

This commit is contained in:
edde746
2026-06-14 06:27:00 +02:00
parent 3ad049cb28
commit 349b5b0513
5 changed files with 47 additions and 6 deletions
@@ -3294,6 +3294,11 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
check(Looper.myLooper() == Looper.getMainLooper())
Log.d(TAG, "Disposing")
surfaceContainer?.let { container ->
container.visibility = View.INVISIBLE
Log.d(TAG, "Hiding surface container during dispose")
}
stopFrameWatchdog()
cancelDecoderHangCheck()
stopPositionUpdates()
@@ -813,6 +813,11 @@ class MpvPlayerCore(private val activity: Activity) : SurfaceHolder.Callback {
check(Looper.myLooper() == Looper.getMainLooper())
Log.d(TAG, "Disposing")
surfaceContainer?.let { container ->
container.visibility = View.INVISIBLE
Log.d(TAG, "Hiding surface container during dispose")
}
handler.removeCallbacksAndMessages(null)
// Clean up frame rate and audio focus.
+34 -2
View File
@@ -1057,6 +1057,32 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
/// latch, MediaSession pause-suppression window) — see [FrameRateMatcher].
final FrameRateMatcher _frameRate = FrameRateMatcher();
Future<Duration?> _pauseAndHidePlayerForRouteExit() async {
final currentPlayer = player;
if (currentPlayer == null || !_isPlayerInitialized) return null;
final exitPosition = currentPlayer.state.position;
if (currentPlayer.state.isActive) {
try {
await currentPlayer.pause();
} catch (e, st) {
appLogger.w('Failed to pause player during route exit', error: e, stackTrace: st);
}
}
if (!mounted || currentPlayer != player) return exitPosition;
if (Platform.isAndroid && PlatformDetector.isTV()) {
try {
await currentPlayer.setVisible(false);
} catch (e, st) {
appLogger.w('Failed to hide Android TV player surface during route exit', error: e, stackTrace: st);
}
}
return exitPosition;
}
/// Handle back button press
/// For non-host participants in Watch Together, shows leave session confirmation
Future<void> _handleBackButton() async {
@@ -1079,7 +1105,10 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
final navigator = Navigator.of(context);
if (navigator.canPop()) {
_isExiting.value = true;
await _sendStoppedProgressOnce();
final exitPosition = await _pauseAndHidePlayerForRouteExit();
if (!mounted) return;
await _sendStoppedProgressOnce(positionOverride: exitPosition);
if (!mounted) return;
await _restoreSystemUiAndOrientation();
if (!mounted) return;
navigator.pop(true);
@@ -1094,7 +1123,10 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
final navigator = Navigator.of(context);
if (navigator.canPop()) {
_isExiting.value = true;
await _sendStoppedProgressOnce();
final exitPosition = await _pauseAndHidePlayerForRouteExit();
if (!mounted) return;
await _sendStoppedProgressOnce(positionOverride: exitPosition);
if (!mounted) return;
await _restoreSystemUiAndOrientation();
if (!mounted) return;
navigator.pop(true);
+1 -2
View File
@@ -580,7 +580,6 @@ class SideNavigationRailState extends State<SideNavigationRail> with MountedSetS
final horizontalPadding = horizontalPaddingForContext(context, isCollapsed: isCollapsed);
final itemHorizontalPadding = itemHorizontalPaddingForContext(context, isCollapsed: isCollapsed);
final hasLiveTv = context.watch<MultiServerProvider>().hasLiveTv;
final surfaceOpacity = PlatformDetector.isTV() ? 0.0 : 1.0;
// Listen to fullscreen + groupLibrariesByServer setting so the rail
// rebuilds when the user toggles "Group libraries by server" in Appearance.
@@ -645,7 +644,7 @@ class SideNavigationRailState extends State<SideNavigationRail> with MountedSetS
children: [
Positioned.fill(
child: AnimatedOpacity(
opacity: surfaceOpacity,
opacity: 1.0,
duration: t.normal,
curve: Curves.easeOutCubic,
child: ColoredBox(color: t.surface),
+2 -2
View File
@@ -177,7 +177,7 @@ void main() {
expect((selectedItemContainer.decoration as BoxDecoration?)?.color, isNull);
});
testWidgets('expanded TV rail keeps its surface transparent', (tester) async {
testWidgets('expanded TV rail draws an opaque surface', (tester) async {
TvDetectionService.debugSetAppleTVOverride(true);
addTearDown(() => TvDetectionService.debugSetAppleTVOverride(null));
await SettingsService.getInstance();
@@ -227,7 +227,7 @@ void main() {
find.descendant(of: find.byType(SideNavigationRail), matching: find.byType(AnimatedOpacity)),
)
.singleWhere((widget) => widget.child is ColoredBox);
expect(surfaceOpacity.opacity, 0.0);
expect(surfaceOpacity.opacity, 1.0);
});
testWidgets('expanded rail keeps selected background outside sidebar keyboard focus', (tester) async {