diff options
Diffstat (limited to 'app/src/main/java/com')
24 files changed, 350 insertions, 354 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt index e91df9c6..ff022fab 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt @@ -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() diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt index 54fc7c97..e157965f 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt @@ -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) diff --git a/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt b/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt index 568f86f3..848977f0 100644 --- a/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/SelectFolderActivity.kt @@ -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() } } diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt index 03e314f6..ea3bac38 100644 --- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt +++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt @@ -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 } @@ -538,22 +565,74 @@ 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" /** diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.kt index 87881968..a64cdced 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.kt index 2410d53e..269e6624 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.kt index 860d8459..e8bf5537 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.kt index 811ff7af..5752b055 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt index 177233a8..bfb1c5d0 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillDecryptActivity.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillDecryptActivity.kt index d7d8daaf..c5e47544 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillDecryptActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillDecryptActivity.kt @@ -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 - } - } } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt index 1a969b51..7467b9c9 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillFilterActivity.kt @@ -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 + )) } } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSaveActivity.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSaveActivity.kt index b5bd9e38..87359333 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSaveActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSaveActivity.kt @@ -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 + 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)) + } + 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) } - 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() } - } 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) } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt index ae2bbc07..341bd3fd 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt @@ -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 } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt b/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt index 8533187f..6a3fdfd5 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt @@ -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>> /** diff --git a/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.kt index 0e89af0f..2af86303 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/CloneOperation.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt index 84e08dfc..66d953e9 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt @@ -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() } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt index 48c28920..c9037e0a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.kt index c543f885..436c81bb 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/PullOperation.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.kt index fc58705f..7a936e73 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/PushOperation.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt index be9e929f..fbd747b3 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.kt index 3af6f08d..6d99828e 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/SyncOperation.kt @@ -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 diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java b/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java index 5a51d28a..aa62fdae 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java @@ -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; } diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt index 55a767d8..94379bbf 100644 --- a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt +++ b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt @@ -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" - } } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt index 6ba03743..ad460550 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt +++ b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt @@ -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) } |