fix: clear violations of RawDispatcherUse
This commit is contained in:
parent
496f421f17
commit
3a4e827f1a
20 changed files with 124 additions and 271 deletions
|
@ -29,183 +29,7 @@
|
|||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt"
|
||||
line="104"
|
||||
column="19"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt"
|
||||
line="132"
|
||||
column="19"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) { finish() }"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt"
|
||||
line="139"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/git/base/BaseGitActivity.kt"
|
||||
line="108"
|
||||
column="19"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" private val scope = CoroutineScope(Job() + Dispatchers.Main)"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/util/services/ClipboardService.kt"
|
||||
line="35"
|
||||
column="46"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/util/services/ClipboardService.kt"
|
||||
line="56"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/util/git/GitCommandExecutor.kt"
|
||||
line="105"
|
||||
column="35"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/util/git/operation/GitOperation.kt"
|
||||
line="179"
|
||||
column="27"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" lifecycleScope.launch(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt"
|
||||
line="358"
|
||||
column="29"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt"
|
||||
line="110"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt"
|
||||
line="137"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt"
|
||||
line="150"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt"
|
||||
line="511"
|
||||
column="29"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt"
|
||||
line="576"
|
||||
column="19"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" coroutineScope.launch(Dispatchers.Main.immediate) { viewHolderBinder.invoke(holder, item) }"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt"
|
||||
line="485"
|
||||
column="29"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" withContext(Dispatchers.Main) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt"
|
||||
line="114"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RawDispatchersUse"
|
||||
message="Use SlackDispatchers."
|
||||
errorLine1=" runBlocking(Dispatchers.Main) {"
|
||||
errorLine1=" runBlocking(Dispatchers.Main) { suspendCoroutine { cont -> askForPassword(cont, isRetry) } }"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt"
|
||||
|
@ -220,7 +44,7 @@
|
|||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/autofill/AutofillFilterView.kt"
|
||||
line="194"
|
||||
line="199"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
|
@ -275,7 +99,7 @@
|
|||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt"
|
||||
line="180"
|
||||
line="182"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
|
@ -297,7 +121,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt"
|
||||
line="298"
|
||||
line="297"
|
||||
column="33"/>
|
||||
</issue>
|
||||
|
||||
|
@ -308,7 +132,7 @@
|
|||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt"
|
||||
line="59"
|
||||
line="62"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,24 +195,25 @@ class AutofillFilterView : AppCompatActivity() {
|
|||
R.string.oreo_autofill_match_with,
|
||||
formOrigin.getPrettyIdentifier(applicationContext)
|
||||
)
|
||||
model.searchResult
|
||||
.flowWithLifecycle(lifecycle)
|
||||
.onEach { result ->
|
||||
lifecycleScope.launch { handleSearchResults() }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleSearchResults() {
|
||||
model.searchResult.collect { result ->
|
||||
val list = result.passwordItems
|
||||
(rvPassword.adapter as SearchableRepositoryAdapter).submitList(list) {
|
||||
rvPassword.scrollToPosition(0)
|
||||
(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() && rvPasswordSwitcher.nextView.id == rvPasswordEmpty.id) ||
|
||||
(list.isNotEmpty() && rvPasswordSwitcher.nextView.id == rvPassword.id)
|
||||
(list.isEmpty() && binding.rvPasswordSwitcher.nextView.id == binding.rvPasswordEmpty.id) ||
|
||||
(list.isNotEmpty() && binding.rvPasswordSwitcher.nextView.id == binding.rvPassword.id)
|
||||
) {
|
||||
rvPasswordSwitcher.showNext()
|
||||
binding.rvPasswordSwitcher.showNext()
|
||||
}
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSearch() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
lifecycleScope.launch {
|
||||
merge(
|
||||
binding.passwordLengthText.afterTextChanges(),
|
||||
binding.passwordSeparatorText.afterTextChanges(),
|
||||
)
|
||||
.onEach { generatePassword(binding) }
|
||||
.launchIn(lifecycleScope)
|
||||
.collect { _ -> generatePassword(binding) }
|
||||
}
|
||||
return builder
|
||||
.run {
|
||||
setTitle(R.string.pwgen_title)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -18,6 +18,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import app.passwordstore.databinding.ActivityOreoAutofillSmsBinding
|
||||
import app.passwordstore.util.autofill.AutofillResponseBuilder
|
||||
import app.passwordstore.util.coroutines.DispatcherProvider
|
||||
import app.passwordstore.util.extensions.viewBinding
|
||||
import com.github.androidpasswordstore.autofillparser.AutofillAction
|
||||
import com.github.androidpasswordstore.autofillparser.Credentials
|
||||
|
@ -29,11 +30,12 @@ import com.google.android.gms.common.ConnectionResult
|
|||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.android.gms.common.api.ResolvableApiException
|
||||
import com.google.android.gms.tasks.Task
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.concurrent.ExecutionException
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import logcat.LogPriority.ERROR
|
||||
|
@ -51,8 +53,11 @@ suspend fun <T> Task<T>.suspendableAwait() =
|
|||
}
|
||||
}
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AutofillSmsActivity : AppCompatActivity() {
|
||||
|
||||
@Inject lateinit var dispatcherProvider: DispatcherProvider
|
||||
|
||||
companion object {
|
||||
|
||||
private var fillOtpFromSmsRequestCode = 1
|
||||
|
@ -129,14 +134,16 @@ class AutofillSmsActivity : AppCompatActivity() {
|
|||
private suspend fun waitForSms() {
|
||||
val smsClient = SmsCodeRetriever.getAutofillClient(this@AutofillSmsActivity)
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) { smsClient.startSmsCodeRetriever().suspendableAwait() }
|
||||
withContext(dispatcherProvider.io()) {
|
||||
smsClient.startSmsCodeRetriever().suspendableAwait()
|
||||
}
|
||||
}
|
||||
.onFailure { e ->
|
||||
if (e is ResolvableApiException) {
|
||||
e.startResolutionForResult(this@AutofillSmsActivity, 1)
|
||||
} else {
|
||||
logcat(ERROR) { e.asLog() }
|
||||
withContext(Dispatchers.Main) { finish() }
|
||||
withContext(dispatcherProvider.main()) { finish() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ public interface DispatcherProvider {
|
|||
|
||||
public fun main(): CoroutineDispatcher = Dispatchers.Main
|
||||
|
||||
public fun mainImmediate(): CoroutineDispatcher = Dispatchers.Main.immediate
|
||||
|
||||
public fun default(): CoroutineDispatcher = Dispatchers.Default
|
||||
|
||||
public fun io(): CoroutineDispatcher = Dispatchers.IO
|
||||
|
|
Loading…
Reference in a new issue