Julian Lachniet
Julian Lachniet

Reputation: 323

Android - MediaStore download does not work on API level 28 or lower

I'm writing a piece of Android code which stores a file in the user's download folder.

@CapacitorPlugin(name = "Downloads")
class DownloadsPlugin : Plugin() {
    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    fun download(call: PluginCall) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val filename = call.getString("fileName")!!
            val data = call.getString("data")!!

            val contentValues = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
            }

            val contentResolver = context.contentResolver
            val uri =
                contentResolver.insert(MediaStore.Files.getContentUri("external"), contentValues)!!
            val outputStream = contentResolver.openOutputStream(uri)!!

            outputStream.write(data.toByteArray(StandardCharsets.UTF_8))
        }

        call.resolve()
    }
}

The code I've written works correctly on Android 10 (API level 29) or higher. On API level 28 or lower, the code does not work. On calling the download method, the following error is logged:

2022-10-08 16:10:59.064 23121-23184/com.example.example E/Capacitor: Serious error executing plugin
    java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:125)
        at com.getcapacitor.Bridge.lambda$callPluginMethod$0$com-getcapacitor-Bridge(Bridge.java:706)
        at com.getcapacitor.Bridge$$ExternalSyntheticLambda5.run(Unknown Source:8)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.os.HandlerThread.run(HandlerThread.java:65)
     Caused by: java.lang.IllegalArgumentException: no path was provided when inserting new file
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:165)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
        at android.content.ContentProviderProxy.insert(ContentProviderNative.java:476)
        at android.content.ContentResolver.insert(ContentResolver.java:1587)
        at com.example.example.DownloadsPlugin.download(DownloadsPlugin.kt:36)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:125) 
        at com.getcapacitor.Bridge.lambda$callPluginMethod$0$com-getcapacitor-Bridge(Bridge.java:706) 
        at com.getcapacitor.Bridge$$ExternalSyntheticLambda5.run(Unknown Source:8) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.os.HandlerThread.run(HandlerThread.java:65) 

How can I correct this error so that the code works on API level 28 or lower?

Upvotes: 3

Views: 665

Answers (1)

Şahan Şenvar
Şahan Şenvar

Reputation: 48

Unfortunatelly It's not supported API 28 and before. At least I couldn't find. change method for legacy

    class SaveBitmapToGalleryUseCase @Inject constructor(
        @ApplicationContext private val context: Context,
    ) {
        operator fun invoke(
            bitmap: Bitmap,
            fileName: String = bitmap.hashCode().toString()
        ): Boolean = runCatching {
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
                val file = File(
                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                    "$fileName.jpeg"
                )
                FileOutputStream(file).use { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it) }
            } else {
                val contentValues = ContentValues().apply {
                    put(MediaStore.MediaColumns.DISPLAY_NAME, "$fileName.jpg")
                    put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
                    put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
                }
                val resolver = context.contentResolver
                val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
                    ?: throw Exception("Failed to create new MediaStore record.")
    
                resolver.openOutputStream(uri)?.use { outputStream ->
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
                } ?: throw Exception("Failed to get output stream.")
            }
        }.onFailure {
            Log.e(t = it)
        }.getOrDefault(false)
    
    
}

Upvotes: 0

Related Questions