aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillResponseBuilder.kt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillResponseBuilder.kt')
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillResponseBuilder.kt233
1 files changed, 0 insertions, 233 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillResponseBuilder.kt b/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillResponseBuilder.kt
deleted file mode 100644
index a091e206..00000000
--- a/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillResponseBuilder.kt
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package dev.msfjarvis.aps.util.autofill
-
-import android.content.Context
-import android.content.IntentSender
-import android.os.Build
-import android.os.Bundle
-import android.service.autofill.Dataset
-import android.service.autofill.FillCallback
-import android.service.autofill.FillResponse
-import android.service.autofill.SaveInfo
-import androidx.annotation.RequiresApi
-import com.github.androidpasswordstore.autofillparser.AutofillAction
-import com.github.androidpasswordstore.autofillparser.AutofillScenario
-import com.github.androidpasswordstore.autofillparser.Credentials
-import com.github.androidpasswordstore.autofillparser.FillableForm
-import com.github.androidpasswordstore.autofillparser.fillWith
-import com.github.michaelbull.result.fold
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import dev.msfjarvis.aps.autofill.oreo.ui.AutofillSmsActivity
-import dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivity
-import dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivityV2
-import dev.msfjarvis.aps.ui.autofill.AutofillFilterView
-import dev.msfjarvis.aps.ui.autofill.AutofillPublisherChangedActivity
-import dev.msfjarvis.aps.ui.autofill.AutofillSaveActivity
-import dev.msfjarvis.aps.util.features.Feature
-import dev.msfjarvis.aps.util.features.Features
-import java.io.File
-import logcat.LogPriority.ERROR
-import logcat.asLog
-import logcat.logcat
-
-@RequiresApi(26)
-class AutofillResponseBuilder
-@AssistedInject
-constructor(
- @Assisted form: FillableForm,
- private val features: Features,
-) {
-
- @AssistedFactory
- interface Factory {
- fun create(form: FillableForm): AutofillResponseBuilder
- }
-
- private val formOrigin = form.formOrigin
- private val scenario = form.scenario
- private val ignoredIds = form.ignoredIds
- private val saveFlags = form.saveFlags
- private val clientState = form.toClientState()
-
- // We do not offer save when the only relevant field is a username field or there is no field.
- private val scenarioSupportsSave = scenario.hasPasswordFieldsToSave
- private val canBeSaved = saveFlags != null && scenarioSupportsSave
-
- private fun makeIntentDataset(
- context: Context,
- action: AutofillAction,
- intentSender: IntentSender,
- metadata: DatasetMetadata,
- ): Dataset {
- return Dataset.Builder(makeRemoteView(context, metadata)).run {
- fillWith(scenario, action, credentials = null)
- setAuthentication(intentSender)
- build()
- }
- }
-
- private fun makeMatchDataset(context: Context, file: File): Dataset? {
- if (!scenario.hasFieldsToFillOn(AutofillAction.Match)) return null
- val metadata = makeFillMatchMetadata(context, file)
- val intentSender =
- if (features.isEnabled(Feature.EnablePGPainlessBackend)) {
- AutofillDecryptActivityV2.makeDecryptFileIntentSender(file, context)
- } else {
- AutofillDecryptActivity.makeDecryptFileIntentSender(file, context)
- }
- return makeIntentDataset(context, AutofillAction.Match, intentSender, metadata)
- }
-
- private fun makeSearchDataset(context: Context): Dataset? {
- if (!scenario.hasFieldsToFillOn(AutofillAction.Search)) return null
- val metadata = makeSearchAndFillMetadata(context)
- val intentSender = AutofillFilterView.makeMatchAndDecryptFileIntentSender(context, formOrigin)
- return makeIntentDataset(context, AutofillAction.Search, intentSender, metadata)
- }
-
- private fun makeGenerateDataset(context: Context): Dataset? {
- if (!scenario.hasFieldsToFillOn(AutofillAction.Generate)) return null
- val metadata = makeGenerateAndFillMetadata(context)
- val intentSender = AutofillSaveActivity.makeSaveIntentSender(context, null, formOrigin)
- return makeIntentDataset(context, AutofillAction.Generate, intentSender, metadata)
- }
-
- private fun makeFillOtpFromSmsDataset(context: Context): Dataset? {
- if (!scenario.hasFieldsToFillOn(AutofillAction.FillOtpFromSms)) return null
- if (!AutofillSmsActivity.shouldOfferFillFromSms(context)) return null
- val metadata = makeFillOtpFromSmsMetadata(context)
- val intentSender = AutofillSmsActivity.makeFillOtpFromSmsIntentSender(context)
- return makeIntentDataset(context, AutofillAction.FillOtpFromSms, intentSender, metadata)
- }
-
- private fun makePublisherChangedDataset(
- context: Context,
- publisherChangedException: AutofillPublisherChangedException,
- ): Dataset {
- val metadata = makeWarningMetadata(context)
- // If the user decides to trust the new publisher, they can choose reset the list of
- // matches. In this case we need to immediately show a new `FillResponse` as if the app were
- // autofilled for the first time. This `FillResponse` needs to be returned as a result from
- // `AutofillPublisherChangedActivity`, which is why we create and pass it on here.
- val fillResponseAfterReset = makeFillResponse(context, emptyList())
- val intentSender =
- AutofillPublisherChangedActivity.makePublisherChangedIntentSender(
- context,
- publisherChangedException,
- fillResponseAfterReset
- )
- return makeIntentDataset(context, AutofillAction.Match, intentSender, metadata)
- }
-
- private fun makePublisherChangedResponse(
- context: Context,
- publisherChangedException: AutofillPublisherChangedException
- ): FillResponse {
- return FillResponse.Builder().run {
- addDataset(makePublisherChangedDataset(context, publisherChangedException))
- setIgnoredIds(*ignoredIds.toTypedArray())
- build()
- }
- }
-
- // TODO: Support multi-step authentication flows in apps via FLAG_DELAY_SAVE
- // See:
- // https://developer.android.com/reference/android/service/autofill/SaveInfo#FLAG_DELAY_SAVE
- private fun makeSaveInfo(): SaveInfo? {
- if (!canBeSaved) return null
- check(saveFlags != null)
- val idsToSave = scenario.fieldsToSave.toTypedArray()
- if (idsToSave.isEmpty()) return null
- var saveDataTypes = SaveInfo.SAVE_DATA_TYPE_PASSWORD
- if (scenario.hasUsername) {
- saveDataTypes = saveDataTypes or SaveInfo.SAVE_DATA_TYPE_USERNAME
- }
- return SaveInfo.Builder(saveDataTypes, idsToSave).run {
- setFlags(saveFlags)
- build()
- }
- }
-
- private fun makeFillResponse(context: Context, matchedFiles: List<File>): FillResponse? {
- var datasetCount = 0
- return FillResponse.Builder().run {
- for (file in matchedFiles) {
- makeMatchDataset(context, file)?.let {
- datasetCount++
- addDataset(it)
- }
- }
- makeGenerateDataset(context)?.let {
- datasetCount++
- addDataset(it)
- }
- makeFillOtpFromSmsDataset(context)?.let {
- datasetCount++
- addDataset(it)
- }
- makeSearchDataset(context)?.let {
- datasetCount++
- addDataset(it)
- }
- if (datasetCount == 0) return null
- if (Build.VERSION.SDK_INT >= 28) {
- setHeader(
- makeRemoteView(
- context,
- makeHeaderMetadata(formOrigin.getPrettyIdentifier(context, untrusted = true))
- )
- )
- }
- makeSaveInfo()?.let { setSaveInfo(it) }
- setClientState(clientState)
- setIgnoredIds(*ignoredIds.toTypedArray())
- build()
- }
- }
-
- /** Creates and returns a suitable [FillResponse] to the Autofill framework. */
- fun fillCredentials(context: Context, callback: FillCallback) {
- AutofillMatcher.getMatchesFor(context, formOrigin)
- .fold(
- success = { matchedFiles -> callback.onSuccess(makeFillResponse(context, matchedFiles)) },
- failure = { e ->
- logcat(ERROR) { e.asLog() }
- callback.onSuccess(makePublisherChangedResponse(context, e))
- }
- )
- }
-
- companion object {
-
- fun makeFillInDataset(
- context: Context,
- credentials: Credentials,
- clientState: Bundle,
- action: AutofillAction
- ): Dataset {
- val scenario = AutofillScenario.fromClientState(clientState)
- // Before Android P, Datasets used for fill-in had to come with a RemoteViews, even
- // though they are rarely shown.
- // FIXME: We should clone the original dataset here and add the credentials to be filled
- // in. Otherwise, the entry in the cached list of datasets will be overwritten by the
- // 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 >= 28) {
- Dataset.Builder()
- } else {
- Dataset.Builder(makeRemoteView(context, makeEmptyMetadata()))
- }
- return builder.run {
- if (scenario != null) fillWith(scenario, action, credentials)
- else logcat(ERROR) { "Failed to recover scenario from client state" }
- build()
- }
- }
- }
-}