/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.fenix.settings.biometric import android.app.Activity.RESULT_OK import android.app.KeyguardManager import android.content.Context import android.content.Intent import android.view.View import androidx.core.content.getSystemService import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import org.mozilla.fenix.ext.settings import org.mozilla.fenix.settings.requirePreference /** * Helper for creating and implementing the [BiometricPromptFeature]. Currently used * for logins and credit cards. */ abstract class BiometricPromptPreferenceFragment : PreferenceFragmentCompat() { private val biometricPromptFeature = ViewBoundFeatureWrapper() /** * Gets the string to be used for [BiometricPromptFeature.requestAuthentication] prompting to * unlock the device. */ abstract fun unlockMessage(): String /** * Navigate when authentication is successful. */ abstract fun navigateOnSuccess() /** * Shows a dialog warning to set up a pin/password when the device is not secured. This is * only used when BiometricPrompt is unavailable on the device. */ abstract fun showPinDialogWarning(context: Context) /** * Toggle preferences to enable or disable navigation during authentication flows. * * @param prefList a list of [Preference]s to toggle. * @param enabled whether or not the preferences should be enabled. */ fun togglePrefsEnabled(prefList: List, enabled: Boolean) { for (preference in prefList) { requirePreference(preference).isEnabled = enabled } } /** * Creates a prompt to verify the device's pin/password and start activity based on the result. * This is only used when BiometricPrompt is unavailable on the device. */ @Suppress("Deprecation") abstract fun showPinVerification(manager: KeyguardManager) /** * Sets the biometric prompt feature. * * @param view the view that the prompt will be associate with. * @param prefList a list of [Preference]s to toggle. */ fun setBiometricPrompt(view: View, prefList: List) { biometricPromptFeature.set( feature = BiometricPromptFeature( context = requireContext(), fragment = this, onAuthFailure = { togglePrefsEnabled(prefList, true) }, onAuthSuccess = ::navigateOnSuccess ), owner = this, view = view ) } /** * Use [BiometricPromptFeature] or [KeyguardManager] to confirm device security. * * @param prefList a list of [Preference]s to disable while authentication is happening. */ fun verifyCredentialsOrShowSetupWarning(context: Context, prefList: List) { // Use the BiometricPrompt if available if (BiometricPromptFeature.canUseFeature(context)) { togglePrefsEnabled(prefList, false) biometricPromptFeature.get()?.requestAuthentication(unlockMessage()) return } // Fallback to prompting for password with the KeyguardManager val manager = context.getSystemService() if (manager?.isKeyguardSecure == true) { showPinVerification(manager) } else { // Warn that the device has not been secured if (context.settings().shouldShowSecurityPinWarning) { showPinDialogWarning(context) } else { navigateOnSuccess() } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == PIN_REQUEST && resultCode == RESULT_OK) { navigateOnSuccess() } } companion object { const val PIN_REQUEST = 303 } }