Replicate key selection flow for directory creation (#999)

* Replicate key selection flow

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>

* Review fixes

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>

* Set --user 0 in adb options to prevent automatically installing to work profile

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>

* Fix committing regression

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>

* Update changelog

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2020-08-10 19:19:01 +05:30 committed by GitHub
parent e0350043d0
commit 5715b59ed4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 22 deletions

View file

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Allow changing the branch used for Git operations - Allow changing the branch used for Git operations
- Allow setting a subdirectory key when creating folders
### Changed ### Changed

View file

@ -24,6 +24,8 @@ android {
} }
} }
adbOptions.installOptions("--user 0")
buildFeatures.viewBinding = true buildFeatures.viewBinding = true
defaultConfig { defaultConfig {

View file

@ -26,7 +26,6 @@ import org.eclipse.jgit.api.CommitCommand
import org.eclipse.jgit.api.PullCommand import org.eclipse.jgit.api.PullCommand
import org.eclipse.jgit.api.PushCommand import org.eclipse.jgit.api.PushCommand
import org.eclipse.jgit.api.RebaseResult import org.eclipse.jgit.api.RebaseResult
import org.eclipse.jgit.api.StatusCommand
import org.eclipse.jgit.transport.RemoteRefUpdate import org.eclipse.jgit.transport.RemoteRefUpdate
import org.eclipse.jgit.transport.SshSessionFactory import org.eclipse.jgit.transport.SshSessionFactory
@ -43,22 +42,14 @@ class GitCommandExecutor(
message = activity.resources.getString(R.string.git_operation_running), message = activity.resources.getString(R.string.git_operation_running),
length = Snackbar.LENGTH_INDEFINITE, length = Snackbar.LENGTH_INDEFINITE,
) )
var nbChanges = 0
var operationResult: Result = Result.Ok var operationResult: Result = Result.Ok
for (command in operation.commands) { for (command in operation.commands) {
try { try {
when (command) { when (command) {
is StatusCommand -> {
// in case we have changes, we want to keep track of it
val status = withContext(Dispatchers.IO) {
command.call()
}
nbChanges = status.changed.size + status.missing.size
}
is CommitCommand -> { is CommitCommand -> {
// the previous status will eventually be used to avoid a commit // the previous status will eventually be used to avoid a commit
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (nbChanges > 0) command.call() command.call()
} }
} }
is PullCommand -> { is PullCommand -> {

View file

@ -18,7 +18,6 @@ class SyncOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOper
override val commands = arrayOf( override val commands = arrayOf(
git.add().addFilepattern("."), git.add().addFilepattern("."),
git.status(),
git.commit().setAll(true).setMessage("[Android Password Store] Sync"), git.commit().setAll(true).setMessage("[Android Password Store] Sync"),
git.pull().setRebase(true).setRemote("origin"), git.pull().setRebase(true).setRemote("origin"),
git.push().setPushAll().setRemote("origin"), git.push().setPushAll().setRemote("origin"),

View file

@ -5,15 +5,29 @@
package com.zeapo.pwdstore.ui.dialogs package com.zeapo.pwdstore.ui.dialogs
import android.app.Dialog import android.app.Dialog
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.checkbox.MaterialCheckBox
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.zeapo.pwdstore.PasswordStore import com.zeapo.pwdstore.PasswordStore
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.crypto.BasePgpActivity
import com.zeapo.pwdstore.crypto.GetKeyIdsActivity
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory
import com.zeapo.pwdstore.utils.commitChange
import com.zeapo.pwdstore.utils.requestInputFocusOnView import com.zeapo.pwdstore.utils.requestInputFocusOnView
import java.io.File import java.io.File
import kotlinx.coroutines.launch
import me.msfjarvis.openpgpktx.util.OpenPgpApi
class FolderCreationDialogFragment : DialogFragment() { class FolderCreationDialogFragment : DialogFragment() {
@ -21,26 +35,61 @@ class FolderCreationDialogFragment : DialogFragment() {
val alertDialogBuilder = MaterialAlertDialogBuilder(requireContext()) val alertDialogBuilder = MaterialAlertDialogBuilder(requireContext())
alertDialogBuilder.setTitle(R.string.title_create_folder) alertDialogBuilder.setTitle(R.string.title_create_folder)
alertDialogBuilder.setView(R.layout.folder_dialog_fragment) alertDialogBuilder.setView(R.layout.folder_dialog_fragment)
alertDialogBuilder.setPositiveButton(getString(R.string.button_create)) { _, _ -> alertDialogBuilder.setPositiveButton(getString(R.string.button_create), null)
createDirectory(requireArguments().getString(CURRENT_DIR_EXTRA)!!)
}
alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel)) { _, _ -> alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel)) { _, _ ->
dismiss() dismiss()
} }
val dialog = alertDialogBuilder.create() val dialog = alertDialogBuilder.create()
dialog.requestInputFocusOnView<TextInputEditText>(R.id.folder_name_text) dialog.requestInputFocusOnView<TextInputEditText>(R.id.folder_name_text)
dialog.setOnShowListener {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
createDirectory(requireArguments().getString(CURRENT_DIR_EXTRA)!!)
}
}
return dialog return dialog
} }
private fun createDirectory(currentDir: String) { private fun createDirectory(currentDir: String) {
val dialog = requireDialog() val dialog = requireDialog()
val materialTextView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text) val folderNameView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text)
val folderName = materialTextView.text.toString() val folderNameViewContainer = dialog.findViewById<TextInputLayout>(R.id.folder_name_container)
val newFolder = File("$currentDir/$folderName") val newFolder = File("$currentDir/${folderNameView.text}")
folderNameViewContainer.error = when {
newFolder.isFile -> getString(R.string.folder_creation_err_file_exists)
newFolder.isDirectory -> getString(R.string.folder_creation_err_folder_exists)
else -> null
}
if (folderNameViewContainer.error != null) return
newFolder.mkdirs() newFolder.mkdirs()
(requireActivity() as PasswordStore).refreshPasswordList(newFolder) (requireActivity() as PasswordStore).refreshPasswordList(newFolder)
if (dialog.findViewById<MaterialCheckBox>(R.id.set_gpg_key).isChecked) {
val gpgIdentifierFile = File(newFolder, ".gpg-id")
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == AppCompatActivity.RESULT_OK) {
result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds ->
gpgIdentifierFile.writeText(keyIds.joinToString("\n"))
val repo = PasswordRepository.getRepository(null)
if (repo != null) {
lifecycleScope.launch {
val repoPath = getRepositoryDirectory().absolutePath
requireActivity().commitChange(
getString(
R.string.git_commit_gpg_id,
BasePgpActivity.getLongName(gpgIdentifierFile.parentFile!!.absolutePath, repoPath, gpgIdentifierFile.name)
),
finishActivityOnEnd = false,
)
dismiss() dismiss()
} }
}
}
}
}.launch(Intent(requireContext(), GetKeyIdsActivity::class.java))
return
} else {
dismiss()
}
}
companion object { companion object {

View file

@ -116,7 +116,6 @@ suspend fun FragmentActivity.commitChange(
object : GitOperation(getRepositoryDirectory(), this@commitChange) { object : GitOperation(getRepositoryDirectory(), this@commitChange) {
override val commands = arrayOf( override val commands = arrayOf(
git.add().addFilepattern("."), git.add().addFilepattern("."),
git.status(),
git.commit().setAll(true).setMessage(message), git.commit().setAll(true).setMessage(message),
) )

View file

@ -3,17 +3,21 @@
~ SPDX-License-Identifier: GPL-3.0-only ~ SPDX-License-Identifier: GPL-3.0-only
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/folder_name_container"
style="@style/AppTheme.TextInputLayout" style="@style/AppTheme.TextInputLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/crypto_name_hint"> android:hint="@string/crypto_name_hint"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/folder_name_text" android:id="@+id/folder_name_text"
@ -22,4 +26,12 @@
android:inputType="textNoSuggestions|textVisiblePassword" /> android:inputType="textNoSuggestions|textVisiblePassword" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/set_gpg_key"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
android:text="@string/new_folder_set_gpg_key"
app:layout_constraintTop_toBottomOf="@id/folder_name_container" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -367,6 +367,7 @@
<string name="short_key_ids_unsupported">A key ID in .gpg-id is too short, please use either long key IDs (16 characters) or fingerprints (40 characters)</string> <string name="short_key_ids_unsupported">A key ID in .gpg-id is too short, please use either long key IDs (16 characters) or fingerprints (40 characters)</string>
<string name="invalid_filename_text">File name must not contain \'/\', set directory above</string> <string name="invalid_filename_text">File name must not contain \'/\', set directory above</string>
<string name="directory_hint">Directory</string> <string name="directory_hint">Directory</string>
<string name="new_folder_set_gpg_key">Set GPG key for directory</string>
<!-- GitException messages --> <!-- GitException messages -->
<string name="git_unknown_error">Unknown error</string> <string name="git_unknown_error">Unknown error</string>
@ -383,4 +384,8 @@
<string name="play_deeplink_template">https://play.google.com/store/apps/details?id=%1$s</string> <string name="play_deeplink_template">https://play.google.com/store/apps/details?id=%1$s</string>
<string name="openkeychain_not_installed_fdroid">F-Droid</string> <string name="openkeychain_not_installed_fdroid">F-Droid</string>
<string name="fdroid_deeplink_template">https://f-droid.org/en/packages/%1$s/</string> <string name="fdroid_deeplink_template">https://f-droid.org/en/packages/%1$s/</string>
<!-- GPG key selection in folder creation -->
<string name="folder_creation_err_file_exists">A file by that name already exists</string>
<string name="folder_creation_err_folder_exists">A folder by that name already exists</string>
</resources> </resources>