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

Commit 9a5967ff authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topics "face_auth_ripple", "udfps_falsing" into sc-dev

* changes:
  Add falsing checks to udfps affordance
  Support face auth ripple animation
parents dcc1a906 1951b0b3
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -100,4 +100,12 @@
            android:ellipsize="marquee"
            android:focusable="true" />
    </LinearLayout>

    <com.android.systemui.biometrics.AuthRippleView
        android:id="@+id/auth_ripple"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true"
    />
</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
+7 −0
Original line number Diff line number Diff line
@@ -642,4 +642,11 @@

    <!-- Whether to use window background blur for the volume dialog. -->
    <bool name="config_volumeDialogUseBackgroundBlur">false</bool>

    <!-- The properties of the face auth camera in pixels -->
    <integer-array name="config_face_auth_props">
        <!-- sensorLocationX -->
        <!-- sensorLocationY -->
        <!--sensorRadius -->
    </integer-array>
</resources>
+42 −27
Original line number Diff line number Diff line
@@ -18,11 +18,12 @@ package com.android.keyguard;

import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;

import static com.android.systemui.classifier.Classifier.DISABLED_UDFPS_AFFORDANCE;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.hardware.biometrics.BiometricSourceType;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

@@ -33,7 +34,9 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;

@@ -53,14 +56,16 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
    @NonNull private final KeyguardViewController mKeyguardViewController;
    @NonNull private final StatusBarStateController mStatusBarStateController;
    @NonNull private final KeyguardStateController mKeyguardStateController;
    @NonNull private final FalsingManager mFalsingManager;
    @NonNull private final Drawable mButton;
    @NonNull private final Drawable mUnlockIcon;

    private boolean mIsDozing;
    private boolean mIsBouncerShowing;
    private boolean mIsKeyguardShowing;
    private boolean mRunningFPS;
    private boolean mCanDismissLockScreen;
    private boolean mQsExpanded;
    private int mStatusBarState;

    private boolean mShowButton;
    private boolean mShowUnlockIcon;
@@ -71,16 +76,19 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
            @NonNull AuthController authController,
            @NonNull KeyguardViewController keyguardViewController,
            @NonNull KeyguardStateController keyguardStateController
            @NonNull KeyguardStateController keyguardStateController,
            @NonNull FalsingManager falsingManager
    ) {
        super(view);
        mView.setOnTouchListener(mOnTouchListener);
        mView.setOnClickListener(v -> onAffordanceClick());
        mView.setOnLongClickListener(v -> onAffordanceClick());
        mView.setSensorProperties(authController.getUdfpsProps().get(0));

        mStatusBarStateController = statusBarStateController;
        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
        mKeyguardViewController = keyguardViewController;
        mKeyguardStateController = keyguardStateController;
        mFalsingManager = falsingManager;

        final Context context = view.getContext();
        mButton = context.getResources().getDrawable(
@@ -94,10 +102,10 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
    @Override
    protected void onViewAttached() {
        mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
        mIsKeyguardShowing = mKeyguardStateController.isShowing();
        mIsDozing = mStatusBarStateController.isDozing();
        mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
        mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
        mStatusBarState = mStatusBarStateController.getState();
        mUnlockIcon.setTint(Utils.getColorAttrDefaultColor(mView.getContext(),
                R.attr.wallpaperTextColorAccent));
        updateVisibility();
@@ -114,6 +122,15 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
        mKeyguardStateController.removeCallback(mKeyguardStateCallback);
    }

    private boolean onAffordanceClick() {
        if (mFalsingManager.isFalseTouch(DISABLED_UDFPS_AFFORDANCE)) {
            return false;
        }
        mView.setVisibility(View.INVISIBLE);
        mKeyguardViewController.showBouncer(/* scrim */ true);
        return true;
    }

    /**
     * Call when this controller is no longer needed. This will remove the view from its parent.
     */
@@ -123,6 +140,14 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
        }
    }

    /**
     * Set whether qs is expanded. When QS is expanded, don't show a DisabledUdfps affordance.
     */
    public void setQsExpanded(boolean expanded) {
        mQsExpanded = expanded;
        updateVisibility();
    }

    private void updateVisibility() {
        mShowButton = !mCanDismissLockScreen && !mRunningFPS && isLockScreen();
        mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
@@ -139,7 +164,10 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
    }

    private boolean isLockScreen() {
        return mIsKeyguardShowing && !mIsDozing && !mIsBouncerShowing;
        return !mIsDozing
                && !mIsBouncerShowing
                && !mQsExpanded
                && mStatusBarState == StatusBarState.KEYGUARD;
    }

    @Override
