Migrate to ActivityResultContracts (#910)

* Move git directory selection to ActivityResultContracts

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

* global: replace all android.app.Activity references

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

* res: resolve ObsoleteSdkInt lint warning

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

* layout: silence some overdraw warnings

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

* PasswordFragment: address deprecation

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

* PasswordStore: start addressing deprecation warnings

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

* autofill: silence deprecation warnings for legacy implementation

I don't want to ever touch these files

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

* Reset scrollTarget after use

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

* Refresh password list after each swipe

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

* Convert if to when

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

* Migrate UserPreference to ActivityResultContracts

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

* Also validate result in git directory selection

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

* AutofillSaveActivity: Switch to ActivityResultContracts

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

* AutofillDecryptActivity: Switch to ActivityResultContracts

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

* AutofillFilterActivity: Switch to ActivityResultContracts

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

* Improve deletion flow

- Silently delete empty directory
- Always refresh password list upon completion

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

* Uniform naming for activity result handlers

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

Co-authored-by: Fabian Henneke <FabianHenneke@users.noreply.github.com>
This commit is contained in:
Harsh Shandilya 2020-07-03 12:54:06 +05:30 committed by GitHub
parent ff3d2fcce3
commit 25b4261574
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 352 additions and 362 deletions

View file

@ -14,9 +14,11 @@ import android.view.MenuItem
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.appcompat.view.ActionMode
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.observe
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
@ -47,17 +49,26 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
private val model: SearchableRepositoryViewModel by activityViewModels()
private val binding by viewBinding(PasswordRecyclerViewBinding::bind)
private val swipeResult = registerForActivityResult(StartActivityForResult()) {
binding.swipeRefresher.isRefreshing = false
requireStore().refreshPasswordList()
}
private fun requireStore() = requireActivity() as PasswordStore
val currentDir: File
get() = model.currentDir.value!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settings = PreferenceManager.getDefaultSharedPreferences(requireContext())
initializePasswordList()
binding.fab.setOnClickListener {
ItemCreationBottomSheet().apply {
setTargetFragment(this@PasswordFragment, 1000)
}.show(parentFragmentManager, "BOTTOM_SHEET")
ItemCreationBottomSheet().show(childFragmentManager, "BOTTOM_SHEET")
}
childFragmentManager.setFragmentResultListener(ITEM_CREATION_REQUEST_KEY, viewLifecycleOwner) { _, bundle ->
when (bundle.getString(ACTION_KEY)) {
ACTION_FOLDER -> requireStore().createFolder()
ACTION_PASSWORD -> requireStore().createPassword()
}
}
}
@ -73,7 +84,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
.setAction(R.string.clone_button) {
val intent = Intent(context, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
swipeResult.launch(intent)
}
.show()
binding.swipeRefresher.isRefreshing = false
@ -87,7 +98,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
}
val intent = Intent(context, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, operationId)
startActivityForResult(intent, operationId)
swipeResult.launch(intent)
}
}
@ -129,22 +140,26 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
// and not on folder navigations since the latter leads to too many removal animations.
(recyclerView.itemAnimator as OnOffItemAnimator).isEnabled = result.isFiltered
recyclerAdapter.submitList(result.passwordItems) {
if (result.isFiltered) {
// 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))
when {
result.isFiltered -> {
// When the result is filtered, we always scroll to the top since that is where
// the best fuzzy match appears.
recyclerView.scrollToPosition(0)
}
scrollTarget == null
} else {
// When the result is not filtered and there is a saved scroll position for it,
// we try to restore it.
recyclerViewStateToRestore?.let {
recyclerView.layoutManager!!.onRestoreInstanceState(it)
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.
recyclerViewStateToRestore?.let {
recyclerView.layoutManager!!.onRestoreInstanceState(it)
}
recyclerViewStateToRestore = null
}
recyclerViewStateToRestore = null
}
}
}
@ -244,9 +259,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
binding.swipeRefresher.isRefreshing = false
}
private fun requireStore() = requireActivity() as PasswordStore
/**
* Returns true if the back press was handled by the [Fragment].
@ -262,16 +275,17 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
return true
}
val currentDir: File
get() = model.currentDir.value!!
fun dismissActionMode() {
actionMode?.finish()
}
fun createFolder() = requireStore().createFolder()
companion object {
const val ITEM_CREATION_REQUEST_KEY = "creation_key"
const val ACTION_KEY = "action"
const val ACTION_FOLDER = "folder"
const val ACTION_PASSWORD = "password"
}
fun createPassword() = requireStore().createPassword()
fun navigateTo(file: File) {
requireStore().clearSearch()

View file

@ -6,7 +6,6 @@ package com.zeapo.pwdstore
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
@ -93,6 +92,46 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
ViewModelProvider.AndroidViewModelFactory(application)
}
private val cloneAction = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
}
}
private val listRefreshAction = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
refreshPasswordList()
}
}
private val repositoryInitAction = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
initializeRepositoryInfo()
}
}
private val directoryChangeAction = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) &&
settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) != null) {
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null)
val dir = externalRepoPath?.let { File(it) }
if (dir != null &&
dir.exists() &&
dir.isDirectory &&
dir.listFilesRecursively().isNotEmpty() &&
getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) {
closeRepository()
checkLocalRepository()
return@registerForActivityResult
}
}
val intent = Intent(activity, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
cloneAction.launch(intent)
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
// open search view on search key, or Ctr+F
if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) &&
@ -295,7 +334,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}
intent = Intent(this, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_PUSH)
startActivityForResult(intent, BaseGitActivity.REQUEST_PUSH)
startActivity(intent)
return true
}
R.id.git_pull -> {
@ -305,7 +344,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}
intent = Intent(this, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_PULL)
startActivityForResult(intent, BaseGitActivity.REQUEST_PULL)
listRefreshAction.launch(intent)
return true
}
R.id.git_sync -> {
@ -315,7 +354,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}
intent = Intent(this, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_SYNC)
startActivityForResult(intent, BaseGitActivity.REQUEST_SYNC)
listRefreshAction.launch(intent)
return true
}
R.id.refresh -> {
@ -383,7 +422,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
.setMessage(resources.getString(R.string.key_dialog_text))
.setPositiveButton(resources.getString(R.string.dialog_positive)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java)
startActivityForResult(intent, BaseGitActivity.REQUEST_INIT)
repositoryInitAction.launch(intent)
}
.setNegativeButton(resources.getString(R.string.dialog_negative), null)
.show()
@ -426,7 +465,11 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
if (repo == null) {
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
startActivityForResult(intent, HOME)
registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
checkLocalRepository()
}
}.launch(intent)
} else {
checkLocalRepository(getRepositoryDirectory(applicationContext))
}
@ -526,7 +569,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
shortcutManager!!.addDynamicShortcuts(listOf(shortcut))
}
}
startActivityForResult(decryptIntent, REQUEST_CODE_DECRYPT_AND_VERIFY)
startActivity(decryptIntent)
}
private fun validateState(): Boolean {
@ -558,7 +601,12 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
val intent = Intent(this, PasswordCreationActivity::class.java)
intent.putExtra("FILE_PATH", currentDir.absolutePath)
intent.putExtra("REPO_PATH", getRepositoryDirectory(applicationContext).absolutePath)
startActivityForResult(intent, REQUEST_CODE_ENCRYPT)
registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
commitChange(resources.getString(R.string.git_commit_add_text, result.data?.extras?.getString("LONG_NAME")))
refreshPasswordList()
}
}.launch(intent)
}
fun createFolder() {
@ -574,6 +622,11 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
else
size += it.file.listFilesRecursively().size
}
if (size == 0) {
selectedItems.map { item -> item.file.deleteRecursively() }
refreshPasswordList()
return
}
MaterialAlertDialogBuilder(this)
.setMessage(resources.getQuantityString(R.plurals.delete_dialog_text, size, size))
.setPositiveButton(resources.getString(R.string.dialog_yes)) { _, _ ->
@ -585,6 +638,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
filesToDelete.add(item.file)
}
selectedItems.map { item -> item.file.deleteRecursively() }
refreshPasswordList()
AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete)
commitChange(resources.getString(R.string.git_commit_remove_text,
selectedItems.joinToString(separator = ", ") { item ->
@ -756,61 +810,6 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
private val currentDir: File
get() = plist?.currentDir ?: getRepositoryDirectory(applicationContext)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
// if we get here with a RESULT_OK then it's probably OK :)
BaseGitActivity.REQUEST_CLONE -> settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
// if went from decrypt->edit and user saved changes, we need to commitChange
REQUEST_CODE_DECRYPT_AND_VERIFY -> {
if (data != null && data.getBooleanExtra("needCommit", false)) {
if (data.getStringExtra("OPERATION") == "EDIT") {
commitChange(resources.getString(R.string.git_commit_edit_text,
data.extras!!.getString("LONG_NAME")))
}
}
}
REQUEST_CODE_ENCRYPT -> {
commitChange(resources.getString(R.string.git_commit_add_text,
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()
HOME -> checkLocalRepository()
// duplicate code
CLONE_REPO_BUTTON -> {
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) &&
settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) != null) {
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null)
val dir = externalRepoPath?.let { File(it) }
if (dir != null &&
dir.exists() &&
dir.isDirectory &&
dir.listFilesRecursively().isNotEmpty() &&
getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) {
closeRepository()
checkLocalRepository()
return // if not empty, just show me the passwords!
}
}
val intent = Intent(activity, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
}
else -> {
d { "Unexpected request code: $requestCode" }
// FIXME: The sync operation returns with a requestCode of 65535 instead of the
// expected 105. It is completely unclear why, but the issue might be resolved
// by switching to ActivityResultContracts. For now, we run the post-sync code
// also when encountering an unexpected request code.
refreshPasswordList()
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private suspend fun moveFile(source: File, destinationFile: File) {
val sourceDestinationMap = if (source.isDirectory) {
destinationFile.mkdirs()
@ -848,7 +847,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
CLONE_REPO_BUTTON -> {
val intent = Intent(activity, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
cloneAction.launch(intent)
}
}
}
@ -858,7 +857,10 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
if (externalRepo == null) {
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation)
when (operation) {
NEW_REPO_BUTTON -> repositoryInitAction.launch(intent)
CLONE_REPO_BUTTON -> directoryChangeAction.launch(intent)
}
} else {
MaterialAlertDialogBuilder(activity)
.setTitle(resources.getString(R.string.directory_selected_title))
@ -869,14 +871,17 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
CLONE_REPO_BUTTON -> {
val intent = Intent(activity, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
cloneAction.launch(intent)
}
}
}
.setNegativeButton(resources.getString(R.string.change)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation)
when (operation) {
NEW_REPO_BUTTON -> repositoryInitAction.launch(intent)
CLONE_REPO_BUTTON -> directoryChangeAction.launch(intent)
}
}
.show()
}
@ -891,7 +896,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
.replace(".gpg", "")
val data = Intent()
data.putExtra("path", path)
setResult(Activity.RESULT_OK, data)
setResult(RESULT_OK, data)
finish()
}
@ -899,13 +904,9 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
get() = getSortOrder(settings)
companion object {
const val REQUEST_CODE_ENCRYPT = 9911
const val REQUEST_CODE_DECRYPT_AND_VERIFY = 9913
const val REQUEST_ARG_PATH = "PATH"
private val TAG = PasswordStore::class.java.name
const val CLONE_REPO_BUTTON = 401
const val NEW_REPO_BUTTON = 402
private const val HOME = 403
private const val REQUEST_EXTERNAL_STORAGE = 50
private fun isPrintable(c: Char): Boolean {
val block = UnicodeBlock.of(c)

View file

@ -4,7 +4,6 @@
*/
package com.zeapo.pwdstore
import android.app.Activity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
@ -43,7 +42,7 @@ class SelectFolderActivity : AppCompatActivity(R.layout.select_folder_layout) {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
setResult(Activity.RESULT_CANCELED)
setResult(RESULT_CANCELED)
finish()
return true
}
@ -54,7 +53,7 @@ class SelectFolderActivity : AppCompatActivity(R.layout.select_folder_layout) {
private fun selectFolder() {
intent.putExtra("SELECTED_FOLDER_PATH", passwordList.currentDir.absolutePath)
setResult(Activity.RESULT_OK, intent)
setResult(RESULT_OK, intent)
finish()
}
}

