all: refactor ActivityResultContracts usages to adhere to API requirements
The newest versions of AndroidX Activity and Fragments correctly enforce the requirement for all contracts to be registered at class init or before the lifecycle has reached `Lifecycle.State.STARTED`. To comply with these requirements, move all instances of `registerForActivityResult` being called at arbitrary points in the code to be done at class init. Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
d792fa5135
commit
cf03c55478
6 changed files with 300 additions and 267 deletions
|
@ -85,6 +85,97 @@ class PasswordStore : BaseGitActivity() {
|
|||
ViewModelProvider.AndroidViewModelFactory(application)
|
||||
}
|
||||
|
||||
private val storagePermissionRequest = registerForActivityResult(RequestPermission()) { granted ->
|
||||
if (granted) checkLocalRepository()
|
||||
}
|
||||
|
||||
private val directorySelectAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
checkLocalRepository()
|
||||
}
|
||||
}
|
||||
|
||||
private val listRefreshAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
refreshPasswordList()
|
||||
}
|
||||
}
|
||||
|
||||
private val passwordMoveAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||
val intentData = result.data ?: return@registerForActivityResult
|
||||
val filesToMove = requireNotNull(intentData.getStringArrayExtra("Files"))
|
||||
val target = File(requireNotNull(intentData.getStringExtra("SELECTED_FOLDER_PATH")))
|
||||
val repositoryPath = getRepositoryDirectory().absolutePath
|
||||
if (!target.isDirectory) {
|
||||
e { "Tried moving passwords to a non-existing folder." }
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
d { "Moving passwords to ${intentData.getStringExtra("SELECTED_FOLDER_PATH")}" }
|
||||
d { filesToMove.joinToString(", ") }
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
for (file in filesToMove) {
|
||||
val source = File(file)
|
||||
if (!source.exists()) {
|
||||
e { "Tried moving something that appears non-existent." }
|
||||
continue
|
||||
}
|
||||
val destinationFile = File(target.absolutePath + "/" + source.name)
|
||||
val basename = source.nameWithoutExtension
|
||||
val sourceLongName = getLongName(requireNotNull(source.parent), repositoryPath, basename)
|
||||
val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename)
|
||||
if (destinationFile.exists()) {
|
||||
e { "Trying to move a file that already exists." }
|
||||
withContext(Dispatchers.Main) {
|
||||
MaterialAlertDialogBuilder(this@PasswordStore)
|
||||
.setTitle(resources.getString(R.string.password_exists_title))
|
||||
.setMessage(resources.getString(
|
||||
R.string.password_exists_message,
|
||||
destinationLongName,
|
||||
sourceLongName)
|
||||
)
|
||||
.setPositiveButton(R.string.dialog_ok) { _, _ ->
|
||||
launch(Dispatchers.IO) {
|
||||
moveFile(source, destinationFile)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
launch(Dispatchers.IO) {
|
||||
moveFile(source, destinationFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
when (filesToMove.size) {
|
||||
1 -> {
|
||||
val source = File(filesToMove[0])
|
||||
val basename = source.nameWithoutExtension
|
||||
val sourceLongName = getLongName(requireNotNull(source.parent), repositoryPath, basename)
|
||||
val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename)
|
||||
withContext(Dispatchers.Main) {
|
||||
commitChange(
|
||||
resources.getString(R.string.git_commit_move_text, sourceLongName, destinationLongName),
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val repoDir = getRepositoryDirectory().absolutePath
|
||||
val relativePath = getRelativePath("${target.absolutePath}/", repoDir)
|
||||
withContext(Dispatchers.Main) {
|
||||
commitChange(
|
||||
resources.getString(R.string.git_commit_move_multiple_text, relativePath),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshPasswordList()
|
||||
plist?.dismissActionMode()
|
||||
}
|
||||
|
||||
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) &&
|
||||
|
@ -288,9 +379,7 @@ class PasswordStore : BaseGitActivity() {
|
|||
Snackbar.LENGTH_INDEFINITE
|
||||
).run {
|
||||
setAction(getString(R.string.snackbar_action_grant)) {
|
||||
registerForActivityResult(RequestPermission()) { granted ->
|
||||
if (granted) checkLocalRepository()
|
||||
}.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
storagePermissionRequest.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
dismiss()
|
||||
}
|
||||
show()
|
||||
|
@ -305,11 +394,7 @@ class PasswordStore : BaseGitActivity() {
|
|||
private fun checkLocalRepository() {
|
||||
val repo = initialize()
|
||||
if (repo == null) {
|
||||
registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
checkLocalRepository()
|
||||
}
|
||||
}.launch(UserPreference.createDirectorySelectionIntent(this))
|
||||
directorySelectAction.launch(UserPreference.createDirectorySelectionIntent(this))
|
||||
} else {
|
||||
checkLocalRepository(getRepositoryDirectory())
|
||||
}
|
||||
|
@ -422,11 +507,7 @@ class PasswordStore : BaseGitActivity() {
|
|||
val intent = Intent(this, PasswordCreationActivity::class.java)
|
||||
intent.putExtra("FILE_PATH", currentDir.absolutePath)
|
||||
intent.putExtra("REPO_PATH", getRepositoryDirectory().absolutePath)
|
||||
registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
refreshPasswordList()
|
||||
}
|
||||
}.launch(intent)
|
||||
listRefreshAction.launch(intent)
|
||||
}
|
||||
|
||||
fun createFolder() {
|
||||
|
@ -477,80 +558,7 @@ class PasswordStore : BaseGitActivity() {
|
|||
val intent = Intent(this, SelectFolderActivity::class.java)
|
||||
val fileLocations = values.map { it.file.absolutePath }.toTypedArray()
|
||||
intent.putExtra("Files", fileLocations)
|
||||
registerForActivityResult(StartActivityForResult()) { result ->
|
||||
val intentData = result.data ?: return@registerForActivityResult
|
||||
val filesToMove = requireNotNull(intentData.getStringArrayExtra("Files"))
|
||||
val target = File(requireNotNull(intentData.getStringExtra("SELECTED_FOLDER_PATH")))
|
||||
val repositoryPath = getRepositoryDirectory().absolutePath
|
||||
if (!target.isDirectory) {
|
||||
e { "Tried moving passwords to a non-existing folder." }
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
d { "Moving passwords to ${intentData.getStringExtra("SELECTED_FOLDER_PATH")}" }
|
||||
d { filesToMove.joinToString(", ") }
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
for (file in filesToMove) {
|
||||
val source = File(file)
|
||||
if (!source.exists()) {
|
||||
e { "Tried moving something that appears non-existent." }
|
||||
continue
|
||||
}
|
||||
val destinationFile = File(target.absolutePath + "/" + source.name)
|
||||
val basename = source.nameWithoutExtension
|
||||
val sourceLongName = getLongName(requireNotNull(source.parent), repositoryPath, basename)
|
||||
val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename)
|
||||
if (destinationFile.exists()) {
|
||||
e { "Trying to move a file that already exists." }
|
||||
withContext(Dispatchers.Main) {
|
||||
MaterialAlertDialogBuilder(this@PasswordStore)
|
||||
.setTitle(resources.getString(R.string.password_exists_title))
|
||||
.setMessage(resources.getString(
|
||||
R.string.password_exists_message,
|
||||
destinationLongName,
|
||||
sourceLongName)
|
||||
)
|
||||
.setPositiveButton(R.string.dialog_ok) { _, _ ->
|
||||
launch(Dispatchers.IO) {
|
||||
moveFile(source, destinationFile)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
launch(Dispatchers.IO) {
|
||||
moveFile(source, destinationFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
when (filesToMove.size) {
|
||||
1 -> {
|
||||
val source = File(filesToMove[0])
|
||||
val basename = source.nameWithoutExtension
|
||||
val sourceLongName = getLongName(requireNotNull(source.parent), repositoryPath, basename)
|
||||
val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename)
|
||||
withContext(Dispatchers.Main) {
|
||||
commitChange(
|
||||
resources.getString(R.string.git_commit_move_text, sourceLongName, destinationLongName),
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val repoDir = getRepositoryDirectory().absolutePath
|
||||
val relativePath = getRelativePath("${target.absolutePath}/", repoDir)
|
||||
withContext(Dispatchers.Main) {
|
||||
commitChange(
|
||||
resources.getString(R.string.git_commit_move_multiple_text, relativePath),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshPasswordList()
|
||||
plist?.dismissActionMode()
|
||||
}.launch(intent)
|
||||
passwordMoveAction.launch(intent)
|
||||
}
|
||||
|
||||
enum class CategoryRenameError(val resource: Int) {
|
||||
|
|
|
@ -65,6 +65,106 @@ typealias ChangeListener = Preference.OnPreferenceChangeListener
|
|||
class UserPreference : AppCompatActivity() {
|
||||
|
||||
private lateinit var prefsFragment: PrefsFragment
|
||||
private var fromIntent = false
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private val directorySelectAction = registerForActivityResult(OpenDocumentTree()) { uri: Uri? ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
|
||||
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 = sharedPrefs
|
||||
|
||||
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) }
|
||||
if (fromIntent) {
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val sshKeyImportAction = registerForActivityResult(OpenDocument()) { uri: Uri? ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
runCatching {
|
||||
SshKey.import(uri)
|
||||
|
||||
Toast.makeText(this, resources.getString(R.string.ssh_key_success_dialog_title), Toast.LENGTH_LONG).show()
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}.onFailure { e ->
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(resources.getString(R.string.ssh_key_error_dialog_title))
|
||||
.setMessage(e.message)
|
||||
.setPositiveButton(resources.getString(R.string.dialog_ok), null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private val storeExportAction = registerForActivityResult(object : OpenDocumentTree() {
|
||||
override fun createIntent(context: Context, input: Uri?): Intent {
|
||||
return super.createIntent(context, input).apply {
|
||||
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
|
||||
}
|
||||
}
|
||||
}) { uri: Uri? ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
val targetDirectory = DocumentFile.fromTreeUri(applicationContext, uri)
|
||||
|
||||
if (targetDirectory != null) {
|
||||
val service = Intent(applicationContext, PasswordExportService::class.java).apply {
|
||||
action = PasswordExportService.ACTION_EXPORT_PASSWORD
|
||||
putExtra("uri", uri)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(service)
|
||||
} else {
|
||||
startService(service)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val storeCustomXkpwdDictionaryAction = registerForActivityResult(OpenDocument()) { uri ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
|
||||
Toast.makeText(
|
||||
this,
|
||||
this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
|
||||
sharedPrefs.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)
|
||||
}
|
||||
|
||||
class PrefsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
|
@ -471,7 +571,10 @@ class UserPreference : AppCompatActivity() {
|
|||
when (intent?.getStringExtra("operation")) {
|
||||
"get_ssh_key" -> getSshKey()
|
||||
"make_ssh_key" -> makeSshKey(false)
|
||||
"git_external" -> selectExternalGitRepository(fromIntent = true)
|
||||
"git_external" -> {
|
||||
fromIntent = true
|
||||
selectExternalGitRepository()
|
||||
}
|
||||
}
|
||||
prefsFragment = PrefsFragment()
|
||||
|
||||
|
@ -484,41 +587,12 @@ class UserPreference : AppCompatActivity() {
|
|||
}
|
||||
|
||||
@Suppress("Deprecation") // for Environment.getExternalStorageDirectory()
|
||||
fun selectExternalGitRepository(fromIntent: Boolean = false) {
|
||||
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) { _, _ ->
|
||||
registerForActivityResult(OpenDocumentTree()) { uri: Uri? ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
|
||||
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 = sharedPrefs
|
||||
|
||||
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) }
|
||||
if (fromIntent) {
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
||||
}.launch(null)
|
||||
directorySelectAction.launch(null)
|
||||
}
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show()
|
||||
|
@ -539,26 +613,7 @@ class UserPreference : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun importSshKey() {
|
||||
registerForActivityResult(OpenDocument()) { uri: Uri? ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
runCatching {
|
||||
SshKey.import(uri)
|
||||
|
||||
Toast.makeText(
|
||||
this,
|
||||
this.resources.getString(R.string.ssh_key_success_dialog_title),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}.onFailure { e ->
|
||||
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(arrayOf("*/*"))
|
||||
sshKeyImportAction.launch(arrayOf("*/*"))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -584,32 +639,7 @@ class UserPreference : AppCompatActivity() {
|
|||
* Exports the passwords
|
||||
*/
|
||||
private fun exportPasswords() {
|
||||
registerForActivityResult(object : OpenDocumentTree() {
|
||||
override fun createIntent(context: Context, input: Uri?): Intent {
|
||||
return super.createIntent(context, input).apply {
|
||||
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
|
||||
}
|
||||
}
|
||||
}) { uri: Uri? ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
val targetDirectory = DocumentFile.fromTreeUri(applicationContext, uri)
|
||||
|
||||
if (targetDirectory != null) {
|
||||
val service = Intent(applicationContext, PasswordExportService::class.java).apply {
|
||||
action = PasswordExportService.ACTION_EXPORT_PASSWORD
|
||||
putExtra("uri", uri)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(service)
|
||||
} else {
|
||||
startService(service)
|
||||
}
|
||||
}
|
||||
}.launch(null)
|
||||
storeExportAction.launch(null)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -628,28 +658,7 @@ class UserPreference : AppCompatActivity() {
|
|||
* Pick custom xkpwd dictionary from sdcard
|
||||
*/
|
||||
private fun storeCustomDictionaryPath() {
|
||||
registerForActivityResult(OpenDocument()) { uri ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
|
||||
Toast.makeText(
|
||||
this,
|
||||
this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
|
||||
sharedPrefs.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(arrayOf("*/*"))
|
||||
storeCustomXkpwdDictionaryAction.launch(arrayOf("*/*"))
|
||||
}
|
||||
|
||||
private val isAccessibilityServiceEnabled: Boolean
|
||||
|
|
|
@ -81,6 +81,18 @@ class AutofillDecryptActivity : AppCompatActivity(), CoroutineScope {
|
|||
}
|
||||
}
|
||||
|
||||
private val decryptInteractionRequiredAction = registerForActivityResult(StartIntentSenderForResult()) { result ->
|
||||
if (continueAfterUserInteraction != null) {
|
||||
val data = result.data
|
||||
if (result.resultCode == RESULT_OK && data != null) {
|
||||
continueAfterUserInteraction?.resume(data)
|
||||
} else {
|
||||
continueAfterUserInteraction?.resumeWithException(Exception("OpenPgpApi ACTION_DECRYPT_VERIFY failed to continue after user interaction"))
|
||||
}
|
||||
continueAfterUserInteraction = null
|
||||
}
|
||||
}
|
||||
|
||||
private var continueAfterUserInteraction: Continuation<Intent>? = null
|
||||
private lateinit var directoryStructure: DirectoryStructure
|
||||
|
||||
|
@ -198,17 +210,7 @@ class AutofillDecryptActivity : AppCompatActivity(), CoroutineScope {
|
|||
val intentToResume = withContext(Dispatchers.Main) {
|
||||
suspendCoroutine<Intent> { cont ->
|
||||
continueAfterUserInteraction = cont
|
||||
registerForActivityResult(StartIntentSenderForResult()) { result ->
|
||||
if (continueAfterUserInteraction != null) {
|
||||
val data = result.data
|
||||
if (result.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())
|
||||
decryptInteractionRequiredAction.launch(IntentSenderRequest.Builder(pendingIntent.intentSender).build())
|
||||
}
|
||||
}
|
||||
decryptCredential(file, intentToResume)
|
||||
|
|
|
@ -79,6 +79,13 @@ class AutofillFilterView : AppCompatActivity() {
|
|||
ViewModelProvider.AndroidViewModelFactory(application)
|
||||
}
|
||||
|
||||
private val decryptAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
setResult(RESULT_OK, result.data)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
@ -197,12 +204,7 @@ class AutofillFilterView : AppCompatActivity() {
|
|||
item.file
|
||||
)
|
||||
// intent?.extras? is checked to be non-null in onCreate
|
||||
registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
setResult(RESULT_OK, result.data)
|
||||
}
|
||||
finish()
|
||||
}.launch(AutofillDecryptActivity.makeDecryptFileIntent(
|
||||
decryptAction.launch(AutofillDecryptActivity.makeDecryptFileIntent(
|
||||
item.file,
|
||||
intent!!.extras!!,
|
||||
this
|
||||
|
|
|
@ -47,6 +47,7 @@ import java.io.File
|
|||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.msfjarvis.openpgpktx.util.OpenPgpApi
|
||||
import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
|
||||
import me.msfjarvis.openpgpktx.util.OpenPgpUtils
|
||||
|
@ -63,6 +64,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
private val oldFileName by lazy { intent.getStringExtra(EXTRA_FILE_NAME) }
|
||||
private var oldCategory: String? = null
|
||||
private var copy: Boolean = false
|
||||
private var encryptionIntent: Intent = Intent()
|
||||
|
||||
private val userInteractionRequiredResult = registerForActivityResult(StartIntentSenderForResult()) { result ->
|
||||
if (result.data == null) {
|
||||
|
@ -80,6 +82,41 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
}
|
||||
}
|
||||
|
||||
private val otpImportAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
binding.otpImportButton.isVisible = false
|
||||
val intentResult = IntentIntegrator.parseActivityResult(RESULT_OK, result.data)
|
||||
val contents = "${intentResult.contents}\n"
|
||||
val currentExtras = binding.extraContent.text.toString()
|
||||
if (currentExtras.isNotEmpty() && currentExtras.last() != '\n')
|
||||
binding.extraContent.append("\n$contents")
|
||||
else
|
||||
binding.extraContent.append(contents)
|
||||
snackbar(message = getString(R.string.otp_import_success))
|
||||
} else {
|
||||
snackbar(message = getString(R.string.otp_import_failure))
|
||||
}
|
||||
}
|
||||
|
||||
private val gpgKeySelectAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds ->
|
||||
lifecycleScope.launch {
|
||||
val gpgIdentifierFile = File(PasswordRepository.getRepositoryDirectory(), ".gpg-id")
|
||||
withContext(Dispatchers.IO) {
|
||||
gpgIdentifierFile.writeText(keyIds.joinToString("\n"))
|
||||
}
|
||||
commitChange(getString(
|
||||
R.string.git_commit_gpg_id,
|
||||
getLongName(gpgIdentifierFile.parentFile!!.absolutePath, repoPath, gpgIdentifierFile.name)
|
||||
)).onSuccess {
|
||||
encrypt(encryptionIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.findTillRoot(fileName: String, rootPath: File): File? {
|
||||
val gpgFile = File(this, fileName)
|
||||
if (gpgFile.exists()) return gpgFile
|
||||
|
@ -107,26 +144,11 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
setContentView(root)
|
||||
generatePassword.setOnClickListener { generatePassword() }
|
||||
otpImportButton.setOnClickListener {
|
||||
registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
otpImportButton.isVisible = false
|
||||
val intentResult = IntentIntegrator.parseActivityResult(RESULT_OK, result.data)
|
||||
val contents = "${intentResult.contents}\n"
|
||||
val currentExtras = extraContent.text.toString()
|
||||
if (currentExtras.isNotEmpty() && currentExtras.last() != '\n')
|
||||
extraContent.append("\n$contents")
|
||||
else
|
||||
extraContent.append(contents)
|
||||
snackbar(message = getString(R.string.otp_import_success))
|
||||
} else {
|
||||
snackbar(message = getString(R.string.otp_import_failure))
|
||||
}
|
||||
}.launch(
|
||||
IntentIntegrator(this@PasswordCreationActivity)
|
||||
.setOrientationLocked(false)
|
||||
.setBeepEnabled(false)
|
||||
.setDesiredBarcodeFormats(QR_CODE)
|
||||
.createScanIntent()
|
||||
otpImportAction.launch(IntentIntegrator(this@PasswordCreationActivity)
|
||||
.setOrientationLocked(false)
|
||||
.setBeepEnabled(false)
|
||||
.setDesiredBarcodeFormats(QR_CODE)
|
||||
.createScanIntent()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -307,8 +329,8 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
copyPasswordToClipboard(editPass)
|
||||
}
|
||||
|
||||
val data = receivedIntent ?: Intent()
|
||||
data.action = OpenPgpApi.ACTION_ENCRYPT
|
||||
encryptionIntent = receivedIntent ?: Intent()
|
||||
encryptionIntent.action = OpenPgpApi.ACTION_ENCRYPT
|
||||
|
||||
// pass enters the key ID into `.gpg-id`.
|
||||
val repoRoot = PasswordRepository.getRepositoryDirectory()
|
||||
|
@ -333,33 +355,19 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
}
|
||||
}
|
||||
if (gpgIdentifiers.isEmpty()) {
|
||||
registerForActivityResult(StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds ->
|
||||
gpgIdentifierFile.writeText(keyIds.joinToString("\n"))
|
||||
lifecycleScope.launch {
|
||||
commitChange(getString(
|
||||
R.string.git_commit_gpg_id,
|
||||
getLongName(gpgIdentifierFile.parentFile!!.absolutePath, repoPath, gpgIdentifierFile.name)
|
||||
)).onSuccess {
|
||||
encrypt(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.launch(Intent(this@PasswordCreationActivity, GetKeyIdsActivity::class.java))
|
||||
gpgKeySelectAction.launch(Intent(this@PasswordCreationActivity, GetKeyIdsActivity::class.java))
|
||||
return@with
|
||||
}
|
||||
val keyIds = gpgIdentifiers.filterIsInstance<GpgIdentifier.KeyId>().map { it.id }.toLongArray()
|
||||
if (keyIds.isNotEmpty()) {
|
||||
data.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIds)
|
||||
encryptionIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIds)
|
||||
}
|
||||
val userIds = gpgIdentifiers.filterIsInstance<GpgIdentifier.UserId>().map { it.email }.toTypedArray()
|
||||
if (userIds.isNotEmpty()) {
|
||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds)
|
||||
encryptionIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds)
|
||||
}
|
||||
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true)
|
||||
encryptionIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true)
|
||||
|
||||
val content = "$editPass\n$editExtra"
|
||||
val inputStream = ByteArrayInputStream(content.toByteArray())
|
||||
|
@ -386,7 +394,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
}
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
api?.executeApiAsync(data, inputStream, outputStream) { result ->
|
||||
api?.executeApiAsync(encryptionIntent, inputStream, outputStream) { result ->
|
||||
when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
||||
OpenPgpApi.RESULT_CODE_SUCCESS -> {
|
||||
runCatching {
|
||||
|
|
|
@ -31,6 +31,30 @@ import me.msfjarvis.openpgpktx.util.OpenPgpApi
|
|||
|
||||
class FolderCreationDialogFragment : DialogFragment() {
|
||||
|
||||
private lateinit var newFolder: File
|
||||
|
||||
private val keySelectAction = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == AppCompatActivity.RESULT_OK) {
|
||||
result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds ->
|
||||
val gpgIdentifierFile = File(newFolder, ".gpg-id")
|
||||
gpgIdentifierFile.writeText(keyIds.joinToString("\n"))
|
||||
val repo = PasswordRepository.getRepository(null)
|
||||
if (repo != null) {
|
||||
lifecycleScope.launch {
|
||||
val repoPath = getRepositoryDirectory().absolutePath
|
||||
requireActivity().commitChange(
|
||||
getString(
|
||||
R.string.git_commit_gpg_id,
|
||||
BasePgpActivity.getLongName(gpgIdentifierFile.parentFile!!.absolutePath, repoPath, gpgIdentifierFile.name)
|
||||
),
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val alertDialogBuilder = MaterialAlertDialogBuilder(requireContext())
|
||||
alertDialogBuilder.setTitle(R.string.title_create_folder)
|
||||
|
@ -53,7 +77,7 @@ class FolderCreationDialogFragment : DialogFragment() {
|
|||
val dialog = requireDialog()
|
||||
val folderNameView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text)
|
||||
val folderNameViewContainer = dialog.findViewById<TextInputLayout>(R.id.folder_name_container)
|
||||
val newFolder = File("$currentDir/${folderNameView.text}")
|
||||
newFolder = File("$currentDir/${folderNameView.text}")
|
||||
folderNameViewContainer.error = when {
|
||||
newFolder.isFile -> getString(R.string.folder_creation_err_file_exists)
|
||||
newFolder.isDirectory -> getString(R.string.folder_creation_err_folder_exists)
|
||||
|
@ -63,27 +87,7 @@ class FolderCreationDialogFragment : DialogFragment() {
|
|||
newFolder.mkdirs()
|
||||
(requireActivity() as PasswordStore).refreshPasswordList(newFolder)
|
||||
if (dialog.findViewById<MaterialCheckBox>(R.id.set_gpg_key).isChecked) {
|
||||
val gpgIdentifierFile = File(newFolder, ".gpg-id")
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == AppCompatActivity.RESULT_OK) {
|
||||
result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds ->
|
||||
gpgIdentifierFile.writeText(keyIds.joinToString("\n"))
|
||||
val repo = PasswordRepository.getRepository(null)
|
||||
if (repo != null) {
|
||||
lifecycleScope.launch {
|
||||
val repoPath = getRepositoryDirectory().absolutePath
|
||||
requireActivity().commitChange(
|
||||
getString(
|
||||
R.string.git_commit_gpg_id,
|
||||
BasePgpActivity.getLongName(gpgIdentifierFile.parentFile!!.absolutePath, repoPath, gpgIdentifierFile.name)
|
||||
),
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.launch(Intent(requireContext(), GetKeyIdsActivity::class.java))
|
||||
keySelectAction.launch(Intent(requireContext(), GetKeyIdsActivity::class.java))
|
||||
return
|
||||
} else {
|
||||
dismiss()
|
||||
|
|
Loading…
Reference in a new issue