I'm trying to save a file as PNG of a canvas where the user can draw something and then call an Intent.ACTION_SEND so that the user can share it's drawing with other apps.
The code is able to save the file without any problems, but when I try to use the MediaScannerConnection.scanFile(), the Uri returned by the function is null. I'm using the absolute path of the file created, so I can't understand why this is happening.
My class, called BitmapAsyncTask inherits from AsyncTask (yes, I know it's deprecated). Here's the important code:
Writing the file to memory:
override fun doInBackground(vararg p0: Any?): String {
var result = ""
try {
val bytes = ByteArrayOutputStream()
mBitmap.compress(Bitmap.CompressFormat.PNG, 95, bytes)
val file = File(externalCacheDir!!.absoluteFile.toString()
+ File.separator + "KidsDrawingApp_"
+ System.currentTimeMillis() / 1000 + ".png")
val fileOutput = FileOutputStream(file)
fileOutput.write(bytes.toByteArray())
fileOutput.close()
result = file.absolutePath
} catch (e: Exception) {
e.printStackTrace()
}
Log.d("File", result)
return result
}
** The mBitmap variable is just the Bitmap generated from the canvas.
Here, the Log.d returns, for instance:
D/File: /storage/emulated/0/Android/data/com.example.kidsdrawingapp/cache/KidsDrawingApp_1599992654.png
I can access the file just fine if I open the Files app and search for it.
But when I run the MediaScannerConnection on onPostExecute(), the function doesn't return an uri based on the absolute path at all. Here's the code:
MediaScannerConnection.scanFile(this@MainActivity, arrayOf(result), null) {
path, uri -> val shareIntent = Intent()
Log.d("Path", path)
Log.d("Uri", uri.toString())
shareIntent.action = Intent.ACTION_SEND
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
shareIntent.type = "image/png"
startActivity(
Intent.createChooser(
shareIntent, "Share image"
)
)
}
Once again, the Log.d("Path", path) returns the same file as the previous Log.d(), but when I try to convert the Uri to string, it crashes because it's null.
I tried adding "file://" + file.absolutePath" like I saw in some other answers but it still didn't work, the uri returned by the scanFile() was still null.
I'm using API 21.
File Provider Code
AndroidManifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.kidsdrawingapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/path" />
</provider>
@xml/path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="captured" path="Android/data/com.example.kidsdrawingapp/files" />
</paths>
It is valid. However, it is not indexable by
MediaStoreon Android 10 and higher.MediaStorewill no longer index files in app-specific external storage, such asgetExternalFilesDir(), which is what you are using.If your objective is to have the image be usable by every app on the device, then getting indexed by
MediaStoreis fine. On Android 10+, you caninsert()into theMediaStoreand use the resultingUrifor writing out your content. See this sample app for a demonstration, though in my case I am writing out a video, not a PNG.If, instead, all you want to do is share this content, then do not use
MediaScannerConnection. Instead, useFileProvider. See the documentation and this sample app (though in my case I am sharing a PDF, not a PNG).