2020-07-17 16:35:39 +02:00
|
|
|
package sushi.hardcore.droidfs.adapters
|
|
|
|
|
2022-04-09 19:20:20 +02:00
|
|
|
import android.annotation.SuppressLint
|
|
|
|
import android.graphics.Bitmap
|
2021-11-11 15:05:33 +01:00
|
|
|
import android.graphics.drawable.Drawable
|
2022-04-09 19:20:20 +02:00
|
|
|
import android.util.LruCache
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
|
|
|
import android.widget.ImageView
|
2021-11-10 19:45:10 +01:00
|
|
|
import android.widget.LinearLayout
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.widget.TextView
|
2021-11-11 15:05:33 +01:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2022-04-09 19:20:20 +02:00
|
|
|
import androidx.core.graphics.drawable.toBitmap
|
2021-11-10 19:45:10 +01:00
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
2021-11-11 15:05:33 +01:00
|
|
|
import com.bumptech.glide.Glide
|
|
|
|
import com.bumptech.glide.request.target.DrawableImageViewTarget
|
|
|
|
import com.bumptech.glide.request.transition.Transition
|
2022-04-20 15:17:33 +02:00
|
|
|
import kotlinx.coroutines.*
|
2021-11-11 15:05:33 +01:00
|
|
|
import sushi.hardcore.droidfs.ConstValues
|
|
|
|
import sushi.hardcore.droidfs.GocryptfsVolume
|
2020-07-17 16:35:39 +02:00
|
|
|
import sushi.hardcore.droidfs.R
|
2020-08-11 20:40:14 +02:00
|
|
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
2020-07-26 21:16:06 +02:00
|
|
|
import sushi.hardcore.droidfs.util.PathUtils
|
2020-07-17 16:35:39 +02:00
|
|
|
import java.text.DateFormat
|
|
|
|
import java.util.*
|
|
|
|
|
2021-11-10 19:45:10 +01:00
|
|
|
class ExplorerElementAdapter(
|
2021-11-11 15:05:33 +01:00
|
|
|
val activity: AppCompatActivity,
|
|
|
|
val gocryptfsVolume: GocryptfsVolume?,
|
2022-04-17 15:52:34 +02:00
|
|
|
private val listener: Listener,
|
2022-03-23 16:35:13 +01:00
|
|
|
val thumbnailMaxSize: Long,
|
2022-04-17 15:52:34 +02:00
|
|
|
) : SelectableAdapter<ExplorerElement>(listener::onSelectionChanged) {
|
2021-11-11 15:05:33 +01:00
|
|
|
val dateFormat: DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault())
|
2021-11-10 19:45:10 +01:00
|
|
|
var explorerElements = listOf<ExplorerElement>()
|
2022-04-09 19:20:20 +02:00
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
|
|
set(value) {
|
|
|
|
field = value
|
|
|
|
thumbnailsCache?.evictAll()
|
|
|
|
notifyDataSetChanged()
|
|
|
|
}
|
2022-04-09 15:09:14 +02:00
|
|
|
var isUsingListLayout = true
|
2022-04-09 19:20:20 +02:00
|
|
|
private var thumbnailsCache: LruCache<String, Bitmap>? = null
|
|
|
|
|
|
|
|
init {
|
|
|
|
if (gocryptfsVolume != null) {
|
|
|
|
thumbnailsCache = LruCache((Runtime.getRuntime().maxMemory() / 1024 / 8).toInt())
|
|
|
|
}
|
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
|
2022-04-17 15:52:34 +02:00
|
|
|
interface Listener {
|
|
|
|
fun onSelectionChanged(size: Int)
|
|
|
|
fun onExplorerElementClick(position: Int)
|
|
|
|
fun onExplorerElementLongClick(position: Int)
|
|
|
|
}
|
|
|
|
|
2022-04-09 20:13:39 +02:00
|
|
|
override fun getItems(): List<ExplorerElement> {
|
|
|
|
return explorerElements
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2022-04-09 20:13:39 +02:00
|
|
|
override fun toggleSelection(position: Int): Boolean {
|
|
|
|
return if (!explorerElements[position].isParentFolder) {
|
|
|
|
super.toggleSelection(position)
|
|
|
|
} else {
|
|
|
|
false
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-09 20:13:39 +02:00
|
|
|
override fun onItemClick(position: Int): Boolean {
|
2022-04-17 15:52:34 +02:00
|
|
|
listener.onExplorerElementClick(position)
|
2022-04-09 20:13:39 +02:00
|
|
|
return super.onItemClick(position)
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2022-04-09 20:13:39 +02:00
|
|
|
override fun onItemLongClick(position: Int): Boolean {
|
2022-04-17 15:52:34 +02:00
|
|
|
listener.onExplorerElementLongClick(position)
|
2022-04-09 20:13:39 +02:00
|
|
|
return super.onItemLongClick(position)
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2022-04-09 20:13:39 +02:00
|
|
|
override fun isSelectable(position: Int): Boolean {
|
|
|
|
return !explorerElements[position].isParentFolder
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
2021-11-11 15:05:33 +01:00
|
|
|
open class ExplorerElementViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
2021-11-10 19:45:10 +01:00
|
|
|
private val textElementName by lazy {
|
|
|
|
itemView.findViewById<TextView>(R.id.text_element_name)
|
|
|
|
}
|
|
|
|
protected val textElementSize: TextView by lazy {
|
|
|
|
itemView.findViewById(R.id.text_element_size)
|
|
|
|
}
|
|
|
|
protected val textElementMtime: TextView by lazy {
|
|
|
|
itemView.findViewById(R.id.text_element_mtime)
|
|
|
|
}
|
|
|
|
protected val icon: ImageView by lazy {
|
|
|
|
itemView.findViewById(R.id.icon_element)
|
|
|
|
}
|
|
|
|
private val selectableContainer: LinearLayout by lazy {
|
|
|
|
itemView.findViewById(R.id.selectable_container)
|
|
|
|
}
|
|
|
|
|
|
|
|
open fun bind(explorerElement: ExplorerElement, position: Int) {
|
|
|
|
textElementName.text = explorerElement.name
|
2022-04-09 20:13:39 +02:00
|
|
|
(bindingAdapter as ExplorerElementAdapter?)?.setSelectable(selectableContainer, itemView, position)
|
2021-11-10 19:45:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 15:05:33 +01:00
|
|
|
open class RegularElementViewHolder(itemView: View) : ExplorerElementViewHolder(itemView) {
|
2021-11-10 19:45:10 +01:00
|
|
|
open fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) {
|
|
|
|
super.bind(explorerElement, position)
|
|
|
|
textElementSize.text = PathUtils.formatSize(explorerElement.size)
|
2021-11-11 15:05:33 +01:00
|
|
|
(bindingAdapter as ExplorerElementAdapter?)?.let {
|
|
|
|
textElementMtime.text = it.dateFormat.format(explorerElement.mTime)
|
|
|
|
}
|
2021-11-10 19:45:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 15:05:33 +01:00
|
|
|
class FileViewHolder(itemView: View) : RegularElementViewHolder(itemView) {
|
2022-04-20 15:17:33 +02:00
|
|
|
private var target: DrawableImageViewTarget? = null
|
|
|
|
private var job: Job? = null
|
|
|
|
private val scope = CoroutineScope(Dispatchers.IO)
|
2021-11-11 15:05:33 +01:00
|
|
|
|
2022-04-09 19:20:20 +02:00
|
|
|
private fun loadThumbnail(fullPath: String, adapter: ExplorerElementAdapter) {
|
|
|
|
adapter.gocryptfsVolume?.let { volume ->
|
2022-04-20 15:17:33 +02:00
|
|
|
job = scope.launch {
|
2022-04-09 19:20:20 +02:00
|
|
|
volume.loadWholeFile(fullPath, maxSize = adapter.thumbnailMaxSize).first?.let {
|
2022-04-20 15:17:33 +02:00
|
|
|
if (isActive) {
|
|
|
|
withContext(Dispatchers.Main) {
|
|
|
|
if (isActive && !adapter.activity.isFinishing) {
|
2022-04-09 19:20:20 +02:00
|
|
|
target = Glide.with(adapter.activity).load(it).skipMemoryCache(true).into(object : DrawableImageViewTarget(icon) {
|
|
|
|
override fun onResourceReady(
|
|
|
|
resource: Drawable,
|
|
|
|
transition: Transition<in Drawable>?
|
|
|
|
) {
|
2022-04-20 15:17:33 +02:00
|
|
|
target = null
|
2022-04-09 19:20:20 +02:00
|
|
|
val bitmap = resource.toBitmap()
|
|
|
|
adapter.thumbnailsCache!!.put(fullPath, bitmap.copy(bitmap.config, true))
|
|
|
|
super.onResourceReady(resource, transition)
|
|
|
|
}
|
|
|
|
})
|
2021-11-11 15:05:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-04-09 19:20:20 +02:00
|
|
|
}
|
2022-04-20 15:17:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun cancelThumbnailLoading(adapter: ExplorerElementAdapter) {
|
|
|
|
job?.cancel()
|
|
|
|
target?.let {
|
|
|
|
Glide.with(adapter.activity).clear(it)
|
2022-04-09 19:20:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2021-11-11 15:05:33 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-09 19:20:20 +02:00
|
|
|
if (setDefaultIcon) {
|
|
|
|
icon.setImageResource(defaultIconId)
|
|
|
|
}
|
2021-11-11 15:05:33 +01:00
|
|
|
}
|
2022-04-09 19:20:20 +02:00
|
|
|
|
2021-11-10 19:45:10 +01:00
|
|
|
override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) {
|
|
|
|
super.bind(explorerElement, position, isSelected)
|
2022-04-09 19:20:20 +02:00
|
|
|
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)
|
2021-11-11 15:05:33 +01:00
|
|
|
}
|
2022-04-09 19:20:20 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2021-11-10 19:45:10 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-11 15:05:33 +01:00
|
|
|
|
|
|
|
class DirectoryViewHolder(itemView: View) : RegularElementViewHolder(itemView) {
|
2021-11-10 19:45:10 +01:00
|
|
|
override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) {
|
|
|
|
super.bind(explorerElement, position, isSelected)
|
|
|
|
icon.setImageResource(R.drawable.icon_folder)
|
|
|
|
}
|
|
|
|
}
|
2021-11-11 15:05:33 +01:00
|
|
|
|
|
|
|
class ParentFolderViewHolder(itemView: View): ExplorerElementViewHolder(itemView) {
|
2021-11-10 19:45:10 +01:00
|
|
|
override fun bind(explorerElement: ExplorerElement, position: Int) {
|
|
|
|
super.bind(explorerElement, position)
|
|
|
|
textElementSize.text = ""
|
|
|
|
textElementMtime.setText(R.string.parent_folder)
|
|
|
|
icon.setImageResource(R.drawable.icon_folder)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 15:05:33 +01:00
|
|
|
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
|
|
|
|
if (holder is FileViewHolder) {
|
2022-04-20 15:17:33 +02:00
|
|
|
holder.cancelThumbnailLoading(this)
|
2021-11-11 15:05:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-10 19:45:10 +01:00
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
2022-04-09 15:09:14 +02:00
|
|
|
val view = activity.layoutInflater.inflate(
|
|
|
|
if (isUsingListLayout) {
|
|
|
|
R.layout.adapter_explorer_element_list
|
|
|
|
} else {
|
|
|
|
R.layout.adapter_explorer_element_grid
|
|
|
|
}, parent, false
|
|
|
|
)
|
2021-11-10 19:45:10 +01:00
|
|
|
return when (viewType) {
|
2021-11-11 15:05:33 +01:00
|
|
|
ExplorerElement.REGULAR_FILE_TYPE -> FileViewHolder(view)
|
|
|
|
ExplorerElement.DIRECTORY_TYPE -> DirectoryViewHolder(view)
|
|
|
|
ExplorerElement.PARENT_FOLDER_TYPE -> ParentFolderViewHolder(view)
|
2021-11-10 19:45:10 +01:00
|
|
|
else -> throw IllegalArgumentException()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
|
|
|
val element = explorerElements[position]
|
|
|
|
if (element.isParentFolder) {
|
|
|
|
(holder as ParentFolderViewHolder).bind(element, position)
|
|
|
|
} else {
|
|
|
|
(holder as RegularElementViewHolder).bind(element, position, selectedItems.contains(position))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun getItemViewType(position: Int): Int {
|
|
|
|
return explorerElements[position].elementType.toInt()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|