Member
Member

Reputation: 87

Didn't find class "android.provider.MediaStore$Downloads"

I am using codes given by Bao Lei with the name private fun saveImage at how to save bitmap to android gallery to save bitmap

To solve deprecated problems in Kotlin, I removed these lines:

 //values.put(MediaStore.Images.Media.DATA, file.absolutePath) 
//context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

and added this:

 val contentValues = ContentValues()

 contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, folderName)

 this.applicationContext.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)

Then, I called the function (in DidtodayActivity ) as:

saveImage(bitmapImg, this.applicationContext)

The image is downloaded to Phone>Android>Data>com.example.MyApp1>files>MyFiles. I can see the downloaded image in my device.

But, the activity file is closed suddenly (and activity_main.xml is opened directly) due to the runTime error at this line:

this.applicationContext.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)

Cause:

java.lang.ClassNotFoundException: Didn't find class "android.provider.MediaStore$Downloads" on path: DexPathList[[zip file "/data/app/com.example.MyApp1-rvQQjSAj4NilLch2h12faQ==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.MyApp1-rvQQjSAj4NilLch2h12faQ==/lib/arm64, /data/app/com.example.MyApp1-rvQQjSAj4NilLch2h12faQ==/base.apk!/lib/arm64-v8a, /system/lib64]]

Detailmessage:

**Didn't find class "android.provider.MediaStore$Downloads"** on path: DexPathList[[zip file "/data/app/com.example.MyApp1-rvQQjSAj4NilLch2h12faQ==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.MyApp1-rvQQjSAj4NilLch2h12faQ==/lib/arm64, /data/app/com.example.MyApp1-rvQQjSAj4NilLch2h12faQ==/base.apk!/lib/arm64-v8a, /system/lib64]]

Failed resolution of: Landroid/provider/MediaStore$Downloads;

Module level build.gradle is:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"

    defaultConfig {
        applicationId "com.example.MyApp1"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        multiDexEnabled true

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    aaptOptions{
        noCompress "tflite"
        noCompress "lite"
    }
}

Also, the following code was added into the dependencies part in the build.gradle file:

 implementation "com.android.support:multidex:2.0.1"

And, I added this code in the Manifest file:

  android:name="androidx.multidex.MultiDexApplication">

How to solve the run time error problem ?

I tried this;

this.applicationContext.contentResolver.insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)

instead of this;

this.applicationContext.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)

But, it crashes again with this runtime error;

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=203, result=-1, data=Intent { (has extras) }} to 
activity {com.example.MyApp1/com.example.MyApp1.DidtodayActivity}: java.lang.SecurityException: Permission Denial: writing com.android.providers.media.MediaProvider uri 
content://media/external/images/media from pid=3812, uid=10270 
requires android.permission.WRITE_EXTERNAL_STORAGE, or grantUriPermission()

However, the following code is already in the Manifest file;

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

So, is there any other alternative to use instead of MediaStore.Downloads?

Upvotes: 4

Views: 3090

Answers (2)

Goran Horia Mihail
Goran Horia Mihail

Reputation: 3645

First of all you need to use at least targetSdkVersion 29

https://developer.android.com/training/data-storage/shared/media
    
Downloaded files, which are stored in the Download/ directory. On devices that run
Android 10 (API level 29) and higher, these files are stored in the MediaStore. 
Downloads table. **This table isn't available on Android 9 (API level 28) and lower.**

As per docs, this api is available starting from Android 10, api 29.

Below api 29 you would have to use the old way of saving a file in the Downloads folder, using Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)

Complete solution:

private val FOLDER = "MyFolder"

private fun saveInDownloads(appContext: Context, fromFile: File) {
    val dst = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        saveInDownloadsForQ(appContext, fromFile.name)
    } else {
        //dont forget to check if you have the permission 
        //to WRITE_EXTERNAL_STORAGE, and ask for it if you don't
        saveInDownloadsBelowQ(appContext, fromFile.name)
    }

    dst?.let {
        try {
            val src = FileInputStream(fromFile)
            dst.channel.transferFrom(src.channel, 0, src.channel.size())
            src.close()
            dst.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

private fun saveInDownloadsForQ(appContext: Context, fileName: String): FileOutputStream? {
    val contentValues = ContentValues().apply {
        put(MediaStore.Downloads.DISPLAY_NAME, fileName)
        put(MediaStore.Downloads.MIME_TYPE, "application/pdf")
        put(MediaStore.Downloads.DATE_ADDED, (System.currentTimeMillis() / 1000).toInt())
        put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + File.separator + FOLDER)
    }

    val resolver = appContext.contentResolver
    val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
    return uri?.let {
        resolver.openOutputStream(uri) as FileOutputStream
    }
}

private fun saveInDownloadsBelowQ(appContext: Context, fileName: String): FileOutputStream {
    val path = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_DOWNLOADS).toString()

    val dir = File(path, FOLDER)
    val dirFlag = dir.mkdirs()

    val file = File(dir, fileName)
    return FileOutputStream(file)
}

Upvotes: 8

tomerpacific
tomerpacific

Reputation: 6510

After Android 5.0, multidexing is enabled by default.

Taken from the documentation:

Multidex support for Android 5.0 and higher Android 5.0 (API level 21) and higher uses a runtime called ART which natively supports loading multiple DEX files from APK files. ART performs pre-compilation at app install time which scans for classesN.dex files and compiles them into a single .oat file for execution by the Android device. Therefore, if your minSdkVersion is 21 or higher multidex is enabled by default, and you do not need the multidex support library.

Regardless, you need to do the following:

In build.gradle:

android {
defaultConfig {
    ...
    minSdkVersion 15 
    targetSdkVersion 28
    multiDexEnabled true. <----
  }
...
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
  <application
        android:name="android.support.multidex.MultiDexApplication" >
    ...
</application>

If you are not using AndroidX library suite:

dependencies {
  implementation 'com.android.support:multidex:1.0.3'
}

If you are using AndroidX:

dependencies {
  def multidex_version = "2.0.1"
  implementation 'androidx.multidex:multidex:$multidex_version'
 }

Upvotes: -1

Related Questions