Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0f95df2 authored by Juan Sebastian Martinez's avatar Juan Sebastian Martinez Committed by Android (Google) Code Review
Browse files

Merge "Using performHapticFeedback on BiometricViewBinder" into udc-qpr-dev

parents dacf60fe 96e79d56
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;

import java.io.PrintWriter;
@@ -288,12 +289,13 @@ public class AuthContainerView extends LinearLayout
            @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractor,
            @NonNull PromptViewModel promptViewModel,
            @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
            @NonNull @Background DelayableExecutor bgExecutor) {
            @NonNull @Background DelayableExecutor bgExecutor,
            @NonNull VibratorHelper vibratorHelper) {
        this(config, featureFlags, applicationCoroutineScope, fpProps, faceProps,
                wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
                jankMonitor, authBiometricFingerprintViewModelProvider, promptSelectorInteractor,
                promptCredentialInteractor, promptViewModel, credentialViewModelProvider,
                new Handler(Looper.getMainLooper()), bgExecutor);
                new Handler(Looper.getMainLooper()), bgExecutor, vibratorHelper);
    }

    @VisibleForTesting
@@ -314,7 +316,8 @@ public class AuthContainerView extends LinearLayout
            @NonNull PromptViewModel promptViewModel,
            @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
            @NonNull Handler mainHandler,
            @NonNull @Background DelayableExecutor bgExecutor) {
            @NonNull @Background DelayableExecutor bgExecutor,
            @NonNull VibratorHelper vibratorHelper) {
        super(config.mContext);

        mConfig = config;
@@ -364,7 +367,8 @@ public class AuthContainerView extends LinearLayout
        if (featureFlags.isEnabled(Flags.BIOMETRIC_BP_STRONG)) {
            showPrompt(config, layoutInflater, promptViewModel,
                    Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
                    Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
                    Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds),
                    vibratorHelper, featureFlags);
        } else {
            showLegacyPrompt(config, layoutInflater, fpProps, faceProps);
        }
