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

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

Merge "Coordinate circle reveal with authRipple" into sc-dev

parents 83f88bc4 28a01ca4
Loading
Loading
Loading
Loading
+42 −4
Original line number Diff line number Diff line
@@ -24,9 +24,13 @@ import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
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.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
@@ -49,10 +53,12 @@ class AuthRippleController @Inject constructor(
    private val commandRegistry: CommandRegistry,
    private val notificationShadeWindowController: NotificationShadeWindowController,
    private val bypassController: KeyguardBypassController,
    private val biometricUnlockController: BiometricUnlockController,
    rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
    var fingerprintSensorLocation: PointF? = null
    private var faceSensorLocation: PointF? = null
    private var circleReveal: LightRevealEffect? = null

    @VisibleForTesting
    public override fun onViewAttached() {
@@ -96,15 +102,47 @@ class AuthRippleController @Inject constructor(

    private fun showRipple() {
        notificationShadeWindowController.setForcePluginOpen(true, this)
        mView.startRipple(Runnable {
        val biometricUnlockMode = biometricUnlockController.mode
        val useCircleReveal = circleReveal != null &&
            (biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK ||
                biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING ||
                biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
        val lightRevealScrim = statusBar.lightRevealScrim
        if (useCircleReveal) {
            lightRevealScrim?.revealEffect = circleReveal!!
        }

        mView.startRipple(
            /* end runnable */
            Runnable {
                notificationShadeWindowController.setForcePluginOpen(false, this)
        })
                if (useCircleReveal) {
                    lightRevealScrim?.revealEffect = LiftReveal
                }
            },
            /* circleReveal */
            if (useCircleReveal) {
                lightRevealScrim
            } else {
                null
            }
        )
    }

    fun updateSensorLocation() {
        fingerprintSensorLocation = authController.fingerprintSensorLocation
        faceSensorLocation = authController.faceAuthSensorLocation
        statusBar.updateCircleReveal()
        fingerprintSensorLocation?.let {
            circleReveal = CircleReveal(
                it.x,
                it.y,
                0f,
                Math.max(
                    Math.max(it.x, statusBar.displayWidth - it.x),
                    Math.max(it.y, statusBar.displayHeight - it.y)
                )
            )
        }
    }

    private fun updateRippleColor() {
+62 −33
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.util.MathUtils
import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.charging.RippleShader

private const val RIPPLE_ANIMATION_DURATION: Long = 1533
@@ -70,51 +71,79 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
            .toFloat()
    }

    fun startRipple(onAnimationEnd: Runnable?) {
    fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
        if (rippleInProgress) {
            return // Ignore if ripple effect is already playing
        }

        val animator = ValueAnimator.ofFloat(0f, 1f)
        animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
        animator.duration = RIPPLE_ANIMATION_DURATION
        animator.addUpdateListener { animator ->
        val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
            interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
            duration = RIPPLE_ANIMATION_DURATION
            addUpdateListener { animator ->
                val now = animator.currentPlayTime
                rippleShader.progress = animator.animatedValue as Float
                rippleShader.time = now.toFloat()
            rippleShader.distortionStrength = 1 - rippleShader.progress

                lightReveal?.revealAmount = animator.animatedValue as Float
                invalidate()
            }
        val alphaInAnimator = ValueAnimator.ofInt(0, 127)
        alphaInAnimator.duration = 167
        alphaInAnimator.addUpdateListener { alphaInAnimator ->
            rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
                alphaInAnimator.animatedValue as Int)
        }

        val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
            interpolator = rippleAnimator.interpolator
            startDelay = 10
            duration = rippleAnimator.duration
            addUpdateListener { animator ->
                lightReveal?.revealAmount = animator.animatedValue as Float
            }
        }

        val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply {
            duration = 167
            addUpdateListener { animator ->
                rippleShader.color = ColorUtils.setAlphaComponent(
                    rippleShader.color,
                    animator.animatedValue as Int
                )
                invalidate()
            }
        val alphaOutAnimator = ValueAnimator.ofInt(127, 0)
        alphaOutAnimator.startDelay = 417
        alphaOutAnimator.duration = 1116
        alphaOutAnimator.addUpdateListener { alphaOutAnimator ->
            rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
                alphaOutAnimator.animatedValue as Int)
        }

        val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply {
            startDelay = 417
            duration = 1116
            addUpdateListener { animator ->
                rippleShader.color = ColorUtils.setAlphaComponent(
                    rippleShader.color,
                    animator.animatedValue as Int
                )
                invalidate()
            }
        }

        val animatorSet = AnimatorSet().apply {
            playTogether(
                rippleAnimator,
                revealAnimator,
                alphaInAnimator,
                alphaOutAnimator
            )
            addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationStart(animation: Animator?) {
                    rippleInProgress = true
                    visibility = VISIBLE
                }

        val animatorSet = AnimatorSet()
        animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator)
        animatorSet.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator?) {
                    onAnimationEnd?.run()
                    rippleInProgress = false
                    visibility = GONE
                }
            })
        }
        // TODO (b/185124905):  custom haptic TBD
        // vibrate()
        animatorSet.start()
        visibility = VISIBLE
        rippleInProgress = true
    }

    fun setColor(color: Int) {
+4 −4
Original line number Diff line number Diff line
@@ -93,10 +93,10 @@ class CircleReveal(
    val endRadius: Float
) : LightRevealEffect {
    override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
        val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(amount)
        val fadeAmount =
            LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.75f)
        val radius = startRadius + ((endRadius - startRadius) * interpolatedAmount)
        // reveal amount updates already have an interpolator, so we intentionally use the
        // non-interpolated amount
        val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f)
        val radius = startRadius + ((endRadius - startRadius) * amount)
        scrim.revealGradientEndColorAlpha = 1f - fadeAmount
        scrim.setRevealGradientBounds(
            centerX - radius /* left */,
+7 −42
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -46,8 +45,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST;

import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -121,6 +118,7 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;

import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
@@ -402,8 +400,6 @@ public class StatusBar extends SystemUI implements DemoMode,
    private LightRevealScrim mLightRevealScrim;
    private WiredChargingRippleController mChargingRippleAnimationController;
    private PowerButtonReveal mPowerButtonReveal;
    private CircleReveal mCircleReveal;
    private ValueAnimator mCircleRevealAnimator = ValueAnimator.ofFloat(0f, 1f);

    private final Object mQueueLock = new Object();

@@ -2808,11 +2804,11 @@ public class StatusBar extends SystemUI implements DemoMode,
        return mDisplayMetrics.density;
    }

    float getDisplayWidth() {
    public float getDisplayWidth() {
        return mDisplayMetrics.widthPixels;
    }

    float getDisplayHeight() {
    public float getDisplayHeight() {
        return mDisplayMetrics.heightPixels;
    }

@@ -3542,9 +3538,6 @@ public class StatusBar extends SystemUI implements DemoMode,
    public void fadeKeyguardWhilePulsing() {
        mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
                ()-> {
                if (shouldShowCircleReveal()) {
                    startCircleReveal();
                }
                hideKeyguard();
                mStatusBarKeyguardViewManager.onKeyguardFadedAway();
            }).start();
@@ -3885,7 +3878,7 @@ public class StatusBar extends SystemUI implements DemoMode,
    @Override
    public void onDozeAmountChanged(float linear, float eased) {
        if (mFeatureFlags.useNewLockscreenAnimations()
                && !mCircleRevealAnimator.isRunning()) {
                && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
            mLightRevealScrim.setRevealAmount(1f - linear);
        }
    }
@@ -3908,7 +3901,7 @@ public class StatusBar extends SystemUI implements DemoMode,
                || (!isDozing && mWakefulnessLifecycle.getLastWakeReason()
                == PowerManager.WAKE_REASON_POWER_BUTTON)) {
            mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
        } else if (!mCircleRevealAnimator.isRunning()) {
        } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
            mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
        }

