summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt12
-rw-r--r--app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt9
-rw-r--r--app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt47
-rw-r--r--app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt4
-rw-r--r--app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt7
-rw-r--r--app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt16
-rw-r--r--app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt5
-rw-r--r--app/src/main/java/app/passwordstore/ui/git/base/BaseGitActivity.kt5
-rw-r--r--app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt3
-rw-r--r--app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt4
-rw-r--r--app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt19
-rw-r--r--app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt9
-rw-r--r--app/src/main/java/app/passwordstore/util/git/GitCommandExecutor.kt17
-rw-r--r--app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt10
-rw-r--r--app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt4
-rw-r--r--app/src/main/java/app/passwordstore/util/services/ClipboardService.kt14
-rw-r--r--app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt9
17 files changed, 107 insertions, 87 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt b/app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt
index 35c34690..f7d5cf9a 100644
--- a/app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt
+++ b/app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt
@@ -15,17 +15,21 @@ import androidx.recyclerview.selection.Selection
import androidx.recyclerview.widget.RecyclerView
import app.passwordstore.R
import app.passwordstore.data.password.PasswordItem
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.viewmodel.SearchableRepositoryAdapter
import app.passwordstore.util.viewmodel.stableId
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-open class PasswordItemRecyclerAdapter(coroutineScope: CoroutineScope) :
+open class PasswordItemRecyclerAdapter(
+ coroutineScope: CoroutineScope,
+ dispatcherProvider: DispatcherProvider,
+) :
SearchableRepositoryAdapter<PasswordItemRecyclerAdapter.PasswordItemViewHolder>(
R.layout.password_row_layout,
::PasswordItemViewHolder,
coroutineScope,
+ dispatcherProvider,
PasswordItemViewHolder::bind,
) {
@@ -52,7 +56,7 @@ open class PasswordItemRecyclerAdapter(coroutineScope: CoroutineScope) :
private val folderIndicator: AppCompatImageView = itemView.findViewById(R.id.folder_indicator)
var itemDetails: ItemDetailsLookup.ItemDetails<String>? = null
- suspend fun bind(item: PasswordItem) {
+ suspend fun bind(item: PasswordItem, dispatcherProvider: DispatcherProvider) {
val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
val source =
if (parentPath.isNotEmpty()) {
@@ -66,7 +70,7 @@ open class PasswordItemRecyclerAdapter(coroutineScope: CoroutineScope) :
if (item.type == PasswordItem.TYPE_CATEGORY) {
folderIndicator.visibility = View.VISIBLE
val count =
- withContext(Dispatchers.IO) {
+ withContext(dispatcherProvider.io()) {
item.file.listFiles { path -> path.isDirectory || path.extension == "gpg" }?.size ?: 0
}
childCount.visibility = if (count > 0) View.VISIBLE else View.GONE
diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt
index 536e11a9..023822a4 100644
--- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt
+++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt
@@ -33,7 +33,6 @@ import dagger.hilt.android.AndroidEntryPoint
import java.io.ByteArrayOutputStream
import java.io.File
import javax.inject.Inject
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -101,7 +100,7 @@ class AutofillDecryptActivity : BasePgpActivity() {
private fun askPassphrase(filePath: String, clientState: Bundle, action: AutofillAction) {
val dialog = PasswordDialog()
lifecycleScope.launch {
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
dialog.password.collectLatest { value ->
if (value != null) {
decrypt(File(filePath), clientState, action, value)
@@ -129,14 +128,14 @@ class AutofillDecryptActivity : BasePgpActivity() {
clientState,
action
)
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
setResult(
RESULT_OK,
Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset) }
)
}
}
- withContext(Dispatchers.Main) { finish() }
+ withContext(dispatcherProvider.main()) { finish() }
}
private suspend fun decryptCredential(file: File, password: String): Credentials? {
@@ -148,7 +147,7 @@ class AutofillDecryptActivity : BasePgpActivity() {
}
.onSuccess { encryptedInput ->
runCatching {
- withContext(Dispatchers.IO) {
+ withContext(dispatcherProvider.io()) {
val outputStream = ByteArrayOutputStream()
repository.decrypt(
password,
diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt
index 3a5e5d19..f0267f26 100644
--- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt
+++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt
@@ -20,7 +20,6 @@ import androidx.core.text.bold
import androidx.core.text.buildSpannedString
import androidx.core.text.underline
import androidx.core.widget.addTextChangedListener
-import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import app.passwordstore.R
@@ -29,6 +28,7 @@ import app.passwordstore.databinding.ActivityOreoAutofillFilterBinding
import app.passwordstore.util.autofill.AutofillMatcher
import app.passwordstore.util.autofill.AutofillPreferences
import app.passwordstore.util.autofill.DirectoryStructure
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.viewmodel.FilterMode
import app.passwordstore.util.viewmodel.ListMode
@@ -37,14 +37,17 @@ import app.passwordstore.util.viewmodel.SearchableRepositoryAdapter
import app.passwordstore.util.viewmodel.SearchableRepositoryViewModel
import com.github.androidpasswordstore.autofillparser.FormOrigin
import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import javax.inject.Inject
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
import logcat.LogPriority.ERROR
import logcat.logcat
@AndroidEntryPoint
class AutofillFilterView : AppCompatActivity() {
+ @Inject lateinit var dispatcherProvider: DispatcherProvider
+
companion object {
private const val HEIGHT_PERCENTAGE = 0.9
@@ -142,7 +145,8 @@ class AutofillFilterView : AppCompatActivity() {
R.layout.oreo_autofill_filter_row,
::PasswordViewHolder,
lifecycleScope,
- ) { item ->
+ dispatcherProvider,
+ ) { item, _ ->
val file = item.file.relativeTo(item.rootDir)
val pathToIdentifier = directoryStructure.getPathToIdentifierFor(file)
val identifier = directoryStructure.getIdentifierFor(file)
@@ -191,23 +195,24 @@ class AutofillFilterView : AppCompatActivity() {
R.string.oreo_autofill_match_with,
formOrigin.getPrettyIdentifier(applicationContext)
)
- model.searchResult
- .flowWithLifecycle(lifecycle)
- .onEach { result ->
- val list = result.passwordItems
- (rvPassword.adapter as SearchableRepositoryAdapter).submitList(list) {
- rvPassword.scrollToPosition(0)
- }
- // Switch RecyclerView out for a "no results" message if the new list is empty and
- // the message is not yet shown (and vice versa).
- if (
- (list.isEmpty() && rvPasswordSwitcher.nextView.id == rvPasswordEmpty.id) ||
- (list.isNotEmpty() && rvPasswordSwitcher.nextView.id == rvPassword.id)
- ) {
- rvPasswordSwitcher.showNext()
- }
- }
- .launchIn(lifecycleScope)
+ lifecycleScope.launch { handleSearchResults() }
+ }
+ }
+
+ private suspend fun handleSearchResults() {
+ model.searchResult.collect { result ->
+ val list = result.passwordItems
+ (binding.rvPassword.adapter as SearchableRepositoryAdapter).submitList(list) {
+ binding.rvPassword.scrollToPosition(0)
+ }
+ // Switch RecyclerView out for a "no results" message if the new list is empty and
+ // the message is not yet shown (and vice versa).
+ if (
+ (list.isEmpty() && binding.rvPasswordSwitcher.nextView.id == binding.rvPasswordEmpty.id) ||
+ (list.isNotEmpty() && binding.rvPasswordSwitcher.nextView.id == binding.rvPassword.id)
+ ) {
+ binding.rvPasswordSwitcher.showNext()
+ }
}
}
diff --git a/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt b/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt
index 4de3c120..56ba382c 100644
--- a/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt
+++ b/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt
@@ -36,8 +36,6 @@ import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import logcat.LogPriority.ERROR
@@ -268,7 +266,7 @@ class DecryptActivity : BasePgpActivity() {
binding.recyclerView.itemAnimator = null
if (entry.hasTotp()) {
- entry.totp.onEach(adapter::updateOTPCode).launchIn(lifecycleScope)
+ entry.totp.collect(adapter::updateOTPCode)
}
}
diff --git a/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt b/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt
index 8d5cdb11..f724ec3c 100644
--- a/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt
+++ b/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt
@@ -56,7 +56,6 @@ import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import javax.inject.Inject
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import logcat.LogPriority.ERROR
@@ -355,10 +354,10 @@ class PasswordCreationActivity : BasePgpActivity() {
else -> "$fullPath/$editName.gpg"
}
- lifecycleScope.launch(Dispatchers.Main) {
+ lifecycleScope.launch(dispatcherProvider.main()) {
runCatching {
val result =
- withContext(Dispatchers.IO) {
+ withContext(dispatcherProvider.io()) {
val outputStream = ByteArrayOutputStream()
repository.encrypt(gpgIdentifiers, content.byteInputStream(), outputStream)
outputStream
@@ -380,7 +379,7 @@ class PasswordCreationActivity : BasePgpActivity() {
return@runCatching
}
- withContext(Dispatchers.IO) { file.writeBytes(result.toByteArray()) }
+ withContext(dispatcherProvider.io()) { file.writeBytes(result.toByteArray()) }
// associate the new password name with the last name's timestamp in
// history
diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt b/app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt
index bb3b6f6f..e158a4c1 100644
--- a/app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt
+++ b/app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt
@@ -26,9 +26,8 @@ import app.passwordstore.util.settings.PreferenceKeys.DICEWARE_SEPARATOR
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
-import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
import reactivecircus.flowbinding.android.widget.afterTextChanges
@AndroidEntryPoint
@@ -47,12 +46,13 @@ class DicewarePasswordGeneratorDialogFragment : DialogFragment() {
binding.passwordLengthText.setText(prefs.getInt(DICEWARE_LENGTH, 5).toString())
binding.passwordText.typeface = Typeface.MONOSPACE
- merge(
- binding.passwordLengthText.afterTextChanges(),
- binding.passwordSeparatorText.afterTextChanges(),
- )
- .onEach { generatePassword(binding) }
- .launchIn(lifecycleScope)
+ lifecycleScope.launch {
+ merge(
+ binding.passwordLengthText.afterTextChanges(),
+ binding.passwordSeparatorText.afterTextChanges(),
+ )
+ .collect { _ -> generatePassword(binding) }
+ }
return builder
.run {
setTitle(R.string.pwgen_title)
diff --git a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt
index a7827e93..a1c33e3f 100644
--- a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt
+++ b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt
@@ -18,6 +18,7 @@ import app.passwordstore.data.password.PasswordItem
import app.passwordstore.databinding.PasswordRecyclerViewBinding
import app.passwordstore.ui.adapters.PasswordItemRecyclerAdapter
import app.passwordstore.ui.passwords.PasswordStore
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.viewmodel.ListMode
import app.passwordstore.util.viewmodel.SearchableRepositoryViewModel
@@ -25,6 +26,7 @@ import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching
import dagger.hilt.android.AndroidEntryPoint
import java.io.File
+import javax.inject.Inject
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import me.zhanghai.android.fastscroll.FastScrollerBuilder
@@ -32,6 +34,7 @@ import me.zhanghai.android.fastscroll.FastScrollerBuilder
@AndroidEntryPoint
class SelectFolderFragment : Fragment(R.layout.password_recycler_view) {
+ @Inject lateinit var dispatcherProvider: DispatcherProvider
private val binding by viewBinding(PasswordRecyclerViewBinding::bind)
private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter
private lateinit var listener: OnFragmentInteractionListener
@@ -42,7 +45,7 @@ class SelectFolderFragment : Fragment(R.layout.password_recycler_view) {
super.onViewCreated(view, savedInstanceState)
binding.fab.hide()
recyclerAdapter =
- PasswordItemRecyclerAdapter(lifecycleScope).onItemClicked { _, item ->
+ PasswordItemRecyclerAdapter(lifecycleScope, dispatcherProvider).onItemClicked { _, item ->
listener.onFragmentInteraction(item)
}
binding.passRecycler.apply {
diff --git a/app/src/main/java/app/passwordstore/ui/git/base/BaseGitActivity.kt b/app/src/main/java/app/passwordstore/ui/git/base/BaseGitActivity.kt
index 8ae2da0f..54a26365 100644
--- a/app/src/main/java/app/passwordstore/ui/git/base/BaseGitActivity.kt
+++ b/app/src/main/java/app/passwordstore/ui/git/base/BaseGitActivity.kt
@@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import app.passwordstore.R
import app.passwordstore.injection.prefs.GitPreferences
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.sharedPrefs
import app.passwordstore.util.git.ErrorMessages
import app.passwordstore.util.git.operation.BreakOutOfDetached
@@ -27,7 +28,6 @@ import com.github.michaelbull.result.mapError
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import logcat.asLog
import logcat.logcat
@@ -55,6 +55,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
}
@Inject lateinit var gitSettings: GitSettings
+ @Inject lateinit var dispatcherProvider: DispatcherProvider
@GitPreferences @Inject lateinit var gitPrefs: SharedPreferences
/**
@@ -105,7 +106,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
gitPrefs.edit { remove(PreferenceKeys.HTTPS_PASSWORD) }
sharedPrefs.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) }
logcat { error.asLog() }
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
MaterialAlertDialogBuilder(this@BaseGitActivity).run {
setTitle(resources.getString(R.string.jgit_error_dialog_title))
setMessage(ErrorMessages[error])
diff --git a/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt b/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt
index 0fab992f..5e2bafb9 100644
--- a/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt
+++ b/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt
@@ -30,7 +30,6 @@ import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import logcat.LogPriority.ERROR
@@ -242,7 +241,7 @@ class GitServerConfigActivity : BaseGitActivity() {
message = getString(R.string.delete_directory_progress_text),
length = Snackbar.LENGTH_INDEFINITE
)
- withContext(Dispatchers.IO) {
+ withContext(dispatcherProvider.io()) {
localDir.deleteRecursively()
localDir.mkdirs()
}
diff --git a/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt b/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt
index 65570bcc..d6bcce11 100644
--- a/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt
+++ b/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt
@@ -33,6 +33,7 @@ import app.passwordstore.ui.dialogs.ItemCreationBottomSheet
import app.passwordstore.ui.git.base.BaseGitActivity
import app.passwordstore.ui.git.config.GitServerConfigActivity
import app.passwordstore.ui.util.OnOffItemAnimator
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.base64
import app.passwordstore.util.extensions.getString
import app.passwordstore.util.extensions.sharedPrefs
@@ -59,6 +60,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
@Inject lateinit var gitSettings: GitSettings
@Inject lateinit var shortcutHandler: ShortcutHandler
+ @Inject lateinit var dispatcherProvider: DispatcherProvider
@Inject @SettingsPreferences lateinit var prefs: SharedPreferences
private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter
private lateinit var listener: OnFragmentInteractionListener
@@ -139,7 +141,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
}
recyclerAdapter =
- PasswordItemRecyclerAdapter(lifecycleScope)
+ PasswordItemRecyclerAdapter(lifecycleScope, dispatcherProvider)
.onItemClicked { _, item -> listener.onFragmentInteraction(item) }
.onSelectionChanged { selection ->
// In order to not interfere with drag selection, we disable the
diff --git a/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt b/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt
index 4c35d6d0..80b08b77 100644
--- a/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt
+++ b/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt
@@ -55,7 +55,6 @@ import dagger.hilt.android.AndroidEntryPoint
import java.io.File
import java.lang.Character.UnicodeBlock
import javax.inject.Inject
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import logcat.LogPriority.ERROR
@@ -94,7 +93,7 @@ class PasswordStore : BaseGitActivity() {
logcat { "Moving passwords to ${intentData.getStringExtra("SELECTED_FOLDER_PATH")}" }
logcat { filesToMove.joinToString(", ") }
- lifecycleScope.launch(Dispatchers.IO) {
+ lifecycleScope.launch(dispatcherProvider.io()) {
for (file in filesToMove) {
val source = File(file)
if (!source.exists()) {
@@ -107,7 +106,7 @@ class PasswordStore : BaseGitActivity() {
val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename)
if (destinationFile.exists()) {
logcat(ERROR) { "Trying to move a file that already exists." }
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
MaterialAlertDialogBuilder(this@PasswordStore)
.setTitle(resources.getString(R.string.password_exists_title))
.setMessage(
@@ -118,13 +117,13 @@ class PasswordStore : BaseGitActivity() {
)
)
.setPositiveButton(R.string.dialog_ok) { _, _ ->
- launch(Dispatchers.IO) { moveFile(source, destinationFile) }
+ launch(dispatcherProvider.io()) { moveFile(source, destinationFile) }
}
.setNegativeButton(R.string.dialog_cancel, null)
.show()
}
} else {
- launch(Dispatchers.IO) { moveFile(source, destinationFile) }
+ launch(dispatcherProvider.io()) { moveFile(source, destinationFile) }
}
}
when (filesToMove.size) {
@@ -134,7 +133,7 @@ class PasswordStore : BaseGitActivity() {
val sourceLongName =
getLongName(requireNotNull(source.parent), repositoryPath, basename)
val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename)
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
commitChange(
resources.getString(
R.string.git_commit_move_text,
@@ -147,7 +146,7 @@ class PasswordStore : BaseGitActivity() {
else -> {
val repoDir = PasswordRepository.getRepositoryDirectory().absolutePath
val relativePath = getRelativePath("${target.absolutePath}/", repoDir)
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
commitChange(
resources.getString(R.string.git_commit_move_multiple_text, relativePath),
)
@@ -493,7 +492,7 @@ class PasswordStore : BaseGitActivity() {
!newCategory.isInsideRepository() ->
renameCategory(oldCategory, CategoryRenameError.DestinationOutsideRepo)
else ->
- lifecycleScope.launch(Dispatchers.IO) {
+ lifecycleScope.launch(dispatcherProvider.io()) {
moveFile(oldCategory.file, newCategory)
// associate the new category with the last category's timestamp in
@@ -508,7 +507,7 @@ class PasswordStore : BaseGitActivity() {
}
}
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
commitChange(
resources.getString(
R.string.git_commit_move_text,
@@ -573,7 +572,7 @@ class PasswordStore : BaseGitActivity() {
}
if (!source.renameTo(destinationFile)) {
logcat(ERROR) { "Something went wrong while moving $source to $destinationFile." }
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
MaterialAlertDialogBuilder(this@PasswordStore)
.setTitle(R.string.password_move_error_title)
.setMessage(getString(R.string.password_move_error_message, source, destinationFile))
diff --git a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt
index 68267fff..790751f9 100644
--- a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt
+++ b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt
@@ -20,6 +20,7 @@ import app.passwordstore.injection.prefs.GitPreferences
import app.passwordstore.ssh.SSHKeyAlgorithm
import app.passwordstore.util.auth.BiometricAuthenticator
import app.passwordstore.util.auth.BiometricAuthenticator.Result
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.keyguardManager
import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.ssh.SSHFacade
@@ -30,7 +31,6 @@ import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -41,6 +41,7 @@ class SshKeyGenActivity : AppCompatActivity() {
private val binding by viewBinding(ActivitySshKeygenBinding::inflate)
@GitPreferences @Inject lateinit var gitPrefs: SharedPreferences
@Inject lateinit var sshFacade: SSHFacade
+ @Inject lateinit var dispatcherProvider: DispatcherProvider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -107,12 +108,12 @@ class SshKeyGenActivity : AppCompatActivity() {
}
binding.generate.text = getString(R.string.ssh_key_gen_generating_progress)
val result = runCatching {
- withContext(Dispatchers.IO) {
+ withContext(dispatcherProvider.io()) {
val requireAuthentication = binding.keyRequireAuthentication.isChecked
if (requireAuthentication) {
val result =
- withContext(Dispatchers.Main) {
- suspendCoroutine<Result> { cont ->
+ withContext(dispatcherProvider.main()) {
+ suspendCoroutine { cont ->
BiometricAuthenticator.authenticate(
this@SshKeyGenActivity,
R.string.biometric_prompt_title_ssh_keygen
diff --git a/app/src/main/java/app/passwordstore/util/git/GitCommandExecutor.kt b/app/src/main/java/app/passwordstore/util/git/GitCommandExecutor.kt
index 1fdcf24e..4299d968 100644
--- a/app/src/main/java/app/passwordstore/util/git/GitCommandExecutor.kt
+++ b/app/src/main/java/app/passwordstore/util/git/GitCommandExecutor.kt
@@ -8,6 +8,7 @@ package app.passwordstore.util.git
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import app.passwordstore.R
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.snackbar
import app.passwordstore.util.extensions.unsafeLazy
import app.passwordstore.util.git.GitException.PullException
@@ -21,7 +22,6 @@ import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.CommitCommand
import org.eclipse.jgit.api.PullCommand
@@ -44,6 +44,7 @@ class GitCommandExecutor(
suspend fun execute(): Result<Unit, Throwable> {
val gitSettings = hiltEntryPoint.gitSettings()
+ val dispatcherProvider = hiltEntryPoint.dispatcherProvider()
val snackbar =
activity.snackbar(
message = activity.resources.getString(R.string.git_operation_running),
@@ -55,13 +56,13 @@ class GitCommandExecutor(
for (command in operation.commands) {
when (command) {
is StatusCommand -> {
- val res = withContext(Dispatchers.IO) { command.call() }
+ val res = withContext(dispatcherProvider.io()) { command.call() }
nbChanges = res.uncommittedChanges.size
}
is CommitCommand -> {
// the previous status will eventually be used to avoid a commit
if (nbChanges > 0) {
- withContext(Dispatchers.IO) {
+ withContext(dispatcherProvider.io()) {
val name = gitSettings.authorName.ifEmpty { "root" }
val email = gitSettings.authorEmail.ifEmpty { "localhost" }
val identity = PersonIdent(name, email)
@@ -70,7 +71,7 @@ class GitCommandExecutor(
}
}
is PullCommand -> {
- val result = withContext(Dispatchers.IO) { command.call() }
+ val result = withContext(dispatcherProvider.io()) { command.call() }
if (result.rebaseResult != null) {
if (!result.rebaseResult.status.isSuccessful) {
throw PullException.PullRebaseFailed
@@ -82,7 +83,7 @@ class GitCommandExecutor(
}
}
is PushCommand -> {
- val results = withContext(Dispatchers.IO) { command.call() }
+ val results = withContext(dispatcherProvider.io()) { command.call() }
for (result in results) {
// Code imported (modified) from Gerrit PushOp, license Apache v2
for (rru in result.remoteUpdates) {
@@ -102,7 +103,7 @@ class GitCommandExecutor(
}
}
RemoteRefUpdate.Status.UP_TO_DATE -> {
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.main()) {
Toast.makeText(
activity,
activity.applicationContext.getString(R.string.git_push_up_to_date),
@@ -117,7 +118,7 @@ class GitCommandExecutor(
}
}
else -> {
- withContext(Dispatchers.IO) { command.call() }
+ withContext(dispatcherProvider.io()) { command.call() }
}
}
}
@@ -130,5 +131,7 @@ class GitCommandExecutor(
interface GitCommandExecutorEntryPoint {
fun gitSettings(): GitSettings
+
+ fun dispatcherProvider(): DispatcherProvider
}
}
diff --git a/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt b/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt
index 396e3c7d..298ae4b1 100644
--- a/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt
+++ b/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt
@@ -17,6 +17,7 @@ import app.passwordstore.util.auth.BiometricAuthenticator.Result.Cancelled
import app.passwordstore.util.auth.BiometricAuthenticator.Result.Failure
import app.passwordstore.util.auth.BiometricAuthenticator.Result.Retry
import app.passwordstore.util.auth.BiometricAuthenticator.Result.Success
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.git.GitCommandExecutor
import app.passwordstore.util.git.sshj.SshAuthMethod
import app.passwordstore.util.git.sshj.SshjSessionFactory
@@ -34,7 +35,6 @@ import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import logcat.LogPriority.ERROR
import logcat.asLog
@@ -176,8 +176,8 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
if (sshFacade.keyExists()) {
if (sshFacade.needsAuthentication()) {
val result =
- withContext(Dispatchers.Main) {
- suspendCoroutine<BiometricAuthenticator.Result> { cont ->
+ withContext(hiltEntryPoint.dispatcherProvider().main()) {
+ suspendCoroutine { cont ->
BiometricAuthenticator.authenticate(
callingActivity,
R.string.biometric_prompt_title_ssh_auth
@@ -233,7 +233,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
open fun preExecute() = true
private suspend fun postExecute() {
- withContext(Dispatchers.IO) { sshSessionFactory?.close() }
+ withContext(hiltEntryPoint.dispatcherProvider().io()) { sshSessionFactory?.close() }
}
companion object {
@@ -246,5 +246,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
@InstallIn(SingletonComponent::class)
interface GitOperationEntryPoint {
fun sshFacade(): SSHFacade
+
+ fun dispatcherProvider(): DispatcherProvider
}
}
diff --git a/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt b/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt
index 787b3e2d..54858530 100644
--- a/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt
+++ b/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt
@@ -57,9 +57,7 @@ abstract class InteractivePasswordFinder : PasswordFinder {
final override fun reqPassword(resource: Resource<*>?): CharArray {
val password =
- runBlocking(Dispatchers.Main) {
- suspendCoroutine<String?> { cont -> askForPassword(cont, isRetry) }
- }
+ runBlocking(Dispatchers.Main) { suspendCoroutine { cont -> askForPassword(cont, isRetry) } }
isRetry = true
return password?.toCharArray() ?: throw SSHException(DisconnectReason.AUTH_CANCELLED_BY_USER)
}
diff --git a/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt b/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt
index dccbd865..47cf5926 100644
--- a/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt
+++ b/app/src/main/java/app/passwordstore/util/services/ClipboardService.kt
@@ -17,11 +17,13 @@ import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
import app.passwordstore.R
+import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.clipboard
import app.passwordstore.util.extensions.sharedPrefs
import app.passwordstore.util.settings.PreferenceKeys
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
@@ -30,9 +32,11 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import logcat.logcat
+@AndroidEntryPoint
class ClipboardService : Service() {
- private val scope = CoroutineScope(Job() + Dispatchers.Main)
+ @Inject lateinit var dispatcherProvider: DispatcherProvider
+ private val scope = CoroutineScope(Job())
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null) {
@@ -52,8 +56,8 @@ class ClipboardService : Service() {
createNotification(time)
scope.launch {
- withContext(Dispatchers.IO) { startTimer(time) }
- withContext(Dispatchers.Main) {
+ withContext(dispatcherProvider.io()) { startTimer(time) }
+ withContext(dispatcherProvider.main()) {
clearClipboard()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
@@ -86,7 +90,7 @@ class ClipboardService : Service() {
val clip = ClipData.newPlainText("pgp_handler_result_pm", "")
clipboard.setPrimaryClip(clip)
if (deepClear) {
- withContext(Dispatchers.IO) {
+ withContext(dispatcherProvider.io()) {
repeat(CLIPBOARD_CLEAR_COUNT) {
val count = (it * 500).toString()
clipboard.setPrimaryClip(ClipData.newPlainText(count, count))
diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
index fcc62ab7..edb82cc0 100644
--- a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
+++ b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt
@@ -38,7 +38,6 @@ import java.util.Locale
import java.util.Stack
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -401,7 +400,9 @@ open class SearchableRepositoryAdapter<T : RecyclerView.ViewHolder>(
private val layoutRes: Int,
private val viewHolderCreator: (view: View) -> T,
private val coroutineScope: CoroutineScope,
- private val viewHolderBinder: suspend T.(item: PasswordItem) -> Unit,
+ private val dispatcherProvider: DispatcherProvider,
+ private val viewHolderBinder:
+ suspend T.(item: PasswordItem, dispatcherProvider: DispatcherProvider) -> Unit,
) : ListAdapter<PasswordItem, T>(PasswordItemDiffCallback), PopupTextProvider {
fun <T : ItemDetailsLookup<String>> makeSelectable(
@@ -482,7 +483,9 @@ open class SearchableRepositoryAdapter<T : RecyclerView.ViewHolder>(
final override fun onBindViewHolder(holder: T, position: Int) {
val item = getItem(position)
holder.apply {
- coroutineScope.launch(Dispatchers.Main.immediate) { viewHolderBinder.invoke(holder, item) }
+ coroutineScope.launch(dispatcherProvider.mainImmediate()) {
+ viewHolderBinder.invoke(holder, item, dispatcherProvider)
+ }
selectionTracker?.let { itemView.isSelected = it.isSelected(item.stableId) }
itemView.setOnClickListener {
// Do not emit custom click events while the user is selecting items.