From 93b5b664d483a711f97c072fd13783b4ae2af1d7 Mon Sep 17 00:00:00 2001 From: locphamtran Date: Sat, 12 Sep 2026 07:51:12 +0700 Subject: [PATCH] Save into DCIM/Camera below API 29 too, not just the expo DCIM root --- .../export/RecipescamExportModule.kt | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/modules/recipescam-export/android/src/main/java/com/locphamtran/recipescamera/export/RecipescamExportModule.kt b/modules/recipescam-export/android/src/main/java/com/locphamtran/recipescamera/export/RecipescamExportModule.kt index 564a968..efa2f50 100644 --- a/modules/recipescam-export/android/src/main/java/com/locphamtran/recipescamera/export/RecipescamExportModule.kt +++ b/modules/recipescam-export/android/src/main/java/com/locphamtran/recipescamera/export/RecipescamExportModule.kt @@ -90,11 +90,24 @@ class RecipescamExportModule : Module() { val src = File(srcPath) if (!src.exists()) return@withContext "" val resolver = ctx.contentResolver + // RELATIVE_PATH/IS_PENDING exist only from API 29 and minSdk is 26, so + // below Q the row is inserted against the explicit DATA path under the + // public DCIM/Camera directory (what the camera app did pre-scoped + // storage) — without this the insert throws and JS falls back to expo, + // which can only reach DCIM (root). + @Suppress("DEPRECATION") + val q = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q val values = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, src.name) put(MediaStore.MediaColumns.MIME_TYPE, mimeType) - put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/Camera") - put(MediaStore.MediaColumns.IS_PENDING, 1) + if (q) { + put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/Camera") + put(MediaStore.MediaColumns.IS_PENDING, 1) + } else { + val dir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera") + if (!dir.exists()) dir.mkdirs() + put(MediaStore.MediaColumns.DATA, File(dir, src.name).absolutePath) + } } val uri = try { resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) @@ -110,7 +123,9 @@ class RecipescamExportModule : Module() { resolver.delete(uri, null, null) return@withContext "" } - resolver.update(uri, ContentValues().apply { put(MediaStore.MediaColumns.IS_PENDING, 0) }, null, null) + if (q) { + resolver.update(uri, ContentValues().apply { put(MediaStore.MediaColumns.IS_PENDING, 0) }, null, null) + } uri.lastPathSegment ?: "" } }