Thumbnails cache & Don't do full reload on selection change

This commit is contained in:
Matéo Duparc 2022-04-09 19:20:20 +02:00
parent 4f9aa55dfe
commit 4de5b41102
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
5 changed files with 124 additions and 63 deletions

View File

@ -145,8 +145,8 @@ class MainActivity : BaseActivity() {
binding.textNoVolumes.visibility = View.GONE
}
private fun unselectAll() {
volumeAdapter.unSelectAll()
private fun unselectAll(notifyChange: Boolean = true) {
volumeAdapter.unSelectAll(notifyChange)
invalidateOptionsMenu()
}
@ -233,7 +233,7 @@ class MainActivity : BaseActivity() {
if (volumeDatabase.removeHash(volumeAdapter.volumes[i]))
volumeAdapter.onVolumeChanged(i)
}
unselectAll()
unselectAll(false)
true
}
R.id.change_password -> {

View File

@ -1,12 +1,16 @@
package sushi.hardcore.droidfs.adapters
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.util.LruCache
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.drawable.toBitmap
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.DrawableImageViewTarget
@ -18,6 +22,7 @@ import sushi.hardcore.droidfs.explorers.ExplorerElement
import sushi.hardcore.droidfs.util.PathUtils
import java.text.DateFormat
import java.util.*
import kotlin.collections.HashSet
class ExplorerElementAdapter(
val activity: AppCompatActivity,
@ -28,8 +33,21 @@ class ExplorerElementAdapter(
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val dateFormat: DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault())
var explorerElements = listOf<ExplorerElement>()
val selectedItems: MutableList<Int> = ArrayList()
@SuppressLint("NotifyDataSetChanged")
set(value) {
field = value
thumbnailsCache?.evictAll()
notifyDataSetChanged()
}
var selectedItems: MutableSet<Int> = HashSet()
var isUsingListLayout = true
private var thumbnailsCache: LruCache<String, Bitmap>? = null
init {
if (gocryptfsVolume != null) {
thumbnailsCache = LruCache((Runtime.getRuntime().maxMemory() / 1024 / 8).toInt())
}
}
override fun getItemCount(): Int {
return explorerElements.size
@ -64,14 +82,21 @@ class ExplorerElementAdapter(
for (i in explorerElements.indices) {
if (!selectedItems.contains(i) && !explorerElements[i].isParentFolder) {
selectedItems.add(i)
notifyItemChanged(i)
}
}
notifyDataSetChanged()
}
fun unSelectAll() {
selectedItems.clear()
notifyDataSetChanged()
fun unSelectAll(notifyChange: Boolean) {
if (notifyChange) {
val whatWasSelected = selectedItems
selectedItems = HashSet()
whatWasSelected.forEach {
notifyItemChanged(it)
}
} else {
selectedItems.clear()
}
}
open class ExplorerElementViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@ -124,50 +149,69 @@ class ExplorerElementAdapter(
var displayThumbnail = true
var target: DrawableImageViewTarget? = null
private fun loadThumbnail(fullPath: String) {
(bindingAdapter as ExplorerElementAdapter?)?.let { adapter ->
adapter.gocryptfsVolume?.let { volume ->
displayThumbnail = true
Thread {
volume.loadWholeFile(fullPath, maxSize = adapter.thumbnailMaxSize).first?.let {
if (displayThumbnail) {
adapter.activity.runOnUiThread {
if (displayThumbnail) {
target = Glide.with(adapter.activity).load(it).into(object : DrawableImageViewTarget(icon) {
override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable>?
) {
super.onResourceReady(resource, transition)
target = null
}
})
}
private fun loadThumbnail(fullPath: String, adapter: ExplorerElementAdapter) {
adapter.gocryptfsVolume?.let { volume ->
displayThumbnail = true
Thread {
volume.loadWholeFile(fullPath, maxSize = adapter.thumbnailMaxSize).first?.let {
if (displayThumbnail) {
adapter.activity.runOnUiThread {
if (displayThumbnail && !adapter.activity.isFinishing) {
target = Glide.with(adapter.activity).load(it).skipMemoryCache(true).into(object : DrawableImageViewTarget(icon) {
override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable>?
) {
val bitmap = resource.toBitmap()
adapter.thumbnailsCache!!.put(fullPath, bitmap.copy(bitmap.config, true))
super.onResourceReady(resource, transition)
target = null
}
})
}
}
}
}.start()
}
}
}.start()
}
}
private fun setThumbnailOrDefaultIcon(fullPath: String, defaultIconId: Int) {
var setDefaultIcon = true
(bindingAdapter as ExplorerElementAdapter?)?.let { adapter ->
adapter.thumbnailsCache?.let {
val thumbnail = it.get(fullPath)
if (thumbnail != null) {
icon.setImageBitmap(thumbnail)
setDefaultIcon = false
} else {
loadThumbnail(fullPath, adapter)
}
}
}
if (setDefaultIcon) {
icon.setImageResource(defaultIconId)
}
}
override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) {
super.bind(explorerElement, position, isSelected)
icon.setImageResource(
when {
ConstValues.isImage(explorerElement.name) -> {
loadThumbnail(explorerElement.fullPath)
R.drawable.icon_file_image
}
ConstValues.isVideo(explorerElement.name) -> {
loadThumbnail(explorerElement.fullPath)
R.drawable.icon_file_video
}
ConstValues.isText(explorerElement.name) -> R.drawable.icon_file_text
ConstValues.isPDF(explorerElement.name) -> R.drawable.icon_file_pdf
ConstValues.isAudio(explorerElement.name) -> R.drawable.icon_file_audio
else -> R.drawable.icon_file_unknown
when {
ConstValues.isImage(explorerElement.name) -> {
setThumbnailOrDefaultIcon(explorerElement.fullPath, R.drawable.icon_file_image)
}
)
ConstValues.isVideo(explorerElement.name) -> {
setThumbnailOrDefaultIcon(explorerElement.fullPath, R.drawable.icon_file_video)
}
else -> icon.setImageResource(
when {
ConstValues.isText(explorerElement.name) -> R.drawable.icon_file_text
ConstValues.isPDF(explorerElement.name) -> R.drawable.icon_file_pdf
ConstValues.isAudio(explorerElement.name) -> R.drawable.icon_file_audio
else -> R.drawable.icon_file_unknown
}
)
}
}
}

View File

@ -1,6 +1,5 @@
package sushi.hardcore.droidfs.adapters
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
@ -12,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.Volume
import sushi.hardcore.droidfs.VolumeDatabase
import java.io.File
class VolumeAdapter(
private val context: Context,
@ -24,7 +22,7 @@ class VolumeAdapter(
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
lateinit var volumes: List<Volume>
val selectedItems: MutableSet<Int> = HashSet()
var selectedItems: MutableSet<Int> = HashSet()
init {
reloadVolumes()
@ -69,24 +67,30 @@ class VolumeAdapter(
notifyItemChanged(position)
}
@SuppressLint("NotifyDataSetChanged")
fun selectAll() {
for (i in volumes.indices) {
if (!selectedItems.contains(i))
if (!selectedItems.contains(i)) {
selectedItems.add(i)
notifyItemChanged(i)
}
}
notifyDataSetChanged()
}
@SuppressLint("NotifyDataSetChanged")
fun unSelectAll() {
selectedItems.clear()
notifyDataSetChanged()
fun unSelectAll(notifyChange: Boolean) {
if (notifyChange) {
val whatWasSelected = selectedItems
selectedItems = HashSet()
whatWasSelected.forEach {
notifyItemChanged(it)
}
} else {
selectedItems.clear()
}
}
fun refresh() {
reloadVolumes()
unSelectAll()
unSelectAll(true)
}
inner class VolumeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

View File

@ -240,8 +240,8 @@ open class BaseExplorerActivity : BaseActivity() {
invalidateOptionsMenu()
}
protected fun unselectAll(){
explorerAdapter.unSelectAll()
protected fun unselectAll(notifyChange: Boolean = true) {
explorerAdapter.unSelectAll(notifyChange)
invalidateOptionsMenu()
}
@ -250,8 +250,8 @@ open class BaseExplorerActivity : BaseActivity() {
synchronized(this) {
ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements)
}
unselectAll(false)
explorerAdapter.explorerElements = explorerElements
unselectAll()
val sharedPrefsEditor = sharedPrefs.edit()
sharedPrefsEditor.putString(ConstValues.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
sharedPrefsEditor.apply()
@ -494,7 +494,7 @@ open class BaseExplorerActivity : BaseActivity() {
if (!noItemSelected) {
if (explorerAdapter.selectedItems.size == 1) {
menu.findItem(R.id.rename).isVisible = true
if (explorerElements[explorerAdapter.selectedItems[0]].isRegularFile) {
if (explorerElements[explorerAdapter.selectedItems.first()].isRegularFile) {
menu.findItem(R.id.open_as)?.isVisible = true
if (usf_open) {
menu.findItem(R.id.external_open)?.isVisible = true
@ -523,7 +523,7 @@ open class BaseExplorerActivity : BaseActivity() {
true
}
R.id.rename -> {
val oldName = explorerElements[explorerAdapter.selectedItems[0]].name
val oldName = explorerElements[explorerAdapter.selectedItems.first()].name
with(EditTextDialog(this, R.string.rename_title) {
rename(oldName, it)
}) {
@ -536,12 +536,22 @@ open class BaseExplorerActivity : BaseActivity() {
true
}
R.id.open_as -> {
showOpenAsDialog(PathUtils.pathJoin(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
showOpenAsDialog(
PathUtils.pathJoin(
currentDirectoryPath,
explorerElements[explorerAdapter.selectedItems.first()].name
)
)
true
}
R.id.external_open -> {
if (usf_open){
openWithExternalApp(PathUtils.pathJoin(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
openWithExternalApp(
PathUtils.pathJoin(
currentDirectoryPath,
explorerElements[explorerAdapter.selectedItems.first()].name
)
)
unselectAll()
}
true

View File

@ -362,7 +362,10 @@ class ExplorerActivity : BaseExplorerActivity() {
if (size > 1) {
dialog.setMessage(getString(R.string.multiple_delete_confirm, explorerAdapter.selectedItems.size.toString()))
} else {
dialog.setMessage(getString(R.string.single_delete_confirm, explorerAdapter.explorerElements[explorerAdapter.selectedItems[0]].name))
dialog.setMessage(getString(
R.string.single_delete_confirm,
explorerAdapter.explorerElements[explorerAdapter.selectedItems.first()].name
))
}
dialog.show()
true