I have a recyclerview that uses Glide to display images from Firebase Realtime database. In the recyclerview I also have a download button. When a user clicks the button, want the image from Firebase to be downloaded into the device internal storage.
Adapter class
class NatureAdapter(private val mContext: Context, private val natureList: ArrayList<Nature>) : RecyclerView.Adapter<NatureAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.nature_image_view, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Glide.with(mContext)
.load(natureList[position].space)
.into(holder.imageView)
}
override fun getItemCount(): Int {
return natureList.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var imageView: ImageView = itemView.findViewById(R.id.NatureView)
}
companion object {
private const val Tag = "RecyclerView"
}
}
Data list class
class Nature (var space: String? = null) {
}
Update
First error
e: C:\Users\Khumo Kwezi Mashapa\AndroidStudioProjects\MyNotepad\app\src\main\java\com\khumomashapa\notes\fragments\NatureWallpapers.kt: (65, 17): Unresolved reference: viewModel
Second error
e: C:\Users\Khumo Kwezi Mashapa\AndroidStudioProjects\MyNotepad\app\src\main\java\com\khumomashapa\notes\fragments\NatureWallpapers.kt: (120, 93): No value passed for parameter 'downloadImage'
CodePudding user response:
- Add Okhttp dependency
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
- Create download function
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()
}
}
}
})
}
}
- Pass download function from your fragment to your adapter
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(),
natureList = natureList, // your list here
downloadImage = { imageUrl ->
viewModel.downloadImage(imageUrl)
}
)
Your adapter constructor will look like this:
class AbstractAdapter(
private val mContext: Context,
private val natureList: ArrayList<Nature>,
private val downloadImage: (imageUrl: String) -> Unit
): RecyclerView.Adapter<AbstractAdapter.ViewHolder>()
- Call downloadImage lambda inside downloadBtn click listener
now we will add downloadImage call inside the click listener
holder.downloadBtn.setOnClickListener {
val imageUrl = natureList[position].space
imageUrl?.let {
downloadImage(it)
}
}
- Add write external storage permission to AndroidManifest.xml
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.