From 5e08fb9c13c27c2579150ba1476f5eece0ed2377 Mon Sep 17 00:00:00 2001 From: Ole-Morten Duesund Date: Thu, 5 Mar 2026 11:56:29 +0100 Subject: [PATCH] Fix bitmap recycle race condition and startActivity crash - Null the Compose state reference before recycling bitmaps to prevent the renderer from drawing a recycled bitmap between recycle() and the state update - Wrap ACTION_VIEW startActivity in try-catch for devices without an image viewer installed Co-Authored-By: Claude Opus 4.6 --- .../java/no/naiv/tiltshift/ui/CameraScreen.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt b/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt index a01d11e..f9d851e 100644 --- a/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt +++ b/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt @@ -6,6 +6,7 @@ import android.graphics.SurfaceTexture import android.location.Location import android.net.Uri import android.opengl.GLSurfaceView +import android.util.Log import android.view.Surface import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -359,9 +360,10 @@ fun CameraScreen( // Cancel button IconButton( onClick = { - galleryBitmap?.recycle() + val oldBitmap = galleryBitmap galleryBitmap = null galleryImageUri = null + oldBitmap?.recycle() }, modifier = Modifier .size(56.dp) @@ -392,9 +394,10 @@ fun CameraScreen( when (result) { is SaveResult.Success -> { haptics.success() - lastThumbnailBitmap?.recycle() + val oldThumb = lastThumbnailBitmap lastThumbnailBitmap = result.thumbnail lastSavedUri = result.uri + oldThumb?.recycle() showSaveSuccess = true delay(1500) showSaveSuccess = false @@ -406,9 +409,10 @@ fun CameraScreen( showSaveError = null } } - galleryBitmap?.recycle() + val oldGalleryBitmap = galleryBitmap galleryBitmap = null galleryImageUri = null + oldGalleryBitmap?.recycle() isCapturing = false } } @@ -492,9 +496,10 @@ fun CameraScreen( when (result) { is SaveResult.Success -> { haptics.success() - lastThumbnailBitmap?.recycle() + val oldThumb = lastThumbnailBitmap lastThumbnailBitmap = result.thumbnail lastSavedUri = result.uri + oldThumb?.recycle() showSaveSuccess = true delay(1500) showSaveSuccess = false @@ -524,11 +529,15 @@ fun CameraScreen( thumbnail = lastThumbnailBitmap, onTap = { lastSavedUri?.let { uri -> - val intent = Intent(Intent.ACTION_VIEW).apply { - setDataAndType(uri, "image/jpeg") - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + try { + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(uri, "image/jpeg") + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + context.startActivity(intent) + } catch (e: android.content.ActivityNotFoundException) { + Log.w("CameraScreen", "No activity found to view image", e) } - context.startActivity(intent) } }, modifier = Modifier