From 948d183210ede1925f02a88f3cf979521d628af8 Mon Sep 17 00:00:00 2001 From: Trevor McGuire Date: Wed, 14 May 2025 16:09:43 -0700 Subject: [PATCH] Workaround for blank camera preview when switching to tabletop mode Switching to/from tabletop mode causes the underlying SurfaceView to destroy and recreate its Surface, but the Camera doesn't know its been destroyed. This is a known issue with SurfaceView in Compose. See: https://issuetracker.google.com/275157240 To workaround this issue, detect when the folding state enters or leaves tabletop mode and manually invalidate the CameraX SurfaceRequest so it will generate a new one. --- .../androidify/camera/CameraScreen.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/feature/camera/src/main/java/com/android/developers/androidify/camera/CameraScreen.kt b/feature/camera/src/main/java/com/android/developers/androidify/camera/CameraScreen.kt index fe2ad7d5..611050d7 100644 --- a/feature/camera/src/main/java/com/android/developers/androidify/camera/CameraScreen.kt +++ b/feature/camera/src/main/java/com/android/developers/androidify/camera/CameraScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -70,6 +71,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.shouldShowRationale +import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch @OptIn( @@ -129,6 +131,21 @@ fun CameraPreviewScreen( } uiState.surfaceRequest?.let { surface -> + // Workaround for https://issuetracker.google.com/275157240 + // When switching to/from tabletop posture, the underlying SurfaceView + // destroys its Surface. Invalidate the SurfaceRequest when this happens + // so CameraX can retrieve the new Surface. + LaunchedEffect(surface) { + val oldIsTableTop = isTableTopPosture(foldingFeature) + snapshotFlow { foldingFeature } + .takeWhile { + val newIsTableTop = isTableTopPosture(it) + val shouldInvalidate = oldIsTableTop != newIsTableTop + if (shouldInvalidate) surface.invalidate() + !shouldInvalidate + }.collect {} + } + CameraPreviewContent( surfaceRequest = surface, autofocusUiState = uiState.autofocusUiState,