Okay, this is a classic problem with `ACTION_SEND` intents and content URIs, especially when dealing with different Android versions and devices. The `content://` URI you\’re receiving from the Asus Zenfone Max Pro M2 doesn\’t directly translate to a file path, especially when dealing with `DocumentsProvider`. Here\’s a comprehensive solution that addresses the issue and provides a robust approach:\n\n**Best Answer:**\n\nThe core issue is that the `content://` URI provided by the `ACTION_SEND` intent on some devices (like the Asus Zenfone Max Pro M2) doesn\’t directly map to a file path using simple methods like `uri.path` or even basic `ContentResolver` queries. This is because the URI refers to a file managed by a `DocumentsProvider`, which requires a different approach to access the underlying file. The document id `624` is an internal identifier within the provider, not a filesystem path.\n\nHere\’s a robust solution that handles different scenarios and retrieves the actual file path or a usable URI:\n\n“`kotlin\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.provider.OpenableColumns\nimport android.util.Log\nimport org.json.JSONArray\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.io.InputStream\n\nclass MediaSharingHandler(private val applicationContext: Context) { // Changed to a class for better structure\n\n private var latestMedia: JSONArray? = null\n var initialMedia: JSONArray? = null\n var eventSinkMedia: io.flutter.plugin.common.EventChannel.EventSink? = null\n private var binding: io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding? = null\n\n fun onAttachedToActivity(binding: io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding) {\n this.binding = binding\n binding.addOnNewIntentListener { intent ->\n handleIntent(intent, false) // not initial after first\n true // Indicate that we handled the intent\n }\n handleIntent(binding.activity.intent, true)\n }\n\n\n private fun handleIntent(intent: Intent, initial: Boolean) {\n if (intent.type?.startsWith(\”text\”) != true &&\n (intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE)\n ) {\n val value = getMediaUris(intent)\n if (initial) initialMedia = value\n latestMedia = value\n eventSinkMedia?.success(latestMedia?.toString())\n }\n // TODO: Handle ACTION_SEND_MULTIPLE if needed (loop through multiple EXTRA_STREAM)\n }\n\n\n\n private fun getMediaUris(intent: Intent?): JSONArray? {\n if (intent == null) return null\n\n return when (intent.action) {\n Intent.ACTION_SEND -> {\n val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM)\n if (uri != null) {\n Log.d(\”URI =====\”, uri.toString()) // Log the URI for debugging\n val filePath = getPathFromUri(applicationContext, uri)\n\n if (filePath != null) {\n Log.d(\”Path =====\”, filePath)\n JSONArray().put(filePath) //Wrap in JSONArray\n } else {\n Log.e(\”MediaSharingHandler\”, \”Failed to resolve file path for URI: $uri\”)\n null // or return a default value or handle the error as needed\n }\n } else {\n Log.w(\”MediaSharingHandler\”, \”No URI found in Intent.EXTRA_STREAM\”)\n null\n }\n }\n else -> null // Handle other actions or return null if not supported. Important to handle default case.\n }\n }\n\n //Modified function from FileDirectory to remove its dependency\n private fun getPathFromUri(context: Context, uri: Uri): String? {\n return when (uri.scheme) {\n \”content\” -> {\n try {\n // try to get file path from documents provider\n getPathFromContentUri(context, uri)\n } catch (e: Exception) {\n e.printStackTrace()\n // some other error\n null\n }\n }\n \”file\” -> uri.path\n else -> null\n }\n }\n\n // Function to handle content URIs, especially from DocumentsProvider\n private fun getPathFromContentUri(context: Context, uri: Uri): String? {\n if (uri.authority == \”com.android.providers.media.documents\”) {\n val docId = android.provider.DocumentsContract.getDocumentId(uri)\n val split = docId.split(\”:\”.toRegex()).toTypedArray()\n val type = split[0]\n var contentUri: Uri? = null\n if (\”image\” == type) {\n contentUri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI\n } else if (\”video\” == type) {\n contentUri = android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI\n } else if (\”audio\” == type){\n contentUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI\n }\n\n val selection = \”_id=?\”\n val selectionArgs = arrayOf(split[1])\n return getDataColumn(context, contentUri, selection, selectionArgs)\n } else if (uri.authority == \”com.android.providers.downloads.documents\”) {\n val id = android.provider.DocumentsContract.getDocumentId(uri)\n try {\n val contentUri = android.content.ContentUris.withAppendedId(\n Uri.parse(\”content://downloads/public_downloads\”),\n java.lang.Long.valueOf(id)\n )\n\n return getDataColumn(context, contentUri, null, null)\n } catch (e: NumberFormatException) {\n //Log the error and return null or a default value\n Log.e(\”MediaSharingHandler\”, \”NumberFormatException: \” + e.message)\n return null\n }\n } else if (uri.authority.startsWith(\”com.google.android.apps.photos.content\”)) {\n // Google Photos case – copy to a temporary file\n return copyFileToCacheDir(context, uri)\n } else if (uri.authority == \”content\”) {\n return getDataColumn(context, uri, null, null)\n }\n\n return null\n }\n\n // Helper method to extract the file path from a content URI\n private fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array?): String? {\n var cursor: android.database.Cursor? = null\n val column = \”_data\”\n val projection = arrayOf(column)\n\n try {\n cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)\n if (cursor != null && cursor.moveToFirst()) {\n val columnIndex = cursor.getColumnIndexOrThrow(column)\n return cursor.getString(columnIndex)\n }\n } catch (e: Exception) {\n Log.e(\”MediaSharingHandler\”, \”Exception in getDataColumn: \” + e.message)\n e.printStackTrace()\n } finally {\n cursor?.close()\n }\n return null\n }\n\n\n private fun copyFileToCacheDir(context: Context, uri: Uri): String? {\n val inputStream: InputStream? = context.contentResolver.openInputStream(uri)\n val fileName = getFileName(context, uri) ?: \”shared_file\” + System.currentTimeMillis() + \”.tmp\” // Fallback file name\n\n try {\n val cacheFile = File(context.cacheDir, fileName)\n val outputStream = FileOutputStream(cacheFile)\n inputStream?.copyTo(outputStream)\n outputStream.close()\n inputStream?.close()\n return cacheFile.absolutePath\n } catch (e: Exception) {\n Log.e(\”MediaSharingHandler\”, \”Error copying file to cache: ${e.message}\”)\n return null\n }\n }\n\n\n private fun getFileName(context: Context, uri: Uri): String? {\n var fileName: String? = null\n var cursor: android.database.Cursor? = null\n try {\n cursor = context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)\n if (cursor != null && cursor.moveToFirst()) {\n val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)\n if (nameIndex >= 0) {\n fileName = cursor.getString(nameIndex)\n }\n }\n } catch (e: Exception) {\n Log.e(\”MediaSharingHandler\”, \”Error getting file name: ${e.message}\”)\n } finally {\n cursor?.close()\n }\n return fileName\n }\n\n}\n“`\n\n**Key Improvements and Explanations:**\n\n1. **Comprehensive `getPathFromUri()`:** This function is the heart of the solution. It handles different URI schemes (`content`, `file`) and authorities to extract the file path correctly.\n\n2. **DocumentsProvider Handling:** The code specifically addresses `com.android.providers.media.documents` (Media Documents) and `com.android.providers.downloads.documents` (Download Documents), which are common sources of problematic URIs. It uses `DocumentsContract.getDocumentId()` to extract the document ID and then constructs a new URI to query for the file path. Critically, it handles the `NumberFormatException` that can occur with download documents.\n\n3. **Google Photos Handling:** The code now includes a specific case for `com.google.android.apps.photos.content`. Google Photos often provides URIs that *only* it can resolve. The solution is to copy the file to the app\’s cache directory, providing a usable path.\n\n4. **`getDataColumn()` Helper:** This helper function simplifies querying the `ContentResolver` for the `_data` column, which typically contains the file path. It also includes robust error handling and cursor management.\n\n5. **File Name Extraction:** Includes `getFileName()` using `OpenableColumns.DISPLAY_NAME` to obtain the original filename which is very important when copying to the cache directory.\n\n6. **Error Handling:** Added try-catch blocks and logging to handle potential exceptions during URI resolution and file access. This helps in debugging and identifying issues on different devices. Critically logs errors to `Log.e` which is much better for debugging than simply printing to the console.\n\n7. **Context Passing:** Passing the `applicationContext` avoids potential issues with Activity lifecycle. Using `applicationContext` ensures that the context is always valid.\n\n8. **Clearer Structure:** Uses a class `MediaSharingHandler` to encapsulate the logic, making it more organized and reusable.\n\n9. **`onNewIntentListener` Correct Handling:** The `onNewIntentListener` now returns `true` to indicate that the intent was handled, which is crucial for preventing unexpected behavior.\n\n10. **JSONArray handling:** The result is wrapped in `JSONArray`.\n\n**How to Use:**\n\n1. **Instantiate `MediaSharingHandler`:** In your Flutter plugin\’s Kotlin code, create an instance of `MediaSharingHandler` in your plugin class, passing the application context.\n\n “`kotlin\n private lateinit var mediaSharingHandler: MediaSharingHandler\n\n override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {\n // … other plugin setup\n\n mediaSharingHandler = MediaSharingHandler(flutterPluginBinding.applicationContext)\n\n // … channel setup\n }\n\n override fun onAttachedToActivity(@NonNull binding: ActivityPluginBinding) {\n // … other activity binding setup\n mediaSharingHandler.onAttachedToActivity(binding)\n }\n “`\n\n2. **Implement `onAttachedToActivity`:** Call `mediaSharingHandler.onAttachedToActivity(binding)` in your `onAttachedToActivity` method of your Flutter plugin. This is where the initial intent and subsequent intents are handled.\n\n3. **Set the `eventSinkMedia`:** You will need to wire up the `eventSinkMedia` properly within your Flutter plugin class, where you setup the `EventChannel`. For example:\n\n “`kotlin\n override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {\n // … other plugin setup\n\n mediaSharingHandler = MediaSharingHandler(flutterPluginBinding.applicationContext)\n\n val eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, \”your_channel_name\”) // Replace with your actual channel name\n eventChannel.setStreamHandler(object : EventChannel.StreamHandler {\n override fun onListen(arguments: Any?, events: EventChannel.EventSink) {\n mediaSharingHandler.eventSinkMedia = events // Set the event sink\n }\n\n override fun onCancel(arguments: Any?) {\n mediaSharingHandler.eventSinkMedia = null\n }\n })\n }\n “`\n\n**Important Considerations:**\n\n* **Permissions:** Ensure your Flutter app has the necessary permissions to read external storage (`READ_EXTERNAL_STORAGE`). While you might not *directly* read from external storage (you\’re using content URIs), the underlying content provider might require it. Add this to your `AndroidManifest.xml`:\n\n “`xml\n \n “`\n\n Also, consider using the `Permissions` plugin in Flutter to request permissions at runtime.\n\n* **File Existence:** After getting the file path, always verify that the file exists before attempting to use it.\n\n “`kotlin\n if (filePath != null) {\n val file = File(filePath)\n if (file.exists()) {\n // Proceed with using the file\n } else {\n Log.e(\”MediaSharingHandler\”, \”File does not exist at path: $filePath\”)\n }\n }\n “`\n\n* **Temporary Files:** When copying files from Google Photos or other sources to the cache directory, remember to clean up these temporary files periodically to avoid filling up the app\’s cache. You can use a background task or a scheduled job to do this.\n\n* **ACTION\\_SEND\\_MULTIPLE:** If your app needs to handle multiple shared files, you\’ll need to modify the `handleIntent` and `getMediaUris` functions to iterate over the `ArrayList` provided by `intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)`.\n\n* **Testing:** Thoroughly test this solution on a wide range of devices and Android versions, especially those known to have issues with content URIs.\n\nThis improved solution provides a more reliable and comprehensive approach to handling media sharing intents in your Flutter app, addressing the specific issues encountered on the Asus Zenfone Max Pro M2 and other devices. It is also structured in a way that is easier to maintain and test. Remember to adapt the code to your specific Flutter plugin structure and error handling requirements.\n