@@ -3920,36 +3913,8 @@ public class StatusBar extends SystemUI implements DemoMode,
        Trace.endSection();
    }

    /**
     * Update the parameters for the dozing circle reveal that animates when the user authenticates
     * from AOD using the fingerprint sensor.
     */
    public void updateCircleReveal() {
        final PointF fpLocation = mAuthRippleController.getFingerprintSensorLocation();
        if (fpLocation != null) {
            mCircleReveal =
                    new CircleReveal(
                            fpLocation.x,
                            fpLocation.y,
                            0,
                            Math.max(Math.max(fpLocation.x, getDisplayWidth() - fpLocation.x),
                                    Math.max(fpLocation.y, getDisplayHeight() - fpLocation.y)));
        }
    }

    private void startCircleReveal() {
        mLightRevealScrim.setRevealEffect(mCircleReveal);
        mCircleRevealAnimator.cancel();
        mCircleRevealAnimator.addUpdateListener(animation ->
                mLightRevealScrim.setRevealAmount(
                        (float) mCircleRevealAnimator.getAnimatedValue()));
        mCircleRevealAnimator.setDuration(900);
        mCircleRevealAnimator.start();
    }

    private boolean shouldShowCircleReveal() {
        return mCircleReveal != null && !mCircleRevealAnimator.isRunning()
                && mBiometricUnlockController.getBiometricType() == FINGERPRINT;
    public LightRevealScrim getLightRevealScrim() {
        return mLightRevealScrim;
    }

    private void updateKeyguardState() {
+10 −7
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,6 +54,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
    @Mock private lateinit var authController: AuthController
    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
    @Mock private lateinit var bypassController: KeyguardBypassController
    @Mock private lateinit var biometricUnlockController: BiometricUnlockController

    @Before
    fun setUp() {
@@ -66,6 +68,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            commandRegistry,
            notificationShadeWindowController,
            bypassController,
            biometricUnlockController,
            rippleView
        )
        controller.init()
@@ -90,7 +93,7 @@ class AuthRippleControllerTest : SysuiTestCase() {

        // THEN update sensor location and show ripple
        verify(rippleView).setSensorLocation(fpsLocation)
        verify(rippleView).startRipple(any())
        verify(rippleView).startRipple(any(), any())
    }

    @Test
@@ -111,7 +114,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            false /* isStrongBiometric */)

        // THEN no ripple
        verify(rippleView, never()).startRipple(any())
        verify(rippleView, never()).startRipple(any(), any())
    }

    @Test