View file

@ -6,7 +6,6 @@ package com.zeapo.pwdstore
import android.accessibilityservice.AccessibilityServiceInfo
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ShortcutManager
@ -21,7 +20,8 @@ import android.text.TextUtils
import android.view.MenuItem
import android.view.accessibility.AccessibilityManager
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatTextView
import androidx.biometric.BiometricManager
@ -167,7 +167,7 @@ class UserPreference : AppCompatActivity() {
false
} else {
val intent = Intent(callingActivity, GetKeyIdsActivity::class.java)
val keySelectResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val keySelectResult = registerForActivityResult(StartActivityForResult()) {
updateKeyIDsSummary(pref)
}
keySelectResult.launch(intent)
@ -490,7 +490,7 @@ class UserPreference : AppCompatActivity() {
override fun onBackPressed() {
super.onBackPressed()
setResult(Activity.RESULT_OK)
setResult(RESULT_OK)
finish()
}
@ -511,13 +511,40 @@ class UserPreference : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
@Suppress("Deprecation") // for Environment.getExternalStorageDirectory()
fun selectExternalGitRepository() {
MaterialAlertDialogBuilder(this)
.setTitle(this.resources.getString(R.string.external_repository_dialog_title))
.setMessage(this.resources.getString(R.string.external_repository_dialog_text))
.setPositiveButton(R.string.dialog_ok) { _, _ ->
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(Intent.createChooser(i, "Choose Directory"), SELECT_GIT_DIRECTORY)
registerForActivityResult(StartActivityForResult()) { result ->
if (!validateResult(result)) return@registerForActivityResult
val uri = result.data?.data
tag(TAG).d { "Selected repository URI is $uri" }
// TODO: This is fragile. Workaround until PasswordItem is backed by DocumentFile
val docId = DocumentsContract.getTreeDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val path = if (split.size > 1) split[1] else split[0]
val repoPath = "${Environment.getExternalStorageDirectory()}/$path"
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
tag(TAG).d { "Selected repository path is $repoPath" }
if (Environment.getExternalStorageDirectory().path == repoPath) {
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.sdcard_root_warning_title))
.setMessage(getString(R.string.sdcard_root_warning_message))
.setPositiveButton("Remove everything") { _, _ ->
prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, uri?.path) }
}
.setNegativeButton(R.string.dialog_cancel, null)
.show()
}
prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, repoPath) }
}.launch(Intent.createChooser(i, "Choose Directory"))
}
.setNegativeButton(R.string.dialog_cancel, null)
.show()
@ -529,7 +556,7 @@ class UserPreference : AppCompatActivity() {
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
android.R.id.home -> {
setResult(Activity.RESULT_OK)
setResult(RESULT_OK)
finish()
true
}
@ -537,23 +564,75 @@ class UserPreference : AppCompatActivity() {
}
}
/**
* Given a [ActivityResult], validates that the result is usable.
*/
private fun validateResult(result: ActivityResult): Boolean {
if (result.resultCode != RESULT_OK) {
return false
}
if (result.data == null) {
setResult(RESULT_CANCELED)
return false
}
return true
}
/**
* Opens a file explorer to import the private key
*/
private fun getSshKey() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
registerForActivityResult(StartActivityForResult()) { result ->
if (!validateResult(result)) return@registerForActivityResult
try {
val uri: Uri = result.data?.data ?: throw IOException("Unable to open file")
copySshKey(uri)
Toast.makeText(
this,
this.resources.getString(R.string.ssh_key_success_dialog_title),
Toast.LENGTH_LONG
).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
prefs.edit { putBoolean(PreferenceKeys.USE_GENERATED_KEY, false) }
getEncryptedPrefs("git_operation").edit { remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) }
// Delete the public key from generation
File("""$filesDir/.ssh_key.pub""").delete()
setResult(RESULT_OK)
finish()
} catch (e: Exception) {
MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.ssh_key_error_dialog_title))
.setMessage(e.message)
.setPositiveButton(resources.getString(R.string.dialog_ok), null)
.show()
}
}.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
startActivityForResult(intent, IMPORT_SSH_KEY)
})
}
/**
* Exports the passwords
*/
private fun exportPasswords() {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(Intent.createChooser(i, "Choose Directory"), EXPORT_PASSWORDS)
registerForActivityResult(StartActivityForResult()) { result ->
if (!validateResult(result)) return@registerForActivityResult
val uri = result.data?.data
if (uri != null) {
val targetDirectory = DocumentFile.fromTreeUri(applicationContext, uri)
if (targetDirectory != null) {
exportPasswords(targetDirectory)
}
}
}.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE))
}
/**
@ -563,7 +642,7 @@ class UserPreference : AppCompatActivity() {
val intent = Intent(applicationContext, SshKeyGenActivity::class.java)
startActivity(intent)
if (!fromPreferences) {
setResult(Activity.RESULT_OK)
setResult(RESULT_OK)
finish()
}
}
@ -572,11 +651,33 @@ class UserPreference : AppCompatActivity() {
* Pick custom xkpwd dictionary from sdcard
*/
private fun storeCustomDictionaryPath() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
registerForActivityResult(StartActivityForResult()) { result ->
if (!validateResult(result)) return@registerForActivityResult
val uri: Uri = result.data?.data ?: throw IOException("Unable to open file")
Toast.makeText(
this,
this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
Toast.LENGTH_SHORT
).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
prefs.edit { putString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, uri.toString()) }
val customDictPref = prefsFragment.findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
setCustomDictSummary(customDictPref, uri)
// copy user selected file to internal storage
val inputStream = contentResolver.openInputStream(uri)
val customDictFile = File(filesDir.toString(), XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE).outputStream()
inputStream?.copyTo(customDictFile, 1024)
inputStream?.close()
customDictFile.close()
setResult(RESULT_OK)
}.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
startActivityForResult(intent, SET_CUSTOM_XKPWD_DICT)
})
}
@Throws(IllegalArgumentException::class, IOException::class)
@ -631,111 +732,6 @@ class UserPreference : AppCompatActivity() {
return autofillManager?.hasEnabledAutofillServices() == true
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
setResult(Activity.RESULT_CANCELED)
return
}
when (requestCode) {
IMPORT_SSH_KEY -> {
try {
val uri: Uri = data.data ?: throw IOException("Unable to open file")
copySshKey(uri)
Toast.makeText(
this,
this.resources.getString(R.string.ssh_key_success_dialog_title),
Toast.LENGTH_LONG
).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
prefs.edit { putBoolean(PreferenceKeys.USE_GENERATED_KEY, false) }
getEncryptedPrefs("git_operation").edit { remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) }
// Delete the public key from generation
File("""$filesDir/.ssh_key.pub""").delete()
setResult(Activity.RESULT_OK)
finish()
} catch (e: Exception) {
MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.ssh_key_error_dialog_title))
.setMessage(e.message)
.setPositiveButton(resources.getString(R.string.dialog_ok), null)
.show()
}
}
SELECT_GIT_DIRECTORY -> {
val uri = data.data
tag(TAG).d { "Selected repository URI is $uri" }
// TODO: This is fragile. Workaround until PasswordItem is backed by DocumentFile
val docId = DocumentsContract.getTreeDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val path = if (split.size > 1) split[1] else split[0]
val repoPath = "${Environment.getExternalStorageDirectory()}/$path"
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
tag(TAG).d { "Selected repository path is $repoPath" }
if (Environment.getExternalStorageDirectory().path == repoPath) {
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.sdcard_root_warning_title))
.setMessage(getString(R.string.sdcard_root_warning_message))
.setPositiveButton("Remove everything") { _, _ ->
prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, uri?.path) }
}
.setNegativeButton(R.string.dialog_cancel, null)
.show()
}
prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, repoPath) }
}
EXPORT_PASSWORDS -> {
val uri = data.data
if (uri != null) {
val targetDirectory = DocumentFile.fromTreeUri(applicationContext, uri)
if (targetDirectory != null) {
exportPasswords(targetDirectory)
}
}
}
SET_CUSTOM_XKPWD_DICT -> {
val uri: Uri = data.data ?: throw IOException("Unable to open file")
Toast.makeText(
this,
this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
Toast.LENGTH_SHORT
).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
prefs.edit { putString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, uri.toString()) }
val customDictPref = prefsFragment.findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
setCustomDictSummary(customDictPref, uri)
// copy user selected file to internal storage
val inputStream = contentResolver.openInputStream(uri)
val customDictFile = File(filesDir.toString(), XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE).outputStream()
inputStream?.copyTo(customDictFile, 1024)
inputStream?.close()
customDictFile.close()
setResult(Activity.RESULT_OK)
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
/**
* Exports passwords to the given directory.
*
@ -808,11 +804,6 @@ class UserPreference : AppCompatActivity() {
}
companion object {
private const val IMPORT_SSH_KEY = 1
private const val SELECT_GIT_DIRECTORY = 2
private const val EXPORT_PASSWORDS = 3
private const val EDIT_GIT_CONFIG = 4
private const val SET_CUSTOM_XKPWD_DICT = 5
private const val TAG = "UserPreference"
/**

View file

@ -2,6 +2,7 @@
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
@file:Suppress("Deprecation")
package com.zeapo.pwdstore.autofill
import android.app.PendingIntent

View file

@ -2,6 +2,7 @@
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
@file:Suppress("Deprecation")
package com.zeapo.pwdstore.autofill
import android.annotation.SuppressLint

View file

@ -2,6 +2,7 @@
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
@file:Suppress("Deprecation")
package com.zeapo.pwdstore.autofill
import android.content.Context

View file

@ -2,6 +2,7 @@
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
@file:Suppress("Deprecation")
package com.zeapo.pwdstore.autofill
import android.content.Context

View file

@ -2,6 +2,7 @@
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
@file:Suppress("Deprecation")
package com.zeapo.pwdstore.autofill
import android.accessibilityservice.AccessibilityService

View file

@ -4,7 +4,6 @@
*/
package com.zeapo.pwdstore.autofill.oreo.ui
import android.app.Activity
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
@ -13,7 +12,10 @@ import android.os.Build
import android.os.Bundle
import android.view.autofill.AutofillManager
import android.widget.Toast
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.github.ajalt.timberkt.d
import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.autofill.oreo.AutofillAction
@ -44,13 +46,12 @@ import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
@RequiresApi(Build.VERSION_CODES.O)
class AutofillDecryptActivity : Activity(), CoroutineScope {
class AutofillDecryptActivity : AppCompatActivity(), CoroutineScope {
companion object {
private const val EXTRA_FILE_PATH = "com.zeapo.pwdstore.autofill.oreo.EXTRA_FILE_PATH"
private const val EXTRA_SEARCH_ACTION =
"com.zeapo.pwdstore.autofill.oreo.EXTRA_SEARCH_ACTION"
private const val REQUEST_CODE_CONTINUE_AFTER_USER_INTERACTION = 1
private const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"
private var decryptFileRequestCode = 1
@ -194,14 +195,17 @@ class AutofillDecryptActivity : Activity(), CoroutineScope {
val intentToResume = withContext(Dispatchers.Main) {
suspendCoroutine<Intent> { cont ->
continueAfterUserInteraction = cont
startIntentSenderForResult(
pendingIntent.intentSender,
REQUEST_CODE_CONTINUE_AFTER_USER_INTERACTION,
null,
0,
0,
0
)
registerForActivityResult(StartIntentSenderForResult()) { result ->
if (continueAfterUserInteraction != null) {
val data = result.data
if (resultCode == RESULT_OK && data != null) {
continueAfterUserInteraction?.resume(data)
} else {
continueAfterUserInteraction?.resumeWithException(Exception("OpenPgpApi ACTION_DECRYPT_VERIFY failed to continue after user interaction"))
}
continueAfterUserInteraction = null
}
}.launch(IntentSenderRequest.Builder(pendingIntent.intentSender).build())
}
}
decryptCredential(file, intentToResume)
@ -230,16 +234,4 @@ class AutofillDecryptActivity : Activity(), CoroutineScope {
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_CONTINUE_AFTER_USER_INTERACTION && continueAfterUserInteraction != null) {
if (resultCode == RESULT_OK && data != null) {
continueAfterUserInteraction?.resume(data)
} else {
continueAfterUserInteraction?.resumeWithException(Exception("OpenPgpApi ACTION_DECRYPT_VERIFY failed to continue after user interaction"))
}
continueAfterUserInteraction = null
}
}
}

View file

@ -14,6 +14,7 @@ import android.os.Bundle
import android.view.View
import android.view.autofill.AutofillManager
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.bold
@ -44,7 +45,6 @@ class AutofillFilterView : AppCompatActivity() {
companion object {
private const val HEIGHT_PERCENTAGE = 0.9
private const val WIDTH_PERCENTAGE = 0.75
private const val DECRYPT_FILL_REQUEST_CODE = 1
private const val EXTRA_FORM_ORIGIN_WEB =
"com.zeapo.pwdstore.autofill.oreo.ui.EXTRA_FORM_ORIGIN_WEB"
@ -197,20 +197,15 @@ class AutofillFilterView : AppCompatActivity() {
item.file
)
// intent?.extras? is checked to be non-null in onCreate
startActivityForResult(
AutofillDecryptActivity.makeDecryptFileIntent(
item.file,
intent!!.extras!!,
this
), DECRYPT_FILL_REQUEST_CODE
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == DECRYPT_FILL_REQUEST_CODE) {
if (resultCode == RESULT_OK) setResult(RESULT_OK, data)
registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
setResult(RESULT_OK, result.data)
}
finish()
}
}.launch(AutofillDecryptActivity.makeDecryptFileIntent(
item.file,
intent!!.extras!!,
this
))
}
}

