Thumbnails cache & Don't do full reload on selection change
This commit is contained in:
parent
4f9aa55dfe
commit
4de5b41102
@ -145,8 +145,8 @@ class MainActivity : BaseActivity() {
|
|||||||
binding.textNoVolumes.visibility = View.GONE
|
binding.textNoVolumes.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unselectAll() {
|
private fun unselectAll(notifyChange: Boolean = true) {
|
||||||
volumeAdapter.unSelectAll()
|
volumeAdapter.unSelectAll(notifyChange)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ class MainActivity : BaseActivity() {
|
|||||||
if (volumeDatabase.removeHash(volumeAdapter.volumes[i]))
|
if (volumeDatabase.removeHash(volumeAdapter.volumes[i]))
|
||||||
volumeAdapter.onVolumeChanged(i)
|
volumeAdapter.onVolumeChanged(i)
|
||||||
}
|
}
|
||||||
unselectAll()
|
unselectAll(false)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.change_password -> {
|
R.id.change_password -> {
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package sushi.hardcore.droidfs.adapters
|
package sushi.hardcore.droidfs.adapters
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.LruCache
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.target.DrawableImageViewTarget
|
import com.bumptech.glide.request.target.DrawableImageViewTarget
|
||||||
@ -18,6 +22,7 @@ import sushi.hardcore.droidfs.explorers.ExplorerElement
|
|||||||
import sushi.hardcore.droidfs.util.PathUtils
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
class ExplorerElementAdapter(
|
class ExplorerElementAdapter(
|
||||||
val activity: AppCompatActivity,
|
val activity: AppCompatActivity,
|
||||||
@ -28,8 +33,21 @@ class ExplorerElementAdapter(
|
|||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
val dateFormat: DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault())
|
val dateFormat: DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault())
|
||||||
var explorerElements = listOf<ExplorerElement>()
|
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
|
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 {
|
override fun getItemCount(): Int {
|
||||||
return explorerElements.size
|
return explorerElements.size
|
||||||
@ -64,14 +82,21 @@ class ExplorerElementAdapter(
|
|||||||
for (i in explorerElements.indices) {
|
for (i in explorerElements.indices) {
|
||||||
if (!selectedItems.contains(i) && !explorerElements[i].isParentFolder) {
|
if (!selectedItems.contains(i) && !explorerElements[i].isParentFolder) {
|
||||||
selectedItems.add(i)
|
selectedItems.add(i)
|
||||||
|
notifyItemChanged(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unSelectAll() {
|
fun unSelectAll(notifyChange: Boolean) {
|
||||||
|
if (notifyChange) {
|
||||||
|
val whatWasSelected = selectedItems
|
||||||
|
selectedItems = HashSet()
|
||||||
|
whatWasSelected.forEach {
|
||||||
|
notifyItemChanged(it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
selectedItems.clear()
|
selectedItems.clear()
|
||||||
notifyDataSetChanged()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class ExplorerElementViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
open class ExplorerElementViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
@ -124,20 +149,21 @@ class ExplorerElementAdapter(
|
|||||||
var displayThumbnail = true
|
var displayThumbnail = true
|
||||||
var target: DrawableImageViewTarget? = null
|
var target: DrawableImageViewTarget? = null
|
||||||
|
|
||||||
private fun loadThumbnail(fullPath: String) {
|
private fun loadThumbnail(fullPath: String, adapter: ExplorerElementAdapter) {
|
||||||
(bindingAdapter as ExplorerElementAdapter?)?.let { adapter ->
|
|
||||||
adapter.gocryptfsVolume?.let { volume ->
|
adapter.gocryptfsVolume?.let { volume ->
|
||||||
displayThumbnail = true
|
displayThumbnail = true
|
||||||
Thread {
|
Thread {
|
||||||
volume.loadWholeFile(fullPath, maxSize = adapter.thumbnailMaxSize).first?.let {
|
volume.loadWholeFile(fullPath, maxSize = adapter.thumbnailMaxSize).first?.let {
|
||||||
if (displayThumbnail) {
|
if (displayThumbnail) {
|
||||||
adapter.activity.runOnUiThread {
|
adapter.activity.runOnUiThread {
|
||||||
if (displayThumbnail) {
|
if (displayThumbnail && !adapter.activity.isFinishing) {
|
||||||
target = Glide.with(adapter.activity).load(it).into(object : DrawableImageViewTarget(icon) {
|
target = Glide.with(adapter.activity).load(it).skipMemoryCache(true).into(object : DrawableImageViewTarget(icon) {
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: Drawable,
|
resource: Drawable,
|
||||||
transition: Transition<in Drawable>?
|
transition: Transition<in Drawable>?
|
||||||
) {
|
) {
|
||||||
|
val bitmap = resource.toBitmap()
|
||||||
|
adapter.thumbnailsCache!!.put(fullPath, bitmap.copy(bitmap.config, true))
|
||||||
super.onResourceReady(resource, transition)
|
super.onResourceReady(resource, transition)
|
||||||
target = null
|
target = null
|
||||||
}
|
}
|
||||||
@ -149,19 +175,36 @@ class ExplorerElementAdapter(
|
|||||||
}.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) {
|
override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) {
|
||||||
super.bind(explorerElement, position, isSelected)
|
super.bind(explorerElement, position, isSelected)
|
||||||
icon.setImageResource(
|
|
||||||
when {
|
when {
|
||||||
ConstValues.isImage(explorerElement.name) -> {
|
ConstValues.isImage(explorerElement.name) -> {
|
||||||
loadThumbnail(explorerElement.fullPath)
|
setThumbnailOrDefaultIcon(explorerElement.fullPath, R.drawable.icon_file_image)
|
||||||
R.drawable.icon_file_image
|
|
||||||
}
|
}
|
||||||
ConstValues.isVideo(explorerElement.name) -> {
|
ConstValues.isVideo(explorerElement.name) -> {
|
||||||
loadThumbnail(explorerElement.fullPath)
|
setThumbnailOrDefaultIcon(explorerElement.fullPath, R.drawable.icon_file_video)
|
||||||
R.drawable.icon_file_video
|
|
||||||
}
|
}
|
||||||
|
else -> icon.setImageResource(
|
||||||
|
when {
|
||||||
ConstValues.isText(explorerElement.name) -> R.drawable.icon_file_text
|
ConstValues.isText(explorerElement.name) -> R.drawable.icon_file_text
|
||||||
ConstValues.isPDF(explorerElement.name) -> R.drawable.icon_file_pdf
|
ConstValues.isPDF(explorerElement.name) -> R.drawable.icon_file_pdf
|
||||||
ConstValues.isAudio(explorerElement.name) -> R.drawable.icon_file_audio
|
ConstValues.isAudio(explorerElement.name) -> R.drawable.icon_file_audio
|
||||||
@ -170,6 +213,7 @@ class ExplorerElementAdapter(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DirectoryViewHolder(itemView: View) : RegularElementViewHolder(itemView) {
|
class DirectoryViewHolder(itemView: View) : RegularElementViewHolder(itemView) {
|
||||||
override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) {
|
override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package sushi.hardcore.droidfs.adapters
|
package sushi.hardcore.droidfs.adapters
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -12,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.Volume
|
import sushi.hardcore.droidfs.Volume
|
||||||
import sushi.hardcore.droidfs.VolumeDatabase
|
import sushi.hardcore.droidfs.VolumeDatabase
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class VolumeAdapter(
|
class VolumeAdapter(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
@ -24,7 +22,7 @@ class VolumeAdapter(
|
|||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||||
lateinit var volumes: List<Volume>
|
lateinit var volumes: List<Volume>
|
||||||
val selectedItems: MutableSet<Int> = HashSet()
|
var selectedItems: MutableSet<Int> = HashSet()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
reloadVolumes()
|
reloadVolumes()
|
||||||
@ -69,24 +67,30 @@ class VolumeAdapter(
|
|||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
fun selectAll() {
|
fun selectAll() {
|
||||||
for (i in volumes.indices) {
|
for (i in volumes.indices) {
|
||||||
if (!selectedItems.contains(i))
|
if (!selectedItems.contains(i)) {
|
||||||
selectedItems.add(i)
|
selectedItems.add(i)
|
||||||
|
notifyItemChanged(i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
fun unSelectAll(notifyChange: Boolean) {
|
||||||
fun unSelectAll() {
|
if (notifyChange) {
|
||||||
|
val whatWasSelected = selectedItems
|
||||||
|
selectedItems = HashSet()
|
||||||
|
whatWasSelected.forEach {
|
||||||
|
notifyItemChanged(it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
selectedItems.clear()
|
selectedItems.clear()
|
||||||
notifyDataSetChanged()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
reloadVolumes()
|
reloadVolumes()
|
||||||
unSelectAll()
|
unSelectAll(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class VolumeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
inner class VolumeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
@ -240,8 +240,8 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun unselectAll(){
|
protected fun unselectAll(notifyChange: Boolean = true) {
|
||||||
explorerAdapter.unSelectAll()
|
explorerAdapter.unSelectAll(notifyChange)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,8 +250,8 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements)
|
ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements)
|
||||||
}
|
}
|
||||||
|
unselectAll(false)
|
||||||
explorerAdapter.explorerElements = explorerElements
|
explorerAdapter.explorerElements = explorerElements
|
||||||
unselectAll()
|
|
||||||
val sharedPrefsEditor = sharedPrefs.edit()
|
val sharedPrefsEditor = sharedPrefs.edit()
|
||||||
sharedPrefsEditor.putString(ConstValues.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
|
sharedPrefsEditor.putString(ConstValues.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
|
||||||
sharedPrefsEditor.apply()
|
sharedPrefsEditor.apply()
|
||||||
@ -494,7 +494,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
if (!noItemSelected) {
|
if (!noItemSelected) {
|
||||||
if (explorerAdapter.selectedItems.size == 1) {
|
if (explorerAdapter.selectedItems.size == 1) {
|
||||||
menu.findItem(R.id.rename).isVisible = true
|
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
|
menu.findItem(R.id.open_as)?.isVisible = true
|
||||||
if (usf_open) {
|
if (usf_open) {
|
||||||
menu.findItem(R.id.external_open)?.isVisible = true
|
menu.findItem(R.id.external_open)?.isVisible = true
|
||||||
@ -523,7 +523,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.rename -> {
|
R.id.rename -> {
|
||||||
val oldName = explorerElements[explorerAdapter.selectedItems[0]].name
|
val oldName = explorerElements[explorerAdapter.selectedItems.first()].name
|
||||||
with(EditTextDialog(this, R.string.rename_title) {
|
with(EditTextDialog(this, R.string.rename_title) {
|
||||||
rename(oldName, it)
|
rename(oldName, it)
|
||||||
}) {
|
}) {
|
||||||
@ -536,12 +536,22 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.open_as -> {
|
R.id.open_as -> {
|
||||||
showOpenAsDialog(PathUtils.pathJoin(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
|
showOpenAsDialog(
|
||||||
|
PathUtils.pathJoin(
|
||||||
|
currentDirectoryPath,
|
||||||
|
explorerElements[explorerAdapter.selectedItems.first()].name
|
||||||
|
)
|
||||||
|
)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.external_open -> {
|
R.id.external_open -> {
|
||||||
if (usf_open){
|
if (usf_open){
|
||||||
openWithExternalApp(PathUtils.pathJoin(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
|
openWithExternalApp(
|
||||||
|
PathUtils.pathJoin(
|
||||||
|
currentDirectoryPath,
|
||||||
|
explorerElements[explorerAdapter.selectedItems.first()].name
|
||||||
|
)
|
||||||
|
)
|
||||||
unselectAll()
|
unselectAll()
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -362,7 +362,10 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
if (size > 1) {
|
if (size > 1) {
|
||||||
dialog.setMessage(getString(R.string.multiple_delete_confirm, explorerAdapter.selectedItems.size.toString()))
|
dialog.setMessage(getString(R.string.multiple_delete_confirm, explorerAdapter.selectedItems.size.toString()))
|
||||||
} else {
|
} 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()
|
dialog.show()
|
||||||
true
|
true
|
||||||
|
Loading…
Reference in New Issue
Block a user