From 5e66d99c852ea67a88b650c03b0e8d55e83eccde Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Sat, 5 Dec 2020 06:07:18 +0530 Subject: Refactor package structure (#1233) * idea: default test runner to Gradle * Kick off package structure revamp * Reparent all classes under dev.msfjarvis.aps Signed-off-by: Harsh Shandilya --- .../aps/util/services/OreoAutofillService.kt | 145 +++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt (limited to 'app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt') diff --git a/app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt b/app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt new file mode 100644 index 00000000..3b431525 --- /dev/null +++ b/app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt @@ -0,0 +1,145 @@ +/* + * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ +package dev.msfjarvis.aps.util.services + +import android.content.Context +import android.os.Build +import android.os.CancellationSignal +import android.service.autofill.AutofillService +import android.service.autofill.FillCallback +import android.service.autofill.FillRequest +import android.service.autofill.FillResponse +import android.service.autofill.SaveCallback +import android.service.autofill.SaveRequest +import androidx.annotation.RequiresApi +import com.github.ajalt.timberkt.d +import com.github.ajalt.timberkt.e +import com.github.androidpasswordstore.autofillparser.AutofillScenario +import com.github.androidpasswordstore.autofillparser.Credentials +import com.github.androidpasswordstore.autofillparser.FillableForm +import com.github.androidpasswordstore.autofillparser.FixedSaveCallback +import com.github.androidpasswordstore.autofillparser.FormOrigin +import com.github.androidpasswordstore.autofillparser.cachePublicSuffixList +import com.github.androidpasswordstore.autofillparser.passwordValue +import com.github.androidpasswordstore.autofillparser.recoverNodes +import com.github.androidpasswordstore.autofillparser.usernameValue +import dev.msfjarvis.aps.BuildConfig +import dev.msfjarvis.aps.R +import dev.msfjarvis.aps.ui.autofill.AutofillSaveActivity +import dev.msfjarvis.aps.util.autofill.Api30AutofillResponseBuilder +import dev.msfjarvis.aps.util.autofill.AutofillResponseBuilder +import dev.msfjarvis.aps.util.settings.PreferenceKeys +import dev.msfjarvis.aps.util.extensions.getString +import dev.msfjarvis.aps.util.extensions.hasFlag +import dev.msfjarvis.aps.util.extensions.sharedPrefs + +@RequiresApi(Build.VERSION_CODES.O) +class OreoAutofillService : AutofillService() { + + companion object { + + // TODO: Provide a user-configurable denylist + private val DENYLISTED_PACKAGES = listOf( + BuildConfig.APPLICATION_ID, + "android", + "com.android.settings", + "com.android.settings.intelligence", + "com.android.systemui", + "com.oneplus.applocker", + "org.sufficientlysecure.keychain", + ) + + private const val DISABLE_AUTOFILL_DURATION_MS = 1000 * 60 * 60 * 24L + } + + override fun onCreate() { + super.onCreate() + cachePublicSuffixList(applicationContext) + } + + override fun onFillRequest( + request: FillRequest, + cancellationSignal: CancellationSignal, + callback: FillCallback + ) { + val structure = request.fillContexts.lastOrNull()?.structure ?: run { + callback.onSuccess(null) + return + } + if (structure.activityComponent.packageName in DENYLISTED_PACKAGES) { + if (Build.VERSION.SDK_INT >= 28) { + callback.onSuccess(FillResponse.Builder().run { + disableAutofill(DISABLE_AUTOFILL_DURATION_MS) + build() + }) + } else { + callback.onSuccess(null) + } + return + } + val formToFill = FillableForm.parseAssistStructure( + this, structure, + isManualRequest = request.flags hasFlag FillRequest.FLAG_MANUAL_REQUEST, + getCustomSuffixes(), + ) ?: run { + d { "Form cannot be filled" } + callback.onSuccess(null) + return + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Api30AutofillResponseBuilder(formToFill).fillCredentials(this, request.inlineSuggestionsRequest, callback) + } else { + AutofillResponseBuilder(formToFill).fillCredentials(this, callback) + } + } + + override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { + // SaveCallback's behavior and feature set differs based on both target and device SDK, so + // we replace it with a wrapper that works the same in all situations. + @Suppress("NAME_SHADOWING") val callback = FixedSaveCallback(this, callback) + val structure = request.fillContexts.lastOrNull()?.structure ?: run { + callback.onFailure(getString(R.string.oreo_autofill_save_app_not_supported)) + return + } + val clientState = request.clientState ?: run { + e { "Received save request without client state" } + callback.onFailure(getString(R.string.oreo_autofill_save_internal_error)) + return + } + val scenario = AutofillScenario.fromClientState(clientState)?.recoverNodes(structure) + ?: run { + e { "Failed to recover client state or nodes from client state" } + callback.onFailure(getString(R.string.oreo_autofill_save_internal_error)) + return + } + val formOrigin = FormOrigin.fromBundle(clientState) ?: run { + e { "Failed to recover form origin from client state" } + callback.onFailure(getString(R.string.oreo_autofill_save_internal_error)) + return + } + + val username = scenario.usernameValue + val password = scenario.passwordValue ?: run { + callback.onFailure(getString(R.string.oreo_autofill_save_passwords_dont_match)) + return + } + callback.onSuccess( + AutofillSaveActivity.makeSaveIntentSender( + this, + credentials = Credentials(username, password, null), + formOrigin = formOrigin + ) + ) + } +} + +fun Context.getDefaultUsername() = sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME) + +fun Context.getCustomSuffixes(): Sequence { + return sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES) + ?.splitToSequence('\n') + ?.filter { it.isNotBlank() && it.first() != '.' && it.last() != '.' } + ?: emptySequence() +} -- cgit v1.2.3