I have a recyclerview that uses Glide to show images from Firebase Realtime database. I have a download button in this recyclerview and I want users to be able to download these images once the click the download button. I heard you can use Glide to download images to device internal storage, but I'm unfamiliar with this, so I would like your assistance.
Adapter class
class AbstractAdapter(private val mContext: Context, private val abstractList: ArrayList<Abstract>) :
RecyclerView.Adapter<AbstractAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.abstract_image_view, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.imageView.setOnClickListener {
val intent = Intent(mContext, AbstractPreview::class.java)
intent.putExtra("abstract", abstractList[position].abstract.toString())
Toast.makeText(mContext, "Fullscreen view", Toast.LENGTH_SHORT).show()
mContext.startActivity(intent)
}
holder.downloadBtn.setOnClickListener {
}
Glide.with(mContext)
.load(abstractList[position].abstract)
.into(holder.imageView)
}
override fun getItemCount(): Int {
return abstractList.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.abstractImageView)
val downloadBtn: Button = itemView.findViewById(R.id.abstractDownloadBtn)
}
companion object {
private const val Tag = "RecyclerView"
}
This is the onClickLister for the download button.
holder.downloadBtn.setOnClickListener {
}
As you can see, I know how to show images using Glide. All I need to know is how to download those images into the device internal storage.
First of all you need to have okhttp dependency on your app gradle:
implementation("com.squareup.okhttp3:okhttp:4.10.0")
Note: if you are having retrofit no need to add okhttp dependency
Ok now we are going to add the download logic in your view model, add okhttp instance declaration and download function to your view model:
Note: you can move download logic to a repository or anywhere you want, it's up to you.
class YourViewModel : ViewModel() {
// add okhttp instance to your view model or you inject it with hilt if your using dependency injection
private val okHttpClient = OkHttpClient.Builder().build()
// add this function to your view model
fun downloadImage(imageUrl: String) {
val request = Request.Builder()
.url(imageUrl)
.build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// Download Failed, you can show error to the user
}
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful) {
// Download Failed, you can show error to the user
return
}
response.body?.let { responseBody ->
try {
// Convert response body to byte array
val imageByteArray = responseBody.byteStream().readBytes()
// Split image url so we can get the image name
val words = imageUrl.split("/").toTypedArray()
// Get the image name
val imageName = words.last()
// Init pathName (Downloads Directory)
val pathName = "${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_DOWNLOADS}"
// Create New file for the image
val file = File(pathName, imageName)
// Set byteArray To Image File
file.writeBytes(imageByteArray)
} catch(e: IOException) {
// Saving Image Failed, you can show error to the user
e.printStackTrace()
}
}
}
})
}
}
Now you need to pass your download function to your adapter inside a lambda function, adapter creation in your fragment should look like this:
val adapter = AbstractAdapter(
context = requireContext(),
abstractList = abstractList, // your list here
downloadImage = { imageUrl ->
viewModel.downloadImage(imageUrl)
}
)
Your adapter constructor will look like this:
class AbstractAdapter(
private val mContext: Context,
private val abstractList: ArrayList<Abstract>,
private val downloadImage: (imageUrl: String) -> Unit
): RecyclerView.Adapter<AbstractAdapter.ViewHolder>()
now we will add downloadImage call inside the click listener
holder.downloadBtn.setOnClickListener {
val imageUrl = abstractList[position].abstract
downloadImage(imageUrl)
}
add this permission to your AndroidManifest.xml file to be able to add files to phone storage
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
Note: This permission is only required for sqk version <= 28 that's why we added android:maxSdkVersion="28"
I hope that everything is clear. This code should give you the result that you want, try it and tell me if there is anything wrong.