summaryrefslogtreecommitdiffstats
path: root/autofill-parser/src/main/java/com/github
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-05 12:05:44 +0100
committerDanilo M. <danix@danix.xyz>2026-11-05 12:10:18 +0100
commitc85e7c8801caf7a73b46dda9ebf75be023cad9b4 (patch)
treec180a57fd54e9b09e84297f9fc51670bf5f4bfab /autofill-parser/src/main/java/com/github
parent73486a180ba3ead1890924920094e7e678a8ef76 (diff)
downloadHardenedPass-c85e7c8801caf7a73b46dda9ebf75be023cad9b4.tar.gz
HardenedPass-c85e7c8801caf7a73b46dda9ebf75be023cad9b4.zip
build: raise minSdk to 31
The project targets GrapheneOS, which only runs current Android. Android 12 gives the platform night mode API and removes the fingerprint dialog path that needs AppCompat. Devices below it stay on 0.2.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'autofill-parser/src/main/java/com/github')
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt3
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt49
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillScenario.kt11
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt3
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategyDsl.kt8
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt18
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt4
-rw-r--r--autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt11
8 files changed, 20 insertions, 87 deletions
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 3dc74f33..eba3b2bc 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
@@ -11,7 +11,6 @@ import android.content.pm.PackageManager.ApplicationInfoFlags
import android.os.Build
import android.os.Bundle
import android.view.autofill.AutofillId
-import androidx.annotation.RequiresApi
import androidx.core.net.toUri
import logcat.logcat
@@ -69,7 +68,6 @@ 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)
private class AutofillFormParser(
context: Context,
structure: AssistStructure,
@@ -200,7 +198,6 @@ 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)
public class FillableForm
private constructor(
public val formOrigin: FormOrigin,
diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt
index 9ff646c9..b177b5c0 100644
--- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt
+++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt
@@ -15,7 +15,6 @@ import android.service.autofill.SaveCallback
import android.util.Base64
import android.view.autofill.AutofillId
import android.widget.Toast
-import androidx.annotation.RequiresApi
import java.security.MessageDigest
import logcat.LogPriority.ERROR
import logcat.logcat
@@ -56,24 +55,22 @@ public fun computeCertificatesHash(context: Context, appPackage: String): String
.signatures
.orEmpty()
val stableHashOld = stableHash(signaturesOld.map { it.toByteArray() })
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- val info =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- context.packageManager.getPackageInfo(
- appPackage,
- PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong()),
- )
- } else {
- context.packageManager.getPackageInfo(appPackage, PackageManager.GET_SIGNING_CERTIFICATES)
- }
- val signaturesNew =
- info.signingInfo?.signingCertificateHistory ?: info.signingInfo?.apkContentsSigners.orEmpty()
- val stableHashNew = stableHash(signaturesNew.map { it.toByteArray() })
- if (stableHashNew != stableHashOld)
- logcat("CertificatesHash", ERROR) {
- "Mismatch between old and new hash: $stableHashNew != $stableHashOld"
- }
- }
+ val info =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ context.packageManager.getPackageInfo(
+ appPackage,
+ PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong()),
+ )
+ } else {
+ context.packageManager.getPackageInfo(appPackage, PackageManager.GET_SIGNING_CERTIFICATES)
+ }
+ val signaturesNew =
+ info.signingInfo?.signingCertificateHistory ?: info.signingInfo?.apkContentsSigners.orEmpty()
+ val stableHashNew = stableHash(signaturesNew.map { it.toByteArray() })
+ if (stableHashNew != stableHashOld)
+ logcat("CertificatesHash", ERROR) {
+ "Mismatch between old and new hash: $stableHashNew != $stableHashOld"
+ }
return stableHashOld
}
@@ -82,15 +79,12 @@ 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)
get() =
webDomain?.let { domain ->
- val scheme =
- (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) webScheme else null) ?: "https"
+ val scheme = webScheme ?: "https"
"$scheme://$domain"
}
-@RequiresApi(Build.VERSION_CODES.O)
public class FixedSaveCallback(private val context: Context, private val callback: SaveCallback) {
private val applicationContext = context.applicationContext
@@ -120,13 +114,7 @@ public class FixedSaveCallback(private val context: Context, private val callbac
applicationContext.startIntentSender(intentSender, null, 0, 0, 0, options)
callback.onSuccess()
}
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> callback.onSuccess(intentSender)
- else -> {
- callback.onSuccess()
- // On SDKs < 28, we cannot advise the Autofill framework to launch the save intent in
- // the context of the app that triggered the save request. Hence, we launch it here.
- applicationContext.startIntentSender(intentSender, null, 0, 0, 0)
- }
+ else -> callback.onSuccess(intentSender)
}
}
}
@@ -147,7 +135,6 @@ private fun visitViewNode(
}
}
-@RequiresApi(Build.VERSION_CODES.O)
internal fun AssistStructure.findNodeByAutofillId(
autofillId: AutofillId
): AssistStructure.ViewNode? {
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 8a5b7fe3..15faabde 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
@@ -11,7 +11,6 @@ import android.service.autofill.Dataset
import android.service.autofill.Field
import android.view.autofill.AutofillId
import android.view.autofill.AutofillValue
-import androidx.annotation.RequiresApi
import androidx.core.os.BundleCompat
import logcat.LogPriority.ERROR
import logcat.asLog
@@ -30,7 +29,6 @@ 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)
public sealed class AutofillScenario<out T : Any> {
public companion object {
@@ -187,7 +185,6 @@ public sealed class AutofillScenario<out T : Any> {
get() = username != null
}
-@RequiresApi(Build.VERSION_CODES.O)
internal data class ClassifiedAutofillScenario<T : Any>(
override val username: T?,
override val fillUsername: Boolean,
@@ -212,7 +209,6 @@ internal data class ClassifiedAutofillScenario<T : Any>(
get() = newPassword.ifEmpty { currentPassword }
}
-@RequiresApi(Build.VERSION_CODES.O)
internal data class GenericAutofillScenario<T : Any>(
override val username: T?,
override val fillUsername: Boolean,
@@ -236,7 +232,6 @@ internal data class GenericAutofillScenario<T : Any>(
get() = genericPassword
}
-@RequiresApi(Build.VERSION_CODES.O)
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
@@ -249,7 +244,6 @@ internal fun AutofillScenario<FormField>.passesOriginCheck(singleOriginMode: Boo
}
}
-@RequiresApi(Build.VERSION_CODES.O)
@JvmName("fillWithAutofillId")
public fun Dataset.Builder.fillWith(
scenario: AutofillScenario<AutofillId>,
@@ -272,7 +266,6 @@ public fun Dataset.Builder.fillWith(
}
}
-@RequiresApi(Build.VERSION_CODES.O)
internal inline fun <T : Any, S : Any> AutofillScenario<T>.map(
transform: (T) -> S
): AutofillScenario<S> {
@@ -292,7 +285,6 @@ internal inline fun <T : Any, S : Any> AutofillScenario<T>.map(
return builder.build()
}
-@RequiresApi(Build.VERSION_CODES.O)
@JvmName("toBundleAutofillId")
internal fun AutofillScenario<AutofillId>.toBundle(): Bundle =
when (this) {
@@ -321,7 +313,6 @@ internal fun AutofillScenario<AutofillId>.toBundle(): Bundle =
}
}
-@RequiresApi(Build.VERSION_CODES.O)
public fun AutofillScenario<AutofillId>.recoverNodes(
structure: AssistStructure
): AutofillScenario<AssistStructure.ViewNode>? {
@@ -329,13 +320,11 @@ public fun AutofillScenario<AutofillId>.recoverNodes(
}
public val AutofillScenario<AssistStructure.ViewNode>.usernameValue: String?
- @RequiresApi(Build.VERSION_CODES.O)
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)
get() {
val distinctValues =
passwordFieldsToSave
diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt
index bc227bad..5e1cebad 100644
--- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt
+++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillStrategy.kt
@@ -4,8 +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 +20,6 @@ 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)
internal val autofillStrategy = strategy {
// Match two new password fields, an optional current password field right below or above, and
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 673c7466..a9b01f25 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
@@ -4,14 +4,11 @@
*/
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)
internal interface FieldMatcher {
fun match(fields: List<FormField>, alreadyMatched: List<FormField>): List<FormField>?
@@ -72,7 +69,6 @@ internal interface FieldMatcher {
}
}
-@RequiresApi(Build.VERSION_CODES.O)
internal class SingleFieldMatcher(
private val take: (FormField, List<FormField>) -> Boolean,
private val tieBreakers: List<(FormField, List<FormField>) -> Boolean>,
@@ -136,7 +132,6 @@ internal class SingleFieldMatcher(
}
}
-@RequiresApi(Build.VERSION_CODES.O)
private class PairOfFieldsMatcher(
private val take: (Pair<FormField, FormField>, List<FormField>) -> Boolean,
private val tieBreakers: List<(Pair<FormField, FormField>, List<FormField>) -> Boolean>,
@@ -174,7 +169,6 @@ private class PairOfFieldsMatcher(
}
}
-@RequiresApi(Build.VERSION_CODES.O)
internal class AutofillRule
private constructor(
private val matchers: List<AutofillRuleMatcher>,
@@ -384,7 +378,6 @@ private constructor(
}
}
-@RequiresApi(Build.VERSION_CODES.O)
internal class AutofillStrategy private constructor(private val rules: List<AutofillRule>) {
@AutofillDsl
@@ -436,6 +429,5 @@ internal class AutofillStrategy private constructor(private val rules: List<Auto
}
}
-@RequiresApi(Build.VERSION_CODES.O)
internal fun strategy(block: AutofillStrategy.Builder.() -> Unit) =
AutofillStrategy.Builder().apply(block).build()
diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt
index de28cd80..2db02f51 100644
--- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt
+++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FeatureAndTrustDetection.kt
@@ -11,7 +11,6 @@ import android.content.pm.PackageManager.ResolveInfoFlags
import android.os.Build
import android.provider.Settings
import android.service.autofill.SaveInfo
-import androidx.annotation.RequiresApi
import androidx.core.net.toUri
/**
@@ -143,7 +142,6 @@ 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)
private val BROWSER_SAVE_FLAG =
mapOf(
"com.duckduckgo.mobile.android" to 0,
@@ -159,7 +157,6 @@ private val BROWSER_SAVE_FLAG =
"com.opera.touch" to 0,
)
-@RequiresApi(Build.VERSION_CODES.O)
private val BROWSER_SAVE_FLAG_IF_NO_ACCESSIBILITY =
mapOf(
"com.android.chrome" to SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE,
@@ -180,7 +177,6 @@ private fun isNoAccessibilityServiceEnabled(context: Context): Boolean {
.isNullOrEmpty()
}
-@RequiresApi(Build.VERSION_CODES.O)
private fun getBrowserSaveFlag(context: Context, appPackage: String): Int? =
BROWSER_SAVE_FLAG[appPackage]
?: BROWSER_SAVE_FLAG_IF_NO_ACCESSIBILITY[appPackage]?.takeIf {
@@ -192,7 +188,6 @@ internal data class BrowserAutofillSupportInfo(
val saveFlags: Int?,
)
-@RequiresApi(Build.VERSION_CODES.O)
internal fun getBrowserAutofillSupportInfoIfTrusted(
context: Context,
appPackage: String,
@@ -215,7 +210,6 @@ public enum class BrowserAutofillSupportLevel {
GeneralFillAndSave,
}
-@RequiresApi(Build.VERSION_CODES.O)
private fun getBrowserAutofillSupportLevel(
context: Context,
appPackage: String,
@@ -230,19 +224,9 @@ private fun getBrowserAutofillSupportLevel(
BrowserAutofillSupportLevel.PasswordFill
browserInfo.saveFlags == null -> BrowserAutofillSupportLevel.GeneralFill
else -> BrowserAutofillSupportLevel.GeneralFillAndSave
- }.takeUnless { supportLevel ->
- // On Android Oreo, only browsers with native Autofill support can be used with Password
- // Store
- // (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
- } ?: BrowserAutofillSupportLevel.None
+ }
}
-@RequiresApi(Build.VERSION_CODES.O)
public fun getInstalledBrowsersWithAutofillSupportLevel(
context: Context
): List<Pair<String, BrowserAutofillSupportLevel>> {
diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt
index 983a8f0a..b8df0664 100644
--- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt
+++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/FormField.kt
@@ -5,11 +5,9 @@
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
-import androidx.annotation.RequiresApi
import androidx.autofill.HintConstants
import java.util.Locale
@@ -24,7 +22,6 @@ 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)
internal class FormField(
node: AssistStructure.ViewNode,
private val index: Int,
@@ -91,7 +88,6 @@ internal class FormField(
.toSet()
.toList()
- @RequiresApi(Build.VERSION_CODES.O)
private fun isSupportedHint(hint: String?) = hint in HINTS_FILLABLE
private val EXCLUDED_TERMS =
diff --git a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt
index 8eb113d5..d14358e8 100644
--- a/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt
+++ b/autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/PublicSuffixListCache.kt
@@ -6,7 +6,6 @@ package com.github.androidpasswordstore.autofillparser
import android.content.Context
import android.net.InetAddresses
-import android.os.Build
import android.util.Patterns
import kotlinx.coroutines.runBlocking
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
@@ -45,21 +44,13 @@ internal fun getPublicSuffixPlusOne(
// We do not check whether the domain actually exists (actually, not even whether its TLD
// exists). As long as we restrict ourselves to syntactically valid domain names,
// getPublicSuffixPlusOne will return non-colliding results.
- if (!Patterns.DOMAIN_NAME.matcher(domain).matches() || isNumericAddress(domain)) {
+ if (!Patterns.DOMAIN_NAME.matcher(domain).matches() || InetAddresses.isNumericAddress(domain)) {
domain
} else {
getCanonicalSuffix(context, domain, customSuffixes)
}
}
-private fun isNumericAddress(domain: String): Boolean {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- InetAddresses.isNumericAddress(domain)
- } else {
- @Suppress("DEPRECATION") Patterns.IP_ADDRESS.matcher(domain).matches()
- }
-}
-
/**
* Returns:
* - [domain], if [domain] equals [suffix];