I used to save images using MediaStore.Images.Media.insertImage
but insertImage
method is now deprecated. The docs say:
This method was deprecated in API level 29. inserting of images should be performed using MediaColumns#IS_PENDING, which offers richer control over lifecycle.
I don't really get it, since MediaColumns.IS_PENDING
is just a flag, how am I supposed to use it?
Should I use ContentValues
?
The filesystem and mediastore modules are deprecated. You should use the new storage module which is relying on Okio Filesystem API. On Android, every files on the shared storage (files visible when using a file manager) are indexed by MediaStore, to allow apps to query them by type, date, size, etc.
↳ android.provider.MediaStore. The contract between the media provider and applications. Contains definitions for the supported URIs and columns. The media provider provides an indexed collection of common media types, such as Audio , Video , and Images , from any attached storage devices.
SOLVED
The code suggested from @CommonsWare has no problem, except the fact that if you are programming with targetSdkVersion 29
, you must add the condition:
val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, System.currentTimeMillis().toString()) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { //this one put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation) put(MediaStore.MediaColumns.IS_PENDING, 1) } }
This method was deprecated in API level 29. inserting of images should be performed using MediaColumns#IS_PENDING, which offers richer control over the lifecycle.
Either I'm too stupid to understand the docs or the Google team really needs to refactor the documentation.
Anyways, posting the complete answer from the links provided by CommonsWare and coroutineDispatcher
Step 1: Decide on which API level you are
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) saveImageInQ(imageBitMap) else saveImageInLegacy(imageBitMap)
Step 2: Save the image in Q style
//Make sure to call this function on a worker thread, else it will block main thread fun saveImageInQ(bitmap: Bitmap):Uri { val filename = "IMG_${System.currentTimeMillis()}.jpg" var fos: OutputStream? = null val imageUri: Uri? = null val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, filename) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg") put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) put(MediaStore.Video.Media.IS_PENDING, 1) } //use application context to get contentResolver val contentResolver = application.contentResolver contentResolver.also { resolver -> imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) fos = imageUri?.let { resolver.openOutputStream(it) } } fos?.use { bitmap.compress(Bitmap.CompressFormat.JPEG, 70, it) } contentValues.clear() contentValues.put(MediaStore.Video.Media.IS_PENDING, 0) resolver.update(imageUri, contentValues, null, null) return imageUri }
Step 3: If not on Q save the image in legacy style
//Make sure to call this function on a worker thread, else it will block main thread fun saveTheImageLegacyStyle(bitmap:Bitmap){ val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val image = File(imagesDir, filename) fos = FileOutputStream(image) fos?.use {bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)} }
This should get you rolling!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With