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

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

Merge "Adding MSDL feedback to the pattern bouncer." into main

parents 6a9fc80a 3beb3984
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ public class LockPatternView extends View {
    private static final String TAG = "LockPatternView";

    private OnPatternListener mOnPatternListener;
    private ExternalHapticsPlayer mExternalHapticsPlayer;
    @UnsupportedAppUsage
    private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9);

@@ -317,6 +318,13 @@ public class LockPatternView extends View {
        void onPatternDetected(List<Cell> pattern);
    }

    /** An external haptics player for pattern updates. */
    public interface ExternalHapticsPlayer{

        /** Perform haptic feedback when a cell is added to the pattern. */
        void performCellAddedFeedback();
    }

    public LockPatternView(Context context) {
        this(context, null);
    }
@@ -460,6 +468,15 @@ public class LockPatternView extends View {
        mOnPatternListener = onPatternListener;
    }

    /**
     * Set the external haptics player for feedback on pattern detection.
     * @param player The external player.
     */
    @UnsupportedAppUsage
    public void setExternalHapticsPlayer(ExternalHapticsPlayer player) {
        mExternalHapticsPlayer = player;
    }

    /**
     * Set the pattern explicitely (rather than waiting for the user to input
     * a pattern).
@@ -847,6 +864,16 @@ public class LockPatternView extends View {
        return null;
    }

    @Override
    public boolean performHapticFeedback(int feedbackConstant, int flags) {
        if (mExternalHapticsPlayer != null) {
            mExternalHapticsPlayer.performCellAddedFeedback();
            return true;
        } else {
            return super.performHapticFeedback(feedbackConstant, flags);
        }
    }

    private void addCellToPattern(Cell newCell) {
        mPatternDrawLookup[newCell.getRow()][newCell.getColumn()] = true;
        mPattern.add(newCell);
+7 −1
Original line number Diff line number Diff line
@@ -30,10 +30,12 @@ import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
@@ -89,6 +91,9 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {

    @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>

    private val kosmos = testKosmos()
    private val msdlPlayer = kosmos.msdlPlayer

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
@@ -112,7 +117,8 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
                mKeyguardMessageAreaControllerFactory,
                mPostureController,
                fakeFeatureFlags,
                mSelectedUserInteractor
                mSelectedUserInteractor,
                msdlPlayer,
            )
        mKeyguardPatternView.onAttachedToWindow()
    }
+2 −1
Original line number Diff line number Diff line
@@ -271,7 +271,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
                        mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                        keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
                        emergencyButtonController, mMessageAreaControllerFactory,
                        mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
                        mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
                        mMSDLPlayer);
            } else if (keyguardInputView instanceof KeyguardPasswordView) {
                return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
                        mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+20 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.internal.widget.LockPatternView.Cell;
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.bouncer.ui.helper.BouncerHapticHelper;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
@@ -43,6 +44,8 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;

import com.google.android.msdl.domain.MSDLPlayer;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -67,6 +70,7 @@ public class KeyguardPatternViewController
    private LockPatternView mLockPatternView;
    private CountDownTimer mCountdownTimer;
    private AsyncTask<?, ?, ?> mPendingLockCheck;
    private MSDLPlayer mMSDLPlayer;

    private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
        @Override
@@ -75,6 +79,10 @@ public class KeyguardPatternViewController
        }
    };

    private final LockPatternView.ExternalHapticsPlayer mExternalHapticsPlayer = () -> {
        BouncerHapticHelper.INSTANCE.playPatternDotFeedback(mMSDLPlayer, mView);
    };

    /**
     * Useful for clearing out the wrong pattern after a delay
     */
@@ -166,6 +174,10 @@ public class KeyguardPatternViewController
                boolean isValidPattern) {
            boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
            if (matched) {
                BouncerHapticHelper.INSTANCE.playMSDLAuthenticationFeedback(
                        /* authenticationSucceeded= */true,
                        /* player =*/mMSDLPlayer
                );
                getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
                if (dismissKeyguard) {
                    mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
@@ -173,6 +185,10 @@ public class KeyguardPatternViewController
                    getKeyguardSecurityCallback().dismiss(true, userId, SecurityMode.Pattern);
                }
            } else {
                BouncerHapticHelper.INSTANCE.playMSDLAuthenticationFeedback(
                        /* authenticationSucceeded= */false,
                        /* player =*/mMSDLPlayer
                );
                mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
                if (isValidPattern) {
                    getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
@@ -200,7 +216,7 @@ public class KeyguardPatternViewController
            EmergencyButtonController emergencyButtonController,
            KeyguardMessageAreaController.Factory messageAreaControllerFactory,
            DevicePostureController postureController, FeatureFlags featureFlags,
            SelectedUserInteractor selectedUserInteractor) {
            SelectedUserInteractor selectedUserInteractor, MSDLPlayer msdlPlayer) {
        super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
                messageAreaControllerFactory, featureFlags, selectedUserInteractor);
        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -212,6 +228,7 @@ public class KeyguardPatternViewController
                featureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
        mLockPatternView = mView.findViewById(R.id.lockPatternView);
        mPostureController = postureController;
        mMSDLPlayer = msdlPlayer;
    }

    @Override
@@ -249,6 +266,7 @@ public class KeyguardPatternViewController
        if (deadline != 0) {
            handleAttemptLockout(deadline);
        }
        mLockPatternView.setExternalHapticsPlayer(mExternalHapticsPlayer);
    }

    @Override
@@ -262,6 +280,7 @@ public class KeyguardPatternViewController
            cancelBtn.setOnClickListener(null);
        }
        mPostureController.removeCallback(mPostureCallback);
        mLockPatternView.setExternalHapticsPlayer(null);
    }

    @Override
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.bouncer.ui.helper

import android.view.HapticFeedbackConstants
import android.view.View
import com.android.keyguard.AuthInteractionProperties
import com.android.systemui.Flags
//noinspection CleanArchitectureDependencyViolation: Data layer only referenced for this enum class
import com.google.android.msdl.data.model.MSDLToken
import com.google.android.msdl.domain.MSDLPlayer

/** A helper object to deliver haptic feedback in bouncer interactions. */
object BouncerHapticHelper {

    private val authInteractionProperties = AuthInteractionProperties()

    /**
     * Deliver MSDL feedback as a result of authenticating through a bouncer.
     *
     * @param[authenticationSucceeded] Whether the authentication was successful or not.
     * @param[player] The [MSDLPlayer] that delivers the correct feedback.
     */
    fun playMSDLAuthenticationFeedback(
        authenticationSucceeded: Boolean,
        player: MSDLPlayer?,
    ) {
        if (player == null || !Flags.msdlFeedback()) {
            return
        }

        val token =
            if (authenticationSucceeded) {
                MSDLToken.UNLOCK
            } else {
                MSDLToken.FAILURE
            }
        player.playToken(token, authInteractionProperties)
    }

    /**
     * Deliver feedback when dragging through cells in the pattern bouncer. This function can play
     * MSDL feedback using a [MSDLPlayer], or fallback to a default haptic feedback using the
     * [View.performHapticFeedback] API and a [View].
     *
     * @param[player] [MSDLPlayer] for MSDL feedback.
     * @param[view] A [View] for default haptic feedback using [View.performHapticFeedback]
     */
    fun playPatternDotFeedback(player: MSDLPlayer?, view: View?) {
        if (player == null || !Flags.msdlFeedback()) {
            view?.performHapticFeedback(
                HapticFeedbackConstants.VIRTUAL_KEY,
                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
            )
        } else {
            player.playToken(MSDLToken.DRAG_INDICATOR)
        }
    }
}