@@ -388,7 +392,10 @@ public class AuthContainerView extends LinearLayout
    private void showPrompt(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
            @NonNull PromptViewModel viewModel,
            @Nullable FingerprintSensorPropertiesInternal fpProps,
            @Nullable FaceSensorPropertiesInternal faceProps) {
            @Nullable FaceSensorPropertiesInternal faceProps,
            @NonNull VibratorHelper vibratorHelper,
            @NonNull FeatureFlags featureFlags
    ) {
        if (Utils.isBiometricAllowed(config.mPromptInfo)) {
            mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication(
                    config.mPromptInfo,
@@ -401,7 +408,8 @@ public class AuthContainerView extends LinearLayout
            mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
                    // TODO(b/201510778): This uses the wrong timeout in some cases
                    getJankListener(view, TRANSIT, AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
                    mBackgroundView, mBiometricCallback, mApplicationCoroutineScope);
                    mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
                    vibratorHelper, featureFlags);

            // TODO(b/251476085): migrate these dependencies
            if (fpProps != null && fpProps.isAnyUdfpsType()) {
+8 −3
Original line number Diff line number Diff line
@@ -85,9 +85,12 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.data.repository.BiometricType;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;

import kotlin.Unit;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -101,7 +104,6 @@ import java.util.Set;
import javax.inject.Inject;
import javax.inject.Provider;

import kotlin.Unit;
import kotlinx.coroutines.CoroutineScope;

/**
@@ -183,6 +185,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
    @NonNull private final UdfpsUtils mUdfpsUtils;
    private final @Background DelayableExecutor mBackgroundExecutor;
    private final DisplayInfo mCachedDisplayInfo = new DisplayInfo();
    @NonNull private final VibratorHelper mVibratorHelper;

    @VisibleForTesting
    final TaskStackListener mTaskStackListener = new TaskStackListener() {
@@ -771,7 +774,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
            @NonNull InteractionJankMonitor jankMonitor,
            @Main Handler handler,
            @Background DelayableExecutor bgExecutor,
            @NonNull UdfpsUtils udfpsUtils) {
            @NonNull UdfpsUtils udfpsUtils,
            @NonNull VibratorHelper vibratorHelper) {
        mContext = context;
        mFeatureFlags = featureFlags;
        mExecution = execution;
@@ -794,6 +798,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
        mFaceEnrolledForUser = new SparseBooleanArray();
        mUdfpsUtils = udfpsUtils;
        mApplicationCoroutineScope = applicationCoroutineScope;
        mVibratorHelper = vibratorHelper;

        mLogContextInteractor = logContextInteractor;
        mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider;
@@ -1341,7 +1346,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
                wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
                mInteractionJankMonitor, mAuthBiometricFingerprintViewModelProvider,
                mPromptCredentialInteractor, mPromptSelectorInteractor, viewModel,
                mCredentialViewModelProvider, bgExecutor);
                mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
    }

    @Override
+19 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.hardware.face.FaceManager
import android.os.Bundle
import android.text.method.ScrollingMovementMethod
import android.util.Log
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
import android.view.accessibility.AccessibilityManager
@@ -54,9 +55,13 @@ import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode
import com.android.systemui.biometrics.ui.viewmodel.PromptMessage
import com.android.systemui.biometrics.ui.viewmodel.PromptSize
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.VibratorHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
@@ -77,6 +82,8 @@ object BiometricViewBinder {
        backgroundView: View,
        legacyCallback: Callback,
        applicationScope: CoroutineScope,
        vibratorHelper: VibratorHelper,
        featureFlags: FeatureFlags,
    ): AuthBiometricViewAdapter {
        val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!

@@ -383,6 +390,18 @@ object BiometricViewBinder {
                        }
                    }
                }

                // Play haptics
                if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
                    launch {
                        viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
                            if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
                                vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
                                viewModel.clearHaptics()
                            }
                        }
                    }
                }
            }
        }

+28 −4
Original line number Diff line number Diff line
@@ -17,11 +17,14 @@ package com.android.systemui.biometrics.ui.viewmodel

import android.hardware.biometrics.BiometricPrompt
import android.util.Log
import android.view.HapticFeedbackConstants
import com.android.systemui.biometrics.AuthBiometricView
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
import kotlinx.coroutines.Job
@@ -43,6 +46,7 @@ class PromptViewModel
constructor(
    private val interactor: PromptSelectorInteractor,
    private val vibrator: VibratorHelper,
    private val featureFlags: FeatureFlags,
) {
    /** The set of modalities available for this prompt */
    val modalities: Flow<BiometricModalities> =
@@ -90,6 +94,11 @@ constructor(
    private val _forceLargeSize = MutableStateFlow(false)
    private val _forceMediumSize = MutableStateFlow(false)

    private val _hapticsToPlay = MutableStateFlow(HapticFeedbackConstants.NO_HAPTICS)

    /** Event fired to the view indicating a [HapticFeedbackConstants] to be played */
    val hapticsToPlay = _hapticsToPlay.asStateFlow()

    /** The size of the prompt. */
    val size: Flow<PromptSize> =
        combine(
@@ -438,11 +447,26 @@ constructor(
        _forceLargeSize.value = true
    }

    private fun VibratorHelper.success(modality: BiometricModality) =
    private fun VibratorHelper.success(modality: BiometricModality) {
        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
            _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
        } else {
            vibrateAuthSuccess("$TAG, modality = $modality BP::success")
        }
    }

    private fun VibratorHelper.error(modality: BiometricModality = BiometricModality.None) =
    private fun VibratorHelper.error(modality: BiometricModality = BiometricModality.None) {
        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
            _hapticsToPlay.value = HapticFeedbackConstants.REJECT
        } else {
            vibrateAuthError("$TAG, modality = $modality BP::error")
        }
    }

    /** Clears the [hapticsToPlay] variable by setting it to the NO_HAPTICS default. */
    fun clearHaptics() {
        _hapticsToPlay.value = HapticFeedbackConstants.NO_HAPTICS
    }

    companion object {
        private const val TAG = "PromptViewModel"
+16 −5
Original line number Diff line number Diff line
@@ -139,6 +139,7 @@ open class AuthContainerViewTest : SysuiTestCase() {
    @Before
    fun setup() {
        featureFlags.set(Flags.BIOMETRIC_BP_STRONG, useNewBiometricPrompt)
        featureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false)
    }

    @After
@@ -151,7 +152,10 @@ open class AuthContainerViewTest : SysuiTestCase() {
    @Test
    fun testNotifiesAnimatedIn() {
        initializeFingerprintContainer()
        verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
        verify(callback).onDialogAnimatedIn(
            authContainer?.requestId ?: 0L,
            true /* startFingerprintNow */
        )
    }

    @Test
@@ -196,7 +200,10 @@ open class AuthContainerViewTest : SysuiTestCase() {
        waitForIdleSync()

        // attaching the view resets the state and allows this to happen again
        verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
        verify(callback).onDialogAnimatedIn(
            authContainer?.requestId ?: 0L,
            true /* startFingerprintNow */
        )
    }

    @Test
@@ -211,7 +218,10 @@ open class AuthContainerViewTest : SysuiTestCase() {

        // the first time is triggered by initializeFingerprintContainer()
        // the second time was triggered by dismissWithoutCallback()
        verify(callback, times(2)).onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
        verify(callback, times(2)).onDialogAnimatedIn(
            authContainer?.requestId ?: 0L,
            true /* startFingerprintNow */
        )
    }

    @Test
@@ -517,10 +527,11 @@ open class AuthContainerViewTest : SysuiTestCase() {
        { authBiometricFingerprintViewModel },
        { promptSelectorInteractor },
        { bpCredentialInteractor },
        PromptViewModel(promptSelectorInteractor, vibrator),
        PromptViewModel(promptSelectorInteractor, vibrator, featureFlags),
        { credentialViewModel },
        Handler(TestableLooper.get(this).looper),
        fakeExecutor
        fakeExecutor,
        vibrator
    ) {
        override fun postOnAnimation(runnable: Runnable) {
            runnable.run()
Loading