aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/com/zeapo/pwdstore/ui
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/ui')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/OnOffItemAnimator.kt71
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/adapters/EntryRecyclerAdapter.kt148
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/adapters/FolderRecyclerAdapter.kt23
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt88
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordRecyclerAdapter.kt120
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt2
6 files changed, 160 insertions, 292 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/OnOffItemAnimator.kt b/app/src/main/java/com/zeapo/pwdstore/ui/OnOffItemAnimator.kt
new file mode 100644
index 00000000..e394e656
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/OnOffItemAnimator.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.ui
+
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.RecyclerView
+
+class OnOffItemAnimator : DefaultItemAnimator() {
+
+ var isEnabled: Boolean = true
+ set(value) {
+ // Defer update until no animation is running anymore.
+ isRunning { field = value }
+ }
+
+ private fun dontAnimate(viewHolder: RecyclerView.ViewHolder): Boolean {
+ dispatchAnimationFinished(viewHolder)
+ return false
+ }
+
+ override fun animateAppearance(
+ viewHolder: RecyclerView.ViewHolder,
+ preLayoutInfo: ItemHolderInfo?,
+ postLayoutInfo: ItemHolderInfo
+ ): Boolean {
+ return if (isEnabled) {
+ super.animateAppearance(viewHolder, preLayoutInfo, postLayoutInfo)
+ } else {
+ dontAnimate(viewHolder)
+ }
+ }
+
+ override fun animateChange(
+ oldHolder: RecyclerView.ViewHolder,
+ newHolder: RecyclerView.ViewHolder,
+ preInfo: ItemHolderInfo,
+ postInfo: ItemHolderInfo
+ ): Boolean {
+ return if (isEnabled) {
+ super.animateChange(oldHolder, newHolder, preInfo, postInfo)
+ } else {
+ dontAnimate(oldHolder)
+ }
+ }
+
+ override fun animateDisappearance(
+ viewHolder: RecyclerView.ViewHolder,
+ preLayoutInfo: ItemHolderInfo,
+ postLayoutInfo: ItemHolderInfo?
+ ): Boolean {
+ return if (isEnabled) {
+ super.animateDisappearance(viewHolder, preLayoutInfo, postLayoutInfo)
+ } else {
+ dontAnimate(viewHolder)
+ }
+ }
+
+ override fun animatePersistence(
+ viewHolder: RecyclerView.ViewHolder,
+ preInfo: ItemHolderInfo,
+ postInfo: ItemHolderInfo
+ ): Boolean {
+ return if (isEnabled) {
+ super.animatePersistence(viewHolder, preInfo, postInfo)
+ } else {
+ dontAnimate(viewHolder)
+ }
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/EntryRecyclerAdapter.kt b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/EntryRecyclerAdapter.kt
deleted file mode 100644
index 9cf477e2..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/EntryRecyclerAdapter.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.ui.adapters
-
-import android.content.SharedPreferences
-import android.text.SpannableString
-import android.text.style.RelativeSizeSpan
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.widget.AppCompatImageView
-import androidx.appcompat.widget.AppCompatTextView
-import androidx.preference.PreferenceManager
-import androidx.recyclerview.widget.RecyclerView
-import com.zeapo.pwdstore.R
-import com.zeapo.pwdstore.utils.PasswordItem
-import com.zeapo.pwdstore.widget.MultiselectableConstraintLayout
-import java.io.File
-import java.util.ArrayList
-import java.util.Locale
-import java.util.TreeSet
-import me.zhanghai.android.fastscroll.PopupTextProvider
-
-abstract class EntryRecyclerAdapter internal constructor(val values: ArrayList<PasswordItem>) : RecyclerView.Adapter<EntryRecyclerAdapter.ViewHolder>(), PopupTextProvider {
- internal val selectedItems: MutableSet<Int> = TreeSet()
- internal var settings: SharedPreferences? = null
-
- // Return the size of your dataset (invoked by the layout manager)
- override fun getItemCount(): Int {
- return values.size
- }
-
- override fun getPopupText(position: Int): String {
- return values[position].name[0].toString().toUpperCase(Locale.getDefault())
- }
-
- fun clear() {
- this.values.clear()
- this.notifyDataSetChanged()
- }
-
- fun addAll(list: ArrayList<PasswordItem>) {
- this.values.addAll(list)
- this.notifyDataSetChanged()
- }
-
- fun add(item: PasswordItem) {
- this.values.add(item)
- this.notifyItemInserted(itemCount)
- }
-
- internal fun toggleSelection(position: Int) {
- if (!selectedItems.remove(position)) {
- selectedItems.add(position)
- }
- }
-
- // use this after an item is removed to update the positions of items in set
- // that followed the removed position
- fun updateSelectedItems(position: Int, selectedItems: MutableSet<Int>) {
- val temp = TreeSet<Int>()
- for (selected in selectedItems) {
- if (selected > position) {
- temp.add(selected - 1)
- } else {
- temp.add(selected)
- }
- }
- selectedItems.clear()
- selectedItems.addAll(temp)
- }
-
- fun remove(position: Int) {
- this.values.removeAt(position)
- this.notifyItemRemoved(position)
-
- // keep selectedItems updated so we know what to notifyItemChanged
- // (instead of just using notifyDataSetChanged)
- updateSelectedItems(position, selectedItems)
- }
-
- internal open fun getOnLongClickListener(holder: ViewHolder, pass: PasswordItem): View.OnLongClickListener {
- return View.OnLongClickListener { false }
- }
-
- // Replace the contents of a view (invoked by the layout manager)
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- settings = settings
- ?: PreferenceManager.getDefaultSharedPreferences(holder.view.context.applicationContext)
- val pass = values[position]
- val showHidden = settings?.getBoolean("show_hidden_folders", false) ?: false
- holder.name.text = pass.toString()
- if (pass.type == PasswordItem.TYPE_CATEGORY) {
- holder.typeImage.setImageResource(R.drawable.ic_multiple_files_24dp)
- holder.folderIndicator.visibility = View.VISIBLE
- val children = pass.file.listFiles { pathname ->
- !(!showHidden && (pathname.isDirectory && pathname.isHidden))
- } ?: emptyArray<File>()
- val childCount = children.size
- holder.childCount.visibility = if (childCount > 0) View.VISIBLE else View.GONE
- holder.childCount.text = "$childCount"
- } else {
- holder.typeImage.setImageResource(R.drawable.ic_action_secure_24dp)
- val parentPath = pass.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
- val source = "$parentPath\n$pass"
- val spannable = SpannableString(source)
- spannable.setSpan(RelativeSizeSpan(0.7f), 0, parentPath.length, 0)
- holder.name.text = spannable
- holder.childCount.visibility = View.GONE
- holder.folderIndicator.visibility = View.GONE
- }
- holder.view.setOnClickListener(getOnClickListener(holder, pass))
- holder.view.setOnLongClickListener(getOnLongClickListener(holder, pass))
-
- // after removal, everything is rebound for some reason; views are shuffled?
- val selected = selectedItems.contains(position)
- holder.view.isSelected = selected
- (holder.itemView as MultiselectableConstraintLayout).setMultiSelected(selected)
- }
-
- protected abstract fun getOnClickListener(holder: ViewHolder, pass: PasswordItem): View.OnClickListener
-
- // Create new views (invoked by the layout manager)
- override fun onCreateViewHolder(
- parent: ViewGroup,
- viewType: Int
- ): ViewHolder {
- // create a new view
- val v = LayoutInflater.from(parent.context)
- .inflate(R.layout.password_row_layout, parent, false)
- return ViewHolder(v)
- }
-
- /*
- Provide a reference to the views for each data item
- Complex data items may need more than one view per item, and
- you provide access to all the views for a data item in a view holder
- each data item is just a string in this case
- */
- class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
- val name: AppCompatTextView = view.findViewById(R.id.label)
- val typeImage: AppCompatImageView = view.findViewById(R.id.type_image)
- val childCount: AppCompatTextView = view.findViewById(R.id.child_count)
- val folderIndicator: AppCompatImageView = view.findViewById(R.id.folder_indicator)
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/FolderRecyclerAdapter.kt b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/FolderRecyclerAdapter.kt
deleted file mode 100644
index 18aef509..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/FolderRecyclerAdapter.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.ui.adapters
-
-import android.view.View
-import com.zeapo.pwdstore.SelectFolderFragment
-import com.zeapo.pwdstore.utils.PasswordItem
-import java.util.ArrayList
-
-class FolderRecyclerAdapter(
- private val listener: SelectFolderFragment.OnFragmentInteractionListener,
- values: ArrayList<PasswordItem>
-) : EntryRecyclerAdapter(values) {
-
- override fun getOnClickListener(holder: ViewHolder, pass: PasswordItem): View.OnClickListener {
- return View.OnClickListener {
- listener.onFragmentInteraction(pass)
- notifyItemChanged(holder.adapterPosition)
- }
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt
new file mode 100644
index 00000000..9a914be9
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.ui.adapters
+
+import android.text.SpannableString
+import android.text.style.RelativeSizeSpan
+import android.view.MotionEvent
+import android.view.View
+import androidx.appcompat.widget.AppCompatImageView
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.preference.PreferenceManager
+import androidx.recyclerview.selection.ItemDetailsLookup
+import androidx.recyclerview.selection.Selection
+import androidx.recyclerview.widget.RecyclerView
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.SearchableRepositoryAdapter
+import com.zeapo.pwdstore.stableId
+import com.zeapo.pwdstore.utils.PasswordItem
+import java.io.File
+
+open class PasswordItemRecyclerAdapter :
+ SearchableRepositoryAdapter<PasswordItemRecyclerAdapter.PasswordItemViewHolder>(
+ R.layout.password_row_layout,
+ ::PasswordItemViewHolder,
+ PasswordItemViewHolder::bind
+ ) {
+
+ fun makeSelectable(recyclerView: RecyclerView) {
+ makeSelectable(recyclerView, ::PasswordItemDetailsLookup)
+ }
+
+ override fun onItemClicked(listener: (holder: PasswordItemViewHolder, item: PasswordItem) -> Unit): PasswordItemRecyclerAdapter {
+ return super.onItemClicked(listener) as PasswordItemRecyclerAdapter
+ }
+
+ override fun onSelectionChanged(listener: (selection: Selection<String>) -> Unit): PasswordItemRecyclerAdapter {
+ return super.onSelectionChanged(listener) as PasswordItemRecyclerAdapter
+ }
+
+ class PasswordItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ private val name: AppCompatTextView = itemView.findViewById(R.id.label)
+ private val typeImage: AppCompatImageView = itemView.findViewById(R.id.type_image)
+ private val childCount: AppCompatTextView = itemView.findViewById(R.id.child_count)
+ private val folderIndicator: AppCompatImageView =
+ itemView.findViewById(R.id.folder_indicator)
+ lateinit var itemDetails: ItemDetailsLookup.ItemDetails<String>
+
+ fun bind(item: PasswordItem) {
+ val settings =
+ PreferenceManager.getDefaultSharedPreferences(itemView.context.applicationContext)
+ val showHidden = settings.getBoolean("show_hidden_folders", false)
+ name.text = item.toString()
+ if (item.type == PasswordItem.TYPE_CATEGORY) {
+ typeImage.setImageResource(R.drawable.ic_multiple_files_24dp)
+ folderIndicator.visibility = View.VISIBLE
+ val children = item.file.listFiles { pathname ->
+ !(!showHidden && (pathname.isDirectory && pathname.isHidden))
+ } ?: emptyArray<File>()
+ val count = children.size
+ childCount.visibility = if (count > 0) View.VISIBLE else View.GONE
+ childCount.text = "$count"
+ } else {
+ typeImage.setImageResource(R.drawable.ic_action_secure_24dp)
+ val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
+ val source = "$parentPath\n$item"
+ val spannable = SpannableString(source)
+ spannable.setSpan(RelativeSizeSpan(0.7f), 0, parentPath.length, 0)
+ name.text = spannable
+ childCount.visibility = View.GONE
+ folderIndicator.visibility = View.GONE
+ }
+ itemDetails = object : ItemDetailsLookup.ItemDetails<String>() {
+ override fun getPosition() = absoluteAdapterPosition
+ override fun getSelectionKey() = item.stableId
+ }
+ }
+ }
+
+ class PasswordItemDetailsLookup(private val recyclerView: RecyclerView) :
+ ItemDetailsLookup<String>() {
+ override fun getItemDetails(event: MotionEvent): ItemDetails<String>? {
+ val view = recyclerView.findChildViewUnder(event.x, event.y) ?: return null
+ return (recyclerView.getChildViewHolder(view) as PasswordItemViewHolder).itemDetails
+ }
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordRecyclerAdapter.kt b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordRecyclerAdapter.kt
deleted file mode 100644
index 39d73563..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordRecyclerAdapter.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.ui.adapters
-
-import android.view.Menu
-import android.view.MenuItem
-import android.view.View
-import androidx.appcompat.view.ActionMode
-import com.zeapo.pwdstore.PasswordFragment
-import com.zeapo.pwdstore.PasswordStore
-import com.zeapo.pwdstore.R
-import com.zeapo.pwdstore.utils.PasswordItem
-import java.util.ArrayList
-import java.util.TreeSet
-
-class PasswordRecyclerAdapter(
- private val activity: PasswordStore,
- private val listener: PasswordFragment.OnFragmentInteractionListener,
- values: ArrayList<PasswordItem>
-) : EntryRecyclerAdapter(values) {
- var actionMode: ActionMode? = null
- private var canEdit: Boolean = false
- private val actionModeCallback = object : ActionMode.Callback {
-
- // Called when the action mode is created; startActionMode() was called
- override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
- // Inflate a menu resource providing context menu items
- mode.menuInflater.inflate(R.menu.context_pass, menu)
- // hide the fab
- activity.findViewById<View>(R.id.fab).visibility = View.GONE
- return true
- }
-
- // Called each time the action mode is shown. Always called after onCreateActionMode, but
- // may be called multiple times if the mode is invalidated.
- override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
- menu.findItem(R.id.menu_edit_password).isVisible = canEdit
- return true // Return false if nothing is done
- }
-
- // Called when the user selects a contextual menu item
- override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
- when (item.itemId) {
- R.id.menu_delete_password -> {
- activity.deletePasswords(this@PasswordRecyclerAdapter, TreeSet(selectedItems))
- mode.finish() // Action picked, so close the CAB
- return true
- }
- R.id.menu_edit_password -> {
- activity.editPassword(values[selectedItems.iterator().next()])
- mode.finish()
- return true
- }
- R.id.menu_move_password -> {
- val selectedPasswords = ArrayList<PasswordItem>()
- for (id in selectedItems) {
- selectedPasswords.add(values[id])
- }
- activity.movePasswords(selectedPasswords)
- return false
- }
- else -> return false
- }
- }
-
- // Called when the user exits the action mode
- override fun onDestroyActionMode(mode: ActionMode) {
- val it = selectedItems.iterator()
- while (it.hasNext()) {
- // need the setSelected line in onBind
- notifyItemChanged(it.next())
- it.remove()
- }
- actionMode = null
- // show the fab
- activity.findViewById<View>(R.id.fab).visibility = View.VISIBLE
- }
- }
-
- override fun getOnLongClickListener(holder: ViewHolder, pass: PasswordItem): View.OnLongClickListener {
- return View.OnLongClickListener {
- if (actionMode != null) {
- return@OnLongClickListener false
- }
- toggleSelection(holder.adapterPosition)
- canEdit = pass.type == PasswordItem.TYPE_PASSWORD
- // Start the CAB using the ActionMode.Callback
- actionMode = activity.startSupportActionMode(actionModeCallback)
- actionMode?.title = "" + selectedItems.size
- actionMode?.invalidate()
- notifyItemChanged(holder.adapterPosition)
- true
- }
- }
-
- override fun getOnClickListener(holder: ViewHolder, pass: PasswordItem): View.OnClickListener {
- return View.OnClickListener {
- if (actionMode != null) {
- toggleSelection(holder.adapterPosition)
- actionMode?.title = "" + selectedItems.size
- if (selectedItems.isEmpty()) {
- actionMode?.finish()
- } else if (selectedItems.size == 1 && (canEdit.not())) {
- if (values[selectedItems.iterator().next()].type == PasswordItem.TYPE_PASSWORD) {
- canEdit = true
- actionMode?.invalidate()
- }
- } else if (selectedItems.size >= 1 && canEdit) {
- canEdit = false
- actionMode?.invalidate()
- }
- } else {
- listener.onFragmentInteraction(pass)
- }
- notifyItemChanged(holder.adapterPosition)
- }
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt
index bff229b9..1426ef1e 100644
--- a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt
@@ -48,7 +48,7 @@ class FolderCreationDialogFragment : DialogFragment() {
val materialTextView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text)
val folderName = materialTextView.text.toString()
File("$currentDir/$folderName").mkdir()
- (requireActivity() as PasswordStore).updateListAdapter()
+ (requireActivity() as PasswordStore).refreshPasswordList()
dismiss()
}