Skip to content
This repository was archived by the owner on Oct 15, 2024. It is now read-only.

Commit 0e9843c

Browse files
FabianHennekemsfjarvis
authored andcommitted
Make autofill-parser API explicit and refactor (#1182)
(cherry picked from commit 73648b3)
1 parent 2494a15 commit 0e9843c

File tree

13 files changed

+129
-440
lines changed

13 files changed

+129
-440
lines changed

app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillResponseBuilder.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ class AutofillResponseBuilder(form: FillableForm) {
3737
private val clientState = form.toClientState()
3838

3939
// We do not offer save when the only relevant field is a username field or there is no field.
40-
private val scenarioSupportsSave =
41-
scenario.fieldsToSave.minus(listOfNotNull(scenario.username)).isNotEmpty()
40+
private val scenarioSupportsSave = scenario.hasPasswordFieldsToSave
4241
private val canBeSaved = saveFlags != null && scenarioSupportsSave
4342

4443
private fun makePlaceholderDataset(
@@ -54,29 +53,29 @@ class AutofillResponseBuilder(form: FillableForm) {
5453
}
5554

5655
private fun makeMatchDataset(context: Context, file: File): Dataset? {
57-
if (scenario.fieldsToFillOn(AutofillAction.Match).isEmpty()) return null
56+
if (scenario.hasFieldsToFillOn(AutofillAction.Match)) return null
5857
val remoteView = makeFillMatchRemoteView(context, file, formOrigin)
5958
val intentSender = AutofillDecryptActivity.makeDecryptFileIntentSender(file, context)
6059
return makePlaceholderDataset(remoteView, intentSender, AutofillAction.Match)
6160
}
6261

6362
private fun makeSearchDataset(context: Context): Dataset? {
64-
if (scenario.fieldsToFillOn(AutofillAction.Search).isEmpty()) return null
63+
if (scenario.hasFieldsToFillOn(AutofillAction.Search)) return null
6564
val remoteView = makeSearchAndFillRemoteView(context, formOrigin)
6665
val intentSender =
6766
AutofillFilterView.makeMatchAndDecryptFileIntentSender(context, formOrigin)
6867
return makePlaceholderDataset(remoteView, intentSender, AutofillAction.Search)
6968
}
7069

7170
private fun makeGenerateDataset(context: Context): Dataset? {
72-
if (scenario.fieldsToFillOn(AutofillAction.Generate).isEmpty()) return null
71+
if (scenario.hasFieldsToFillOn(AutofillAction.Generate)) return null
7372
val remoteView = makeGenerateAndFillRemoteView(context, formOrigin)
7473
val intentSender = AutofillSaveActivity.makeSaveIntentSender(context, null, formOrigin)
7574
return makePlaceholderDataset(remoteView, intentSender, AutofillAction.Generate)
7675
}
7776

7877
private fun makeFillOtpFromSmsDataset(context: Context): Dataset? {
79-
if (scenario.fieldsToFillOn(AutofillAction.FillOtpFromSms).isEmpty()) return null
78+
if (scenario.hasFieldsToFillOn(AutofillAction.FillOtpFromSms)) return null
8079
if (!AutofillSmsActivity.shouldOfferFillFromSms(context)) return null
8180
val remoteView = makeFillOtpFromSmsRemoteView(context, formOrigin)
8281
val intentSender = AutofillSmsActivity.makeFillOtpFromSmsIntentSender(context)
@@ -115,10 +114,10 @@ class AutofillResponseBuilder(form: FillableForm) {
115114
private fun makeSaveInfo(): SaveInfo? {
116115
if (!canBeSaved) return null
117116
check(saveFlags != null)
118-
val idsToSave = scenario.fieldsToSave.map { it.autofillId }.toTypedArray()
117+
val idsToSave = scenario.fieldsToSave.toTypedArray()
119118
if (idsToSave.isEmpty()) return null
120119
var saveDataTypes = SaveInfo.SAVE_DATA_TYPE_PASSWORD
121-
if (scenario.username != null) {
120+
if (scenario.hasUsername) {
122121
saveDataTypes = saveDataTypes or SaveInfo.SAVE_DATA_TYPE_USERNAME
123122
}
124123
return SaveInfo.Builder(saveDataTypes, idsToSave).run {
@@ -179,7 +178,7 @@ class AutofillResponseBuilder(form: FillableForm) {
179178
action: AutofillAction
180179
): Dataset {
181180
val remoteView = makePlaceholderRemoteView(context)
182-
val scenario = AutofillScenario.fromBundle(clientState)
181+
val scenario = AutofillScenario.fromClientState(clientState)
183182
// Before Android P, Datasets used for fill-in had to come with a RemoteViews, even
184183
// though they are never shown.
185184
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {

app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class OreoAutofillService : AutofillService() {
102102
callback.onFailure(getString(R.string.oreo_autofill_save_internal_error))
103103
return
104104
}
105-
val scenario = AutofillScenario.fromBundle(clientState)?.recoverNodes(structure) ?: run {
105+
val scenario = AutofillScenario.fromClientState(clientState)?.recoverNodes(structure) ?: run {
106106
e { "Failed to recover client state or nodes from client state" }
107107
callback.onFailure(getString(R.string.oreo_autofill_save_internal_error))
108108
return

autofill-parser/api/autofill-parser.api

Lines changed: 5 additions & 261 deletions
Large diffs are not rendered by default.

autofill-parser/build.gradle.kts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,20 @@ fun getCredential(type: String): String {
2121

2222
android {
2323
defaultConfig {
24-
versionCode = 1
25-
versionName = "1.0"
24+
versionCode = 2
25+
versionName = "2.0"
2626
consumerProguardFiles("consumer-rules.pro")
2727
}
28+
29+
kotlin {
30+
explicitApi()
31+
}
32+
33+
kotlinOptions {
34+
freeCompilerArgs = freeCompilerArgs + listOf(
35+
"-Xexplicit-api=strict"
36+
)
37+
}
2838
}
2939

3040
afterEvaluate {

autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillFormParser.kt

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ import com.github.ajalt.timberkt.d
1717
/**
1818
* A unique identifier for either an Android app (package name) or a website (origin minus port).
1919
*/
20-
sealed class FormOrigin(open val identifier: String) {
20+
public sealed class FormOrigin(public open val identifier: String) {
2121

22-
data class Web(override val identifier: String) : FormOrigin(identifier)
23-
data class App(override val identifier: String) : FormOrigin(identifier)
22+
public data class Web(override val identifier: String) : FormOrigin(identifier)
23+
public data class App(override val identifier: String) : FormOrigin(identifier)
2424

25-
companion object {
25+
public companion object {
2626

2727
private const val BUNDLE_KEY_WEB_IDENTIFIER = "webIdentifier"
2828
private const val BUNDLE_KEY_APP_IDENTIFIER = "appIdentifier"
2929

30-
fun fromBundle(bundle: Bundle): FormOrigin? {
30+
public fun fromBundle(bundle: Bundle): FormOrigin? {
3131
val webIdentifier = bundle.getString(BUNDLE_KEY_WEB_IDENTIFIER)
3232
if (webIdentifier != null) {
3333
return Web(webIdentifier)
@@ -37,18 +37,19 @@ sealed class FormOrigin(open val identifier: String) {
3737
}
3838
}
3939

40-
fun getPrettyIdentifier(context: Context, untrusted: Boolean = true) = when (this) {
41-
is Web -> identifier
42-
is App -> {
43-
val info = context.packageManager.getApplicationInfo(
44-
identifier, PackageManager.GET_META_DATA
45-
)
46-
val label = context.packageManager.getApplicationLabel(info)
47-
if (untrusted) "$label" else "$label"
40+
public fun getPrettyIdentifier(context: Context, untrusted: Boolean = true): String =
41+
when (this) {
42+
is Web -> identifier
43+
is App -> {
44+
val info = context.packageManager.getApplicationInfo(
45+
identifier, PackageManager.GET_META_DATA
46+
)
47+
val label = context.packageManager.getApplicationLabel(info)
48+
if (untrusted) "$label" else "$label"
49+
}
4850
}
49-
}
5051

51-
fun toBundle() = Bundle().apply {
52+
public fun toBundle(): Bundle = Bundle().apply {
5253
when (this@FormOrigin) {
5354
is Web -> putString(BUNDLE_KEY_WEB_IDENTIFIER, identifier)
5455
is App -> putString(BUNDLE_KEY_APP_IDENTIFIER, identifier)
@@ -183,36 +184,36 @@ private class AutofillFormParser(
183184
}
184185
}
185186

186-
data class Credentials(val username: String?, val password: String?, val otp: String?)
187+
public data class Credentials(val username: String?, val password: String?, val otp: String?)
187188

188189
/**
189190
* Represents a collection of fields in a specific app that can be filled or saved. This is the
190191
* entry point to all fill and save features.
191192
*/
192193
@RequiresApi(Build.VERSION_CODES.O)
193-
class FillableForm private constructor(
194-
val formOrigin: FormOrigin,
195-
val scenario: AutofillScenario<FormField>,
196-
val ignoredIds: List<AutofillId>,
197-
val saveFlags: Int?
194+
public class FillableForm private constructor(
195+
public val formOrigin: FormOrigin,
196+
public val scenario: AutofillScenario<AutofillId>,
197+
public val ignoredIds: List<AutofillId>,
198+
public val saveFlags: Int?
198199
) {
199-
companion object {
200+
public companion object {
200201
/**
201202
* Returns a [FillableForm] if a login form could be detected in [structure].
202203
*/
203-
fun parseAssistStructure(
204+
public fun parseAssistStructure(
204205
context: Context,
205206
structure: AssistStructure,
206207
isManualRequest: Boolean,
207208
customSuffixes: Sequence<String> = emptySequence(),
208209
): FillableForm? {
209210
val form = AutofillFormParser(context, structure, isManualRequest, customSuffixes)
210211
if (form.formOrigin == null || form.scenario == null) return null
211-
return FillableForm(form.formOrigin, form.scenario, form.ignoredIds, form.saveFlags)
212+
return FillableForm(form.formOrigin, form.scenario.map { it.autofillId }, form.ignoredIds, form.saveFlags)
212213
}
213214
}
214215

215-
fun toClientState() = scenario.toBundle().apply {
216+
public fun toClientState(): Bundle = scenario.toBundle().apply {
216217
putAll(formOrigin.toBundle())
217218
}
218219
}

autofill-parser/src/main/java/com/github/androidpasswordstore/autofillparser/AutofillHelper.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ private fun stableHash(array: Collection<ByteArray>): String {
4242
* In most cases apps will only have a single certificate. If there are multiple, this functions
4343
* returns all of them in sorted order and separated with `;`.
4444
*/
45-
fun computeCertificatesHash(context: Context, appPackage: String): String {
45+
public fun computeCertificatesHash(context: Context, appPackage: String): String {
4646
// The warning does not apply since 1) we are specifically hashing **all** signatures and 2) it
4747
// no longer applies to Android 4.4+.
4848
// Even though there is a new way to get the certificates as of Android Pie, we need to keep
@@ -68,18 +68,18 @@ fun computeCertificatesHash(context: Context, appPackage: String): String {
6868
* Returns the "origin" (without port information) of the [AssistStructure.ViewNode] derived from
6969
* its `webDomain` and `webScheme`, if available.
7070
*/
71-
val AssistStructure.ViewNode.webOrigin: String?
71+
internal val AssistStructure.ViewNode.webOrigin: String?
7272
@RequiresApi(Build.VERSION_CODES.O) get() = webDomain?.let { domain ->
7373
val scheme = (if (Build.VERSION.SDK_INT >= 28) webScheme else null) ?: "https"
7474
"$scheme://$domain"
7575
}
7676

7777
@RequiresApi(Build.VERSION_CODES.O)
78-
class FixedSaveCallback(context: Context, private val callback: SaveCallback) {
78+
public class FixedSaveCallback(context: Context, private val callback: SaveCallback) {
7979

8080
private val applicationContext = context.applicationContext
8181

82-
fun onFailure(message: CharSequence) {
82+
public fun onFailure(message: CharSequence) {
8383
callback.onFailure(message)
8484
// When targeting SDK 29, the message is no longer shown as a toast.
8585
// See https://developer.android.com/reference/android/service/autofill/SaveCallback#onFailure(java.lang.CharSequence)
@@ -88,7 +88,7 @@ class FixedSaveCallback(context: Context, private val callback: SaveCallback) {
8888
}
8989
}
9090

91-
fun onSuccess(intentSender: IntentSender) {
91+
public fun onSuccess(intentSender: IntentSender) {
9292
if (Build.VERSION.SDK_INT >= 28) {
9393
callback.onSuccess(intentSender)
9494
} else {
@@ -117,7 +117,7 @@ private fun visitViewNode(
117117
}
118118

119119
@RequiresApi(Build.VERSION_CODES.O)
120-
fun AssistStructure.findNodeByAutofillId(autofillId: AutofillId): AssistStructure.ViewNode? {
120+
internal fun AssistStructure.findNodeByAutofillId(autofillId: AutofillId): AssistStructure.ViewNode? {
121121
var node: AssistStructure.ViewNode? = null
122122
visitViewNodes(this) {
123123
if (it.autofillId == autofillId)

0 commit comments

Comments
 (0)