Fix ClipboardService crash (#1928)
* Fix clipboard service crash on API 31 * all: use ints directly for SDK version checks I much prefer it this way
This commit is contained in:
parent
fb7c81124c
commit
184391599b
31 changed files with 81 additions and 84 deletions
|
@ -6,11 +6,10 @@ package dev.msfjarvis.aps.autofill.oreo.ui
|
|||
|
||||
import android.content.Context
|
||||
import android.content.IntentSender
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
class AutofillSmsActivity : AppCompatActivity() {
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
|
|||
import org.openintents.openpgp.IOpenPgpService2
|
||||
import org.openintents.openpgp.OpenPgpError
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
@AndroidEntryPoint
|
||||
class AutofillDecryptActivity : AppCompatActivity() {
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ import kotlinx.coroutines.withContext
|
|||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
@AndroidEntryPoint
|
||||
class AutofillDecryptActivityV2 : AppCompatActivity() {
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ import javax.inject.Inject
|
|||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
@TargetApi(26)
|
||||
@AndroidEntryPoint
|
||||
class AutofillFilterView : AppCompatActivity() {
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ import dev.msfjarvis.aps.util.extensions.viewBinding
|
|||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
@TargetApi(26)
|
||||
class AutofillPublisherChangedActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -8,7 +8,6 @@ import android.app.PendingIntent
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentSender
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.autofill.AutofillManager
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||
|
@ -33,7 +32,7 @@ import javax.inject.Inject
|
|||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
@AndroidEntryPoint
|
||||
class AutofillSaveActivity : AppCompatActivity() {
|
||||
|
||||
|
|
|
@ -239,7 +239,7 @@ open class BasePgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBou
|
|||
action = ClipboardService.ACTION_START
|
||||
putExtra(ClipboardService.EXTRA_NOTIFICATION_TIME, clearAfter)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
startForegroundService(service)
|
||||
} else {
|
||||
startService(service)
|
||||
|
|
|
@ -129,7 +129,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
return@registerForActivityResult
|
||||
}
|
||||
val bitmap =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
ImageDecoder.decodeBitmap(ImageDecoder.createSource(contentResolver, imageUri))
|
||||
.copy(Bitmap.Config.ARGB_8888, true)
|
||||
} else {
|
||||
|
|
|
@ -105,7 +105,7 @@ class PasswordCreationActivityV2 : BasePgpActivity() {
|
|||
return@registerForActivityResult
|
||||
}
|
||||
val bitmap =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
ImageDecoder.decodeBitmap(ImageDecoder.createSource(contentResolver, imageUri))
|
||||
.copy(Bitmap.Config.ARGB_8888, true)
|
||||
} else {
|
||||
|
|
|
@ -35,11 +35,11 @@ class AutofillSettings(private val activity: FragmentActivity) : SettingsProvide
|
|||
|
||||
private val isAutofillServiceEnabled: Boolean
|
||||
get() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
|
||||
if (Build.VERSION.SDK_INT < 26) return false
|
||||
return activity.autofillManager?.hasEnabledAutofillServices() == true
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
private fun showAutofillDialog(pref: SwitchPreference) {
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
when (event) {
|
||||
|
@ -95,10 +95,10 @@ class AutofillSettings(private val activity: FragmentActivity) : SettingsProvide
|
|||
builder.apply {
|
||||
switch(PreferenceKeys.AUTOFILL_ENABLE) {
|
||||
titleRes = R.string.pref_autofill_enable_title
|
||||
visible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
visible = Build.VERSION.SDK_INT >= 26
|
||||
defaultValue = isAutofillServiceEnabled
|
||||
onClick {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return@onClick true
|
||||
if (Build.VERSION.SDK_INT < 26) return@onClick true
|
||||
if (isAutofillServiceEnabled) {
|
||||
activity.autofillManager?.disableAutofillServices()
|
||||
} else {
|
||||
|
|
|
@ -97,7 +97,7 @@ class GeneralSettings(private val activity: FragmentActivity) : SettingsProvider
|
|||
}
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
if (Build.VERSION.SDK_INT >= 25) {
|
||||
activity.getSystemService<ShortcutManager>()?.apply {
|
||||
removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class MiscSettings(activity: FragmentActivity) : SettingsProvider {
|
|||
putExtra("uri", uri)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
activity.startForegroundService(service)
|
||||
} else {
|
||||
activity.startService(service)
|
||||
|
|
|
@ -166,7 +166,7 @@ class RepositorySettings(private val activity: FragmentActivity) : SettingsProvi
|
|||
}
|
||||
.onFailure { it.message?.let { message -> activity.snackbar(message = message) } }
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
if (Build.VERSION.SDK_INT >= 25) {
|
||||
activity.getSystemService<ShortcutManager>()?.apply {
|
||||
removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package dev.msfjarvis.aps.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
|
||||
|
@ -36,7 +35,7 @@ import logcat.asLog
|
|||
import logcat.logcat
|
||||
|
||||
/** Implements [AutofillResponseBuilder]'s methods for API 30 and above */
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
@RequiresApi(30)
|
||||
class Api30AutofillResponseBuilder
|
||||
@AssistedInject
|
||||
constructor(
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package dev.msfjarvis.aps.util.autofill
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.github.androidpasswordstore.autofillparser.Credentials
|
||||
import dev.msfjarvis.aps.data.passfile.PasswordEntry
|
||||
|
@ -103,7 +102,7 @@ enum class DirectoryStructure(val value: String) {
|
|||
?: file.nameWithoutExtension
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
fun getSaveFolderName(sanitizedIdentifier: String, username: String?) =
|
||||
when (this) {
|
||||
EncryptedUsername -> "/"
|
||||
|
|
|
@ -35,7 +35,7 @@ import logcat.LogPriority.ERROR
|
|||
import logcat.asLog
|
||||
import logcat.logcat
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
class AutofillResponseBuilder
|
||||
@AssistedInject
|
||||
constructor(
|
||||
|
@ -175,7 +175,7 @@ constructor(
|
|||
addDataset(it)
|
||||
}
|
||||
if (datasetCount == 0) return null
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
setHeader(
|
||||
makeRemoteView(
|
||||
context,
|
||||
|
@ -218,7 +218,7 @@ constructor(
|
|||
// fill-in dataset without any visual representation. This causes it to be missing from
|
||||
// the Autofill suggestions shown after the user clears the filled out form fields.
|
||||
val builder =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
Dataset.Builder()
|
||||
} else {
|
||||
Dataset.Builder(makeRemoteView(context, makeEmptyMetadata()))
|
||||
|
|
|
@ -47,7 +47,7 @@ fun makeInlinePresentation(
|
|||
imeSpec: InlinePresentationSpec,
|
||||
metadata: DatasetMetadata
|
||||
): InlinePresentation? {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return null
|
||||
if (Build.VERSION.SDK_INT < 30) return null
|
||||
|
||||
if (UiVersions.INLINE_UI_VERSION_1 !in UiVersions.getVersions(imeSpec.style)) return null
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Base64
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
|
@ -33,7 +32,7 @@ import logcat.logcat
|
|||
|
||||
/** Get an instance of [AutofillManager]. Only available on Android Oreo and above */
|
||||
val Context.autofillManager: AutofillManager?
|
||||
@RequiresApi(Build.VERSION_CODES.O) get() = getSystemService()
|
||||
@RequiresApi(26) get() = getSystemService()
|
||||
|
||||
/** Get an instance of [ClipboardManager] */
|
||||
val Context.clipboard
|
||||
|
|
|
@ -120,7 +120,7 @@ object SshKey {
|
|||
context.sharedPrefs.edit { putString(PreferenceKeys.GIT_REMOTE_KEY_TYPE, value?.value) }
|
||||
|
||||
private val isStrongBoxSupported by unsafeLazy {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
if (Build.VERSION.SDK_INT >= 28)
|
||||
context.packageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)
|
||||
else false
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ object SshKey {
|
|||
setKeySize(256)
|
||||
setAlgorithmParameterSpec(java.security.spec.ECGenParameterSpec("secp256r1"))
|
||||
setDigests(KeyProperties.DIGEST_SHA256)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
setIsStrongBoxBacked(isStrongBoxSupported)
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ object SshKey {
|
|||
apply(algorithm.applyToSpec)
|
||||
if (requireAuthentication) {
|
||||
setUserAuthenticationRequired(true)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
setUserAuthenticationParameters(30, KeyProperties.AUTH_DEVICE_CREDENTIAL)
|
||||
} else {
|
||||
@Suppress("DEPRECATION") setUserAuthenticationValidityDurationSeconds(30)
|
||||
|
|
|
@ -112,8 +112,17 @@ class ClipboardService : Service() {
|
|||
val clearTimeMs = clearTime * 1000L
|
||||
val clearIntent = Intent(this, ClipboardService::class.java).apply { action = ACTION_CLEAR }
|
||||
val pendingIntent =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PendingIntent.getForegroundService(this, 0, clearIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
PendingIntent.getForegroundService(
|
||||
this,
|
||||
0,
|
||||
clearIntent,
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
)
|
||||
} else {
|
||||
PendingIntent.getService(
|
||||
this,
|
||||
|
@ -127,7 +136,7 @@ class ClipboardService : Service() {
|
|||
)
|
||||
}
|
||||
val notification =
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
|
||||
if (Build.VERSION.SDK_INT <= 23) {
|
||||
createNotificationApi23(pendingIntent)
|
||||
} else {
|
||||
createNotificationApi24(pendingIntent, clearTimeMs)
|
||||
|
@ -148,7 +157,7 @@ class ClipboardService : Service() {
|
|||
.build()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
@RequiresApi(24)
|
||||
private fun createNotificationApi24(
|
||||
pendingIntent: PendingIntent,
|
||||
clearTimeMs: Long
|
||||
|
@ -167,7 +176,7 @@ class ClipboardService : Service() {
|
|||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
val serviceChannel =
|
||||
NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
|
|
|
@ -37,7 +37,7 @@ import javax.inject.Inject
|
|||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
@AndroidEntryPoint
|
||||
class OreoAutofillService : AutofillService() {
|
||||
|
||||
|
@ -102,7 +102,7 @@ class OreoAutofillService : AutofillService() {
|
|||
callback.onSuccess(null)
|
||||
return
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
api30ResponseBuilderFactory
|
||||
.create(formToFill)
|
||||
.fillCredentials(this, request.inlineSuggestionsRequest, callback)
|
||||
|
|
|
@ -65,7 +65,7 @@ class PasswordExportService : Service() {
|
|||
logcat { "Copying ${repositoryDirectory.path} to $targetDirectory" }
|
||||
|
||||
val dateString =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)
|
||||
} else {
|
||||
String.format("%tFT%<tRZ", Calendar.getInstance(TimeZone.getTimeZone("Z")))
|
||||
|
@ -135,7 +135,7 @@ class PasswordExportService : Service() {
|
|||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
val serviceChannel =
|
||||
NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
|
|
|
@ -42,7 +42,7 @@ constructor(
|
|||
* [MAX_SHORTCUT_COUNT] and older items are removed by a simple LRU sweep.
|
||||
*/
|
||||
fun addDynamicShortcut(item: PasswordItem, intent: Intent) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return
|
||||
if (Build.VERSION.SDK_INT < 25) return
|
||||
val shortcutManager: ShortcutManager = context.getSystemService() ?: return
|
||||
val shortcut = buildShortcut(item, intent)
|
||||
val shortcuts = shortcutManager.dynamicShortcuts
|
||||
|
@ -67,7 +67,7 @@ constructor(
|
|||
* a no-op if the user's default launcher does not support pinned shortcuts.
|
||||
*/
|
||||
fun addPinnedShortcut(item: PasswordItem, intent: Intent) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
if (Build.VERSION.SDK_INT < 26) return
|
||||
val shortcutManager: ShortcutManager = context.getSystemService() ?: return
|
||||
if (!shortcutManager.isRequestPinShortcutSupported) {
|
||||
logcat { "addPinnedShortcut: pin shortcuts unsupported" }
|
||||
|
@ -78,7 +78,7 @@ constructor(
|
|||
}
|
||||
|
||||
/** Creates a [ShortcutInfo] from [item] and assigns [intent] to it. */
|
||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
||||
@RequiresApi(25)
|
||||
private fun buildShortcut(item: PasswordItem, intent: Intent): ShortcutInfo {
|
||||
return ShortcutInfo.Builder(context, item.fullPathToParent)
|
||||
.setShortLabel(item.toString())
|
||||
|
@ -93,7 +93,7 @@ constructor(
|
|||
* data, which ensures that the get/set dance in [addDynamicShortcut] does not cause invalidation
|
||||
* of icon assets, resulting in invisible icons in all but the newest launcher shortcut.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
||||
@RequiresApi(25)
|
||||
private fun rebuildShortcut(shortcut: ShortcutInfo): ShortcutInfo {
|
||||
// Non-null assertions are fine since we know these values aren't null.
|
||||
return ShortcutInfo.Builder(context, shortcut.id)
|
||||
|
|
|
@ -52,7 +52,7 @@ suspend fun <T> Task<T>.suspendableAwait() =
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
class AutofillSmsActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
@ -60,7 +60,7 @@ class AutofillSmsActivity : AppCompatActivity() {
|
|||
private var fillOtpFromSmsRequestCode = 1
|
||||
|
||||
fun shouldOfferFillFromSms(context: Context): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return false
|
||||
if (Build.VERSION.SDK_INT < 28) return false
|
||||
val googleApiAvailabilityInstance = GoogleApiAvailability.getInstance()
|
||||
val googleApiStatus = googleApiAvailabilityInstance.isGooglePlayServicesAvailable(context)
|
||||
if (googleApiStatus != ConnectionResult.SUCCESS) {
|
||||
|
|
|
@ -8,7 +8,6 @@ import android.app.assist.AssistStructure
|
|||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.autofill.AutofillId
|
||||
import androidx.annotation.RequiresApi
|
||||
|
@ -60,7 +59,7 @@ public sealed class FormOrigin(public open val identifier: String) {
|
|||
/**
|
||||
* Manages the detection of fields to fill in an [AssistStructure] and determines the [FormOrigin].
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
private class AutofillFormParser(
|
||||
context: Context,
|
||||
structure: AssistStructure,
|
||||
|
@ -191,7 +190,7 @@ public data class Credentials(val username: String?, val password: String?, val
|
|||
* Represents a collection of fields in a specific app that can be filled or saved. This is the
|
||||
* entry point to all fill and save features.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
public class FillableForm
|
||||
private constructor(
|
||||
public val formOrigin: FormOrigin,
|
||||
|
|
|
@ -71,14 +71,14 @@ public fun computeCertificatesHash(context: Context, appPackage: String): String
|
|||
* its `webDomain` and `webScheme`, if available.
|
||||
*/
|
||||
internal val AssistStructure.ViewNode.webOrigin: String?
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
get() =
|
||||
webDomain?.let { domain ->
|
||||
val scheme = (if (Build.VERSION.SDK_INT >= 28) webScheme else null) ?: "https"
|
||||
"$scheme://$domain"
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
public class FixedSaveCallback(context: Context, private val callback: SaveCallback) {
|
||||
|
||||
private val applicationContext = context.applicationContext
|
||||
|
@ -121,7 +121,7 @@ private fun visitViewNode(
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal fun AssistStructure.findNodeByAutofillId(
|
||||
autofillId: AutofillId
|
||||
): AssistStructure.ViewNode? {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package com.github.androidpasswordstore.autofillparser
|
||||
|
||||
import android.app.assist.AssistStructure
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.service.autofill.Dataset
|
||||
import android.view.autofill.AutofillId
|
||||
|
@ -28,7 +27,7 @@ public enum class AutofillAction {
|
|||
* [FormField], [AssistStructure.ViewNode] or [AutofillId], depending on how much metadata about the
|
||||
* field is needed and available in the particular situation.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
public sealed class AutofillScenario<out T : Any> {
|
||||
|
||||
public companion object {
|
||||
|
@ -163,7 +162,7 @@ public sealed class AutofillScenario<out T : Any> {
|
|||
get() = username != null
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal data class ClassifiedAutofillScenario<T : Any>(
|
||||
override val username: T?,
|
||||
override val fillUsername: Boolean,
|
||||
|
@ -184,7 +183,7 @@ internal data class ClassifiedAutofillScenario<T : Any>(
|
|||
get() = newPassword.ifEmpty { currentPassword }
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal data class GenericAutofillScenario<T : Any>(
|
||||
override val username: T?,
|
||||
override val fillUsername: Boolean,
|
||||
|
@ -204,7 +203,7 @@ internal data class GenericAutofillScenario<T : Any>(
|
|||
get() = genericPassword
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal fun AutofillScenario<FormField>.passesOriginCheck(singleOriginMode: Boolean): Boolean {
|
||||
return if (singleOriginMode) {
|
||||
// In single origin mode, only the browsers URL bar (which is never filled) should have
|
||||
|
@ -217,7 +216,7 @@ internal fun AutofillScenario<FormField>.passesOriginCheck(singleOriginMode: Boo
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
@JvmName("fillWithAutofillId")
|
||||
public fun Dataset.Builder.fillWith(
|
||||
scenario: AutofillScenario<AutofillId>,
|
||||
|
@ -236,7 +235,7 @@ public fun Dataset.Builder.fillWith(
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal inline fun <T : Any, S : Any> AutofillScenario<T>.map(
|
||||
transform: (T) -> S
|
||||
): AutofillScenario<S> {
|
||||
|
@ -256,7 +255,7 @@ internal inline fun <T : Any, S : Any> AutofillScenario<T>.map(
|
|||
return builder.build()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
@JvmName("toBundleAutofillId")
|
||||
internal fun AutofillScenario<AutofillId>.toBundle(): Bundle =
|
||||
when (this) {
|
||||
|
@ -285,7 +284,7 @@ internal fun AutofillScenario<AutofillId>.toBundle(): Bundle =
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
public fun AutofillScenario<AutofillId>.recoverNodes(
|
||||
structure: AssistStructure
|
||||
): AutofillScenario<AssistStructure.ViewNode>? {
|
||||
|
@ -293,13 +292,13 @@ public fun AutofillScenario<AutofillId>.recoverNodes(
|
|||
}
|
||||
|
||||
public val AutofillScenario<AssistStructure.ViewNode>.usernameValue: String?
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
get() {
|
||||
val value = username?.autofillValue ?: return null
|
||||
return if (value.isText) value.textValue.toString() else null
|
||||
}
|
||||
public val AutofillScenario<AssistStructure.ViewNode>.passwordValue: String?
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
get() {
|
||||
val distinctValues =
|
||||
passwordFieldsToSave
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*/
|
||||
package com.github.androidpasswordstore.autofillparser
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.github.androidpasswordstore.autofillparser.CertaintyLevel.Certain
|
||||
import com.github.androidpasswordstore.autofillparser.CertaintyLevel.Likely
|
||||
|
@ -22,7 +21,7 @@ private inline fun <T> Pair<T, T>.none(predicate: T.() -> Boolean) =
|
|||
* The strategy used to detect [AutofillScenario] s; expressed using the DSL implemented in
|
||||
* [AutofillDsl].
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal val autofillStrategy = strategy {
|
||||
|
||||
// Match two new password fields, an optional current password field right below or above, and
|
||||
|
|
|
@ -4,14 +4,13 @@
|
|||
*/
|
||||
package com.github.androidpasswordstore.autofillparser
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import logcat.LogPriority.WARN
|
||||
import logcat.logcat
|
||||
|
||||
@DslMarker internal annotation class AutofillDsl
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal interface FieldMatcher {
|
||||
|
||||
fun match(fields: List<FormField>, alreadyMatched: List<FormField>): List<FormField>?
|
||||
|
@ -72,7 +71,7 @@ internal interface FieldMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal class SingleFieldMatcher(
|
||||
private val take: (FormField, List<FormField>) -> Boolean,
|
||||
private val tieBreakers: List<(FormField, List<FormField>) -> Boolean>
|
||||
|
@ -136,7 +135,7 @@ internal class SingleFieldMatcher(
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
private class PairOfFieldsMatcher(
|
||||
private val take: (Pair<FormField, FormField>, List<FormField>) -> Boolean,
|
||||
private val tieBreakers: List<(Pair<FormField, FormField>, List<FormField>) -> Boolean>
|
||||
|
@ -174,7 +173,7 @@ private class PairOfFieldsMatcher(
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal class AutofillRule
|
||||
private constructor(
|
||||
private val matchers: List<AutofillRuleMatcher>,
|
||||
|
@ -382,7 +381,7 @@ private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal class AutofillStrategy private constructor(private val rules: List<AutofillRule>) {
|
||||
|
||||
@AutofillDsl
|
||||
|
@ -435,6 +434,6 @@ internal class AutofillStrategy private constructor(private val rules: List<Auto
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal fun strategy(block: AutofillStrategy.Builder.() -> Unit) =
|
||||
AutofillStrategy.Builder().apply(block).build()
|
||||
|
|
|
@ -142,7 +142,7 @@ private fun getBrowserMultiOriginMethod(appPackage: String): BrowserMultiOriginM
|
|||
* Some browsers may not issue save requests automatically and thus need
|
||||
* `FLAG_SAVE_ON_ALL_VIEW_INVISIBLE` to be set.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
private val BROWSER_SAVE_FLAG =
|
||||
mapOf(
|
||||
"com.duckduckgo.mobile.android" to 0,
|
||||
|
@ -156,7 +156,7 @@ private val BROWSER_SAVE_FLAG =
|
|||
"com.opera.touch" to 0,
|
||||
)
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
private val BROWSER_SAVE_FLAG_IF_NO_ACCESSIBILITY =
|
||||
mapOf(
|
||||
"com.android.chrome" to SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE,
|
||||
|
@ -176,7 +176,7 @@ private fun isNoAccessibilityServiceEnabled(context: Context): Boolean {
|
|||
.isNullOrEmpty()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
private fun getBrowserSaveFlag(context: Context, appPackage: String): Int? =
|
||||
BROWSER_SAVE_FLAG[appPackage]
|
||||
?: BROWSER_SAVE_FLAG_IF_NO_ACCESSIBILITY[appPackage]?.takeIf {
|
||||
|
@ -188,7 +188,7 @@ internal data class BrowserAutofillSupportInfo(
|
|||
val saveFlags: Int?
|
||||
)
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal fun getBrowserAutofillSupportInfoIfTrusted(
|
||||
context: Context,
|
||||
appPackage: String
|
||||
|
@ -214,7 +214,7 @@ public enum class BrowserAutofillSupportLevel {
|
|||
GeneralFillAndSave,
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
private fun getBrowserAutofillSupportLevel(
|
||||
context: Context,
|
||||
appPackage: String
|
||||
|
@ -234,13 +234,12 @@ private fun getBrowserAutofillSupportLevel(
|
|||
// (compatibility mode is only available on Android Pie and higher). Since all known browsers
|
||||
// with native Autofill support offer full save support as well, we reuse the list of those
|
||||
// browsers here.
|
||||
supportLevel != BrowserAutofillSupportLevel.GeneralFillAndSave &&
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.P
|
||||
supportLevel != BrowserAutofillSupportLevel.GeneralFillAndSave && Build.VERSION.SDK_INT < 28
|
||||
}
|
||||
?: BrowserAutofillSupportLevel.None
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
public fun getInstalledBrowsersWithAutofillSupportLevel(
|
||||
context: Context
|
||||
): List<Pair<String, BrowserAutofillSupportLevel>> {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package com.github.androidpasswordstore.autofillparser
|
||||
|
||||
import android.app.assist.AssistStructure
|
||||
import android.os.Build
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.view.autofill.AutofillId
|
||||
|
@ -24,7 +23,7 @@ internal enum class CertaintyLevel {
|
|||
* Represents a single potentially fillable or saveable field together with all meta data extracted
|
||||
* from its [AssistStructure.ViewNode].
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@RequiresApi(26)
|
||||
internal class FormField(
|
||||
node: AssistStructure.ViewNode,
|
||||
private val index: Int,
|
||||
|
@ -114,8 +113,7 @@ internal class FormField(
|
|||
.toSet()
|
||||
.toList()
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun isSupportedHint(hint: String?) = hint in HINTS_FILLABLE
|
||||
@RequiresApi(26) private fun isSupportedHint(hint: String?) = hint in HINTS_FILLABLE
|
||||
private val EXCLUDED_TERMS =
|
||||
listOf(
|
||||
"url_bar", // Chrome/Edge/Firefox address bar
|
||||
|
|
Loading…
Reference in a new issue