diff options
35 files changed, 141 insertions, 155 deletions
diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 137d61d6..b0702655 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> <issue id="StopShip" @@ -27,17 +27,6 @@ </issue> <issue - id="RawDispatchersUse" - message="Use SlackDispatchers." - errorLine1=" runBlocking(Dispatchers.Main) { suspendCoroutine { cont -> askForPassword(cont, isRetry) } }" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt" - line="60" - column="19"/> - </issue> - - <issue id="DenyListedApi" message="Use Context#getDrawableCompat() instead" errorLine1=" ContextCompat.getDrawable(itemView.context, R.drawable.ic_content_copy)" @@ -60,39 +49,6 @@ </issue> <issue - id="DenyListedApi" - message="Use the structured concurrent CoroutineScope#launch and Flow#collect APIs instead of reactive Flow#onEach and Flow#launchIn. Suspend calls like Flow#collect can be refactored into standalone suspend funs and mixed in with regular control flow in a suspend context, but calls that invoke CoroutineScope#launch and Flow#collect at the same time hide the suspend context, encouraging the developer to continue working in the reactive domain." - errorLine1=" .launchIn(lifecycleScope)" - errorLine2=" ~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt" - line="212" - column="8"/> - </issue> - - <issue - id="DenyListedApi" - message="Use the structured concurrent CoroutineScope#launch and Flow#collect APIs instead of reactive Flow#onEach and Flow#launchIn. Suspend calls like Flow#collect can be refactored into standalone suspend funs and mixed in with regular control flow in a suspend context, but calls that invoke CoroutineScope#launch and Flow#collect at the same time hide the suspend context, encouraging the developer to continue working in the reactive domain." - errorLine1=" .launchIn(lifecycleScope)" - errorLine2=" ~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt" - line="68" - column="8"/> - </issue> - - <issue - id="DenyListedApi" - message="Use the structured concurrent CoroutineScope#launch and Flow#collect APIs instead of reactive Flow#onEach and Flow#launchIn. Suspend calls like Flow#collect can be refactored into standalone suspend funs and mixed in with regular control flow in a suspend context, but calls that invoke CoroutineScope#launch and Flow#collect at the same time hide the suspend context, encouraging the developer to continue working in the reactive domain." - errorLine1=" .launchIn(lifecycleScope)" - errorLine2=" ~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt" - line="65" - column="8"/> - </issue> - - <issue id="MissingQuantity" message="For locale "it" (Italian) the following quantity should also be defined: `many`" errorLine1=" <plurals name="delete_title">" @@ -227,37 +183,4 @@ column="4"/> </issue> - <issue - id="UnknownNullness" - message="Should explicitly declare type here since implicit type does not specify nullness (Lazy<Array<(GitCommand<out (Any or Any?)> or GitCommand<out (Any or Any?)>?)>>)" - errorLine1=" override val commands by unsafeLazy {" - errorLine2=" ~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/util/git/operation/BreakOutOfDetached.kt" - line="33" - column="16"/> - </issue> - - <issue - id="UnknownNullness" - message="Should explicitly declare type here since implicit type does not specify nullness (Array<(GitCommand<out (Any or Any?)> or GitCommand<out (Any or Any?)>?)>)" - errorLine1=" override val commands =" - errorLine2=" ~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/util/git/operation/ResetToRemoteOperation.kt" - line="14" - column="16"/> - </issue> - - <issue - id="UnknownNullness" - message="Should explicitly declare type here since implicit type does not specify nullness (Array<(GitCommand<out (Any or Any?)> or GitCommand<out (Any or Any?)>?)>)" - errorLine1=" override val commands =" - errorLine2=" ~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/util/git/operation/SyncOperation.kt" - line="14" - column="16"/> - </issue> - </issues> diff --git a/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt b/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt index 1ddf1c61..454054cf 100644 --- a/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt +++ b/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt @@ -114,7 +114,11 @@ object PasswordRepository { val dir = getRepositoryDirectory() // Un-initialize the repo if the dir does not exist or is absolutely empty settings.edit { - if (!dir.exists() || !dir.isDirectory || requireNotNull(dir.listFiles()).isEmpty()) { + if ( + !dir.exists() || + !dir.isDirectory || + requireNotNull(dir.listFiles()) { "Failed to list files in ${dir.path}" }.isEmpty() + ) { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) } else { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt b/app/src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt index da1a060a..1f37e205 100644 --- a/app/src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt +++ b/app/src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt @@ -31,9 +31,8 @@ import app.passwordstore.util.settings.PreferenceKeys import com.github.michaelbull.result.getOrElse import com.github.michaelbull.result.runCatching import com.google.android.material.dialog.MaterialAlertDialogBuilder -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 import reactivecircus.flowbinding.android.widget.checkedChanges @@ -55,17 +54,18 @@ class PasswordGeneratorDialogFragment : DialogFragment() { binding.lengthNumber.setText(prefs.getInt(PreferenceKeys.LENGTH, 20).toString()) binding.passwordText.typeface = Typeface.MONOSPACE - merge( - binding.numerals.checkedChanges().skipInitialValue(), - binding.symbols.checkedChanges().skipInitialValue(), - binding.uppercase.checkedChanges().skipInitialValue(), - binding.lowercase.checkedChanges().skipInitialValue(), - binding.ambiguous.checkedChanges().skipInitialValue(), - binding.pronounceable.checkedChanges().skipInitialValue(), - binding.lengthNumber.afterTextChanges().skipInitialValue(), - ) - .onEach { generate(binding.passwordText) } - .launchIn(lifecycleScope) + lifecycleScope.launch { + merge( + binding.numerals.checkedChanges().skipInitialValue(), + binding.symbols.checkedChanges().skipInitialValue(), + binding.uppercase.checkedChanges().skipInitialValue(), + binding.lowercase.checkedChanges().skipInitialValue(), + binding.ambiguous.checkedChanges().skipInitialValue(), + binding.pronounceable.checkedChanges().skipInitialValue(), + binding.lengthNumber.afterTextChanges().skipInitialValue(), + ) + .collect { generate(binding.passwordText) } + } return builder .run { diff --git a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt index a1c33e3f..9a8080a4 100644 --- a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt +++ b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt @@ -27,8 +27,7 @@ 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 kotlinx.coroutines.launch import me.zhanghai.android.fastscroll.FastScrollerBuilder @AndroidEntryPoint @@ -57,12 +56,16 @@ class SelectFolderFragment : Fragment(R.layout.password_recycler_view) { FastScrollerBuilder(binding.passRecycler).build() registerForContextMenu(binding.passRecycler) - val path = requireNotNull(requireArguments().getString(PasswordStore.REQUEST_ARG_PATH)) + val path = + requireNotNull(requireArguments().getString(PasswordStore.REQUEST_ARG_PATH)) { + "Cannot navigate if ${PasswordStore.REQUEST_ARG_PATH} is not provided" + } model.navigateTo(File(path), listMode = ListMode.DirectoriesOnly, pushPreviousLocation = false) - model.searchResult - .flowWithLifecycle(lifecycle) - .onEach { result -> recyclerAdapter.submitList(result.passwordItems) } - .launchIn(lifecycleScope) + lifecycleScope.launch { + model.searchResult.flowWithLifecycle(lifecycle).collect { result -> + recyclerAdapter.submitList(result.passwordItems) + } + } } override fun onAttach(context: Context) { diff --git a/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt b/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt index 5e2bafb9..9a3029bc 100644 --- a/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt @@ -221,7 +221,10 @@ class GitServerConfigActivity : BaseGitActivity() { /** Clones the repository, the directory exists, deletes it */ private fun cloneRepository() { - val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory()) + val localDir = + requireNotNull(PasswordRepository.getRepositoryDirectory()) { + "Repository directory must be set before cloning" + } val localDirFiles = localDir.listFiles() ?: emptyArray() // Warn if non-empty folder unless it's a just-initialized store that has just a .git folder if ( diff --git a/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt b/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt index d6bcce11..60580ff5 100644 --- a/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt +++ b/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt @@ -50,8 +50,6 @@ 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 kotlinx.coroutines.launch import me.zhanghai.android.fastscroll.FastScrollerBuilder @@ -177,11 +175,13 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) { recyclerAdapter.makeSelectable(recyclerView) registerForContextMenu(recyclerView) - val path = requireNotNull(requireArguments().getString(PasswordStore.REQUEST_ARG_PATH)) + val path = + requireNotNull(requireArguments().getString(PasswordStore.REQUEST_ARG_PATH)) { + "Cannot navigate if ${PasswordStore.REQUEST_ARG_PATH} is not provided" + } model.navigateTo(File(path), pushPreviousLocation = false) - model.searchResult - .flowWithLifecycle(lifecycle) - .onEach { result -> + lifecycleScope.launch { + model.searchResult.flowWithLifecycle(lifecycle).collect { result -> // Only run animations when the new list is filtered, i.e., the user submitted a search, // and not on folder navigation since the latter leads to too many removal animations. (recyclerView.itemAnimator as OnOffItemAnimator).isEnabled = result.isFiltered @@ -209,7 +209,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) { } } } - .launchIn(lifecycleScope) + } } private val actionModeCallback = diff --git a/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt b/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt index 8aeee609..861250c8 100644 --- a/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt +++ b/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt @@ -83,8 +83,16 @@ class PasswordStore : BaseGitActivity() { private val passwordMoveAction = registerForActivityResult(StartActivityForResult()) { result -> val intentData = result.data ?: return@registerForActivityResult - val filesToMove = requireNotNull(intentData.getStringArrayExtra("Files")) - val target = File(requireNotNull(intentData.getStringExtra("SELECTED_FOLDER_PATH"))) + val filesToMove = + requireNotNull(intentData.getStringArrayExtra("Files")) { + "'Files' intent extra must be set" + } + val target = + File( + requireNotNull(intentData.getStringExtra("SELECTED_FOLDER_PATH")) { + "'SELECTED_FOLDER_PATH' intent extra must be set" + } + ) val repositoryPath = PasswordRepository.getRepositoryDirectory().absolutePath if (!target.isDirectory) { logcat(ERROR) { "Tried moving passwords to a non-existing folder." } @@ -103,7 +111,12 @@ class PasswordStore : BaseGitActivity() { } val destinationFile = File(target.absolutePath + "/" + source.name) val basename = source.nameWithoutExtension - val sourceLongName = getLongName(requireNotNull(source.parent), repositoryPath, basename) + val sourceLongName = + getLongName( + requireNotNull(source.parent) { "$file has no parent" }, + repositoryPath, + basename + ) val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename) if (destinationFile.exists()) { logcat(ERROR) { "Trying to move a file that already exists." } @@ -132,7 +145,11 @@ class PasswordStore : BaseGitActivity() { val source = File(filesToMove[0]) val basename = source.nameWithoutExtension val sourceLongName = - getLongName(requireNotNull(source.parent), repositoryPath, basename) + getLongName( + requireNotNull(source.parent) { "$basename has no parent" }, + repositoryPath, + basename + ) val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename) withContext(dispatcherProvider.main()) { commitChange( diff --git a/app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt b/app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt index 77322116..8b087f99 100644 --- a/app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt +++ b/app/src/main/java/app/passwordstore/util/autofill/Api26AutofillResponseBuilder.kt @@ -125,7 +125,7 @@ private constructor( // https://developer.android.com/reference/android/service/autofill/SaveInfo#FLAG_DELAY_SAVE private fun makeSaveInfo(): SaveInfo? { if (!canBeSaved) return null - check(saveFlags != null) + check(saveFlags != null) { "saveFlags must not be null" } val idsToSave = scenario.fieldsToSave.toTypedArray() if (idsToSave.isEmpty()) return null var saveDataTypes = SaveInfo.SAVE_DATA_TYPE_PASSWORD diff --git a/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt b/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt index 49970fa6..ef1c302c 100644 --- a/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt +++ b/app/src/main/java/app/passwordstore/util/autofill/Api30AutofillResponseBuilder.kt @@ -244,7 +244,7 @@ private constructor( // https://developer.android.com/reference/android/service/autofill/SaveInfo#FLAG_DELAY_SAVE private fun makeSaveInfo(): SaveInfo? { if (!canBeSaved) return null - check(saveFlags != null) + check(saveFlags != null) { "saveFlags must not be null" } val idsToSave = scenario.fieldsToSave.toTypedArray() if (idsToSave.isEmpty()) return null var saveDataTypes = SaveInfo.SAVE_DATA_TYPE_PASSWORD diff --git a/app/src/main/java/app/passwordstore/util/autofill/AutofillMatcher.kt b/app/src/main/java/app/passwordstore/util/autofill/AutofillMatcher.kt index 52c3d778..56171f6b 100644 --- a/app/src/main/java/app/passwordstore/util/autofill/AutofillMatcher.kt +++ b/app/src/main/java/app/passwordstore/util/autofill/AutofillMatcher.kt @@ -40,7 +40,9 @@ class AutofillPublisherChangedException(val formOrigin: FormOrigin) : ) { init { - require(formOrigin is FormOrigin.App) + require(formOrigin is FormOrigin.App) { + "${this::class.java.simpleName} is only applicable for apps" + } } } diff --git a/app/src/main/java/app/passwordstore/util/git/operation/BreakOutOfDetached.kt b/app/src/main/java/app/passwordstore/util/git/operation/BreakOutOfDetached.kt index b6f31b18..77a19da6 100644 --- a/app/src/main/java/app/passwordstore/util/git/operation/BreakOutOfDetached.kt +++ b/app/src/main/java/app/passwordstore/util/git/operation/BreakOutOfDetached.kt @@ -9,6 +9,7 @@ import app.passwordstore.R import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.util.extensions.unsafeLazy import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.eclipse.jgit.api.GitCommand import org.eclipse.jgit.api.RebaseCommand import org.eclipse.jgit.api.ResetCommand import org.eclipse.jgit.lib.RepositoryState @@ -30,7 +31,7 @@ class BreakOutOfDetached(callingActivity: AppCompatActivity) : GitOperation(call git.checkout().setName(localBranch), ) - override val commands by unsafeLazy { + override val commands: Array<GitCommand<out Any>> by unsafeLazy { if (merging) { // We need to run some non-command operations first repository.writeMergeCommitMsg(null) diff --git a/app/src/main/java/app/passwordstore/util/git/operation/CredentialFinder.kt b/app/src/main/java/app/passwordstore/util/git/operation/CredentialFinder.kt index 0210a744..e90e3b23 100644 --- a/app/src/main/java/app/passwordstore/util/git/operation/CredentialFinder.kt +++ b/app/src/main/java/app/passwordstore/util/git/operation/CredentialFinder.kt @@ -15,6 +15,7 @@ import androidx.core.widget.doOnTextChanged import androidx.fragment.app.FragmentActivity import app.passwordstore.R import app.passwordstore.injection.prefs.GitPreferences +import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.git.sshj.InteractivePasswordFinder import app.passwordstore.util.settings.AuthMode import app.passwordstore.util.settings.PreferenceKeys @@ -32,7 +33,8 @@ import kotlin.coroutines.resume class CredentialFinder( private val callingActivity: FragmentActivity, private val authMode: AuthMode, -) : InteractivePasswordFinder() { + dispatcherProvider: DispatcherProvider, +) : InteractivePasswordFinder(dispatcherProvider) { private val hiltEntryPoint = EntryPointAccessors.fromApplication( diff --git a/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt b/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt index 1cc42633..d4ccfaab 100644 --- a/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt +++ b/app/src/main/java/app/passwordstore/util/git/operation/GitOperation.kt @@ -121,7 +121,8 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { authMethod: SshAuthMethod, credentialsProvider: CredentialsProvider? = null ) { - sshSessionFactory = SshjSessionFactory(authMethod, hostKeyFile, sshFacade) + sshSessionFactory = + SshjSessionFactory(authMethod, hostKeyFile, sshFacade, hiltEntryPoint.dispatcherProvider()) commands.filterIsInstance<TransportCommand<*, *>>().forEach { command -> command.setTransportConfigCallback { transport: Transport -> (transport as? SshTransport)?.sshSessionFactory = sshSessionFactory @@ -217,7 +218,13 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { } AuthMode.Password -> { val httpsCredentialProvider = - HttpsCredentialsProvider(CredentialFinder(callingActivity, AuthMode.Password)) + HttpsCredentialsProvider( + CredentialFinder( + callingActivity, + AuthMode.Password, + hiltEntryPoint.dispatcherProvider() + ) + ) registerAuthProviders(SshAuthMethod.Password(authActivity), httpsCredentialProvider) } AuthMode.None -> {} diff --git a/app/src/main/java/app/passwordstore/util/git/operation/ResetToRemoteOperation.kt b/app/src/main/java/app/passwordstore/util/git/operation/ResetToRemoteOperation.kt index 08cc195a..f6b4fdcd 100644 --- a/app/src/main/java/app/passwordstore/util/git/operation/ResetToRemoteOperation.kt +++ b/app/src/main/java/app/passwordstore/util/git/operation/ResetToRemoteOperation.kt @@ -6,12 +6,13 @@ package app.passwordstore.util.git.operation import androidx.appcompat.app.AppCompatActivity import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode.TRACK +import org.eclipse.jgit.api.GitCommand import org.eclipse.jgit.api.ResetCommand class ResetToRemoteOperation(callingActivity: AppCompatActivity, remoteBranch: String) : GitOperation(callingActivity) { - override val commands = + override val commands: Array<GitCommand<out Any>> = arrayOf( // Fetch everything from the origin remote git.fetch().setRemote("origin").setRemoveDeletedRefs(true), diff --git a/app/src/main/java/app/passwordstore/util/git/operation/SyncOperation.kt b/app/src/main/java/app/passwordstore/util/git/operation/SyncOperation.kt index e36b01b1..57be9439 100644 --- a/app/src/main/java/app/passwordstore/util/git/operation/SyncOperation.kt +++ b/app/src/main/java/app/passwordstore/util/git/operation/SyncOperation.kt @@ -5,13 +5,14 @@ package app.passwordstore.util.git.operation import androidx.appcompat.app.AppCompatActivity +import org.eclipse.jgit.api.GitCommand class SyncOperation( callingActivity: AppCompatActivity, rebase: Boolean, ) : GitOperation(callingActivity) { - override val commands = + override val commands: Array<GitCommand<out Any>> = arrayOf( // Stage all files git.add().addFilepattern("."), diff --git a/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt b/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt index 54858530..20bb01d6 100644 --- a/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt +++ b/app/src/main/java/app/passwordstore/util/git/sshj/SshjSessionFactory.kt @@ -6,6 +6,7 @@ package app.passwordstore.util.git.sshj import android.util.Base64 import androidx.appcompat.app.AppCompatActivity +import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.git.operation.CredentialFinder import app.passwordstore.util.settings.AuthMode import app.passwordstore.util.ssh.SSHFacade @@ -20,7 +21,6 @@ import java.util.Collections import java.util.concurrent.TimeUnit import kotlin.coroutines.Continuation import kotlin.coroutines.suspendCoroutine -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import logcat.LogPriority.WARN import logcat.logcat @@ -49,15 +49,18 @@ sealed class SshAuthMethod(val activity: AppCompatActivity) { class SshKey(activity: AppCompatActivity) : SshAuthMethod(activity) } -abstract class InteractivePasswordFinder : PasswordFinder { +abstract class InteractivePasswordFinder(private val dispatcherProvider: DispatcherProvider) : + PasswordFinder { private var isRetry = false abstract fun askForPassword(cont: Continuation<String?>, isRetry: Boolean) - final override fun reqPassword(resource: Resource<*>?): CharArray { + override fun reqPassword(resource: Resource<*>?): CharArray { val password = - runBlocking(Dispatchers.Main) { suspendCoroutine { cont -> askForPassword(cont, isRetry) } } + runBlocking(dispatcherProvider.main()) { + suspendCoroutine { cont -> askForPassword(cont, isRetry) } + } isRetry = true return password?.toCharArray() ?: throw SSHException(DisconnectReason.AUTH_CANCELLED_BY_USER) } @@ -69,6 +72,7 @@ class SshjSessionFactory( private val authMethod: SshAuthMethod, private val hostKeyFile: File, private val sshFacade: SSHFacade, + private val dispatcherProvider: DispatcherProvider, ) : SshSessionFactory() { private var currentSession: SshjSession? = null @@ -80,10 +84,12 @@ class SshjSessionFactory( tms: Int ): RemoteSession { return currentSession - ?: SshjSession(uri, uri.user, authMethod, hostKeyFile, sshFacade).connect().also { - logcat { "New SSH connection created" } - currentSession = it - } + ?: SshjSession(uri, uri.user, authMethod, hostKeyFile, sshFacade, dispatcherProvider) + .connect() + .also { + logcat { "New SSH connection created" } + currentSession = it + } } fun close() { @@ -125,6 +131,7 @@ private class SshjSession( private val authMethod: SshAuthMethod, private val hostKeyFile: File, private val sshFacade: SSHFacade, + private val dispatcherProvider: DispatcherProvider, ) : RemoteSession { private lateinit var ssh: SSHClient @@ -151,7 +158,8 @@ private class SshjSession( ssh.addHostKeyVerifier(makeTofuHostKeyVerifier(hostKeyFile)) ssh.connect(uri.host, uri.port.takeUnless { it == -1 } ?: 22) if (!ssh.isConnected) throw IOException() - val passwordAuth = AuthPassword(CredentialFinder(authMethod.activity, AuthMode.Password)) + val passwordAuth = + AuthPassword(CredentialFinder(authMethod.activity, AuthMode.Password, dispatcherProvider)) when (authMethod) { is SshAuthMethod.Password -> { ssh.auth(username, passwordAuth) @@ -159,7 +167,10 @@ private class SshjSession( is SshAuthMethod.SshKey -> { val pubkeyAuth = AuthPublickey( - sshFacade.keyProvider(ssh, CredentialFinder(authMethod.activity, AuthMode.SshKey)) + sshFacade.keyProvider( + ssh, + CredentialFinder(authMethod.activity, AuthMode.SshKey, dispatcherProvider) + ) ) ssh.auth(username, pubkeyAuth, passwordAuth) } diff --git a/app/src/main/java/app/passwordstore/util/services/PasswordExportService.kt b/app/src/main/java/app/passwordstore/util/services/PasswordExportService.kt index 9243f52d..d5032f4f 100644 --- a/app/src/main/java/app/passwordstore/util/services/PasswordExportService.kt +++ b/app/src/main/java/app/passwordstore/util/services/PasswordExportService.kt @@ -57,7 +57,10 @@ class PasswordExportService : Service() { */ private fun exportPasswords(targetDirectory: DocumentFile) { - val repositoryDirectory = requireNotNull(PasswordRepository.getRepositoryDirectory()) + val repositoryDirectory = + requireNotNull(PasswordRepository.getRepositoryDirectory()) { + "Password directory must be set to export them" + } val sourcePassDir = DocumentFile.fromFile(repositoryDirectory) logcat { "Copying ${repositoryDirectory.path} to $targetDirectory" } diff --git a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt index 56124104..bfbf9c85 100644 --- a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt +++ b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt @@ -68,7 +68,7 @@ constructor( var url get() = settings.getString(PreferenceKeys.GIT_REMOTE_URL) private set(value) { - require(value != null) + require(value != null) { "Cannot set a null URL" } if (value == url) return settings.edit { putString(PreferenceKeys.GIT_REMOTE_URL, value) } if (PasswordRepository.isInitialized) PasswordRepository.addRemote("origin", value, true) diff --git a/app/src/nonFree/java/app/passwordstore/autofill/oreo/ui/AutofillSmsActivity.kt b/app/src/nonFree/java/app/passwordstore/autofill/oreo/ui/AutofillSmsActivity.kt index 61b42342..ebd10020 100644 --- a/app/src/nonFree/java/app/passwordstore/autofill/oreo/ui/AutofillSmsActivity.kt +++ b/app/src/nonFree/java/app/passwordstore/autofill/oreo/ui/AutofillSmsActivity.kt @@ -15,6 +15,7 @@ import android.os.Build import android.os.Bundle import android.view.autofill.AutofillManager import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import app.passwordstore.databinding.ActivityOreoAutofillSmsBinding import app.passwordstore.util.autofill.AutofillResponseBuilder @@ -113,18 +114,19 @@ class AutofillSmsActivity : AppCompatActivity() { return } - registerReceiver( + ContextCompat.registerReceiver( + this, smsCodeRetrievedReceiver, IntentFilter(SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION), SmsRetriever.SEND_PERMISSION, - null + null, + ContextCompat.RECEIVER_EXPORTED, ) lifecycleScope.launch { waitForSms() } } // Retry starting the SMS code retriever after a permission request. @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != Activity.RESULT_OK) return diff --git a/autofill-parser/lint-baseline.xml b/autofill-parser/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/autofill-parser/lint-baseline.xml +++ b/autofill-parser/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt index 6a8e16f3..badaabcb 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt @@ -116,7 +116,7 @@ private class AutofillFormParser( if (trustedBrowserInfo?.multiOriginMethod == BrowserMultiOriginMethod.WebView) { FormField(node, fieldIndex, true, inheritedWebOrigin) } else { - check(inheritedWebOrigin == null) + check(inheritedWebOrigin == null) { "'inheritedWebOrigin' should be null here" } FormField(node, fieldIndex, false) } if (field.relevantField) { diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt index 97419048..c7bd99fc 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt @@ -105,7 +105,9 @@ public sealed class AutofillScenario<out T : Any> { val genericPassword = mutableListOf<T>() fun build(): AutofillScenario<T> { - require(genericPassword.isEmpty() || (currentPassword.isEmpty() && newPassword.isEmpty())) + require(genericPassword.isEmpty() || (currentPassword.isEmpty() && newPassword.isEmpty())) { + "Password requirements failed." + } return if (currentPassword.isNotEmpty() || newPassword.isNotEmpty()) { ClassifiedAutofillScenario( username = username, diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt index 0a75f078..04e8d98c 100644 --- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt +++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt @@ -357,13 +357,17 @@ private constructor( logcat { "$name: Matched $type" } when (type) { FillableFieldType.Username -> { - check(matchResult.size == 1 && scenarioBuilder.username == null) + check(matchResult.size == 1 && scenarioBuilder.username == null) { + "Scenario has existing username or too many matches" + } scenarioBuilder.username = matchResult.single() // Hidden username fields should be saved but not filled. scenarioBuilder.fillUsername = scenarioBuilder.username!!.isVisible == true } FillableFieldType.Otp -> { - check(matchResult.size == 1 && scenarioBuilder.otp == null) + check(matchResult.size == 1 && scenarioBuilder.otp == null) { + "Scenario has existing OTP or too many matches" + } scenarioBuilder.otp = matchResult.single() } FillableFieldType.CurrentPassword -> scenarioBuilder.currentPassword.addAll(matchResult) diff --git a/autofill-parser/src/test/kotlin/mozilla/components/lib/publicsuffixlist/PublicSuffixListLoaderTest.kt b/autofill-parser/src/test/kotlin/mozilla/components/lib/publicsuffixlist/PublicSuffixListLoaderTest.kt index 0ee72880..f48759bb 100644 --- a/autofill-parser/src/test/kotlin/mozilla/components/lib/publicsuffixlist/PublicSuffixListLoaderTest.kt +++ b/autofill-parser/src/test/kotlin/mozilla/components/lib/publicsuffixlist/PublicSuffixListLoaderTest.kt @@ -10,9 +10,9 @@ import kotlin.test.Test class PublicSuffixListLoaderTest { @Test fun testLoadingBundledPublicSuffixList() { - requireNotNull(javaClass.classLoader).getResourceAsStream("publicsuffixes").buffered().use { - stream -> - PublicSuffixListLoader.load(stream) - } + requireNotNull(javaClass.classLoader) { "Null classloader????" } + .getResourceAsStream("publicsuffixes") + .buffered() + .use { stream -> PublicSuffixListLoader.load(stream) } } } diff --git a/coroutine-utils/lint-baseline.xml b/coroutine-utils/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/coroutine-utils/lint-baseline.xml +++ b/coroutine-utils/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/crypto/common/lint-baseline.xml b/crypto/common/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/crypto/common/lint-baseline.xml +++ b/crypto/common/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/crypto/pgpainless/lint-baseline.xml b/crypto/pgpainless/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/crypto/pgpainless/lint-baseline.xml +++ b/crypto/pgpainless/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/format/common/lint-baseline.xml b/format/common/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/format/common/lint-baseline.xml +++ b/format/common/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ac634139..0038993b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -89,7 +89,7 @@ thirdparty-nonfree-googlePlayAuthApiPhone = "com.google.android.gms:play-service thirdparty-nonfree-sentry = "io.sentry:sentry-android:6.29.0" thirdparty-pgpainless = "org.pgpainless:pgpainless-core:1.6.2" thirdparty-plumber = { module = "com.squareup.leakcanary:plumber-android-startup", version.ref = "leakcanary" } -thirdparty-slack-lints = "com.slack.lint:slack-lint-checks:0.5.1" +thirdparty-slack-lints = "com.slack.lint:slack-lint-checks:0.6.0" thirdparty-slf4j-api = { module = "org.slf4j:slf4j-api", version = { strictly = "[1.7, 1.8[", prefer = "1.7.36" } } thirdparty-sshj = "com.hierynomus:sshj:0.36.0" thirdparty-uri = "com.eygraber:uri-kmp:0.0.14" diff --git a/passgen/diceware/lint-baseline.xml b/passgen/diceware/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/passgen/diceware/lint-baseline.xml +++ b/passgen/diceware/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/passgen/diceware/src/test/kotlin/app/passwordstore/passgen/diceware/WordListParserTest.kt b/passgen/diceware/src/test/kotlin/app/passwordstore/passgen/diceware/WordListParserTest.kt index bcb85cb4..c9757351 100644 --- a/passgen/diceware/src/test/kotlin/app/passwordstore/passgen/diceware/WordListParserTest.kt +++ b/passgen/diceware/src/test/kotlin/app/passwordstore/passgen/diceware/WordListParserTest.kt @@ -30,7 +30,7 @@ class WordListParserTest { companion object { fun getDefaultWordList(): InputStream { - return requireNotNull(this::class.java.classLoader) + return requireNotNull(this::class.java.classLoader) { "Null classloader????" } .getResourceAsStream("diceware_wordlist.txt") } } diff --git a/passgen/random/lint-baseline.xml b/passgen/random/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/passgen/random/lint-baseline.xml +++ b/passgen/random/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/sentry-stub/lint-baseline.xml b/sentry-stub/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/sentry-stub/lint-baseline.xml +++ b/sentry-stub/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> diff --git a/ssh/lint-baseline.xml b/ssh/lint-baseline.xml index dae997eb..c12a7d0f 100644 --- a/ssh/lint-baseline.xml +++ b/ssh/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> <issue id="InvalidPackage" diff --git a/ui/compose/lint-baseline.xml b/ui/compose/lint-baseline.xml index 8d55468b..1f0a317f 100644 --- a/ui/compose/lint-baseline.xml +++ b/ui/compose/lint-baseline.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.3.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha01)" variant="all" version="8.3.0-alpha01"> +<issues format="6" by="lint 8.3.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha05)" variant="all" version="8.3.0-alpha05"> </issues> |