@@ -148,20 +176,13 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
        pw.println("  mShowBouncerButton: " + mShowButton);
        pw.println("  mShowUnlockIcon: " + mShowUnlockIcon);
        pw.println("  mIsDozing: " + mIsDozing);
        pw.println("  mIsKeyguardShowing: " + mIsKeyguardShowing);
        pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
        pw.println("  mRunningFPS: " + mRunningFPS);
        pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
        pw.println("  mStatusBarState: " + StatusBarState.toShortString(mStatusBarState));
        pw.println("  mQsExpanded: " + mQsExpanded);
    }

    private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            mKeyguardViewController.showBouncer(/* scrim */ true);
            return true;
        }
    };

    private StatusBarStateController.StateListener mStatusBarStateListener =
            new StatusBarStateController.StateListener() {
                @Override
@@ -169,6 +190,12 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i
                    mIsDozing = isDozing;
                    updateVisibility();
                }

                @Override
                public void onStateChanged(int statusBarState) {
                    mStatusBarState = statusBarState;
                    updateVisibility();
                }
            };

    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
@@ -192,22 +219,10 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i

    private final KeyguardStateController.Callback mKeyguardStateCallback =
            new KeyguardStateController.Callback() {
        @Override
        public void onKeyguardShowingChanged() {
            updateIsKeyguardShowing();
            updateVisibility();
        }

        @Override
        public void onUnlockedChanged() {
            updateIsKeyguardShowing();
            mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
            updateVisibility();
        }

        private void updateIsKeyguardShowing() {
            mIsKeyguardShowing = mKeyguardStateController.isShowing()
                    && !mKeyguardStateController.isKeyguardGoingAway();
        }
    };
}
+37 −2
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.PointF;
import android.graphics.RectF;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
@@ -82,6 +83,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
    @Nullable private final List<FingerprintSensorPropertiesInternal> mFpProps;
    @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
    @Nullable private final List<FingerprintSensorPropertiesInternal> mUdfpsProps;
    @Nullable private final PointF mFaceAuthSensorLocation;

    // TODO: These should just be saved from onSaveState
    private SomeArgs mCurrentDialogArgs;
@@ -261,10 +263,34 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
    }

    /**
     * @return where the UDFPS exists on the screen in pixels.
     * @return where the UDFPS exists on the screen in pixels in portrait mode.
     */
    public RectF getUdfpsRegion() {
        return mUdfpsController == null ? null : mUdfpsController.getSensorLocation();
        return mUdfpsController == null
                ? null
                : mUdfpsController.getSensorLocation();
    }

    /**
     * @return where the UDFPS exists on the screen in pixels in portrait mode.
     */
    public PointF getUdfpsSensorLocation() {
        if (mUdfpsController == null) {
            return null;
        }
        return new PointF(mUdfpsController.getSensorLocation().centerX(),
                mUdfpsController.getSensorLocation().centerY());
    }

    /**
     * @return where the face authentication sensor exists relative to the screen in pixels in
     * portrait mode.
     */
    public PointF getFaceAuthSensorLocation() {
        if (mFaceProps == null || mFaceAuthSensorLocation == null) {
            return null;
        }
        return new PointF(mFaceAuthSensorLocation.x, mFaceAuthSensorLocation.y);
    }

    /**
@@ -339,6 +365,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
            }
        }
        mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
        int[] faceAuthLocation = context.getResources().getIntArray(
                com.android.systemui.R.array.config_face_auth_props);
        if (faceAuthLocation == null || faceAuthLocation.length < 2) {
            mFaceAuthSensorLocation = null;
        } else {
            mFaceAuthSensorLocation = new PointF(
                    (float) faceAuthLocation[0],
                    (float) faceAuthLocation[1]);
        }

        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+113 −52
Original line number Diff line number Diff line
@@ -17,17 +17,19 @@
package com.android.systemui.biometrics

import android.content.Context
import android.content.res.Configuration
import android.graphics.PointF
import android.hardware.biometrics.BiometricSourceType
import android.view.View
import android.view.ViewGroup
import com.android.internal.annotations.VisibleForTesting
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
import java.io.PrintWriter
import javax.inject.Inject

@@ -35,30 +37,82 @@ import javax.inject.Inject
 * Controls the ripple effect that shows when authentication is successful.
 * The ripple uses the accent color of the current theme.
 */
