diff options
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.kt | 233 |
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() - } - } - } -} |
