Compile against SDK 33 (#2023)

* Compile against SDK 33

* autofill-parser: fix warnings for SDK 33 upgrade

* app: fix warnings for SDK 33 upgrade

* Mark all clipboard content as sensitive from crypto activities

* Skip Snackbar on Android 13 and above

* detekt: raise `TooManyFunctions` limit to 15
This commit is contained in:
Harsh Shandilya 2022-07-20 01:29:12 +05:30 committed by GitHub
parent 9cd2f5f446
commit ade73fd5bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 210 additions and 71 deletions

View file

@ -22,6 +22,9 @@ import app.passwordstore.databinding.ActivityOreoAutofillPublisherChangedBinding
import app.passwordstore.util.autofill.AutofillMatcher
import app.passwordstore.util.autofill.AutofillPublisherChangedException
import app.passwordstore.util.extensions.asLog
import app.passwordstore.util.extensions.getApplicationInfoCompat
import app.passwordstore.util.extensions.getPackageInfoCompat
import app.passwordstore.util.extensions.getParcelableExtraCompat
import app.passwordstore.util.extensions.viewBinding
import com.github.androidpasswordstore.autofillparser.FormOrigin
import com.github.androidpasswordstore.autofillparser.computeCertificatesHash
@ -93,7 +96,8 @@ class AutofillPublisherChangedActivity : AppCompatActivity() {
this@AutofillPublisherChangedActivity,
FormOrigin.App(appPackage)
)
val fillResponse = intent.getParcelableExtra<FillResponse>(EXTRA_FILL_RESPONSE_AFTER_RESET)
val fillResponse =
intent.getParcelableExtraCompat<FillResponse>(EXTRA_FILL_RESPONSE_AFTER_RESET)
setResult(
RESULT_OK,
Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillResponse) }
@ -106,11 +110,13 @@ class AutofillPublisherChangedActivity : AppCompatActivity() {
private fun showPackageInfo() {
runCatching {
with(binding) {
val packageInfo = packageManager.getPackageInfo(appPackage, PackageManager.GET_META_DATA)
val packageInfo =
packageManager.getPackageInfoCompat(appPackage, PackageManager.GET_META_DATA)
val installTime = DateUtils.getRelativeTimeSpanString(packageInfo.firstInstallTime)
warningAppInstallDate.text =
getString(R.string.oreo_autofill_warning_publisher_install_time, installTime)
val appInfo = packageManager.getApplicationInfo(appPackage, PackageManager.GET_META_DATA)
val appInfo =
packageManager.getApplicationInfoCompat(appPackage, PackageManager.GET_META_DATA)
warningAppName.text =
getString(
R.string.oreo_autofill_warning_publisher_app_name,

View file

@ -10,6 +10,7 @@ import android.content.Intent
import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import android.os.PersistableBundle
import android.view.WindowManager
import androidx.annotation.CallSuper
import androidx.annotation.StringRes
@ -68,8 +69,12 @@ open class BasePgpActivity : AppCompatActivity() {
) {
val clipboard = clipboard ?: return
val clip = ClipData.newPlainText("pgp_handler_result_pm", text)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
clip.description.extras =
PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) }
}
clipboard.setPrimaryClip(clip)
if (showSnackbar) {
if (showSnackbar && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
snackbar(message = resources.getString(snackbarTextRes))
}
}
@ -80,7 +85,7 @@ open class BasePgpActivity : AppCompatActivity() {
* clearing the clipboard.
*/
fun copyPasswordToClipboard(password: String?) {
copyTextToClipboard(password, showSnackbar = false)
copyTextToClipboard(password)
val clearAfter = settings.getString(PreferenceKeys.GENERAL_SHOW_TIME)?.toIntOrNull() ?: 45
@ -95,9 +100,6 @@ open class BasePgpActivity : AppCompatActivity() {
} else {
startService(service)
}
snackbar(message = resources.getString(R.string.clipboard_password_toast_text, clearAfter))
} else {
snackbar(message = resources.getString(R.string.clipboard_password_no_clear_toast_text))
}
}

View file

@ -78,7 +78,7 @@ class DecryptActivity : BasePgpActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> onBackPressed()
android.R.id.home -> onBackPressedDispatcher.onBackPressed()
R.id.edit_password -> editPassword()
R.id.share_password_as_plaintext -> shareAsPlaintext()
R.id.copy_password -> copyPasswordToClipboard(passwordEntry?.password)

View file

@ -262,7 +262,7 @@ class PasswordCreationActivity : BasePgpActivity() {
when (item.itemId) {
android.R.id.home -> {
setResult(RESULT_CANCELED)
onBackPressed()
onBackPressedDispatcher.onBackPressed()
}
R.id.save_password -> {
copy = false

View file

@ -49,7 +49,7 @@ class SelectFolderActivity : AppCompatActivity(R.layout.select_folder_layout) {
when (item.itemId) {
android.R.id.home -> {
setResult(RESULT_CANCELED)
onBackPressed()
onBackPressedDispatcher.onBackPressed()
}
R.id.crypto_select -> selectFolder()
else -> return super.onOptionsItemSelected(item)

View file

@ -69,7 +69,7 @@ class GitConfigActivity : BaseGitActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
onBackPressedDispatcher.onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)

View file

@ -202,7 +202,7 @@ class GitServerConfigActivity : BaseGitActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
onBackPressedDispatcher.onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)

View file

@ -6,6 +6,7 @@
package app.passwordstore.ui.onboarding.activity
import android.os.Bundle
import androidx.activity.addCallback
import androidx.appcompat.app.AppCompatActivity
import app.passwordstore.R
@ -14,13 +15,11 @@ class OnboardingActivity : AppCompatActivity(R.layout.activity_onboarding) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar?.hide()
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount == 0) {
finishAffinity()
} else {
super.onBackPressed()
val callback = onBackPressedDispatcher.addCallback(enabled = false) { finishAffinity() }
supportFragmentManager.addOnBackStackChangedListener {
if (supportFragmentManager.backStackEntryCount == 0) {
callback.isEnabled = true
}
}
}
}

View file

@ -4,7 +4,6 @@
*/
package app.passwordstore.ui.passwords
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@ -14,7 +13,6 @@ import android.view.Menu
import android.view.MenuItem
import android.view.MenuItem.OnActionExpandListener
import android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.activity.viewModels
import androidx.appcompat.widget.SearchView
@ -77,18 +75,6 @@ class PasswordStore : BaseGitActivity() {
ViewModelProvider.AndroidViewModelFactory(application)
}
private val storagePermissionRequest =
registerForActivityResult(RequestPermission()) { granted ->
if (granted) checkLocalRepository()
}
private val directorySelectAction =
registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
checkLocalRepository()
}
}
private val listRefreshAction =
registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
@ -196,7 +182,6 @@ class PasswordStore : BaseGitActivity() {
return super.onKeyDown(keyCode, event)
}
@SuppressLint("NewApi")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pwdstore)
@ -313,12 +298,14 @@ class PasswordStore : BaseGitActivity() {
}
}
R.id.refresh -> refreshPasswordList()
android.R.id.home -> onBackPressed()
android.R.id.home -> onBackPressedDispatcher.onBackPressed()
else -> return super.onOptionsItemSelected(item)
}
return true
}
@Deprecated("Deprecated in Java")
@Suppress("DEPRECATION")
override fun onBackPressed() {
if (getPasswordFragment()?.onBackPressedInActivity() != true) super.onBackPressed()
}