@SysUISingleton
@StatusBarScope
class AuthRippleController @Inject constructor(
    commandRegistry: CommandRegistry,
    configurationController: ConfigurationController,
    private val context: Context,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor
) {
    private val sysuiContext: Context,
    private val authController: AuthController,
    private val configurationController: ConfigurationController,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val commandRegistry: CommandRegistry,
    private val notificationShadeWindowController: NotificationShadeWindowController,
    rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
    private var fingerprintSensorLocation: PointF? = null
    private var faceSensorLocation: PointF? = null

    @VisibleForTesting
    var rippleView: AuthRippleView = AuthRippleView(context, attrs = null)
    public override fun onViewAttached() {
        updateRippleColor()
        updateSensorLocation()
        configurationController.addCallback(configurationChangedListener)
        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
        commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
    }

    @VisibleForTesting
    public override fun onViewDetached() {
        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
        configurationController.removeCallback(configurationChangedListener)
        commandRegistry.unregisterCommand("auth-ripple")

        notificationShadeWindowController.setForcePluginOpen(false, this)
    }

    private fun showRipple(biometricSourceType: BiometricSourceType?) {
        if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
            fingerprintSensorLocation != null) {
            mView.setSensorLocation(fingerprintSensorLocation!!)
            showRipple()
        } else if (biometricSourceType == BiometricSourceType.FACE &&
            faceSensorLocation != null) {
            mView.setSensorLocation(faceSensorLocation!!)
            showRipple()
        }
    }

    private fun showRipple() {
        notificationShadeWindowController.setForcePluginOpen(true, this)
        mView.startRipple(Runnable {
            notificationShadeWindowController.setForcePluginOpen(false, this)
        })
    }

    private fun updateSensorLocation() {
        fingerprintSensorLocation = authController.udfpsSensorLocation
        faceSensorLocation = authController.faceAuthSensorLocation
    }

    private fun updateRippleColor() {
        mView.setColor(
            Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
    }

    val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
    val keyguardUpdateMonitorCallback =
        object : KeyguardUpdateMonitorCallback() {
            override fun onBiometricAuthenticated(
                userId: Int,
                biometricSourceType: BiometricSourceType?,
                isStrongBiometric: Boolean
            ) {
            if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
                rippleView.startRipple()
            }
                showRipple(biometricSourceType)
            }
    }

    init {
        val configurationChangedListener = object : ConfigurationController.ConfigurationListener {
    val configurationChangedListener =
        object : ConfigurationController.ConfigurationListener {
            override fun onConfigChanged(newConfig: Configuration?) {
                updateSensorLocation()
            }
            override fun onUiModeChanged() {
                updateRippleColor()
            }
@@ -69,42 +123,49 @@ class AuthRippleController @Inject constructor(
                updateRippleColor()
            }
    }
        configurationController.addCallback(configurationChangedListener)

        commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
    inner class AuthRippleCommand : Command {
        override fun execute(pw: PrintWriter, args: List<String>) {
            if (args.isEmpty()) {
                invalidCommand(pw)
            } else {
                when (args[0]) {
                    "fingerprint" -> {
                        pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
                        showRipple(BiometricSourceType.FINGERPRINT)
                    }

    fun setSensorLocation(x: Float, y: Float) {
        rippleView.setSensorLocation(x, y)
                    "face" -> {
                        pw.println("face ripple sensorLocation=$faceSensorLocation")
                        showRipple(BiometricSourceType.FACE)
                    }

    fun setViewHost(viewHost: View) {
        // Add the ripple view to its host layout
        viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
            override fun onViewDetachedFromWindow(view: View?) {}

            override fun onViewAttachedToWindow(view: View?) {
                (viewHost as ViewGroup).addView(rippleView)
                keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
                viewHost.removeOnAttachStateChangeListener(this)
                    "custom" -> {
                        if (args.size != 3 ||
                            args[1].toFloatOrNull() == null ||
                            args[2].toFloatOrNull() == null) {
                            invalidCommand(pw)
                            return
                        }
        })

        updateRippleColor()
                        pw.println("custom ripple sensorLocation=" + args[1].toFloat() + ", " +
                            args[2].toFloat())
                        mView.setSensorLocation(PointF(args[1].toFloat(), args[2].toFloat()))
                        showRipple()
                    }
                    else -> invalidCommand(pw)
                }

    private fun updateRippleColor() {
        rippleView.setColor(
            Utils.getColorAttr(context, android.R.attr.colorAccent).defaultColor)
            }

    inner class AuthRippleCommand : Command {
        override fun execute(pw: PrintWriter, args: List<String>) {
            rippleView.startRipple()
        }

        override fun help(pw: PrintWriter) {
            pw.println("Usage: adb shell cmd statusbar auth-ripple")
            pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
            pw.println("Available commands:")
            pw.println("  fingerprint")
            pw.println("  face")
            pw.println("  custom <x-location: int> <y-location: int>")
        }

        fun invalidCommand(pw: PrintWriter) {
            pw.println("invalid command")
            help(pw)
        }
    }
}
Loading