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:
parent
ff3d2fcce3
commit
25b4261574
29 changed files with 352 additions and 362 deletions
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>>
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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">
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="enable_accessibility_autofill">true</bool>
|
||||
</resources>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue