fix(tv): stop playback behind menu
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user