View file

@ -10,6 +10,7 @@ import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import app.passwordstore.R
import app.passwordstore.databinding.ActivityPreferenceRecyclerviewBinding
import app.passwordstore.util.extensions.getParcelableCompat
import app.passwordstore.util.extensions.viewBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import de.Maxr1998.modernpreferences.Preference
@ -84,7 +85,7 @@ class SettingsActivity : AppCompatActivity() {
}
}
savedInstanceState
?.getParcelable<PreferencesAdapter.SavedState>("adapter")
?.getParcelableCompat<PreferencesAdapter.SavedState>("adapter")
?.let(adapter::loadSavedState)
binding.preferenceRecyclerView.adapter = adapter
}
@ -106,6 +107,8 @@ class SettingsActivity : AppCompatActivity() {
}
}
@Deprecated("Deprecated in Java")
@Suppress("DEPRECATION")
override fun onBackPressed() {
if (!preferencesAdapter.goBack()) super.onBackPressed()
}

View file

@ -103,7 +103,7 @@ class SshKeyGenActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
onBackPressedDispatcher.onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)

View file

@ -7,9 +7,11 @@ package app.passwordstore.util.autofill
import android.content.Context
import android.content.IntentSender
import android.os.Build
import android.service.autofill.Dataset
import android.service.autofill.FillCallback
import android.service.autofill.FillResponse
import android.service.autofill.Presentations
import android.service.autofill.SaveInfo
import android.view.inputmethod.InlineSuggestionsRequest
import android.widget.inline.InlinePresentationSpec
@ -60,6 +62,23 @@ constructor(
intentSender: IntentSender,
metadata: DatasetMetadata,
imeSpec: InlinePresentationSpec?,
): Dataset {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
makeIntentDataSetTiramisu(context, action, intentSender, metadata, imeSpec)
} else {
makeIntentDataSetR(context, action, intentSender, metadata, imeSpec)
}
}
/** Helper for creating an Autofill [Dataset]s for Android R and above. */
@RequiresApi(Build.VERSION_CODES.R)
@Suppress("DEPRECATION")
private fun makeIntentDataSetR(
context: Context,
action: AutofillAction,
intentSender: IntentSender,
metadata: DatasetMetadata,
imeSpec: InlinePresentationSpec?,
): Dataset {
return Dataset.Builder(makeRemoteView(context, metadata)).run {
fillWith(scenario, action, credentials = null)
@ -74,6 +93,30 @@ constructor(
}
}
/** Helper for creating Autofill [Dataset]s for Android Tiramisu and above. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun makeIntentDataSetTiramisu(
context: Context,
action: AutofillAction,
intentSender: IntentSender,
metadata: DatasetMetadata,
imeSpec: InlinePresentationSpec?,
): Dataset {
val presentationsBuilder = Presentations.Builder()
if (imeSpec != null) {
val inlinePresentation = makeInlinePresentation(context, imeSpec, metadata)
if (inlinePresentation != null) {
presentationsBuilder.setInlinePresentation(inlinePresentation)
}
}
val presentations = presentationsBuilder.build()
return Dataset.Builder(presentations).run {
fillWith(scenario, action, credentials = null)
setAuthentication(intentSender)
build()
}
}
private fun makeMatchDataset(
context: Context,
file: File,

View file

@ -54,6 +54,7 @@ constructor(
private val scenarioSupportsSave = scenario.hasPasswordFieldsToSave
private val canBeSaved = saveFlags != null && scenarioSupportsSave
@Suppress("DEPRECATION")
private fun makeIntentDataset(
context: Context,
action: AutofillAction,
@ -212,7 +213,7 @@ constructor(
if (Build.VERSION.SDK_INT >= 28) {
Dataset.Builder()
} else {
Dataset.Builder(makeRemoteView(context, makeEmptyMetadata()))
@Suppress("DEPRECATION") Dataset.Builder(makeRemoteView(context, makeEmptyMetadata()))
}
return builder.run {
if (scenario != null) fillWith(scenario, action, credentials)

View file

@ -10,7 +10,14 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.PackageManager.PackageInfoFlags
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.util.Base64
import android.util.TypedValue
import android.view.View
@ -132,3 +139,45 @@ fun SharedPreferences.getString(key: String): String? = getString(key, null)
fun String.base64(): String {
return Base64.encodeToString(encodeToByteArray(), Base64.NO_WRAP)
}
inline fun <reified T> Bundle.getParcelableCompat(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, T::class.java)
} else {
@Suppress("DEPRECATION") getParcelable(key)
}
}
inline fun <reified T : Parcelable> Bundle.getParcelableArrayListCompat(
key: String
): ArrayList<T>? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableArrayList(key, T::class.java)
} else {
@Suppress("DEPRECATION") getParcelableArrayList(key)
}
}
inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableExtra(key, T::class.java)
} else {
@Suppress("DEPRECATION") getParcelableExtra(key)
}
}
fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int): PackageInfo {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getPackageInfo(packageName, PackageInfoFlags.of(flags.toLong()))
} else {
@Suppress("DEPRECATION") getPackageInfo(packageName, flags)
}
}
fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): ApplicationInfo {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getApplicationInfo(packageName, ApplicationInfoFlags.of(flags.toLong()))
} else {
@Suppress("DEPRECATION") getApplicationInfo(packageName, flags)
}
}

View file

@ -39,7 +39,11 @@ class ClipboardService : Service() {
when (intent.action) {
ACTION_CLEAR -> {
clearClipboard()
stopForeground(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE)
} else {
@Suppress("DEPRECATION") stopForeground(true)
}
stopSelf()
return super.onStartCommand(intent, flags, startId)
}
@ -55,7 +59,11 @@ class ClipboardService : Service() {
withContext(Dispatchers.IO) { startTimer(time) }
withContext(Dispatchers.Main) {
clearClipboard()
stopForeground(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE)
} else {
@Suppress("DEPRECATION") stopForeground(true)
}
stopSelf()
}
}

View file

@ -17,6 +17,7 @@ import androidx.core.content.getSystemService
import androidx.documentfile.provider.DocumentFile
import app.passwordstore.R
import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.util.extensions.getParcelableExtraCompat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.Calendar
@ -29,7 +30,7 @@ class PasswordExportService : Service() {
if (intent != null) {
when (intent.action) {
ACTION_EXPORT_PASSWORD -> {
val uri = intent.getParcelableExtra<Uri>("uri")
val uri = intent.getParcelableExtraCompat<Uri>("uri")
if (uri != null) {
val targetDirectory = DocumentFile.fromTreeUri(applicationContext, uri)

View file

@ -33,8 +33,6 @@
<string name="git_commit_move_text">Benenne %1$s in %2$s um.</string>
<string name="git_commit_move_multiple_text">Verschiebe mehrere Passwörter nach %1$s.</string>
<!-- PGPHandler -->
<string name="clipboard_password_toast_text">Passwort ist in der Zwischenablage, du hast %d Sekunden, um es einzufügen.</string>
<string name="clipboard_password_no_clear_toast_text">Passwort wurde in die Zwischenablage kopiert</string>
<string name="clipboard_copied_text">In die Zwischenablage kopiert</string>
<string name="file_toast_text">Bitte setze einen Pfad</string>
<string name="path_toast_text">Bitte setze einen Pfad</string>

View file

@ -37,8 +37,6 @@
<string name="git_commit_move_text">Renommer %1$sà %2$s. </string>
<string name="git_commit_move_multiple_text">Déplacement de mots de passe vers %1$s.</string>
<!-- PGPHandler -->
<string name="clipboard_password_toast_text">Mot de passe copié dans le presse papier, vous avez %d secondes pour coller celui-ci.</string>
<string name="clipboard_password_no_clear_toast_text">Mot de passe copié dans le presse-papiers</string>
<string name="clipboard_copied_text">Copié dans le presse-papiers</string>
<string name="file_toast_text">Veuillez fournir un nom de fichier</string>
<string name="path_toast_text">Veuillez fournir un chemin d\'accès au fichier</string>

View file

@ -37,8 +37,6 @@
<string name="git_commit_move_text">Mudar nome %1$s a %2$s.</string>
<string name="git_commit_move_multiple_text">Mover varios contrasinais a %1$s.</string>
<!-- PGPHandler -->
<string name="clipboard_password_toast_text">Contrasinal copiado ao portapapeis, tes %d segundos para pegala nalgures.</string>
<string name="clipboard_password_no_clear_toast_text">Contrasinal copiado ao portapapeis</string>
<string name="clipboard_copied_text">Copiada ó portapapeis</string>
<string name="file_toast_text">Debes proporcionar un nome de ficheiro</string>
<string name="path_toast_text">Por favor indica a ruta ao ficheiro</string>

View file

@ -37,8 +37,6 @@
<string name="git_commit_move_text">Rinomina %1$s in %2$s.</string>
<string name="git_commit_move_multiple_text">Sposta più password in %1$s.</string>
<!-- PGPHandler -->
<string name="clipboard_password_toast_text">Password copiata negli appunti, hai %d secondi per incollarla da qualche parte.</string>
<string name="clipboard_password_no_clear_toast_text">Password copiata negli appunti</string>
<string name="clipboard_copied_text">Copiato negli appunti</string>
<string name="file_toast_text">Sei pregato di fornire il nome di un file</string>
<string name="path_toast_text">Sei pregato di fornire il percorso di un file</string>

View file

@ -37,8 +37,6 @@
<string name="git_commit_move_text">Renomear %1$s para %2$s.</string>
<string name="git_commit_move_multiple_text">Mova múltiplas senhas para %1$s.</string>
<!-- PGPHandler -->
<string name="clipboard_password_toast_text">Senha copiada para área de transferência, você tem %d segundos para colá-la em algum lugar.</string>
<string name="clipboard_password_no_clear_toast_text">Senha copiada para área de transferência</string>
<string name="clipboard_copied_text">Copiado para a área de transferência</string>
<string name="file_toast_text">Por favor, informe um nome de arquivo</string>
<string name="path_toast_text">Por favor, forneça o caminho do arquivo</string>

View file

@ -41,8 +41,6 @@
<string name="git_commit_move_text">Переименовать %1$sв%2$s.</string>
<string name="git_commit_move_multiple_text">Переместить несколько паролей в %1$s.</string>
<!-- PGPHandler -->
<string name="clipboard_password_toast_text">Пароль скопирован в буфер обмена, у вас есть %d секунд чтобы вставить его.</string>
<string name="clipboard_password_no_clear_toast_text">Пароль скопирован в буфер обмена</string>
<string name="clipboard_copied_text">Скопировано в буфер обмена</string>
<string name="file_toast_text">Пожалуйста, укажите имя файла</string>
<string name="path_toast_text">Пожалуйста, задайте путь к файлу</string>

View file

@ -45,8 +45,6 @@
<string name="git_commit_move_multiple_text">Move multiple passwords to %1$s.</string>
<!-- PGPHandler -->
<string name="clipboard_password_toast_text">Password copied to clipboard, you have %d seconds to paste it somewhere.</string>
<string name="clipboard_password_no_clear_toast_text">Password copied to clipboard</string>
<string name="clipboard_copied_text">Copied to clipboard</string>
<string name="file_toast_text">Please provide a file name</string>
<string name="path_toast_text">Please provide a file path</string>

View file

@ -7,7 +7,9 @@ package com.github.androidpasswordstore.autofillparser
import android.app.assist.AssistStructure
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.autofill.AutofillId
import androidx.annotation.RequiresApi
@ -41,7 +43,15 @@ public sealed class FormOrigin(public open val identifier: String) {
is Web -> identifier
is App -> {
val info =
context.packageManager.getApplicationInfo(identifier, PackageManager.GET_META_DATA)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.getApplicationInfo(
identifier,
ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong())
)
} else {
@Suppress("DEPRECATION")
context.packageManager.getApplicationInfo(identifier, PackageManager.GET_META_DATA)
}
val label = context.packageManager.getApplicationLabel(info)
if (untrusted) "$label" else "$label"
}

View file

@ -54,7 +54,15 @@ public fun computeCertificatesHash(context: Context, appPackage: String): String
val stableHashOld = stableHash(signaturesOld.map { it.toByteArray() })
if (Build.VERSION.SDK_INT >= 28) {
val info =
context.packageManager.getPackageInfo(appPackage, PackageManager.GET_SIGNING_CERTIFICATES)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.getPackageInfo(
appPackage,
PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong())
)
} else {
@Suppress("DEPRECATION")
context.packageManager.getPackageInfo(appPackage, PackageManager.GET_SIGNING_CERTIFICATES)
}
val signaturesNew =
info.signingInfo.signingCertificateHistory ?: info.signingInfo.apkContentsSigners
val stableHashNew = stableHash(signaturesNew.map { it.toByteArray() })

View file

@ -5,8 +5,11 @@
package com.github.androidpasswordstore.autofillparser
import android.app.assist.AssistStructure
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.service.autofill.Dataset
import android.service.autofill.Field
import android.view.autofill.AutofillId
import android.view.autofill.AutofillValue
import androidx.annotation.RequiresApi
@ -54,17 +57,19 @@ public sealed class AutofillScenario<out T : Any> {
return try {
Builder<AutofillId>()
.apply {
username = clientState.getParcelable(BUNDLE_KEY_USERNAME_ID)
username = clientState.getParcelableCompat(BUNDLE_KEY_USERNAME_ID)
fillUsername = clientState.getBoolean(BUNDLE_KEY_FILL_USERNAME)
otp = clientState.getParcelable(BUNDLE_KEY_OTP_ID)
otp = clientState.getParcelableCompat(BUNDLE_KEY_OTP_ID)
currentPassword.addAll(
clientState.getParcelableArrayList(BUNDLE_KEY_CURRENT_PASSWORD_IDS) ?: emptyList()
clientState.getParcelableArrayListCompat(BUNDLE_KEY_CURRENT_PASSWORD_IDS)
?: emptyList()
)
newPassword.addAll(
clientState.getParcelableArrayList(BUNDLE_KEY_NEW_PASSWORD_IDS) ?: emptyList()
clientState.getParcelableArrayListCompat(BUNDLE_KEY_NEW_PASSWORD_IDS) ?: emptyList()
)
genericPassword.addAll(
clientState.getParcelableArrayList(BUNDLE_KEY_GENERIC_PASSWORD_IDS) ?: emptyList()
clientState.getParcelableArrayListCompat(BUNDLE_KEY_GENERIC_PASSWORD_IDS)
?: emptyList()
)
}
.build()
@ -73,6 +78,24 @@ public sealed class AutofillScenario<out T : Any> {
null
}
}
private inline fun <reified T> Bundle.getParcelableCompat(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, T::class.java)
} else {
@Suppress("DEPRECATION") getParcelable(key)
}
}
private inline fun <reified T : Parcelable> Bundle.getParcelableArrayListCompat(
key: String
): ArrayList<T>? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableArrayList(key, T::class.java)
} else {
@Suppress("DEPRECATION") getParcelableArrayList(key)
}
}
}
internal class Builder<T : Any> {
@ -231,7 +254,11 @@ public fun Dataset.Builder.fillWith(
scenario.otp -> credentialsToFill.otp
else -> credentialsToFill.password
}
setValue(field, AutofillValue.forText(value))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
setField(field, Field.Builder().setValue(AutofillValue.forText(value)).build())
} else {
@Suppress("DEPRECATION") setValue(field, AutofillValue.forText(value))
}
}
}

View file

@ -7,6 +7,7 @@ package com.github.androidpasswordstore.autofillparser
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ResolveInfoFlags
import android.net.Uri
import android.os.Build
import android.provider.Settings
@ -247,7 +248,15 @@ public fun getInstalledBrowsersWithAutofillSupportLevel(
): List<Pair<String, BrowserAutofillSupportLevel>> {
val testWebIntent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("https://example.org") }
val installedBrowsers =
context.packageManager.queryIntentActivities(testWebIntent, PackageManager.MATCH_ALL)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.queryIntentActivities(
testWebIntent,
ResolveInfoFlags.of(PackageManager.MATCH_ALL.toLong())
)
} else {
@Suppress("DEPRECATION")
context.packageManager.queryIntentActivities(testWebIntent, PackageManager.MATCH_ALL)
}
return installedBrowsers
.map { it to getBrowserAutofillSupportLevel(context, it.activityInfo.packageName) }
.filter { it.first.isDefault || it.second != BrowserAutofillSupportLevel.None }

View file

@ -10,7 +10,7 @@ import org.gradle.kotlin.dsl.configure
object AndroidCommon {
fun configure(project: Project) {
project.extensions.configure<TestedExtension> {
setCompileSdkVersion(32)
setCompileSdkVersion(33)
defaultConfig {
minSdk = 23
targetSdk = 31

View file

@ -158,11 +158,11 @@ complexity:
TooManyFunctions:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
thresholdInFiles: 11
thresholdInClasses: 11
thresholdInInterfaces: 11
thresholdInObjects: 11
thresholdInEnums: 11
thresholdInFiles: 15
thresholdInClasses: 15
thresholdInInterfaces: 15
thresholdInObjects: 15
thresholdInEnums: 15
ignoreDeprecated: false
ignorePrivate: false
ignoreOverridden: false