summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt8
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt74
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt2
3 files changed, 79 insertions, 5 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
index f4c1685e..a47e6e70 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
@@ -154,6 +154,9 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
// 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 =
+ recyclerAdapter.getSelectedItems(requireContext())
+ .all { it.type == PasswordItem.TYPE_CATEGORY }
return true
}
@@ -170,6 +173,11 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
requireStore().movePasswords(recyclerAdapter.getSelectedItems(requireContext()))
false
}
+ R.id.menu_edit_password -> {
+ requireStore().renameCategory(recyclerAdapter.getSelectedItems(requireContext()))
+ mode.finish()
+ false
+ }
else -> false
}
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
index 0e03804f..b0f6fd44 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
@@ -43,6 +43,7 @@ import com.github.ajalt.timberkt.i
import com.github.ajalt.timberkt.w
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
+import com.google.android.material.textfield.TextInputEditText
import com.zeapo.pwdstore.autofill.oreo.AutofillMatcher
import com.zeapo.pwdstore.autofill.oreo.BrowserAutofillSupportLevel
import com.zeapo.pwdstore.autofill.oreo.getInstalledBrowsersWithAutofillSupportLevel
@@ -66,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.commitChange
import com.zeapo.pwdstore.utils.listFilesRecursively
+import com.zeapo.pwdstore.utils.requestInputFocusOnView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -628,7 +630,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
)
.setPositiveButton(R.string.dialog_ok) { _, _ ->
launch(Dispatchers.IO) {
- movePassword(source, destinationFile)
+ moveFile(source, destinationFile)
}
}
.setNegativeButton(R.string.dialog_cancel, null)
@@ -636,7 +638,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}
} else {
launch(Dispatchers.IO) {
- movePassword(source, destinationFile)
+ moveFile(source, destinationFile)
}
}
}
@@ -664,6 +666,69 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}.launch(intent)
}
+ private fun isInsideRepository(file: File): Boolean {
+ return file.canonicalPath.contains(getRepositoryDirectory(this).canonicalPath)
+ }
+
+ enum class CategoryRenameError(val resource: Int) {
+ None(0),
+ EmptyField(R.string.message_category_error_empty_field),
+ CategoryExists(R.string.message_category_error_category_exists),
+ DestinationOutsideRepo(R.string.message_category_error_destination_outside_repo),
+ }
+
+ /**
+ * Prompt the user with a new category name to assign,
+ * if the new category forms/leads a path (i.e. contains "/"), intermediate directories will be created
+ * and new category will be placed inside.
+ *
+ * @param oldCategory The category to change its name
+ * @param error Determines whether to show an error to the user in the alert dialog,
+ * this error may be due to the new category the user entered already exists or the field was empty or the
+ * destination path is outside the repository
+ *
+ * @see [CategoryRenameError]
+ * @see [isInsideRepository]
+ */
+ private fun renameCategory(oldCategory: PasswordItem, error: CategoryRenameError = CategoryRenameError.None) {
+ val view = layoutInflater.inflate(R.layout.folder_dialog_fragment, null)
+ val newCategoryEditText = view.findViewById<TextInputEditText>(R.id.folder_name_text)
+
+ if (error != CategoryRenameError.None) {
+ newCategoryEditText.error = getString(error.resource)
+ }
+
+ val dialog = MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.title_rename_folder)
+ .setView(view)
+ .setMessage(getString(R.string.message_rename_folder, oldCategory.name))
+ .setPositiveButton(R.string.dialog_ok) { _, _ ->
+ val newCategory = File("${oldCategory.file.parent}/${newCategoryEditText.text}")
+ when {
+ newCategoryEditText.text.isNullOrBlank() -> renameCategory(oldCategory, CategoryRenameError.EmptyField)
+ newCategory.exists() -> renameCategory(oldCategory, CategoryRenameError.CategoryExists)
+ !isInsideRepository(newCategory) -> renameCategory(oldCategory, CategoryRenameError.DestinationOutsideRepo)
+ else -> lifecycleScope.launch(Dispatchers.IO) {
+ moveFile(oldCategory.file, newCategory)
+ withContext(Dispatchers.Main) {
+ commitChange(resources.getString(R.string.git_commit_move_text, oldCategory.name, newCategory.name))
+ }
+ }
+ }
+ }
+ .setNegativeButton(R.string.dialog_skip, null)
+ .create()
+
+ dialog.requestInputFocusOnView<TextInputEditText>(R.id.folder_name_text)
+ dialog.show()
+ }
+
+ fun renameCategory(categories: List<PasswordItem>) {
+ for (oldCategory in categories) {
+ renameCategory(oldCategory)
+ }
+ }
+
/**
* Resets navigation to the repository root and refreshes the password list accordingly.
*
@@ -736,8 +801,9 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
super.onActivityResult(requestCode, resultCode, data)
}
- private suspend fun movePassword(source: File, destinationFile: File) {
+ private suspend fun moveFile(source: File, destinationFile: File) {
val sourceDestinationMap = if (source.isDirectory) {
+ destinationFile.mkdirs()
// Recursively list all files (not directories) below `source`, then
// obtain the corresponding target file by resolving the relative path
// starting at the destination folder.
@@ -746,7 +812,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
mapOf(source to destinationFile)
}
if (!source.renameTo(destinationFile)) {
- e { "Something went wrong while moving." }
+ e { "Something went wrong while moving $source to $destinationFile." }
withContext(Dispatchers.Main) {
MaterialAlertDialogBuilder(this@PasswordStore)
.setTitle(R.string.password_move_error_title)
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 a5ff0bc1..2f268c56 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
@@ -20,7 +20,7 @@ class FolderCreationDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val alertDialogBuilder = MaterialAlertDialogBuilder(requireContext())
alertDialogBuilder.setTitle(R.string.title_create_folder)
- alertDialogBuilder.setView(R.layout.folder_creation_dialog_fragment)
+ alertDialogBuilder.setView(R.layout.folder_dialog_fragment)
alertDialogBuilder.setPositiveButton(getString(R.string.button_create)) { _, _ ->
createDirectory(requireArguments().getString(CURRENT_DIR_EXTRA)!!)
}