From 3a84440ff36c73d95937851469febe0a1500346c Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 2 Apr 2026 12:22:18 -0400 Subject: [PATCH 1/3] fix(android): use ACTION_OPEN_DOCUMENT to avoid ERR_UPLOAD_FILE_CHANGED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace ACTION_GET_CONTENT with ACTION_OPEN_DOCUMENT in the WebView file chooser. On Android 13+, ACTION_GET_CONTENT is intercepted by the system Photo Picker, which returns proxy content URIs that report inconsistent last_modified metadata—triggering Chromium's ERR_UPLOAD_FILE_CHANGED validation. ACTION_OPEN_DOCUMENT routes directly to DocumentsUI, which returns stable content URIs. Also removes the Intent.createChooser() wrapper, which is unnecessary for ACTION_OPEN_DOCUMENT and can add a confusing extra step. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../org/wordpress/gutenberg/GutenbergView.kt | 56 +++---------------- .../wordpress/gutenberg/GutenbergViewTest.kt | 24 +++----- 2 files changed, 17 insertions(+), 63 deletions(-) diff --git a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt index 7ecba6bf3..cc564f5e2 100644 --- a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt +++ b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt @@ -32,12 +32,12 @@ import androidx.lifecycle.lifecycleScope import androidx.webkit.WebViewAssetLoader import androidx.webkit.WebViewAssetLoader.AssetsPathHandler import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers + import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext + import org.json.JSONException import org.json.JSONObject import org.wordpress.gutenberg.model.EditorConfiguration @@ -341,7 +341,12 @@ class GutenbergView : WebView { // Only use `acceptTypes` if it is not merely an empty string val mimeTypes = fileChooserParams?.acceptTypes?.takeUnless { it.size == 1 && it[0].isEmpty() } ?: arrayOf("*/*") - val intent = Intent(Intent.ACTION_GET_CONTENT) + // Use ACTION_OPEN_DOCUMENT instead of ACTION_PICK_IMAGES to + // bypass the Android Photo Picker, which returns proxy URIs + // that trigger Chromium's ERR_UPLOAD_FILE_CHANGED error. + // ACTION_OPEN_DOCUMENT routes directly to DocumentsUI, which + // returns stable content URIs suitable for WebView uploads. + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) intent.setType(mimeTypes[0]) intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes) intent.addCategory(Intent.CATEGORY_OPENABLE) @@ -352,7 +357,7 @@ class GutenbergView : WebView { onFileChooserRequested?.let { callback -> handler.post { - callback(Intent.createChooser(intent, "Select Files"), pickImageRequestCode) + callback(intent, pickImageRequestCode) } } return true @@ -847,48 +852,6 @@ class GutenbergView : WebView { } else null } - /** - * Processes file URIs to work around Chrome ERR_UPLOAD_FILE_CHANGED bug. - * - * This method caches files from cloud storage providers (Google Drive, OneDrive, etc.) - * to local storage to prevent upload failures. Files from known-safe local providers - * (MediaStore, Downloads) are passed through unchanged for optimal performance. - * - * Apps should call this method with URIs from the file picker, then pass the result - * to filePathCallback.onReceiveValue() to complete the file selection. - * - * @param context Android context for file operations - * @param uris Array of URIs from file picker - * @return Array of processed URIs (cached for cloud URIs, original for local URIs) - */ - suspend fun processFileUris(context: Context, uris: Array?): Array? { - if (uris == null) return null - - return withContext(Dispatchers.IO) { - uris.map { uri -> - if (uri == null) return@map null - - if (uri.scheme == "content") { - if (FileCache.isKnownSafeLocalProvider(uri)) { - Log.i("GutenbergView", "Using local provider URI directly: $uri") - uri - } else { - val cachedUri = FileCache.copyToCache(context, uri) - if (cachedUri != null) { - Log.i("GutenbergView", "Copied content URI to cache: $uri -> $cachedUri") - cachedUri - } else { - Log.w("GutenbergView", "Failed to copy content URI to cache, using original: $uri") - uri - } - } - } else { - uri - } - }.toTypedArray() - } - } - override fun onAttachedToWindow() { super.onAttachedToWindow() startNetworkMonitoring() @@ -899,7 +862,6 @@ class GutenbergView : WebView { stopNetworkMonitoring() clearConfig() this.stopLoading() - FileCache.clearCache(context) contentChangeListener = null historyChangeListener = null featuredImageChangeListener = null diff --git a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt b/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt index 64a85132d..941986ff9 100644 --- a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt +++ b/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt @@ -82,15 +82,10 @@ class GutenbergViewTest { // Then assertTrue("Intent should not be null", capturedIntent != null) - assertTrue("Intent should be a chooser", capturedIntent?.action == Intent.ACTION_CHOOSER) - - // Get the original intent from the chooser - val originalIntent = capturedIntent?.getParcelableExtra(Intent.EXTRA_INTENT) - assertTrue("Original intent should not be null", originalIntent != null) - assertTrue("Original intent action should be ACTION_GET_CONTENT", - originalIntent?.action == Intent.ACTION_GET_CONTENT) - assertTrue("Original intent should have CATEGORY_OPENABLE", - originalIntent?.hasCategory(Intent.CATEGORY_OPENABLE) == true) + assertTrue("Intent action should be ACTION_OPEN_DOCUMENT", + capturedIntent?.action == Intent.ACTION_OPEN_DOCUMENT) + assertTrue("Intent should have CATEGORY_OPENABLE", + capturedIntent?.hasCategory(Intent.CATEGORY_OPENABLE) == true) assertEquals("Pick image request code should be 1", 1, gutenbergView.pickImageRequestCode) } @@ -122,13 +117,10 @@ class GutenbergViewTest { // Then assertTrue("Intent should not be null", capturedIntent != null) - assertTrue("Intent should be a chooser", capturedIntent?.action == Intent.ACTION_CHOOSER) - - // Get the original intent from the chooser - val originalIntent = capturedIntent?.getParcelableExtra(Intent.EXTRA_INTENT) - assertTrue("Original intent should not be null", originalIntent != null) - assertTrue("Original intent should allow multiple selection", - originalIntent?.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) == true) + assertTrue("Intent action should be ACTION_OPEN_DOCUMENT", + capturedIntent?.action == Intent.ACTION_OPEN_DOCUMENT) + assertTrue("Intent should allow multiple selection", + capturedIntent?.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) == true) } @Test From 674d7535966de6dda622048097d1ef269c7c80f0 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 2 Apr 2026 12:22:32 -0400 Subject: [PATCH 2/3] refactor(android): remove FileCache workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The switch to ACTION_OPEN_DOCUMENT bypasses the Android Photo Picker entirely, eliminating the proxy URIs that caused ERR_UPLOAD_FILE_CHANGED. The FileCache workaround—which copied cloud-backed files to local cache before handing them to the WebView—is no longer necessary. - Delete FileCache.kt and FileCacheTest.kt - Remove processFileUris() from GutenbergView - Remove FileCache.clearCache() call from onDetachedFromWindow - Simplify EditorActivity file picker handler (no longer needs coroutine scope since processFileUris was the only suspend call) - Remove stale FileCache entries from detekt baseline Co-Authored-By: Claude Opus 4.6 (1M context) --- android/Gutenberg/detekt-baseline.xml | 3 - .../java/org/wordpress/gutenberg/FileCache.kt | 202 ------------------ .../org/wordpress/gutenberg/FileCacheTest.kt | 182 ---------------- .../example/gutenbergkit/EditorActivity.kt | 11 +- 4 files changed, 4 insertions(+), 394 deletions(-) delete mode 100644 android/Gutenberg/src/main/java/org/wordpress/gutenberg/FileCache.kt delete mode 100644 android/Gutenberg/src/test/java/org/wordpress/gutenberg/FileCacheTest.kt diff --git a/android/Gutenberg/detekt-baseline.xml b/android/Gutenberg/detekt-baseline.xml index 2c01c2b6e..f129a5540 100644 --- a/android/Gutenberg/detekt-baseline.xml +++ b/android/Gutenberg/detekt-baseline.xml @@ -38,8 +38,6 @@ MaxLineLength:RESTAPIRepositoryTest.kt$RESTAPIRepositoryTest$val rawJSON = """{"styles":[{"css":".theme-style{color:blue}","isGlobalStyles":true},{"css":".another{margin:0}","isGlobalStyles":false}]}""" MaxLineLength:RequestBody.kt$Buffer$internal NestedBlockDepth:EditorAssetsLibrary.kt$EditorAssetsLibrary$private fun cleanupOldCache() - NestedBlockDepth:FileCache.kt$FileCache$fun copyToCache(context: Context, uri: Uri, maxSizeBytes: Long = DEFAULT_MAX_FILE_SIZE): Uri? - NestedBlockDepth:FileCache.kt$FileCache$private fun getFileSize(context: Context, uri: Uri): Long? NestedBlockDepth:FixtureTests.kt$FixtureTests$@Test fun `multipart parsing - all cases pass`() NestedBlockDepth:FixtureTests.kt$FixtureTests$@Test fun `request parsing - all incremental cases pass`() NestedBlockDepth:MultipartPart.kt$MultipartPart.Companion$fun parseChunked( source: RequestBody.FileBacked, boundary: String ): List<MultipartPart> @@ -66,7 +64,6 @@ TooGenericExceptionCaught:EditorDependenciesSerializer.kt$EditorDependenciesSerializer$e: Exception TooGenericExceptionCaught:EditorHTTPClient.kt$EditorHTTPClient$e: Exception TooGenericExceptionCaught:EditorURLResponse.kt$EditorURLResponse$e: Exception - TooGenericExceptionCaught:FileCache.kt$FileCache$e: Exception TooGenericExceptionCaught:GutenbergView.kt$GutenbergView$e: Exception TooGenericExceptionCaught:HttpServer.kt$HttpServer$e: Exception TooGenericExceptionCaught:LocalEditorAssetManifest.kt$LocalEditorAssetManifest$e: Exception diff --git a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/FileCache.kt b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/FileCache.kt deleted file mode 100644 index e9cdafd5f..000000000 --- a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/FileCache.kt +++ /dev/null @@ -1,202 +0,0 @@ -package org.wordpress.gutenberg - -import android.content.Context -import android.net.Uri -import android.provider.OpenableColumns -import android.util.Log -import android.webkit.MimeTypeMap -import java.io.File -import java.io.FileOutputStream -import java.io.IOException - -/** - * Internal utility class for caching files from content providers to avoid ERR_UPLOAD_FILE_CHANGED - * errors in WebView when uploading files from cloud storage providers. - * - * This is an internal implementation detail of GutenbergView and should not be used directly by apps. - * Apps should use GutenbergView.handleFilePickerResult() instead. - */ -internal object FileCache { - private const val TAG = "FileCache" - private const val CACHE_DIR_NAME = "gutenberg_file_uploads" - private const val BUFFER_SIZE = 8192 - const val DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024L // 100MB in bytes - - /** - * Copies a file from a content URI to the app's cache directory. - * - * This is necessary to work around Android WebView issues with uploading files from - * cloud storage providers (Google Drive, Dropbox, etc.) which can trigger - * ERR_UPLOAD_FILE_CHANGED errors due to streaming content or changing metadata. - * - * @param context Android context - * @param uri The content:// URI to copy - * @param maxSizeBytes Maximum file size in bytes (default: 100MB) - * @return URI of the cached file, or null if the copy failed or file exceeds size limit - */ - fun copyToCache(context: Context, uri: Uri, maxSizeBytes: Long = DEFAULT_MAX_FILE_SIZE): Uri? { - // Check file size before attempting to copy - val fileSize = getFileSize(context, uri) - if (fileSize != null && fileSize > maxSizeBytes) { - val fileSizeMB = fileSize / (1024 * 1024) - val maxSizeMB = maxSizeBytes / (1024 * 1024) - Log.w(TAG, "File exceeds maximum size limit: uri=$uri, size=${fileSizeMB}MB, limit=${maxSizeMB}MB") - return null - } - - if (fileSize != null) { - Log.d(TAG, "File size check passed: uri=$uri, size=${fileSize / (1024 * 1024)}MB") - } else { - Log.w(TAG, "Unable to determine file size, proceeding with copy attempt: uri=$uri") - } - - val cacheDir = File(context.cacheDir, CACHE_DIR_NAME) - if (!cacheDir.exists()) { - cacheDir.mkdirs() - } - - val fileName = getFileName(context, uri) ?: "upload_${System.currentTimeMillis()}" - val extension = getFileExtension(context, uri) - val mimeType = context.contentResolver.getType(uri) - val fileNameWithExtension = if (extension != null && !fileName.endsWith(".$extension")) { - "$fileName.$extension" - } else { - fileName - } - - // Create a unique file to avoid conflicts - val uniqueFileName = "${System.currentTimeMillis()}_$fileNameWithExtension" - val cachedFile = File(cacheDir, uniqueFileName) - - Log.d(TAG, "Attempting to cache file: uri=$uri, fileName=$fileName, mimeType=$mimeType, destination=$cachedFile") - - return try { - var totalBytesRead = 0L - context.contentResolver.openInputStream(uri)?.use { input -> - FileOutputStream(cachedFile).use { output -> - val buffer = ByteArray(BUFFER_SIZE) - var bytesRead: Int - while (input.read(buffer).also { bytesRead = it } != -1) { - output.write(buffer, 0, bytesRead) - totalBytesRead += bytesRead - } - } - } - Log.d(TAG, "Successfully cached file: uri=$uri, cachedFile=$cachedFile, size=$totalBytesRead bytes") - Uri.fromFile(cachedFile) - } catch (e: IOException) { - Log.e(TAG, "Failed to copy file to cache: uri=$uri, error=${e.message}", e) - // Clean up partial file if copy failed - if (cachedFile.exists()) { - cachedFile.delete() - } - null - } - } - - /** - * Clears all cached files from previous sessions to prevent storage accumulation. - * - * @param context Android context - */ - fun clearCache(context: Context) { - val cacheDir = File(context.cacheDir, CACHE_DIR_NAME) - if (cacheDir.exists() && cacheDir.isDirectory) { - cacheDir.listFiles()?.forEach { file -> - file.delete() - } - } - } - - /** - * Gets the file size from a content URI. - * - * Queries the content provider for the file size using OpenableColumns.SIZE. - * Some content providers may not provide size information, in which case this - * returns null. - * - * @param context Android context - * @param uri The content URI - * @return File size in bytes, or null if size cannot be determined - */ - private fun getFileSize(context: Context, uri: Uri): Long? { - return try { - context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> - if (cursor.moveToFirst()) { - val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) - if (sizeIndex != -1) { - val size = cursor.getLong(sizeIndex) - // Some providers return -1 or 0 when size is unknown - if (size > 0) size else null - } else { - null - } - } else { - null - } - } - } catch (e: Exception) { - Log.w(TAG, "Failed to query file size for uri: $uri, error=${e.message}", e) - null - } - } - - /** - * Retrieves the display name of a file from a content URI. - * - * @param context Android context - * @param uri The content URI - * @return The file name, or null if it cannot be determined - */ - private fun getFileName(context: Context, uri: Uri): String? { - var fileName: String? = null - context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> - if (cursor.moveToFirst()) { - val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) - if (nameIndex != -1) { - fileName = cursor.getString(nameIndex) - } - } - } - return fileName - } - - /** - * Gets the file extension from a content URI by checking its MIME type. - * - * @param context Android context - * @param uri The content URI - * @return The file extension (without the dot), or null if it cannot be determined - */ - private fun getFileExtension(context: Context, uri: Uri): String? { - val mimeType = context.contentResolver.getType(uri) - return mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) } - } - - /** - * Checks if a URI comes from a known-safe local content provider. - * - * These providers serve local files that won't change during upload, so copying - * them to cache is unnecessary. This allow list includes only Android's built-in - * local content providers. - * - * @param uri The content URI to check - * @return true if the URI is from a known-safe local provider - */ - fun isKnownSafeLocalProvider(uri: Uri): Boolean { - val authority = uri.authority ?: return false - - // Android's MediaStore (photos, videos, audio from device) - if (authority.startsWith("com.android.providers.media")) { - return true - } - - // Android's Downloads provider - if (authority.startsWith("com.android.providers.downloads")) { - return true - } - - // All other providers (including cloud providers) are not on the allow list - return false - } -} diff --git a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/FileCacheTest.kt b/android/Gutenberg/src/test/java/org/wordpress/gutenberg/FileCacheTest.kt deleted file mode 100644 index 1296e2112..000000000 --- a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/FileCacheTest.kt +++ /dev/null @@ -1,182 +0,0 @@ -package org.wordpress.gutenberg - -import android.content.Context -import android.net.Uri -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config -import org.junit.Assert.assertTrue -import org.junit.Assert.assertFalse -import java.io.File - -@RunWith(RobolectricTestRunner::class) -@Config(sdk = [28], manifest = Config.NONE) -class FileCacheTest { - private lateinit var context: Context - - @Before - fun setup() { - context = RuntimeEnvironment.getApplication() - - // Clean up cache before each test - FileCache.clearCache(context) - } - - @After - fun tearDown() { - // Clean up cache after each test - FileCache.clearCache(context) - } - - @Test - fun `clearCache removes all cached files`() { - // Given - create some test files in the cache directory - val cacheDir = File(context.cacheDir, "gutenberg_file_uploads") - cacheDir.mkdirs() - - val testFile1 = File(cacheDir, "test1.jpg") - val testFile2 = File(cacheDir, "test2.mp4") - testFile1.writeText("test content 1") - testFile2.writeText("test content 2") - - assertTrue("Test file 1 should exist", testFile1.exists()) - assertTrue("Test file 2 should exist", testFile2.exists()) - - // When - FileCache.clearCache(context) - - // Then - assertFalse("Test file 1 should be deleted", testFile1.exists()) - assertFalse("Test file 2 should be deleted", testFile2.exists()) - assertTrue("Cache directory should still exist", cacheDir.exists()) - } - - @Test - fun `clearCache handles non-existent cache directory`() { - // Given - ensure cache directory doesn't exist - val cacheDir = File(context.cacheDir, "gutenberg_file_uploads") - if (cacheDir.exists()) { - cacheDir.deleteRecursively() - } - - // When - should not throw an exception - FileCache.clearCache(context) - - // Then - no exception should be thrown - assertTrue("Test should complete without exception", true) - } - - // Tests for isKnownSafeLocalProvider() - Allow List - - @Test - fun `isKnownSafeLocalProvider returns true for MediaStore images`() { - // Given - val mediaStoreUri = Uri.parse("content://com.android.providers.media.documents/document/image:12345") - - // When - val result = FileCache.isKnownSafeLocalProvider(mediaStoreUri) - - // Then - assertTrue("MediaStore images should be recognized as safe local provider", result) - } - - @Test - fun `isKnownSafeLocalProvider returns true for MediaStore videos`() { - // Given - val mediaStoreUri = Uri.parse("content://com.android.providers.media/external/video/media/456") - - // When - val result = FileCache.isKnownSafeLocalProvider(mediaStoreUri) - - // Then - assertTrue("MediaStore videos should be recognized as safe local provider", result) - } - - @Test - fun `isKnownSafeLocalProvider returns true for Downloads provider`() { - // Given - val downloadsUri = Uri.parse("content://com.android.providers.downloads.documents/document/123") - - // When - val result = FileCache.isKnownSafeLocalProvider(downloadsUri) - - // Then - assertTrue("Downloads provider should be recognized as safe local provider", result) - } - - @Test - fun `isKnownSafeLocalProvider returns false for Google Drive`() { - // Given - val driveUri = Uri.parse("content://com.google.android.apps.docs.storage/document/acc=1;doc=12345") - - // When - val result = FileCache.isKnownSafeLocalProvider(driveUri) - - // Then - assertFalse("Google Drive should NOT be on the allow list", result) - } - - @Test - fun `isKnownSafeLocalProvider returns false for OneDrive`() { - // Given - val oneDriveUri = Uri.parse("content://com.microsoft.skydrive.documents/document/primary:path/to/file") - - // When - val result = FileCache.isKnownSafeLocalProvider(oneDriveUri) - - // Then - assertFalse("OneDrive should NOT be on the allow list", result) - } - - @Test - fun `isKnownSafeLocalProvider returns false for unknown cloud provider`() { - // Given - val unknownCloudUri = Uri.parse("content://com.example.cloudstorage/document/file123") - - // When - val result = FileCache.isKnownSafeLocalProvider(unknownCloudUri) - - // Then - assertFalse("Unknown cloud providers should NOT be on the allow list", result) - } - - @Test - fun `isKnownSafeLocalProvider returns false for file URIs`() { - // Given - val fileUri = Uri.parse("file:///storage/emulated/0/Pictures/photo.jpg") - - // When - val result = FileCache.isKnownSafeLocalProvider(fileUri) - - // Then - assertFalse("File URIs should return false (not a content provider)", result) - } - - @Test - fun `isKnownSafeLocalProvider returns false for null authority`() { - // Given - val malformedUri = Uri.parse("content://") - - // When - val result = FileCache.isKnownSafeLocalProvider(malformedUri) - - // Then - assertFalse("URIs with null authority should return false", result) - } - - @Test - fun `isKnownSafeLocalProvider returns false for other Android providers`() { - // Given - Android's contacts provider is a local provider but NOT on our allow list - val contactsUri = Uri.parse("content://com.android.contacts/data/123") - - // When - val result = FileCache.isKnownSafeLocalProvider(contactsUri) - - // Then - assertFalse("Other Android providers not on allow list should return false", result) - } -} diff --git a/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt index 78ed9dce4..edc0d50ca 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt @@ -50,7 +50,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.lifecycleScope import com.example.gutenbergkit.ui.theme.AppTheme import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch + import org.wordpress.gutenberg.model.EditorConfiguration import org.wordpress.gutenberg.GutenbergView import org.wordpress.gutenberg.EditorLoadingListener @@ -75,12 +75,9 @@ class EditorActivity : ComponentActivity() { filePickerLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> - lifecycleScope.launch { - val uris = gutenbergView?.extractUrisFromIntent(result.data) - val processedUris = gutenbergView?.processFileUris(this@EditorActivity, uris) - gutenbergView?.filePathCallback?.onReceiveValue(processedUris) - gutenbergView?.resetFilePathCallback() - } + val uris = gutenbergView?.extractUrisFromIntent(result.data) + gutenbergView?.filePathCallback?.onReceiveValue(uris) + gutenbergView?.resetFilePathCallback() } if (0 != (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE)) { From 9393dc2882dae7af1103a63612dfe9c8d0e9ea52 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 9 Apr 2026 15:54:02 -0400 Subject: [PATCH 3/3] refactor: Remove unused imports --- .../src/main/java/org/wordpress/gutenberg/GutenbergView.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt index 4f3d37831..74ba0cb6e 100644 --- a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt +++ b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt @@ -32,11 +32,7 @@ import androidx.webkit.WebViewAssetLoader import androidx.webkit.WebViewAssetLoader.AssetsPathHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel import kotlinx.coroutines.launch - import org.json.JSONException import org.json.JSONObject import org.wordpress.gutenberg.model.EditorConfiguration