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

View file

@ -6,7 +6,6 @@ package com.zeapo.pwdstore
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -93,6 +92,46 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
ViewModelProvider.AndroidViewModelFactory(application) 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 { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
// open search view on search key, or Ctr+F // open search view on search key, or Ctr+F
if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) && 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 = Intent(this, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_PUSH) intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_PUSH)
startActivityForResult(intent, BaseGitActivity.REQUEST_PUSH) startActivity(intent)
return true return true
} }
R.id.git_pull -> { R.id.git_pull -> {
@ -305,7 +344,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
intent = Intent(this, GitOperationActivity::class.java) intent = Intent(this, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_PULL) intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_PULL)
startActivityForResult(intent, BaseGitActivity.REQUEST_PULL) listRefreshAction.launch(intent)
return true return true
} }
R.id.git_sync -> { R.id.git_sync -> {
@ -315,7 +354,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
intent = Intent(this, GitOperationActivity::class.java) intent = Intent(this, GitOperationActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_SYNC) intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_SYNC)
startActivityForResult(intent, BaseGitActivity.REQUEST_SYNC) listRefreshAction.launch(intent)
return true return true
} }
R.id.refresh -> { R.id.refresh -> {
@ -383,7 +422,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
.setMessage(resources.getString(R.string.key_dialog_text)) .setMessage(resources.getString(R.string.key_dialog_text))
.setPositiveButton(resources.getString(R.string.dialog_positive)) { _, _ -> .setPositiveButton(resources.getString(R.string.dialog_positive)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java) val intent = Intent(activity, UserPreference::class.java)
startActivityForResult(intent, BaseGitActivity.REQUEST_INIT) repositoryInitAction.launch(intent)
} }
.setNegativeButton(resources.getString(R.string.dialog_negative), null) .setNegativeButton(resources.getString(R.string.dialog_negative), null)
.show() .show()
@ -426,7 +465,11 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
if (repo == null) { if (repo == null) {
val intent = Intent(activity, UserPreference::class.java) val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external") intent.putExtra("operation", "git_external")
startActivityForResult(intent, HOME) registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
checkLocalRepository()
}
}.launch(intent)
} else { } else {
checkLocalRepository(getRepositoryDirectory(applicationContext)) checkLocalRepository(getRepositoryDirectory(applicationContext))
} }
@ -526,7 +569,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
shortcutManager!!.addDynamicShortcuts(listOf(shortcut)) shortcutManager!!.addDynamicShortcuts(listOf(shortcut))
} }
} }
startActivityForResult(decryptIntent, REQUEST_CODE_DECRYPT_AND_VERIFY) startActivity(decryptIntent)
} }
private fun validateState(): Boolean { private fun validateState(): Boolean {
@ -558,7 +601,12 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
val intent = Intent(this, PasswordCreationActivity::class.java) val intent = Intent(this, PasswordCreationActivity::class.java)
intent.putExtra("FILE_PATH", currentDir.absolutePath) intent.putExtra("FILE_PATH", currentDir.absolutePath)
intent.putExtra("REPO_PATH", getRepositoryDirectory(applicationContext).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() { fun createFolder() {
@ -574,6 +622,11 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
else else
size += it.file.listFilesRecursively().size size += it.file.listFilesRecursively().size
} }
if (size == 0) {
selectedItems.map { item -> item.file.deleteRecursively() }
refreshPasswordList()
return
}
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMessage(resources.getQuantityString(R.plurals.delete_dialog_text, size, size)) .setMessage(resources.getQuantityString(R.plurals.delete_dialog_text, size, size))
.setPositiveButton(resources.getString(R.string.dialog_yes)) { _, _ -> .setPositiveButton(resources.getString(R.string.dialog_yes)) { _, _ ->
@ -585,6 +638,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
filesToDelete.add(item.file) filesToDelete.add(item.file)
} }
selectedItems.map { item -> item.file.deleteRecursively() } selectedItems.map { item -> item.file.deleteRecursively() }
refreshPasswordList()
AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete) AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete)
commitChange(resources.getString(R.string.git_commit_remove_text, commitChange(resources.getString(R.string.git_commit_remove_text,
selectedItems.joinToString(separator = ", ") { item -> selectedItems.joinToString(separator = ", ") { item ->
@ -756,61 +810,6 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
private val currentDir: File private val currentDir: File
get() = plist?.currentDir ?: getRepositoryDirectory(applicationContext) 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) { private suspend fun moveFile(source: File, destinationFile: File) {
val sourceDestinationMap = if (source.isDirectory) { val sourceDestinationMap = if (source.isDirectory) {
destinationFile.mkdirs() destinationFile.mkdirs()
@ -848,7 +847,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
CLONE_REPO_BUTTON -> { CLONE_REPO_BUTTON -> {
val intent = Intent(activity, GitServerConfigActivity::class.java) val intent = Intent(activity, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE) 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) { if (externalRepo == null) {
val intent = Intent(activity, UserPreference::class.java) val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external") intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation) when (operation) {
NEW_REPO_BUTTON -> repositoryInitAction.launch(intent)
CLONE_REPO_BUTTON -> directoryChangeAction.launch(intent)
}
} else { } else {
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity)
.setTitle(resources.getString(R.string.directory_selected_title)) .setTitle(resources.getString(R.string.directory_selected_title))
@ -869,14 +871,17 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
CLONE_REPO_BUTTON -> { CLONE_REPO_BUTTON -> {
val intent = Intent(activity, GitServerConfigActivity::class.java) val intent = Intent(activity, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE) intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE) cloneAction.launch(intent)
} }
} }
} }
.setNegativeButton(resources.getString(R.string.change)) { _, _ -> .setNegativeButton(resources.getString(R.string.change)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java) val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external") intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation) when (operation) {
NEW_REPO_BUTTON -> repositoryInitAction.launch(intent)
CLONE_REPO_BUTTON -> directoryChangeAction.launch(intent)
}
} }
.show() .show()
} }
@ -891,7 +896,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
.replace(".gpg", "") .replace(".gpg", "")
val data = Intent() val data = Intent()
data.putExtra("path", path) data.putExtra("path", path)
setResult(Activity.RESULT_OK, data) setResult(RESULT_OK, data)
finish() finish()
} }
@ -899,13 +904,9 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
get() = getSortOrder(settings) get() = getSortOrder(settings)
companion object { companion object {
const val REQUEST_CODE_ENCRYPT = 9911
const val REQUEST_CODE_DECRYPT_AND_VERIFY = 9913
const val REQUEST_ARG_PATH = "PATH" const val REQUEST_ARG_PATH = "PATH"
private val TAG = PasswordStore::class.java.name
const val CLONE_REPO_BUTTON = 401 const val CLONE_REPO_BUTTON = 401
const val NEW_REPO_BUTTON = 402 const val NEW_REPO_BUTTON = 402
private const val HOME = 403
private const val REQUEST_EXTERNAL_STORAGE = 50 private const val REQUEST_EXTERNAL_STORAGE = 50
private fun isPrintable(c: Char): Boolean { private fun isPrintable(c: Char): Boolean {
val block = UnicodeBlock.of(c) val block = UnicodeBlock.of(c)

View file

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

View file

@ -6,7 +6,6 @@ package com.zeapo.pwdstore
import android.accessibilityservice.AccessibilityServiceInfo import android.accessibilityservice.AccessibilityServiceInfo
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.ShortcutManager import android.content.pm.ShortcutManager
@ -21,7 +20,8 @@ import android.text.TextUtils
import android.view.MenuItem import android.view.MenuItem
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import android.widget.Toast 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.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
@ -167,7 +167,7 @@ class UserPreference : AppCompatActivity() {
false false
} else { } else {
val intent = Intent(callingActivity, GetKeyIdsActivity::class.java) val intent = Intent(callingActivity, GetKeyIdsActivity::class.java)
val keySelectResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { val keySelectResult = registerForActivityResult(StartActivityForResult()) {
updateKeyIDsSummary(pref) updateKeyIDsSummary(pref)
} }
keySelectResult.launch(intent) keySelectResult.launch(intent)
@ -490,7 +490,7 @@ class UserPreference : AppCompatActivity() {
override fun onBackPressed() { override fun onBackPressed() {
super.onBackPressed() super.onBackPressed()
setResult(Activity.RESULT_OK) setResult(RESULT_OK)
finish() finish()
} }
@ -511,13 +511,40 @@ class UserPreference : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }
@Suppress("Deprecation") // for Environment.getExternalStorageDirectory()
fun selectExternalGitRepository() { fun selectExternalGitRepository() {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(this.resources.getString(R.string.external_repository_dialog_title)) .setTitle(this.resources.getString(R.string.external_repository_dialog_title))
.setMessage(this.resources.getString(R.string.external_repository_dialog_text)) .setMessage(this.resources.getString(R.string.external_repository_dialog_text))
.setPositiveButton(R.string.dialog_ok) { _, _ -> .setPositiveButton(R.string.dialog_ok) { _, _ ->
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) 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) .setNegativeButton(R.string.dialog_cancel, null)
.show() .show()
@ -529,7 +556,7 @@ class UserPreference : AppCompatActivity() {
// as you specify a parent activity in AndroidManifest.xml. // as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) { return when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
setResult(Activity.RESULT_OK) setResult(RESULT_OK)
finish() finish()
true 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 * Opens a file explorer to import the private key
*/ */
private fun getSshKey() { 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) addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*" type = "*/*"
} })
startActivityForResult(intent, IMPORT_SSH_KEY)
} }
/** /**
* Exports the passwords * Exports the passwords
*/ */
private fun exportPasswords() { private fun exportPasswords() {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) registerForActivityResult(StartActivityForResult()) { result ->
startActivityForResult(Intent.createChooser(i, "Choose Directory"), EXPORT_PASSWORDS) 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) val intent = Intent(applicationContext, SshKeyGenActivity::class.java)
startActivity(intent) startActivity(intent)
if (!fromPreferences) { if (!fromPreferences) {
setResult(Activity.RESULT_OK) setResult(RESULT_OK)
finish() finish()
} }
} }
@ -572,11 +651,33 @@ class UserPreference : AppCompatActivity() {
* Pick custom xkpwd dictionary from sdcard * Pick custom xkpwd dictionary from sdcard
*/ */
private fun storeCustomDictionaryPath() { 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) addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*" type = "*/*"
} })
startActivityForResult(intent, SET_CUSTOM_XKPWD_DICT)
} }
@Throws(IllegalArgumentException::class, IOException::class) @Throws(IllegalArgumentException::class, IOException::class)
@ -631,111 +732,6 @@ class UserPreference : AppCompatActivity() {
return autofillManager?.hasEnabledAutofillServices() == true 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. * Exports passwords to the given directory.
* *
@ -808,11 +804,6 @@ class UserPreference : AppCompatActivity() {
} }
companion object { 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" private const val TAG = "UserPreference"
/** /**

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,6 @@
*/ */
package com.zeapo.pwdstore.autofill.oreo.ui package com.zeapo.pwdstore.autofill.oreo.ui
import android.app.Activity
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -13,7 +12,10 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.autofill.AutofillManager import android.view.autofill.AutofillManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.github.ajalt.timberkt.d import com.github.ajalt.timberkt.d
import com.github.ajalt.timberkt.e import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.autofill.oreo.AutofillAction import com.zeapo.pwdstore.autofill.oreo.AutofillAction
@ -44,13 +46,12 @@ import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
class AutofillDecryptActivity : Activity(), CoroutineScope { class AutofillDecryptActivity : AppCompatActivity(), CoroutineScope {
companion object { companion object {
private const val EXTRA_FILE_PATH = "com.zeapo.pwdstore.autofill.oreo.EXTRA_FILE_PATH" private const val EXTRA_FILE_PATH = "com.zeapo.pwdstore.autofill.oreo.EXTRA_FILE_PATH"
private const val EXTRA_SEARCH_ACTION = private const val EXTRA_SEARCH_ACTION =
"com.zeapo.pwdstore.autofill.oreo.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 const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"
private var decryptFileRequestCode = 1 private var decryptFileRequestCode = 1
@ -194,14 +195,17 @@ class AutofillDecryptActivity : Activity(), CoroutineScope {
val intentToResume = withContext(Dispatchers.Main) { val intentToResume = withContext(Dispatchers.Main) {
suspendCoroutine<Intent> { cont -> suspendCoroutine<Intent> { cont ->
continueAfterUserInteraction = cont continueAfterUserInteraction = cont
startIntentSenderForResult( registerForActivityResult(StartIntentSenderForResult()) { result ->
pendingIntent.intentSender, if (continueAfterUserInteraction != null) {
REQUEST_CODE_CONTINUE_AFTER_USER_INTERACTION, val data = result.data
null, if (resultCode == RESULT_OK && data != null) {
0, continueAfterUserInteraction?.resume(data)
0, } else {
0 continueAfterUserInteraction?.resumeWithException(Exception("OpenPgpApi ACTION_DECRYPT_VERIFY failed to continue after user interaction"))
) }
continueAfterUserInteraction = null
}
}.launch(IntentSenderRequest.Builder(pendingIntent.intentSender).build())
} }
} }
decryptCredential(file, intentToResume) 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.View
import android.view.autofill.AutofillManager import android.view.autofill.AutofillManager
import android.widget.TextView import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.bold import androidx.core.text.bold
@ -44,7 +45,6 @@ class AutofillFilterView : AppCompatActivity() {
companion object { companion object {
private const val HEIGHT_PERCENTAGE = 0.9 private const val HEIGHT_PERCENTAGE = 0.9
private const val WIDTH_PERCENTAGE = 0.75 private const val WIDTH_PERCENTAGE = 0.75
private const val DECRYPT_FILL_REQUEST_CODE = 1
private const val EXTRA_FORM_ORIGIN_WEB = private const val EXTRA_FORM_ORIGIN_WEB =
"com.zeapo.pwdstore.autofill.oreo.ui.EXTRA_FORM_ORIGIN_WEB" "com.zeapo.pwdstore.autofill.oreo.ui.EXTRA_FORM_ORIGIN_WEB"
@ -197,20 +197,15 @@ class AutofillFilterView : AppCompatActivity() {
item.file item.file
) )
// intent?.extras? is checked to be non-null in onCreate // intent?.extras? is checked to be non-null in onCreate
startActivityForResult( registerForActivityResult(StartActivityForResult()) { result ->
AutofillDecryptActivity.makeDecryptFileIntent( if (result.resultCode == RESULT_OK) {
setResult(RESULT_OK, result.data)
}
finish()
}.launch(AutofillDecryptActivity.makeDecryptFileIntent(
item.file, item.file,
intent!!.extras!!, intent!!.extras!!,
this 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)
finish()
}
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,8 +4,8 @@
*/ */
package com.zeapo.pwdstore.git package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
@ -18,7 +18,7 @@ import java.io.File
* @param fileDir the git working tree directory * @param fileDir the git working tree directory
* @param callingActivity the calling activity * @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 * Sets the command

View file

@ -4,8 +4,8 @@
*/ */
package com.zeapo.pwdstore.git package com.zeapo.pwdstore.git
import android.app.Activity
import android.content.Intent import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
@ -18,7 +18,7 @@ import java.io.File
* @param fileDir the git working tree directory * @param fileDir the git working tree directory
* @param callingActivity the calling activity * @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 * Sets the command

View file

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

View file

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

View file

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

View file

@ -11,11 +11,15 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.FrameLayout 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.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment 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.R
import com.zeapo.pwdstore.utils.resolveAttribute import com.zeapo.pwdstore.utils.resolveAttribute
@ -50,11 +54,11 @@ class ItemCreationBottomSheet : BottomSheetDialogFragment() {
addBottomSheetCallback(bottomSheetCallback) addBottomSheetCallback(bottomSheetCallback)
} }
dialog.findViewById<View>(R.id.create_folder)?.setOnClickListener { dialog.findViewById<View>(R.id.create_folder)?.setOnClickListener {
(requireTargetFragment() as PasswordFragment).createFolder() setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_FOLDER))
dismiss() dismiss()
} }
dialog.findViewById<View>(R.id.create_password)?.setOnClickListener { dialog.findViewById<View>(R.id.create_password)?.setOnClickListener {
(requireTargetFragment() as PasswordFragment).createPassword() setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_PASSWORD))
dismiss() dismiss()
} }
} }
@ -69,8 +73,4 @@ class ItemCreationBottomSheet : BottomSheetDialogFragment() {
super.dismiss() super.dismiss()
behavior?.removeBottomSheetCallback(bottomSheetCallback) 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 package com.zeapo.pwdstore.utils
import android.app.Activity
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -18,6 +17,7 @@ import androidx.annotation.IdRes
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey import androidx.security.crypto.MasterKey
@ -49,7 +49,7 @@ fun CharArray.clear() {
val Context.clipboard get() = getSystemService<ClipboardManager>() val Context.clipboard get() = getSystemService<ClipboardManager>()
fun Activity.snackbar( fun AppCompatActivity.snackbar(
view: View = findViewById(android.R.id.content), view: View = findViewById(android.R.id.content),
message: String, message: String,
length: Int = Snackbar.LENGTH_SHORT length: Int = Snackbar.LENGTH_SHORT
@ -96,10 +96,10 @@ fun Context.getEncryptedPrefs(fileName: String): SharedPreferences {
} }
@MainThread @MainThread
fun Activity.commitChange(message: String, finishWithResultOnEnd: Intent? = null) { fun AppCompatActivity.commitChange(message: String, finishWithResultOnEnd: Intent? = null) {
if (!PasswordRepository.isGitRepo()) { if (!PasswordRepository.isGitRepo()) {
if (finishWithResultOnEnd != null) { if (finishWithResultOnEnd != null) {
setResult(Activity.RESULT_OK, finishWithResultOnEnd) setResult(AppCompatActivity.RESULT_OK, finishWithResultOnEnd)
finish() finish()
} }
return return
@ -141,6 +141,6 @@ val Context.autofillManager: AutofillManager?
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
get() = getSystemService() get() = getSystemService()
fun Activity.isInsideRepository(file: File): Boolean { fun AppCompatActivity.isInsideRepository(file: File): Boolean {
return file.canonicalPath.contains(getRepositoryDirectory(this).canonicalPath) return file.canonicalPath.contains(getRepositoryDirectory(this).canonicalPath)
} }

View file

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

View file

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

View file

@ -8,7 +8,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin" android:padding="@dimen/activity_horizontal_margin"
tools:context="com.zeapo.pwdstore.crypto.PasswordCreationActivity"> 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"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<bool name="leak_canary_allow_in_non_debuggable_build">true</bool> <bool name="leak_canary_allow_in_non_debuggable_build">true</bool>
<bool name="enable_accessibility_autofill">true</bool>
</resources> </resources>