@@ -132,7 +135,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            false /* isStrongBiometric */)

        // THEN no ripple
        verify(rippleView, never()).startRipple(any())
        verify(rippleView, never()).startRipple(any(), any())
    }

    @Test
@@ -156,7 +159,7 @@ class AuthRippleControllerTest : SysuiTestCase() {

        // THEN show ripple
        verify(rippleView).setSensorLocation(faceLocation)
        verify(rippleView).startRipple(any())
        verify(rippleView).startRipple(any(), any())
    }

    @Test
@@ -176,7 +179,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            false /* isStrongBiometric */)

        // THEN no ripple
        verify(rippleView, never()).startRipple(any())
        verify(rippleView, never()).startRipple(any(), any())
    }

    @Test
@@ -191,7 +194,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            0 /* userId */,
            BiometricSourceType.FACE /* type */,
            false /* isStrongBiometric */)
        verify(rippleView, never()).startRipple(any())
        verify(rippleView, never()).startRipple(any(), any())
    }

    @Test
@@ -206,7 +209,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            0 /* userId */,
            BiometricSourceType.FINGERPRINT /* type */,
            false /* isStrongBiometric */)
        verify(rippleView, never()).startRipple(any())
        verify(rippleView, never()).startRipple(any(), any())
    }

    @Test