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