From b0a80784607a828574cd43d19f6eb90c3b83491f Mon Sep 17 00:00:00 2001 From: Rebecca Franks Date: Mon, 11 Aug 2025 13:32:12 +0100 Subject: [PATCH 1/3] Revert to not re-using the surface due to crashes with `android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@8189b37 -- permission denied for window type 2030 ` --- .../customize/ComposableBitmapRendererImpl.kt | 46 ++++++++----------- .../customize/CustomizeExportViewModel.kt | 1 - 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt index 97674806..c4bc3544 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt @@ -27,6 +27,7 @@ import android.util.Log import android.view.Display import android.view.Surface import android.view.ViewGroup +import android.view.WindowManager import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable @@ -52,15 +53,13 @@ import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner +import com.android.developers.androidify.theme.R import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import javax.inject.Inject import javax.inject.Singleton interface ComposableBitmapRenderer { - fun initialize() - - fun dispose() suspend fun renderComposableToBitmap(canvasSize: Size, composableContent: @Composable () -> Unit): Bitmap? } @@ -79,34 +78,25 @@ interface ComposableBitmapRenderer { */ @Singleton class ComposableBitmapRendererImpl @Inject constructor(private val application: Application) : ComposableBitmapRenderer { - private val texture = SurfaceTexture(false) - private val surface = Surface(texture) - private var virtualDisplay: VirtualDisplay? = null - - override fun initialize() { - virtualDisplay = - (application.getSystemService(DISPLAY_SERVICE) as DisplayManager).createVirtualDisplay( - "virtualDisplay", - 1, - 1, - 72, - surface, - DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY, - ) - } - override fun dispose() { - virtualDisplay?.release() + private suspend fun useVirtualDisplay(callback: suspend (display: Display) -> T): T? { + val texture = SurfaceTexture(false) + val surface = Surface(texture) + val virtualDisplay: VirtualDisplay? = + (application.getSystemService(DISPLAY_SERVICE) as DisplayManager).createVirtualDisplay( + "virtualDisplay", + 1, + 1, + 72, + surface, + DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY, + ) + + val result = callback(virtualDisplay!!.display) + virtualDisplay.release() surface.release() texture.release() - } - - private suspend fun useVirtualDisplay(callback: suspend (display: Display) -> T): T? { - if (virtualDisplay == null) { - Log.e("OffscreenBitmapManager", "virtualDisplay is null") - initialize() - } - return callback(virtualDisplay!!.display) + return result } override suspend fun renderComposableToBitmap(canvasSize: Size, composableContent: @Composable () -> Unit): Bitmap? { diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt index adb98869..d136ab11 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt @@ -54,7 +54,6 @@ class CustomizeExportViewModel @Inject constructor( override fun onCleared() { super.onCleared() - composableBitmapRenderer.dispose() } fun setArguments( resultImageUrl: Bitmap, From 7c283439d2b16f18f9f46ffbb74c3f3b97244fb8 Mon Sep 17 00:00:00 2001 From: Rebecca Franks Date: Mon, 11 Aug 2025 13:48:12 +0100 Subject: [PATCH 2/3] Remove fake dispose/init methods --- .../developers/testing/util/FakeComposableBitmapRenderer.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/testing/src/main/java/com/android/developers/testing/util/FakeComposableBitmapRenderer.kt b/core/testing/src/main/java/com/android/developers/testing/util/FakeComposableBitmapRenderer.kt index 04afd7ca..b87dbaf3 100644 --- a/core/testing/src/main/java/com/android/developers/testing/util/FakeComposableBitmapRenderer.kt +++ b/core/testing/src/main/java/com/android/developers/testing/util/FakeComposableBitmapRenderer.kt @@ -22,11 +22,6 @@ import androidx.core.graphics.createBitmap import com.android.developers.androidify.customize.ComposableBitmapRenderer class FakeComposableBitmapRenderer : ComposableBitmapRenderer { - override fun initialize() { - } - - override fun dispose() { - } override suspend fun renderComposableToBitmap( canvasSize: Size, From f81c2ad60b6ebc743440b964880d5aec44bf4b9a Mon Sep 17 00:00:00 2001 From: Rebecca Franks Date: Mon, 11 Aug 2025 14:37:06 +0100 Subject: [PATCH 3/3] Cleanup --- .../androidify/customize/ComposableBitmapRendererImpl.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt index c4bc3544..4b3daafe 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt @@ -23,11 +23,9 @@ import android.graphics.Bitmap import android.graphics.SurfaceTexture import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay -import android.util.Log import android.view.Display import android.view.Surface import android.view.ViewGroup -import android.view.WindowManager import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable @@ -53,7 +51,6 @@ import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner -import com.android.developers.androidify.theme.R import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import javax.inject.Inject