View file

@ -4,7 +4,6 @@
*/
package com.zeapo.pwdstore.autofill.oreo.ui
import android.app.Activity
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
@ -12,10 +11,11 @@ import android.content.IntentSender
import android.os.Build
import android.os.Bundle
import android.view.autofill.AutofillManager
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.PasswordStore
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.autofill.oreo.AutofillAction
import com.zeapo.pwdstore.autofill.oreo.AutofillMatcher
@ -29,7 +29,7 @@ import com.zeapo.pwdstore.utils.commitChange
import java.io.File
@RequiresApi(Build.VERSION_CODES.O)
class AutofillSaveActivity : Activity() {
class AutofillSaveActivity : AppCompatActivity() {
companion object {
private const val EXTRA_FOLDER_NAME =
@ -109,50 +109,46 @@ class AutofillSaveActivity : Activity() {
)
)
}
startActivityForResult(saveIntent, PasswordStore.REQUEST_CODE_ENCRYPT)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PasswordStore.REQUEST_CODE_ENCRYPT && resultCode == RESULT_OK && data != null) {
val createdPath = data.getStringExtra("CREATED_FILE")!!
formOrigin?.let {
AutofillMatcher.addMatchFor(this, it, File(createdPath))
}
val longName = data.getStringExtra("LONG_NAME")!!
val password = data.getStringExtra("PASSWORD")
val result = if (password != null) {
// Password was generated and should be filled into a form.
val username = data.getStringExtra("USERNAME")
val clientState =
intent?.getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE) ?: run {
e { "AutofillDecryptActivity started without EXTRA_CLIENT_STATE" }
finish()
return
}
val credentials = Credentials(username, password, null)
val fillInDataset = FillableForm.makeFillInDataset(
this,
credentials,
clientState,
AutofillAction.Generate
)
Intent().apply {
putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset)
registerForActivityResult(StartActivityForResult()) { result ->
val data = result.data
if (result.resultCode == RESULT_OK && data != null) {
val createdPath = data.getStringExtra("CREATED_FILE")!!
formOrigin?.let {
AutofillMatcher.addMatchFor(this, it, File(createdPath))
}
} else {
// Password was extracted from a form, there is nothing to fill.
Intent()
val longName = data.getStringExtra("LONG_NAME")!!
val password = data.getStringExtra("PASSWORD")
val resultIntent = if (password != null) {
// Password was generated and should be filled into a form.
val username = data.getStringExtra("USERNAME")
val clientState =
intent?.getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE) ?: run {
e { "AutofillDecryptActivity started without EXTRA_CLIENT_STATE" }
finish()
return@registerForActivityResult
}
val credentials = Credentials(username, password, null)
val fillInDataset = FillableForm.makeFillInDataset(
this,
credentials,
clientState,
AutofillAction.Generate
)
Intent().apply {
putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset)
}
} else {
// Password was extracted from a form, there is nothing to fill.
Intent()
}
// PasswordCreationActivity delegates committing the added file to PasswordStore. Since
// PasswordStore is not involved in an AutofillScenario, we have to commit the file ourselves.
commitChange(
getString(R.string.git_commit_add_text, longName),
finishWithResultOnEnd = resultIntent
)
// GitAsyncTask will finish the activity for us.
}
// PasswordCreationActivity delegates committing the added file to PasswordStore. Since
// PasswordStore is not involved in an AutofillScenario, we have to commit the file ourselves.
commitChange(
getString(R.string.git_commit_add_text, longName),
finishWithResultOnEnd = result
)
// GitAsyncTask will finish the activity for us.
} else {
finish()
}
}.launch(saveIntent)
}
}

