Things are changing very fast in tech world. And if you want to be an Android Developer, then be ready to keep upgrading yourself. Actually this learning curve is the best thing that I like about this industry (My personal opinion). So, here is a new about about “Android Save Bitmap to Gallery”.
Saving file to internal storage, is required many times in our projects. We have done this thing many times before, but the method for saving file to internal storage has been changed completely for devices running with OS >= android10.
Basically your old way of saving files will not work with new versions of android, starting with android10. You can still go the old way, but it is a temporary solution. In this post we are going to talk about all these things.
Table of Contents
File Storage in Android
Now, we have two kinds of storage to store files.
- App-specific storage: The files for your application only. Files store here cannot be accessed outside your application. And you do not need any permission to access this storage.
- Shared Storage: The files that can be shared with other apps as well. For example Media Files (Images, Videos, Audios), Documents and other files.
To know more details about File Storage in Android, you can go through this official guide.
In this post we will be saving a Bitmap to Shared Storage as an Image File.
To demonstrate saving file to internal storage in android10, I will be creating an app that will fetch an Image from the Given URL and then it will save it to external storage.
Android Save Bitmap to Gallery
Let’s first start with the main function, that we need to save a Bitmap instance to gallery as an Image File.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
fun saveMediaToStorage(bitmap: Bitmap) { //Generating a file name val filename = "${System.currentTimeMillis()}.jpg" //Output stream var fos: OutputStream? = null //For devices running android >= Q if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { //getting the contentResolver context?.contentResolver?.also { resolver -> //Content resolver will process the contentvalues val contentValues = ContentValues().apply { //putting file information in content values put(MediaStore.MediaColumns.DISPLAY_NAME, filename) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg") put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) } //Inserting the contentValues to contentResolver and getting the Uri val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) //Opening an outputstream with the Uri that we got fos = imageUri?.let { resolver.openOutputStream(it) } } } else { //These for devices running on android < Q //So I don't think an explanation is needed here val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val image = File(imagesDir, filename) fos = FileOutputStream(image) } fos?.use { //Finally writing the bitmap to the output stream that we opened bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it) context?.toast("Saved to Photos") } } |
Let’s understand the above function.
- We have the bitmap instance that we want to save to our gallery in the function parameter.
- Then I’ve generated a file name using System.currentTimeMillis()Â , you apply your own logic here if you want a different file name. I used it just to generate a unique name quickly.
- Now we have created an OutputStream instance.
- The main thing here is we need to write two logics, because method of android 10 and above won’t work for lower versions; and that is why I have written a check here to identify if the device is >= VERSION_CODES.QÂ .
- Inside the if block, first I’ve got the ContentResolver from the Context .
- Inside ContentResolver , we have created ContentValues and inside ContentValues we have added the Image File informations.
- Now we have got the Uri after inserting the content values using the insert() function.
- After getting the Uri , we have opened output stream using openOutputStream() function.
- I am not explaining the else part, as it is the old way, that we already know.
- Finally using the output stream we are writing the Bitmap to stream using compress()Â function. And that’s it.
Now let’s build a complete application that will save image from an URL to the external storage.Â
Creating an Image Downloader Application
Again we will start by creating a new Android Studio project. I have created a project named ImageDownloader. And now we will start by adding all the required dependencies first.
Adding Permissions
For this app we require Internet and Storage permission. So define these permision in your AndroidManifest.xml file.
1 2 3 4 5 6 7 |
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:usesCleartextTraffic="true" |
Also make sure you add android:usesCleartextTraffic="true"Â as I did.
Setting Up Dependencies
We will add the following dependencies inside app level build.gradle file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//androidx activity for handling permission request implementation "androidx.activity:activity:1.2.0-alpha08" implementation 'androidx.fragment:fragment:1.3.0-alpha08' //Coroutines implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" //Life cycle components implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha07" //new material design implementation "com.google.android.material:material:1.3.0-alpha02" //Library for downloading image implementation "io.coil-kt:coil:0.13.0" |
Designing UI
I have designed the following UI for the application.
The UI is pretty simple and I hope you can make a UI like this easily. I am not posting the XML code here; but if you need it you can get the source code at the end of this post.
So basically here we have an EditText to input an Image URL. Two buttons, one to paste the copied URL and another one to Download the image from the given URL.
Creating Utils Function
Create a file named Utils.kt and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
fun View.visible(isVisible: Boolean) { visibility = if (isVisible) View.VISIBLE else View.GONE } fun View.enable(isEnabled: Boolean) { setEnabled(isEnabled) alpha = if (isEnabled) 1f else 0.5f } fun Context.toast(text: String?) { Toast.makeText(this, text, Toast.LENGTH_LONG).show() } fun Context.showPermissionRequestDialog( title: String, body: String, callback: () -> Unit ) { AlertDialog.Builder(this).also { it.setTitle(title) it.setMessage(body) it.setPositiveButton("Ok") { _, _ -> callback() } }.create().show() } |
We will be using these functions, in our MainActivity .
Downloading and Saving Bitmap to Gallery
Now we just need to add the functionality in our MainActivity.kt file. The code is very straight forward so I am directly showing the whole MainActivity here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
class MainActivity : AppCompatActivity() { //ImageLoader instance private lateinit var imageLoader: ImageLoader //Permission Request Handler private lateinit var requestPermissionLauncher: ActivityResultLauncher<String> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //Setting up Activity Permission Request Handler setPermissionCallback() progressbar.visible(false) //getting imageloader instance imageLoader = Coil.imageLoader(this) button_paste_link.setOnClickListener { pasteLink() } button_download.setOnClickListener { val bitmapURL = edit_text_image_url.text.toString().trim() //when download is pressed check permission and save bitmap from url checkPermissionAndDownloadBitmap(bitmapURL) } edit_text_image_url.addTextChangedListener { button_download.enable(it.toString().isNotEmpty()) } } //Allowing activity to automatically handle permission request private fun setPermissionCallback() { requestPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { isGranted: Boolean -> if (isGranted) { getBitmapFromUrl(edit_text_image_url.text.toString().trim()) } } } //function to check and request storage permission private fun checkPermissionAndDownloadBitmap(bitmapURL: String) { when { ContextCompat.checkSelfPermission( this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED -> { getBitmapFromUrl(bitmapURL) } shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) -> { showPermissionRequestDialog( getString(R.string.permission_title), getString(R.string.write_permission_request) ) { requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) } } else -> { requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) } } } //this function will fetch the Bitmap from the given URL private fun getBitmapFromUrl(bitmapURL: String) = lifecycleScope.launch { progressbar.visible(true) image_view.load(bitmapURL) val request = ImageRequest.Builder(this@MainActivity) .data(bitmapURL) .build() try { val downloadedBitmap = (imageLoader.execute(request).drawable as BitmapDrawable).bitmap image_view.setImageBitmap(downloadedBitmap) saveMediaToStorage(downloadedBitmap) } catch (e: Exception) { toast(e.message) } progressbar.visible(false) } //the function I already explained, it is used to save the Bitmap to external storage private fun saveMediaToStorage(bitmap: Bitmap) { val filename = "${System.currentTimeMillis()}.jpg" var fos: OutputStream? = null if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { contentResolver?.also { resolver -> 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) } val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) fos = imageUri?.let { resolver.openOutputStream(it) } } } else { val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val image = File(imagesDir, filename) fos = FileOutputStream(image) } fos?.use { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it) toast("Saved to Photos") } } //Pasting the value from Clipboard to EditText private fun pasteLink() { val clipboard: ClipboardManager? = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager? if (clipboard?.hasPrimaryClip() == true) { edit_text_image_url.setText(clipboard.primaryClip?.getItemAt(0)?.text.toString()) } } } |
The code is very straight forward, but in case you have a problem; don’t hesitate to ask.
Now you can run your application and you will see the following result.
Simplified Coding in Quora.
You can join here to ask your question in detail.
Android Save Bitmap to Gallery Source Code
In case you need the source code of this tutorial, you can get it from here.
So that is all for this tutorial friends. Make sure you share it with your friends, if you found this post helpful. Thank You