aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Henneke <FabianHenneke@users.noreply.github.com>2020-07-01 18:18:21 +0200
committerGitHub <noreply@github.com>2020-07-01 21:48:21 +0530
commit1c9f7971ce006d349c4cf8c1dee441c4188d5d58 (patch)
tree504434c900c5ec67a2cff5cb9be6a75ac61ab239
parentc5a93b8b8168eead37f3f93a8e8378211c1d240b (diff)
Scroll to files and enter folders when created (#909)
-rw-r--r--CHANGELOG.md1
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt26
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt17
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt5
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt17
6 files changed, 56 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f34dfa6..17de770b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
- TOTP support is reintroduced by popular demand. HOTP continues to be unsupported and heavily discouraged.
- Initial support for detecting and filling OTP fields with Autofill
- Importing TOTP secrets using QR codes
+- Navigate into newly created folders and scroll to newly created passwords
## [1.9.2] - 2020-06-30
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
index ca53b320..e91df9c6 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
@@ -43,6 +43,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
private var recyclerViewStateToRestore: Parcelable? = null
private var actionMode: ActionMode? = null
+ private var scrollTarget: File? = null
private val model: SearchableRepositoryViewModel by activityViewModels()
private val binding by viewBinding(PasswordRecyclerViewBinding::bind)
@@ -132,6 +133,11 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
// When the result is filtered, we always scroll to the top since that is where
// the best fuzzy match appears.
recyclerView.scrollToPosition(0)
+ } else if (scrollTarget != null) {
+ scrollTarget?.let {
+ recyclerView.scrollToPosition(recyclerAdapter.getPositionForFile(it))
+ }
+ scrollTarget == null
} else {
// When the result is not filtered and there is a saved scroll position for it,
// we try to restore it.
@@ -223,12 +229,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
listener = object : OnFragmentInteractionListener {
override fun onFragmentInteraction(item: PasswordItem) {
if (item.type == PasswordItem.TYPE_CATEGORY) {
- requireStore().clearSearch()
- model.navigateTo(
- item.file,
- recyclerViewState = binding.passRecycler.layoutManager!!.onSaveInstanceState()
- )
- requireStore().supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ navigateTo(item.file)
} else {
if (requireArguments().getBoolean("matchWith", false)) {
requireStore().matchPasswordWithApp(item)
@@ -272,6 +273,19 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
fun createPassword() = requireStore().createPassword()
+ fun navigateTo(file: File) {
+ requireStore().clearSearch()
+ model.navigateTo(
+ file,
+ recyclerViewState = binding.passRecycler.layoutManager!!.onSaveInstanceState()
+ )
+ requireStore().supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
+
+ fun scrollToOnNextRefresh(file: File) {
+ scrollTarget = file
+ }
+
interface OnFragmentInteractionListener {
fun onFragmentInteraction(item: PasswordItem)
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
index ee258b80..54fc7c97 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
@@ -67,6 +67,7 @@ import com.zeapo.pwdstore.utils.PasswordRepository.Companion.isInitialized
import com.zeapo.pwdstore.utils.PasswordRepository.PasswordSortOrder.Companion.getSortOrder
import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.commitChange
+import com.zeapo.pwdstore.utils.contains
import com.zeapo.pwdstore.utils.isInsideRepository
import com.zeapo.pwdstore.utils.listFilesRecursively
import com.zeapo.pwdstore.utils.requestInputFocusOnView
@@ -734,10 +735,17 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
/**
* Refreshes the password list by re-executing the last navigation or search action, preserving
* the navigation stack and scroll position. If the current directory no longer exists,
- * navigation is reset to the repository root.
+ * navigation is reset to the repository root. If the optional [target] argument is provided,
+ * it will be entered if it is a directory or scrolled into view if it is a file (both inside
+ * the current directory).
*/
- fun refreshPasswordList() {
- if (model.currentDir.value?.isDirectory == true) {
+ fun refreshPasswordList(target: File? = null) {
+ if (target?.isDirectory == true && model.currentDir.value?.contains(target) == true) {
+ plist?.navigateTo(target)
+ } else if (target?.isFile == true && model.currentDir.value?.contains(target) == true) {
+ // Creating new passwords is handled by an activity, so we will refresh in onStart.
+ plist?.scrollToOnNextRefresh(target)
+ } else if (model.currentDir.value?.isDirectory == true) {
model.forceRefresh()
} else {
model.reset()
@@ -764,7 +772,8 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}
REQUEST_CODE_ENCRYPT -> {
commitChange(resources.getString(R.string.git_commit_add_text,
- data!!.extras!!.getString("LONG_NAME")))
+ data!!.extras!!.getString(PasswordCreationActivity.RETURN_EXTRA_LONG_NAME)))
+ refreshPasswordList(File(data.extras!!.getString(PasswordCreationActivity.RETURN_EXTRA_CREATED_FILE)!!))
}
BaseGitActivity.REQUEST_INIT, NEW_REPO_BUTTON -> initializeRepositoryInfo()
BaseGitActivity.REQUEST_SYNC, BaseGitActivity.REQUEST_PULL -> refreshPasswordList()
diff --git a/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt b/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt
index 860676e4..19ba866e 100644
--- a/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt
@@ -447,6 +447,8 @@ open class SearchableRepositoryAdapter<T : RecyclerView.ViewHolder>(
return selectedFiles.map { it.toPasswordItem(root) }
}
+ fun getPositionForFile(file: File) = itemKeyProvider.getPosition(file.absolutePath)
+
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): T {
val view = LayoutInflater.from(parent.context)
.inflate(layoutRes, parent, false)
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 2f268c56..3af84039 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
@@ -36,8 +36,9 @@ class FolderCreationDialogFragment : DialogFragment() {
val dialog = requireDialog()
val materialTextView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text)
val folderName = materialTextView.text.toString()
- File("$currentDir/$folderName").mkdir()
- (requireActivity() as PasswordStore).refreshPasswordList()
+ val newFolder = File("$currentDir/$folderName")
+ newFolder.mkdir()
+ (requireActivity() as PasswordStore).refreshPasswordList(newFolder)
dismiss()
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
index dcede7da..6ba03743 100644
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
@@ -59,6 +59,23 @@ fun Activity.snackbar(
fun File.listFilesRecursively() = walkTopDown().filter { !it.isDirectory }.toList()
+/**
+ * Checks whether this [File] is a directory that contains [other].
+ */
+fun File.contains(other: File): Boolean {
+ if (!isDirectory)
+ return false
+ if (!other.exists())
+ return false
+ val relativePath = try {
+ other.relativeTo(this)
+ } catch (e: Exception) {
+ return false
+ }
+ // Direct containment is equivalent to the relative path being equal to the filename.
+ return relativePath.path == other.name
+}
+
fun Context.resolveAttribute(attr: Int): Int {
val typedValue = TypedValue()
this.theme.resolveAttribute(attr, typedValue, true)