View file

@ -4,7 +4,6 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
@ -167,7 +166,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
*/
fun launchGitOperation(operation: Int) {
if (url == null) {
setResult(Activity.RESULT_CANCELED)
setResult(RESULT_CANCELED)
finish()
return
}

View file

@ -4,7 +4,7 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.Git
@ -13,7 +13,7 @@ import org.eclipse.jgit.api.PushCommand
import org.eclipse.jgit.api.RebaseCommand
import java.io.File
class BreakOutOfDetached(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
class BreakOutOfDetached(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
private lateinit var commands: List<GitCommand<out Any>>
/**

View file

@ -4,8 +4,8 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.CloneCommand
@ -18,7 +18,7 @@ import java.io.File
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class CloneOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
class CloneOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
/**
* Sets the command using the repository uri

View file

@ -4,11 +4,11 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.git.config.SshjSessionFactory
@ -28,14 +28,14 @@ import java.lang.ref.WeakReference
class GitAsyncTask(
activity: Activity,
activity: AppCompatActivity,
private val operation: GitOperation,
private val finishWithResultOnEnd: Intent?,
private val silentlyExecute: Boolean = false
) : AsyncTask<GitCommand<*>, Int, GitAsyncTask.Result>() {
private val activityWeakReference: WeakReference<Activity> = WeakReference(activity)
private val activity: Activity?
private val activityWeakReference: WeakReference<AppCompatActivity> = WeakReference(activity)
private val activity: AppCompatActivity?
get() = activityWeakReference.get()
private val context: Context = activity.applicationContext
private val dialog = ProgressDialog(activity)
@ -86,7 +86,8 @@ class GitAsyncTask(
RemoteRefUpdate.Status.REJECTED_NODELETE,
RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
RemoteRefUpdate.Status.NON_EXISTING,
RemoteRefUpdate.Status.NOT_ATTEMPTED ->
RemoteRefUpdate.Status.NOT_ATTEMPTED
->
(activity!!.getString(R.string.git_push_generic_error) + rru.status.name)
RemoteRefUpdate.Status.REJECTED_OTHER_REASON -> {
if
@ -149,21 +150,21 @@ class GitAsyncTask(
// Currently, this is only executed when the user cancels a password prompt
// during authentication.
if (finishWithResultOnEnd != null) {
activity?.setResult(Activity.RESULT_CANCELED)
activity?.setResult(AppCompatActivity.RESULT_CANCELED)
activity?.finish()
}
} else {
e(result.err)
operation.onError(rootCauseException(result.err))
if (finishWithResultOnEnd != null) {
activity?.setResult(Activity.RESULT_CANCELED)
activity?.setResult(AppCompatActivity.RESULT_CANCELED)
}
}
}
is Result.Ok -> {
operation.onSuccess()
if (finishWithResultOnEnd != null) {
activity?.setResult(Activity.RESULT_OK, finishWithResultOnEnd)
activity?.setResult(AppCompatActivity.RESULT_OK, finishWithResultOnEnd)
activity?.finish()
}
}

View file

@ -5,10 +5,10 @@
package com.zeapo.pwdstore.git
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.preference.PreferenceManager
@ -40,7 +40,10 @@ import kotlin.coroutines.resume
import com.google.android.material.R as materialR
private class GitOperationCredentialFinder(val callingActivity: Activity, val connectionMode: ConnectionMode) : InteractivePasswordFinder() {
private class GitOperationCredentialFinder(
val callingActivity: AppCompatActivity,
val connectionMode: ConnectionMode
) : InteractivePasswordFinder() {
override fun askForPassword(cont: Continuation<String?>, isRetry: Boolean) {
val gitOperationPrefs = callingActivity.getEncryptedPrefs("git_operation")
@ -118,7 +121,7 @@ private class GitOperationCredentialFinder(val callingActivity: Activity, val co
* @param gitDir the git working tree directory
* @param callingActivity the calling activity
*/
abstract class GitOperation(gitDir: File, internal val callingActivity: Activity) {
abstract class GitOperation(gitDir: File, internal val callingActivity: AppCompatActivity) {
protected val repository: Repository? = PasswordRepository.getRepository(gitDir)
internal var provider: CredentialsProvider? = null

View file

@ -4,8 +4,8 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.Git
@ -18,7 +18,7 @@ import java.io.File
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class PullOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
class PullOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
/**
* Sets the command

View file

@ -4,8 +4,8 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.Git
@ -18,7 +18,7 @@ import java.io.File
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class PushOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
class PushOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
/**
* Sets the command

View file

@ -4,8 +4,8 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.AddCommand
@ -20,7 +20,7 @@ import java.io.File
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class ResetToRemoteOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
class ResetToRemoteOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
private var addCommand: AddCommand? = null
private var fetchCommand: FetchCommand? = null

View file

@ -4,8 +4,8 @@
*/
package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.AddCommand
@ -22,7 +22,7 @@ import java.io.File
* @param fileDir the git working tree directory
* @param callingActivity the calling activity
*/
class SyncOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
class SyncOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
private var addCommand: AddCommand? = null
private var statusCommand: StatusCommand? = null

View file

@ -4,13 +4,13 @@
*/
package com.zeapo.pwdstore.git.config;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@ -245,7 +245,7 @@ public class SshApiSessionFactory extends JschConfigSessionFactory {
// back
// though onActivityResult
callingActivity.onActivityResult(
requestCode, Activity.RESULT_OK, null);
requestCode, AppCompatActivity.RESULT_OK, null);
}
@Override
@ -289,7 +289,7 @@ public class SshApiSessionFactory extends JschConfigSessionFactory {
private final String description;
private final String alg;
private final byte[] publicKey;
private final Activity callingActivity;
private final AppCompatActivity callingActivity;
private final SshAuthenticationApi api;
private CountDownLatch latch;
private byte[] signature;
@ -299,7 +299,7 @@ public class SshApiSessionFactory extends JschConfigSessionFactory {
String description,
byte[] publicKey,
String alg,
Activity callingActivity,
AppCompatActivity callingActivity,
SshAuthenticationApi api) {
this.keyId = keyId;
this.description = description;
@ -310,7 +310,7 @@ public class SshApiSessionFactory extends JschConfigSessionFactory {
}
@Override
public boolean setPassphrase(byte[] passphrase) throws JSchException {
public boolean setPassphrase(byte[] passphrase) {
// We are not encrypted with a passphrase
return true;
}

View file

@ -11,11 +11,15 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResult
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.zeapo.pwdstore.PasswordFragment
import com.zeapo.pwdstore.PasswordFragment.Companion.ACTION_FOLDER
import com.zeapo.pwdstore.PasswordFragment.Companion.ACTION_KEY
import com.zeapo.pwdstore.PasswordFragment.Companion.ACTION_PASSWORD
import com.zeapo.pwdstore.PasswordFragment.Companion.ITEM_CREATION_REQUEST_KEY
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.resolveAttribute
@ -50,11 +54,11 @@ class ItemCreationBottomSheet : BottomSheetDialogFragment() {
addBottomSheetCallback(bottomSheetCallback)
}
dialog.findViewById<View>(R.id.create_folder)?.setOnClickListener {
(requireTargetFragment() as PasswordFragment).createFolder()
setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_FOLDER))
dismiss()
}
dialog.findViewById<View>(R.id.create_password)?.setOnClickListener {
(requireTargetFragment() as PasswordFragment).createPassword()
setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_PASSWORD))
dismiss()
}
}
@ -69,8 +73,4 @@ class ItemCreationBottomSheet : BottomSheetDialogFragment() {
super.dismiss()
behavior?.removeBottomSheetCallback(bottomSheetCallback)
}
private fun requireTargetFragment(): Fragment = requireNotNull(targetFragment) {
"A target fragment must be set for $this"
}
}

View file

@ -4,7 +4,6 @@
*/
package com.zeapo.pwdstore.utils
import android.app.Activity
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
@ -18,6 +17,7 @@ import androidx.annotation.IdRes
import androidx.annotation.MainThread
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
@ -49,7 +49,7 @@ fun CharArray.clear() {
val Context.clipboard get() = getSystemService<ClipboardManager>()
fun Activity.snackbar(
fun AppCompatActivity.snackbar(
view: View = findViewById(android.R.id.content),
message: String,
length: Int = Snackbar.LENGTH_SHORT
@ -96,10 +96,10 @@ fun Context.getEncryptedPrefs(fileName: String): SharedPreferences {
}
@MainThread
fun Activity.commitChange(message: String, finishWithResultOnEnd: Intent? = null) {
fun AppCompatActivity.commitChange(message: String, finishWithResultOnEnd: Intent? = null) {
if (!PasswordRepository.isGitRepo()) {
if (finishWithResultOnEnd != null) {
setResult(Activity.RESULT_OK, finishWithResultOnEnd)
setResult(AppCompatActivity.RESULT_OK, finishWithResultOnEnd)
finish()
}
return
@ -141,6 +141,6 @@ val Context.autofillManager: AutofillManager?
@RequiresApi(Build.VERSION_CODES.O)
get() = getSystemService()
fun Activity.isInsideRepository(file: File): Boolean {
fun AppCompatActivity.isInsideRepository(file: File): Boolean {
return file.canonicalPath.contains(getRepositoryDirectory(this).canonicalPath)
}

View file

@ -8,7 +8,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:padding="@dimen/activity_horizontal_margin"
tools:context="com.zeapo.pwdstore.git.GitConfigActivity"
tools:layout_editor_absoluteX="0dp"

View file

@ -8,7 +8,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:orientation="vertical"
tools:context="com.zeapo.pwdstore.crypto.DecryptActivity">

View file

@ -8,7 +8,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin"
tools:context="com.zeapo.pwdstore.crypto.PasswordCreationActivity">

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="enable_accessibility_autofill">true</bool>
</resources>

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="leak_canary_allow_in_non_debuggable_build">true</bool>
<bool name="enable_accessibility_autofill">true</bool>
</resources>