diff --git a/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt b/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt index 9133a6e..c5a9ceb 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt @@ -1,14 +1,13 @@ package sushi.hardcore.droidfs.adapters import android.content.Context -import android.graphics.Color import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.BaseAdapter import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView -import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView import sushi.hardcore.droidfs.ConstValues.Companion.getAssociatedDrawable import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.explorers.ExplorerElement @@ -16,79 +15,47 @@ import sushi.hardcore.droidfs.util.PathUtils import java.text.DateFormat import java.util.* -class ExplorerElementAdapter(private val context: Context) : BaseAdapter() { - private val dateFormat: DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault()) - private var explorerElements = listOf() +class ExplorerElementAdapter( + context: Context, + private val onExplorerElementClick: (Int) -> Unit, + private val onExplorerElementLongClick: (Int) -> Unit +) : RecyclerView.Adapter() { + private val dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault()) + var explorerElements = listOf() + set(value) { + field = value + unSelectAll() + } private val inflater: LayoutInflater = LayoutInflater.from(context) val selectedItems: MutableList = ArrayList() - override fun getCount(): Int { + + override fun getItemCount(): Int { return explorerElements.size } - override fun getItem(position: Int): ExplorerElement { - return explorerElements[position] - } - - override fun getItemId(position: Int): Long { - return 0 - } - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val view: View = convertView ?: inflater.inflate(R.layout.adapter_explorer_element, parent, false) - val currentElement = getItem(position) - val textElementName = view.findViewById(R.id.text_element_name) - textElementName.text = currentElement.name - val textElementMtime = view.findViewById(R.id.text_element_mtime) - val textElementSize = view.findViewById(R.id.text_element_size) - if (!currentElement.isParentFolder){ - textElementSize.text = PathUtils.formatSize(currentElement.size) - } else { - textElementSize.text = "" - } - var drawableId = R.drawable.icon_folder - when { - currentElement.isDirectory -> { - textElementMtime.text = dateFormat.format(currentElement.mTime) - } - currentElement.isParentFolder -> { - textElementMtime.setText(R.string.parent_folder) - } - else -> { - textElementMtime.text = dateFormat.format(currentElement.mTime) - drawableId = getAssociatedDrawable(currentElement.name) - } - } - view.findViewById(R.id.icon_element).setImageResource(drawableId) - if (selectedItems.contains(position)) { - view.setBackgroundColor(ContextCompat.getColor(context, R.color.itemSelected)) - } else { - view.setBackgroundColor(Color.alpha(0)) - } - return view - } - - fun onItemClick(position: Int) { - if (selectedItems.isNotEmpty()) { - if (!explorerElements[position].isParentFolder) { - if (selectedItems.contains(position)) { - selectedItems.remove(position) - } else { - selectedItems.add(position) - } - notifyDataSetChanged() - } - } - } - - fun onItemLongClick(position: Int) { + private fun toggleSelection(position: Int): Boolean { if (!explorerElements[position].isParentFolder) { - if (!selectedItems.contains(position)) { - selectedItems.add(position) - } else { + if (selectedItems.contains(position)) { selectedItems.remove(position) + } else { + selectedItems.add(position) + return true } - notifyDataSetChanged() } + return false + } + + private fun onItemClick(position: Int): Boolean { + onExplorerElementClick(position) + if (selectedItems.isNotEmpty()) { + return toggleSelection(position) + } + return false + } + + private fun onItemLongClick(position: Int): Boolean { + onExplorerElementLongClick(position) + return toggleSelection(position) } fun selectAll() { @@ -105,8 +72,112 @@ class ExplorerElementAdapter(private val context: Context) : BaseAdapter() { notifyDataSetChanged() } - fun setExplorerElements(explorer_elements: List) { - this.explorerElements = explorer_elements - unSelectAll() + open class ExplorerElementViewHolder( + itemView: View, + private val onClick: (Int) -> Boolean, + private val onLongClick: (Int) -> Boolean, + ) : RecyclerView.ViewHolder(itemView) { + private val textElementName by lazy { + itemView.findViewById(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) + } + + protected fun setBackground(isSelected: Boolean) { + itemView.setBackgroundResource(if (isSelected) { R.color.itemSelected } else { 0 }) + } + + open fun bind(explorerElement: ExplorerElement, position: Int) { + textElementName.text = explorerElement.name + selectableContainer.setOnClickListener { + setBackground(onClick(position)) + } + selectableContainer.setOnLongClickListener { + setBackground(onLongClick(position)) + true + } + } + } + + open class RegularElementViewHolder( + itemView: View, + private val dateFormat: DateFormat, + onClick: (Int) -> Boolean, + onLongClick: (Int) -> Boolean, + ) : ExplorerElementViewHolder(itemView, onClick, onLongClick) { + open fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) { + super.bind(explorerElement, position) + textElementSize.text = PathUtils.formatSize(explorerElement.size) + textElementMtime.text = dateFormat.format(explorerElement.mTime) + setBackground(isSelected) + } + } + + class FileViewHolder( + itemView: View, + dateFormat: DateFormat, + onClick: (Int) -> Boolean, + onLongClick: (Int) -> Boolean, + ) : RegularElementViewHolder(itemView, dateFormat, onClick, onLongClick) { + override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) { + super.bind(explorerElement, position, isSelected) + icon.setImageResource(getAssociatedDrawable(explorerElement.name)) + } + } + class DirectoryViewHolder( + itemView: View, + dateFormat: DateFormat, + onClick: (Int) -> Boolean, + onLongClick: (Int) -> Boolean, + ) : RegularElementViewHolder(itemView, dateFormat, onClick, onLongClick) { + override fun bind(explorerElement: ExplorerElement, position: Int, isSelected: Boolean) { + super.bind(explorerElement, position, isSelected) + icon.setImageResource(R.drawable.icon_folder) + } + } + class ParentFolderViewHolder( + itemView: View, + onClick: (Int) -> Boolean, + onLongClick: (Int) -> Boolean, + ): ExplorerElementViewHolder(itemView, onClick, onLongClick) { + 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) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val view = inflater.inflate(R.layout.adapter_explorer_element, parent, false) + return when (viewType) { + ExplorerElement.REGULAR_FILE_TYPE -> FileViewHolder(view, dateFormat, ::onItemClick, ::onItemLongClick) + ExplorerElement.DIRECTORY_TYPE -> DirectoryViewHolder(view, dateFormat, ::onItemClick, ::onItemLongClick) + ExplorerElement.PARENT_FOLDER_TYPE -> ParentFolderViewHolder(view, ::onItemClick, ::onItemLongClick) + 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() } } \ No newline at end of file diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt index c06cc7a..ab0a05e 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt @@ -11,16 +11,15 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.WindowManager -import android.widget.AdapterView.OnItemClickListener -import android.widget.AdapterView.OnItemLongClickListener import android.widget.EditText -import android.widget.ListView import android.widget.TextView import android.widget.Toast import androidx.core.content.ContextCompat import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import sushi.hardcore.droidfs.BaseActivity import sushi.hardcore.droidfs.ConstValues @@ -67,7 +66,7 @@ open class BaseExplorerActivity : BaseActivity() { protected var usf_keep_open = false private lateinit var toolbar: androidx.appcompat.widget.Toolbar private lateinit var titleText: TextView - private lateinit var listExplorer: ListView + private lateinit var recycler_view_explorer: RecyclerView private lateinit var refresher: SwipeRefreshLayout private lateinit var textDirEmpty: TextView private lateinit var currentPathText: TextView @@ -88,7 +87,7 @@ open class BaseExplorerActivity : BaseActivity() { init() toolbar = findViewById(R.id.toolbar) titleText = findViewById(R.id.title_text) - listExplorer = findViewById(R.id.list_explorer) + recycler_view_explorer = findViewById(R.id.recycler_view_explorer) refresher = findViewById(R.id.refresher) textDirEmpty = findViewById(R.id.text_dir_empty) currentPathText = findViewById(R.id.current_path_text) @@ -96,14 +95,13 @@ open class BaseExplorerActivity : BaseActivity() { setSupportActionBar(toolbar) title = "" titleText.text = getString(R.string.volume, volumeName) - explorerAdapter = ExplorerElementAdapter(this) + explorerAdapter = ExplorerElementAdapter(this, ::onExplorerItemClick, ::onExplorerItemLongClick) explorerViewModel= ViewModelProvider(this).get(ExplorerViewModel::class.java) currentDirectoryPath = explorerViewModel.currentDirectoryPath setCurrentPath(currentDirectoryPath) - listExplorer.apply { + recycler_view_explorer.apply { adapter = explorerAdapter - onItemClickListener = OnItemClickListener { _, _, position, _ -> onExplorerItemClick(position) } - onItemLongClickListener = OnItemLongClickListener { _, _, position, _ -> onExplorerItemLongClick(position); true } + layoutManager = LinearLayoutManager(this@BaseExplorerActivity) } refresher.setOnRefreshListener { setCurrentPath(currentDirectoryPath) @@ -172,7 +170,6 @@ open class BaseExplorerActivity : BaseActivity() { protected open fun onExplorerItemClick(position: Int) { val wasSelecting = explorerAdapter.selectedItems.isNotEmpty() - explorerAdapter.onItemClick(position) if (explorerAdapter.selectedItems.isEmpty()) { if (!wasSelecting) { val fullPath = explorerElements[position].fullPath @@ -203,7 +200,6 @@ open class BaseExplorerActivity : BaseActivity() { } protected open fun onExplorerItemLongClick(position: Int) { - explorerAdapter.onItemLongClick(position) invalidateOptionsMenu() } @@ -262,7 +258,7 @@ open class BaseExplorerActivity : BaseActivity() { ) } } - explorerAdapter.setExplorerElements(explorerElements) + explorerAdapter.explorerElements = explorerElements } }.start() } diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt index ad065a8..1698f04 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt @@ -235,9 +235,8 @@ class ExplorerActivity : BaseExplorerActivity() { } override fun onExplorerItemLongClick(position: Int) { + super.onExplorerItemLongClick(position) cancelItemAction() - explorerAdapter.onItemLongClick(position) - invalidateOptionsMenu() } private fun createNewFile(fileName: String){ @@ -380,7 +379,7 @@ 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.getItem(explorerAdapter.selectedItems[0]).name)) + dialog.setMessage(getString(R.string.single_delete_confirm, explorerAdapter.explorerElements[explorerAdapter.selectedItems[0]].name)) } dialog.show() true @@ -441,7 +440,7 @@ class ExplorerActivity : BaseExplorerActivity() { private fun removeSelectedItems() { var failedItem: String? = null for (i in explorerAdapter.selectedItems) { - val element = explorerAdapter.getItem(i) + val element = explorerAdapter.explorerElements[i] val fullPath = PathUtils.pathJoin(currentDirectoryPath, element.name) if (element.isDirectory) { val result = gocryptfsVolume.recursiveRemoveDirectory(fullPath) diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt index 2fc19e8..2c89d1d 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt @@ -23,7 +23,6 @@ class ExplorerActivityPick : BaseExplorerActivity() { override fun onExplorerItemClick(position: Int) { val wasSelecting = explorerAdapter.selectedItems.isNotEmpty() - explorerAdapter.onItemClick(position) if (explorerAdapter.selectedItems.isEmpty()) { if (!wasSelecting) { val fullPath = PathUtils.pathJoin(currentDirectoryPath, explorerElements[position].name) diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt index 0a81d9d..5b928b9 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt @@ -8,15 +8,19 @@ class ExplorerElement(val name: String, val elementType: Short, var size: Long = val fullPath: String = PathUtils.pathJoin(parentPath, name) val isDirectory: Boolean - get() = elementType.toInt() == 0 + get() = elementType.toInt() == DIRECTORY_TYPE val isParentFolder: Boolean - get() = elementType.toInt() == -1 + get() = elementType.toInt() == PARENT_FOLDER_TYPE val isRegularFile: Boolean - get() = elementType.toInt() == 1 + get() = elementType.toInt() == REGULAR_FILE_TYPE companion object { + const val DIRECTORY_TYPE = 0 + const val PARENT_FOLDER_TYPE = -1 + const val REGULAR_FILE_TYPE = 1 + @JvmStatic //this function is needed because I had some problems calling the constructor from JNI, probably due to arguments with default values fun new(name: String, elementType: Short, size: Long, mTime: Long, parentPath: String): ExplorerElement { diff --git a/app/src/main/res/layout/activity_explorer.xml b/app/src/main/res/layout/activity_explorer.xml index 22fb317..d005fe9 100644 --- a/app/src/main/res/layout/activity_explorer.xml +++ b/app/src/main/res/layout/activity_explorer.xml @@ -14,27 +14,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - + - - - - - - - + diff --git a/app/src/main/res/layout/activity_explorer_drop.xml b/app/src/main/res/layout/activity_explorer_drop.xml index 53bb65c..119ae6e 100644 --- a/app/src/main/res/layout/activity_explorer_drop.xml +++ b/app/src/main/res/layout/activity_explorer_drop.xml @@ -12,27 +12,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - + - - + android:layout_height="match_parent" + android:background="?attr/selectableItemBackground"> - + + android:layout_marginTop="3dp" + android:orientation="vertical"> + android:textSize="@dimen/adapter_text_size"/> - + android:orientation="horizontal" + android:layout_gravity="bottom"> + + + + + + + - \ No newline at end of file diff --git a/app/src/main/res/layout/explorer_content.xml b/app/src/main/res/layout/explorer_content.xml new file mode 100644 index 0000000..4f256e5 --- /dev/null +++ b/app/src/main/res/layout/explorer_content.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 27d7e4a..a5d8b4e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -53,10 +53,6 @@ @drawable/button_background -