From 9e3fa1033c1fb43c82abf93f231636a4b103c0e4 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Wed, 8 Nov 2017 17:16:55 -0800 Subject: Refactoring ScrimController ScrimController is now a state machine with tests. The main motivation for refactoring this class was to centralize ownership of the scrim state. Before, animations could be triggered by StatusBar, StatusBarKeyguardViewManager or DozeScrimController simultaneously, causing collision, sometimes overriding an expected state due to the call order and making it hard to calculate an actual state. Change-Id: I4f4d82549235d3fc7be35b235a2668e70b956cb7 Fixes: 64397851 Fixes: 65688233 Bug: 64155983 Test: runtest -x tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java Test: runtest -x tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java Test: unlock using fingerprint, or challenge (pin/password/pattern) Test: trigger in-app bouncer (camera app) Test: pull down notification shade locked and unlocked Test: lock, look at AoD (or black display when AoD isn't supported) --- core/res/res/values/config.xml | 2 +- packages/SystemUI/res/values/config.xml | 5 +- packages/SystemUI/res/values/ids.xml | 1 + .../src/com/android/systemui/SystemUIFactory.java | 7 +- .../src/com/android/systemui/doze/DozeHost.java | 1 - .../systemui/keyguard/KeyguardViewMediator.java | 7 +- .../com/android/systemui/statusbar/ScrimView.java | 7 + .../systemui/statusbar/phone/DozeParameters.java | 16 +- .../statusbar/phone/DozeScrimController.java | 317 ++-------- .../phone/FingerprintUnlockController.java | 10 +- .../systemui/statusbar/phone/ScrimController.java | 655 +++++++++++---------- .../systemui/statusbar/phone/ScrimState.java | 208 +++++++ .../systemui/statusbar/phone/StatusBar.java | 112 ++-- .../phone/StatusBarKeyguardViewManager.java | 88 +-- .../policy/BrightnessMirrorController.java | 20 +- .../com/android/systemui/doze/DozeHostFake.java | 8 +- .../android/systemui/statusbar/ScrimViewTest.java | 4 +- .../statusbar/phone/DozeScrimControllerTest.java | 99 ++++ .../statusbar/phone/ScrimControllerTest.java | 300 ++++++++++ 19 files changed, 1131 insertions(+), 736 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c10461643406..edcdbe137a5f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1955,7 +1955,7 @@ false diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index cf7923805f0f..8e065d1bf06b 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -250,10 +250,7 @@ - 900 - - - 130 + 130 6000 diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index cfd95b41825a..884e18918fd6 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -47,6 +47,7 @@ + diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 0c067ff38295..526a8f464441 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -28,6 +28,7 @@ import com.android.systemui.Dependency.DependencyProvider; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockIcon; @@ -86,10 +87,10 @@ public class SystemUIFactory { public ScrimController createScrimController(LightBarController lightBarController, ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim, - LockscreenWallpaper lockscreenWallpaper, - Consumer scrimVisibleListener) { + LockscreenWallpaper lockscreenWallpaper, Consumer scrimVisibleListener, + DozeParameters dozeParameters) { return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim, - scrimVisibleListener); + scrimVisibleListener, dozeParameters); } public NotificationIconAreaController createNotificationIconAreaController(Context context, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 7db118d7fb1c..2f607eee4f16 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -35,7 +35,6 @@ public interface DozeHost { boolean isBlockingDoze(); void startPendingIntentDismissingKeyguard(PendingIntent intent); - void abortPulsing(); void extendPulse(); void setAnimateWakeup(boolean animateWakeup); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index b99e76a28c6d..c92acd068745 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2028,12 +2028,9 @@ public class KeyguardViewMediator extends SystemUI { } public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar, - ViewGroup container, - ScrimController scrimController, - FingerprintUnlockController fingerprintUnlockController) { + ViewGroup container, FingerprintUnlockController fingerprintUnlockController) { mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container, - scrimController, fingerprintUnlockController, - mDismissCallbackRegistry); + fingerprintUnlockController, mDismissCallbackRegistry); return mStatusBarKeyguardViewManager; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java index a53e348fd16c..fb9c037c236c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java @@ -252,6 +252,13 @@ public class ScrimView extends View implements ConfigurationController.Configura return false; } + /** + * It might look counterintuitive to have another method to set the alpha instead of + * only using {@link #setAlpha(float)}. In this case we're in a hardware layer + * optimizing blend modes, so it makes sense. + * + * @param alpha Gradient alpha from 0 to 1. + */ public void setViewAlpha(float alpha) { if (alpha != mViewAlpha) { mViewAlpha = alpha; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 6b7397b3f8ca..3f57c2f9384f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -46,10 +46,8 @@ public class DozeParameters { public void dump(PrintWriter pw) { pw.println(" DozeParameters:"); pw.print(" getDisplayStateSupported(): "); pw.println(getDisplayStateSupported()); - pw.print(" getPulseDuration(pickup=false): "); pw.println(getPulseDuration(false)); - pw.print(" getPulseDuration(pickup=true): "); pw.println(getPulseDuration(true)); - pw.print(" getPulseInDuration(pickup=false): "); pw.println(getPulseInDuration(false)); - pw.print(" getPulseInDuration(pickup=true): "); pw.println(getPulseInDuration(true)); + pw.print(" getPulseDuration(): "); pw.println(getPulseDuration()); + pw.print(" getPulseInDuration(): "); pw.println(getPulseInDuration()); pw.print(" getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration()); pw.print(" getPulseOutDuration(): "); pw.println(getPulseOutDuration()); pw.print(" getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion()); @@ -81,14 +79,12 @@ public class DozeParameters { return mContext.getResources().getBoolean(R.bool.doze_suspend_display_state_supported); } - public int getPulseDuration(boolean pickup) { - return getPulseInDuration(pickup) + getPulseVisibleDuration() + getPulseOutDuration(); + public int getPulseDuration() { + return getPulseInDuration() + getPulseVisibleDuration() + getPulseOutDuration(); } - public int getPulseInDuration(boolean pickupOrDoubleTap) { - return pickupOrDoubleTap - ? getInt("doze.pulse.duration.in.pickup", R.integer.doze_pulse_duration_in_pickup) - : getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in); + public int getPulseInDuration() { + return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in); } public int getPulseVisibleDuration() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 8afb8490808e..1011383b72e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -16,16 +16,11 @@ package com.android.systemui.statusbar.phone; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.Context; import android.os.Handler; import android.util.Log; -import android.view.animation.Interpolator; -import com.android.systemui.Interpolators; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; @@ -40,74 +35,59 @@ public class DozeScrimController { private final Handler mHandler = new Handler(); private final ScrimController mScrimController; - private final Context mContext; - private boolean mDozing; private DozeHost.PulseCallback mPulseCallback; private int mPulseReason; - private Animator mInFrontAnimator; - private Animator mBehindAnimator; - private float mInFrontTarget; - private float mBehindTarget; - private boolean mDozingAborted; - private boolean mWakeAndUnlocking; private boolean mFullyPulsing; - private float mAodFrontScrimOpacity = 0; - private Runnable mSetDozeInFrontAlphaDelayed; + private final ScrimController.Callback mScrimCallback = new ScrimController.Callback() { + @Override + public void onDisplayBlanked() { + if (DEBUG) { + Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason=" + + DozeLog.pulseReasonToString(mPulseReason)); + } + if (!mDozing) { + return; + } + + // Signal that the pulse is ready to turn the screen on and draw. + pulseStarted(); + } + + @Override + public void onFinished() { + if (DEBUG) { + Log.d(TAG, "Pulse in finished, mDozing=" + mDozing); + } + if (!mDozing) { + return; + } + mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); + mHandler.postDelayed(mPulseOutExtended, + mDozeParameters.getPulseVisibleDurationExtended()); + mFullyPulsing = true; + } + + /** + * Transition was aborted before it was over. + */ + @Override + public void onCancelled() { + pulseFinished(); + } + }; public DozeScrimController(ScrimController scrimController, Context context) { - mContext = context; mScrimController = scrimController; mDozeParameters = new DozeParameters(context); } - public void setDozing(boolean dozing, boolean animate) { + public void setDozing(boolean dozing) { if (mDozing == dozing) return; mDozing = dozing; - mWakeAndUnlocking = false; - if (mDozing) { - mDozingAborted = false; - abortAnimations(); - mScrimController.setDozeBehindAlpha(1f); - setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? mAodFrontScrimOpacity : 1f); - } else { + if (!mDozing) { cancelPulsing(); - if (animate) { - startScrimAnimation(false /* inFront */, 0f /* target */, - NotificationPanelView.DOZE_ANIMATION_DURATION, - Interpolators.LINEAR_OUT_SLOW_IN); - startScrimAnimation(true /* inFront */, 0f /* target */, - NotificationPanelView.DOZE_ANIMATION_DURATION, - Interpolators.LINEAR_OUT_SLOW_IN); - } else { - abortAnimations(); - mScrimController.setDozeBehindAlpha(0f); - setDozeInFrontAlpha(0f); - } - } - } - - /** - * Set the opacity of the front scrim when showing AOD1 - * - * Used to emulate lower brightness values than the hardware supports natively. - */ - public void setAodDimmingScrim(float scrimOpacity) { - mAodFrontScrimOpacity = scrimOpacity; - if (mDozing && !isPulsing() && !mDozingAborted && !mWakeAndUnlocking - && mDozeParameters.getAlwaysOn()) { - setDozeInFrontAlpha(mAodFrontScrimOpacity); - } - } - - public void setWakeAndUnlocking() { - // Immediately abort the doze scrims in case of wake-and-unlock - // for pulsing so the Keyguard fade-out animation scrim can take over. - if (!mWakeAndUnlocking) { - mWakeAndUnlocking = true; - mScrimController.setDozeBehindAlpha(0f); - setDozeInFrontAlpha(0f); } } @@ -118,37 +98,21 @@ public class DozeScrimController { } if (!mDozing || mPulseCallback != null) { + if (DEBUG) { + Log.d(TAG, "Pulse supressed. Dozing: " + mDozeParameters + " had callback? " + + (mPulseCallback != null)); + } // Pulse suppressed. callback.onPulseFinished(); return; } - // Begin pulse. Note that it's very important that the pulse finished callback + // Begin pulse. Note that it's very important that the pulse finished callback // be invoked when we're done so that the caller can drop the pulse wakelock. mPulseCallback = callback; mPulseReason = reason; - setDozeInFrontAlpha(1f); - mHandler.post(mPulseIn); - } - - /** - * Aborts pulsing immediately. - */ - public void abortPulsing() { - cancelPulsing(); - if (mDozing && !mWakeAndUnlocking) { - mScrimController.setDozeBehindAlpha(1f); - setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() && !mDozingAborted - ? mAodFrontScrimOpacity : 1f); - } - } - /** - * Aborts dozing immediately. - */ - public void abortDoze() { - mDozingAborted = true; - abortPulsing(); + mScrimController.transitionTo(ScrimState.PULSING, mScrimCallback); } public void pulseOutNow() { @@ -157,17 +121,6 @@ public class DozeScrimController { } } - public void onScreenTurnedOn() { - if (isPulsing()) { - final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP - || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP; - startScrimAnimation(true /* inFront */, 0f, - mDozeParameters.getPulseInDuration(pickupOrDoubleTap), - pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT, - mPulseInFinished); - } - } - public boolean isPulsing() { return mPulseCallback != null; } @@ -181,11 +134,9 @@ public class DozeScrimController { } private void cancelPulsing() { - if (DEBUG) Log.d(TAG, "Cancel pulsing"); - if (mPulseCallback != null) { + if (DEBUG) Log.d(TAG, "Cancel pulsing"); mFullyPulsing = false; - mHandler.removeCallbacks(mPulseIn); mHandler.removeCallbacks(mPulseOut); mHandler.removeCallbacks(mPulseOutExtended); pulseFinished(); @@ -193,151 +144,20 @@ public class DozeScrimController { } private void pulseStarted() { + DozeLog.tracePulseStart(mPulseReason); if (mPulseCallback != null) { mPulseCallback.onPulseStarted(); } } private void pulseFinished() { + DozeLog.tracePulseFinish(); if (mPulseCallback != null) { mPulseCallback.onPulseFinished(); mPulseCallback = null; } } - private void abortAnimations() { - if (mInFrontAnimator != null) { - mInFrontAnimator.cancel(); - } - if (mBehindAnimator != null) { - mBehindAnimator.cancel(); - } - } - - private void startScrimAnimation(final boolean inFront, float target, long duration, - Interpolator interpolator) { - startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */); - } - - private void startScrimAnimation(final boolean inFront, float target, long duration, - Interpolator interpolator, final Runnable endRunnable) { - Animator current = getCurrentAnimator(inFront); - if (current != null) { - float currentTarget = getCurrentTarget(inFront); - if (currentTarget == target) { - return; - } - current.cancel(); - } - ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target); - anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float value = (float) animation.getAnimatedValue(); - setDozeAlpha(inFront, value); - } - }); - anim.setInterpolator(interpolator); - anim.setDuration(duration); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - setCurrentAnimator(inFront, null); - if (endRunnable != null) { - endRunnable.run(); - } - } - }); - anim.start(); - setCurrentAnimator(inFront, anim); - setCurrentTarget(inFront, target); - } - - private float getCurrentTarget(boolean inFront) { - return inFront ? mInFrontTarget : mBehindTarget; - } - - private void setCurrentTarget(boolean inFront, float target) { - if (inFront) { - mInFrontTarget = target; - } else { - mBehindTarget = target; - } - } - - private Animator getCurrentAnimator(boolean inFront) { - return inFront ? mInFrontAnimator : mBehindAnimator; - } - - private void setCurrentAnimator(boolean inFront, Animator animator) { - if (inFront) { - mInFrontAnimator = animator; - } else { - mBehindAnimator = animator; - } - } - - private void setDozeAlpha(boolean inFront, float alpha) { - if (mWakeAndUnlocking) { - return; - } - if (inFront) { - mScrimController.setDozeInFrontAlpha(alpha); - } else { - mScrimController.setDozeBehindAlpha(alpha); - } - } - - private float getDozeAlpha(boolean inFront) { - return inFront - ? mScrimController.getDozeInFrontAlpha() - : mScrimController.getDozeBehindAlpha(); - } - - private void setDozeInFrontAlpha(float opacity) { - setDozeInFrontAlphaDelayed(opacity, 0 /* delay */); - - } - - private void setDozeInFrontAlphaDelayed(float opacity, long delayMs) { - if (mSetDozeInFrontAlphaDelayed != null) { - mHandler.removeCallbacks(mSetDozeInFrontAlphaDelayed); - mSetDozeInFrontAlphaDelayed = null; - } - if (delayMs <= 0) { - mScrimController.setDozeInFrontAlpha(opacity); - } else { - mHandler.postDelayed(mSetDozeInFrontAlphaDelayed = () -> { - setDozeInFrontAlpha(opacity); - }, delayMs); - } - } - - private final Runnable mPulseIn = new Runnable() { - @Override - public void run() { - if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason=" - + DozeLog.pulseReasonToString(mPulseReason)); - if (!mDozing) return; - DozeLog.tracePulseStart(mPulseReason); - - // Signal that the pulse is ready to turn the screen on and draw. - pulseStarted(); - } - }; - - private final Runnable mPulseInFinished = new Runnable() { - @Override - public void run() { - if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing); - if (!mDozing) return; - mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); - mHandler.postDelayed(mPulseOutExtended, - mDozeParameters.getPulseVisibleDurationExtended()); - mFullyPulsing = true; - } - }; - private final Runnable mPulseOutExtended = new Runnable() { @Override public void run() { @@ -354,38 +174,13 @@ public class DozeScrimController { mHandler.removeCallbacks(mPulseOutExtended); if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing); if (!mDozing) return; - startScrimAnimation(true /* inFront */, 1, - mDozeParameters.getPulseOutDuration(), - Interpolators.ALPHA_IN, mPulseOutFinishing); - } - }; - - private final Runnable mPulseOutFinishing = new Runnable() { - @Override - public void run() { - if (DEBUG) Log.d(TAG, "Pulse out finished"); - DozeLog.tracePulseFinish(); - if (mDozeParameters.getAlwaysOn() && mDozing) { - // Setting power states can block rendering. For AOD, delay finishing the pulse and - // setting the power state until the fully black scrim had time to hit the - // framebuffer. - mHandler.postDelayed(mPulseOutFinished, 30); - } else { - mPulseOutFinished.run(); - } - } - }; - - private final Runnable mPulseOutFinished = new Runnable() { - @Override - public void run() { - // Signal that the pulse is all finished so we can turn the screen off now. - DozeScrimController.this.pulseFinished(); - if (mDozeParameters.getAlwaysOn()) { - // Setting power states can happen after we push out the frame. Make sure we - // stay fully opaque until the power state request reaches the lower levels. - setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 100); - } + mScrimController.transitionTo(ScrimState.AOD, + new ScrimController.Callback() { + @Override + public void onDisplayBlanked() { + pulseFinished(); + } + }); } }; -} +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java index 91369dbd5f89..80d4061b3864 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java @@ -181,9 +181,9 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { } private boolean pulsingOrAod() { - boolean pulsing = mDozeScrimController.isPulsing(); - boolean dozingWithScreenOn = mStatusBar.isDozing() && !mStatusBar.isScreenFullyOff(); - return pulsing || dozingWithScreenOn; + final ScrimState scrimState = mScrimController.getState(); + return scrimState == ScrimState.AOD + || scrimState == ScrimState.PULSING; } @Override @@ -246,15 +246,12 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { true /* allowEnterAnimation */); } else if (mMode == MODE_WAKE_AND_UNLOCK){ Trace.beginSection("MODE_WAKE_AND_UNLOCK"); - mDozeScrimController.abortDoze(); } else { Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM"); mUpdateMonitor.awakenFromDream(); } mStatusBarWindowManager.setStatusBarFocusable(false); mKeyguardViewMediator.onWakeAndUnlocking(); - mScrimController.setWakeAndUnlocking(); - mDozeScrimController.setWakeAndUnlocking(); if (mStatusBar.getNavigationBarView() != null) { mStatusBar.getNavigationBarView().setWakeAndUnlocking(true); } @@ -269,6 +266,7 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { } private void showBouncer() { + mScrimController.transitionTo(ScrimState.BOUNCER); mStatusBarKeyguardViewManager.animateCollapsePanels( FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR); mPendingShowBouncer = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 702afa3a38b1..dfd4c1768268 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -25,7 +25,9 @@ import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Handler; import android.os.Trace; +import android.util.Log; import android.util.MathUtils; import android.view.View; import android.view.ViewGroup; @@ -34,12 +36,14 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener; import com.android.internal.graphics.ColorUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; +import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.statusbar.ExpandableNotificationRow; @@ -47,7 +51,10 @@ import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.stack.ViewState; +import com.android.systemui.util.wakelock.DelayedWakeLock; +import com.android.systemui.util.wakelock.WakeLock; +import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.function.Consumer; @@ -56,33 +63,54 @@ import java.util.function.Consumer; * security method gets shown). */ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, - OnHeadsUpChangedListener, OnColorsChangedListener { + OnHeadsUpChangedListener, OnColorsChangedListener, Dumpable { + + private static final String TAG = "ScrimController"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + public static final long ANIMATION_DURATION = 220; public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR = new PathInterpolator(0f, 0, 0.7f, 1f); public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED = new PathInterpolator(0.3f, 0f, 0.8f, 1f); - // Default alpha value for most scrims, if unsure use this constant + /** + * Default alpha value for most scrims. + */ public static final float GRADIENT_SCRIM_ALPHA = 0.45f; - // A scrim varies its opacity based on a busyness factor, for example - // how many notifications are currently visible. + /** + * A scrim varies its opacity based on a busyness factor, for example + * how many notifications are currently visible. + */ public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f; + /** + * The most common scrim, the one under the keyguard. + */ protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA; + /** + * We fade out the bottom scrim when the bouncer is visible. + */ protected static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; - private static final float SCRIM_IN_FRONT_ALPHA = GRADIENT_SCRIM_ALPHA_BUSY; - private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY; - private static final int TAG_KEY_ANIM = R.id.scrim; + /** + * Opacity of the scrim behind the bouncer (the one doing actual background protection.) + */ + protected static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY; + + static final int TAG_KEY_ANIM = R.id.scrim; + static final int TAG_KEY_ANIM_BLANK = R.id.scrim_blanking; private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target; private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; private static final int TAG_END_ALPHA = R.id.scrim_alpha_end; private static final float NOT_INITIALIZED = -1; - private final LightBarController mLightBarController; + private ScrimState mState = ScrimState.UNINITIALIZED; + private final Context mContext; protected final ScrimView mScrimBehind; protected final ScrimView mScrimInFront; - private final UnlockMethodCache mUnlockMethodCache; private final View mHeadsUpScrim; + private final LightBarController mLightBarController; + private final UnlockMethodCache mUnlockMethodCache; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final DozeParameters mDozeParameters; private final SysuiColorExtractor mColorExtractor; private GradientColors mLockColors; @@ -94,61 +122,53 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING; - protected boolean mKeyguardShowing; private float mFraction; private boolean mDarkenWhileDragging; - protected boolean mBouncerShowing; - protected boolean mBouncerIsKeyguard = false; - private boolean mWakeAndUnlocking; protected boolean mAnimateChange; private boolean mUpdatePending; private boolean mTracking; private boolean mAnimateKeyguardFadingOut; - protected long mDurationOverride = -1; + protected long mAnimationDuration = -1; private long mAnimationDelay; private Runnable mOnAnimationFinished; private boolean mDeferFinishedListener; private final Interpolator mInterpolator = new DecelerateInterpolator(); - private boolean mDozing; - private float mDozeInFrontAlpha; - private float mDozeBehindAlpha; private float mCurrentInFrontAlpha = NOT_INITIALIZED; private float mCurrentBehindAlpha = NOT_INITIALIZED; - private float mCurrentHeadsUpAlpha = NOT_INITIALIZED; + private int mCurrentInFrontTint; + private int mCurrentBehindTint; private int mPinnedHeadsUpCount; private float mTopHeadsUpDragAmount; private View mDraggedHeadsUpView; - private boolean mForceHideScrims; - private boolean mSkipFirstFrame; - private boolean mDontAnimateBouncerChanges; private boolean mKeyguardFadingOutInProgress; - private boolean mAnimatingDozeUnlock; private ValueAnimator mKeyguardFadeoutAnimation; - /** Wake up from AOD transition is starting; need fully opaque front scrim */ - private boolean mWakingUpFromAodStarting; - /** Wake up from AOD transition is in progress; need black tint */ - private boolean mWakingUpFromAodInProgress; - /** Wake up from AOD transition is animating; need to reset when animation finishes */ - private boolean mWakingUpFromAodAnimationRunning; - private boolean mScrimsVisble; + private boolean mScrimsVisible; private final Consumer mScrimVisibleListener; + private boolean mBlankScreen; + private boolean mScreenBlankingCallbackCalled; + private Callback mCallback; + + private final WakeLock mWakeLock; + private boolean mWakeLockHeld; public ScrimController(LightBarController lightBarController, ScrimView scrimBehind, - ScrimView scrimInFront, View headsUpScrim, - Consumer scrimVisibleListener) { + ScrimView scrimInFront, View headsUpScrim, Consumer scrimVisibleListener, + DozeParameters dozeParameters) { mScrimBehind = scrimBehind; mScrimInFront = scrimInFront; mHeadsUpScrim = headsUpScrim; mScrimVisibleListener = scrimVisibleListener; - final Context context = scrimBehind.getContext(); - mUnlockMethodCache = UnlockMethodCache.getInstance(context); - mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context); + mContext = scrimBehind.getContext(); + mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); + mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); mLightBarController = lightBarController; - mScrimBehindAlphaResValue = context.getResources().getFloat(R.dimen.scrim_behind_alpha); + mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha); + mWakeLock = createWakeLock(); // Scrim alpha is initially set to the value on the resource but might be changed // to make sure that text on top of it is legible. mScrimBehindAlpha = mScrimBehindAlphaResValue; + mDozeParameters = dozeParameters; mColorExtractor = Dependency.get(SysuiColorExtractor.class); mColorExtractor.addOnColorsChangedListener(this); @@ -158,159 +178,155 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, ColorExtractor.TYPE_DARK, true /* ignoreVisibility */); mNeedsDrawableColorUpdate = true; + final ScrimState[] states = ScrimState.values(); + for (int i = 0; i < states.length; i++) { + states[i].init(mScrimInFront, mScrimBehind, mDozeParameters); + states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard); + } + mState = ScrimState.UNINITIALIZED; + updateHeadsUpScrim(false); updateScrims(); } - public void setKeyguardShowing(boolean showing) { - mKeyguardShowing = showing; - - // Showing/hiding the keyguard means that scrim colors have to be switched - mNeedsDrawableColorUpdate = true; - scheduleUpdate(); - } - - protected void setScrimBehindValues(float scrimBehindAlphaKeyguard, - float scrimBehindAlphaUnlocking) { - mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard; - mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking; - scheduleUpdate(); - } - - public void onTrackingStarted() { - mTracking = true; - mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer(); - } - - public void onExpandingFinished() { - mTracking = false; + public void transitionTo(ScrimState state) { + transitionTo(state, null); } - public void setPanelExpansion(float fraction) { - if (mFraction != fraction) { - mFraction = fraction; - scheduleUpdate(); - if (mPinnedHeadsUpCount != 0) { - updateHeadsUpScrim(false); - } - if (mKeyguardFadeoutAnimation != null && mTracking) { - mKeyguardFadeoutAnimation.cancel(); - } + public void transitionTo(ScrimState state, Callback callback) { + if (state == mState) { + return; + } else if (DEBUG) { + Log.d(TAG, "State changed to: " + state); } - } - - public void setBouncerShowing(boolean showing) { - mBouncerShowing = showing; - mAnimateChange = !mTracking && !mDontAnimateBouncerChanges && !mKeyguardFadingOutInProgress; - scheduleUpdate(); - } - /** Prepares the wakeUpFromAod animation (while turning on screen); Forces black scrims. */ - public void prepareWakeUpFromAod() { - if (mWakingUpFromAodInProgress) { - return; + if (state == ScrimState.UNINITIALIZED) { + throw new IllegalArgumentException("Cannot change to UNINITIALIZED."); } - mWakingUpFromAodInProgress = true; - mWakingUpFromAodStarting = true; - mAnimateChange = false; - scheduleUpdate(); - onPreDraw(); - } - /** Starts the wakeUpFromAod animation (once screen is on); animate to transparent scrims. */ - public void wakeUpFromAod() { - if (mWakeAndUnlocking || mAnimateKeyguardFadingOut) { - // Wake and unlocking has a separate transition that must not be interfered with. - mWakingUpFromAodStarting = false; - mWakingUpFromAodInProgress = false; - return; + if (mCallback != null) { + mCallback.onCancelled(); } - if (mWakingUpFromAodStarting) { - mWakingUpFromAodInProgress = true; - mWakingUpFromAodStarting = false; - mAnimateChange = true; - scheduleUpdate(); + mCallback = callback; + + state.prepare(mState); + mScreenBlankingCallbackCalled = false; + mAnimationDelay = 0; + mBlankScreen = state.getBlanksScreen(); + mAnimateChange = state.getAnimateChange(); + mAnimationDuration = state.getAnimationDuration(); + mCurrentInFrontTint = state.getFrontTint(); + mCurrentBehindTint = state.getBehindTint(); + mCurrentInFrontAlpha = state.getFrontAlpha(); + mCurrentBehindAlpha = state.getBehindAlpha(); + + // Showing/hiding the keyguard means that scrim colors have to be switched, not necessary + // to do the same when you're just showing the brightness mirror. + mNeedsDrawableColorUpdate = state != ScrimState.BRIGHTNESS_MIRROR; + + if (mKeyguardFadeoutAnimation != null) { + mKeyguardFadeoutAnimation.cancel(); } - } - public void setWakeAndUnlocking() { - mWakeAndUnlocking = true; - mAnimatingDozeUnlock = true; - mWakingUpFromAodStarting = false; - mWakingUpFromAodInProgress = false; - scheduleUpdate(); - } + mState = state; - public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished, - boolean skipFirstFrame) { - mWakeAndUnlocking = false; - mAnimateKeyguardFadingOut = true; - mDurationOverride = duration; - mAnimationDelay = delay; - mAnimateChange = true; - mSkipFirstFrame = skipFirstFrame; - mOnAnimationFinished = onAnimationFinished; + // Do not let the device sleep until we're done with all animations + if (!mWakeLockHeld) { + if (mWakeLock != null) { + mWakeLockHeld = true; + mWakeLock.acquire(); + } else { + Log.w(TAG, "Cannot hold wake lock, it has not been set yet"); + } + } if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) { scheduleUpdate(); - - // No need to wait for the next frame to be drawn for this case - onPreDraw will execute - // the changes we just scheduled. - onPreDraw(); } else { - // In case the user isn't unlocked, make sure to delay a bit because the system is hosed - // with too many things in this case, in order to not skip the initial frames. + // with too many things at this case, in order to not skip the initial frames. mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16); + mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY; } } - public void abortKeyguardFadingOut() { - if (mAnimateKeyguardFadingOut) { - endAnimateKeyguardFadingOut(true /* force */); - } + public ScrimState getState() { + return mState; } - public void animateKeyguardUnoccluding(long duration) { - mAnimateChange = false; - setScrimBehindAlpha(0f); - mAnimateChange = true; + protected void setScrimBehindValues(float scrimBehindAlphaKeyguard, + float scrimBehindAlphaUnlocking) { + mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard; + mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking; + ScrimState[] states = ScrimState.values(); + for (int i = 0; i < states.length; i++) { + states[i].setScrimBehindAlphaKeyguard(scrimBehindAlphaKeyguard); + } scheduleUpdate(); - mDurationOverride = duration; } - public void animateGoingToFullShade(long delay, long duration) { - mDurationOverride = duration; - mAnimationDelay = delay; - mAnimateChange = true; - scheduleUpdate(); + public void onTrackingStarted() { + mTracking = true; + mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer(); } - public void setDozing(boolean dozing) { - if (mDozing != dozing) { - mDozing = dozing; - scheduleUpdate(); - } + public void onExpandingFinished() { + mTracking = false; } - public void setDozeInFrontAlpha(float alpha) { - mDozeInFrontAlpha = alpha; - updateScrimColor(mScrimInFront); - } + /** + * Current state of the shade expansion when pulling it from the top. + * This value is 1 when on top of the keyguard and goes to 0 as the user drags up. + * + * The expansion fraction is tied to the scrim opacity. + * + * @param fraction From 0 to 1 where 0 means collapse and 1 expanded. + */ + public void setPanelExpansion(float fraction) { + if (mFraction != fraction) { + mFraction = fraction; - public void setDozeBehindAlpha(float alpha) { - mDozeBehindAlpha = alpha; - updateScrimColor(mScrimBehind); - } + if (mState == ScrimState.UNLOCKED) { + // Darken scrim as you pull down the shade when unlocked + float behindFraction = getInterpolatedFraction(); + behindFraction = (float) Math.pow(behindFraction, 0.8f); + mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard; + mCurrentInFrontAlpha = 0; + } else if (mState == ScrimState.KEYGUARD) { + if (mUpdatePending) { + return; + } - public float getDozeBehindAlpha() { - return mDozeBehindAlpha; - } + // Either darken of make the scrim transparent when you + // pull down the shade + float interpolatedFract = getInterpolatedFraction(); + if (mDarkenWhileDragging) { + mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking, + mScrimBehindAlphaKeyguard, interpolatedFract); + mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED; + } else { + mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard, + interpolatedFract); + mCurrentInFrontAlpha = 0; + } + } else { + Log.w(TAG, "Invalid state, cannot set panel expansion when: " + mState); + return; + } + + if (mPinnedHeadsUpCount != 0) { + updateHeadsUpScrim(false); + } - public float getDozeInFrontAlpha() { - return mDozeInFrontAlpha; + updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha); + updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha); + } } + /** + * Keyguard and shade scrim opacity varies according to how many notifications are visible. + * @param notificationCount Number of visible notifications. + */ public void setNotificationCount(int notificationCount) { final float maxNotificationDensity = 3; float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f); @@ -319,15 +335,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, notificationDensity); if (mScrimBehindAlphaKeyguard != newAlpha) { mScrimBehindAlphaKeyguard = newAlpha; - mAnimateChange = true; - scheduleUpdate(); - } - } - private float getScrimInFrontAlpha() { - return mKeyguardUpdateMonitor.needsSlowUnlockTransition() - ? SCRIM_IN_FRONT_ALPHA_LOCKED - : SCRIM_IN_FRONT_ALPHA; + if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER) { + scheduleUpdate(); + } + } } /** @@ -352,7 +364,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, if (mNeedsDrawableColorUpdate) { mNeedsDrawableColorUpdate = false; final GradientColors currentScrimColors; - if (mKeyguardShowing) { + if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER) { // Always animate color changes if we're seeing the keyguard mScrimInFront.setColors(mLockColors, true /* animated */); mScrimBehind.setColors(mLockColors, true /* animated */); @@ -375,77 +387,31 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mLightBarController.setScrimColor(mScrimInFront.getColors()); } - if (mAnimateKeyguardFadingOut || mForceHideScrims) { - setScrimInFrontAlpha(0f); - setScrimBehindAlpha(0f); - } else if (mWakeAndUnlocking) { - // During wake and unlock, we first hide everything behind a black scrim, which then - // gets faded out from animateKeyguardFadingOut. This must never be animated. - mAnimateChange = false; - if (mDozing) { - setScrimInFrontAlpha(0f); - setScrimBehindAlpha(1f); - } else { - setScrimInFrontAlpha(1f); - setScrimBehindAlpha(0f); - } - } else if (!mKeyguardShowing && !mBouncerShowing && !mWakingUpFromAodStarting) { - updateScrimNormal(); - setScrimInFrontAlpha(0); - } else { - updateScrimKeyguard(); - } - mAnimateChange = false; + setScrimInFrontAlpha(mCurrentInFrontAlpha); + setScrimBehindAlpha(mCurrentBehindAlpha); + dispatchScrimsVisible(); } private void dispatchScrimsVisible() { boolean scrimsVisible = mScrimBehind.getViewAlpha() > 0 || mScrimInFront.getViewAlpha() > 0; - if (mScrimsVisble != scrimsVisible) { - mScrimsVisble = scrimsVisible; + if (mScrimsVisible != scrimsVisible) { + mScrimsVisible = scrimsVisible; mScrimVisibleListener.accept(scrimsVisible); } } - private void updateScrimKeyguard() { - if (mTracking && mDarkenWhileDragging) { - float behindFraction = Math.max(0, Math.min(mFraction, 1)); - float fraction = 1 - behindFraction; - fraction = (float) Math.pow(fraction, 0.8f); - behindFraction = (float) Math.pow(behindFraction, 0.8f); - setScrimInFrontAlpha(fraction * getScrimInFrontAlpha()); - setScrimBehindAlpha(behindFraction * mScrimBehindAlphaKeyguard); - } else if (mBouncerShowing && !mBouncerIsKeyguard) { - setScrimInFrontAlpha(getScrimInFrontAlpha()); - updateScrimNormal(); - } else if (mBouncerShowing) { - setScrimInFrontAlpha(0f); - setScrimBehindAlpha(mScrimBehindAlpha); - } else { - float fraction = Math.max(0, Math.min(mFraction, 1)); - if (mWakingUpFromAodStarting) { - setScrimInFrontAlpha(1f); - } else { - setScrimInFrontAlpha(0f); - } - setScrimBehindAlpha(fraction - * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking) - + mScrimBehindAlphaUnlocking); - } - } - - private void updateScrimNormal() { + private float getInterpolatedFraction() { float frac = mFraction; // let's start this 20% of the way down the screen frac = frac * 1.2f - 0.2f; if (frac <= 0) { - setScrimBehindAlpha(0); + return 0; } else { // woo, special effects - final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); - setScrimBehindAlpha(k * mScrimBehindAlpha); + return (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); } } @@ -455,102 +421,76 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private void setScrimInFrontAlpha(float alpha) { setScrimAlpha(mScrimInFront, alpha); - if (alpha == 0f) { - mScrimInFront.setClickable(false); - } else { - // Eat touch events (unless dozing). - mScrimInFront.setClickable(!mDozing); - } } private void setScrimAlpha(View scrim, float alpha) { - updateScrim(mAnimateChange, scrim, alpha, getCurrentScrimAlpha(scrim)); - } - - protected float getDozeAlpha(View scrim) { - return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha; - } - - protected float getCurrentScrimAlpha(View scrim) { - return scrim == mScrimBehind ? mCurrentBehindAlpha - : scrim == mScrimInFront ? mCurrentInFrontAlpha - : mCurrentHeadsUpAlpha; - } - - private void setCurrentScrimAlpha(View scrim, float alpha) { - if (scrim == mScrimBehind) { - mCurrentBehindAlpha = alpha; - mLightBarController.setScrimAlpha(mCurrentBehindAlpha); - } else if (scrim == mScrimInFront) { - mCurrentInFrontAlpha = alpha; + if (alpha == 0f) { + scrim.setClickable(false); } else { - alpha = Math.max(0.0f, Math.min(1.0f, alpha)); - mCurrentHeadsUpAlpha = alpha; + // Eat touch events (unless dozing). + scrim.setClickable(!(mState == ScrimState.AOD)); } + updateScrim(mAnimateChange, scrim, alpha); } - private void updateScrimColor(View scrim) { - float alpha1 = getCurrentScrimAlpha(scrim); + private void updateScrimColor(View scrim, float alpha, int tint) { + alpha = Math.max(0, Math.min(1.0f, alpha)); if (scrim instanceof ScrimView) { ScrimView scrimView = (ScrimView) scrim; - float dozeAlpha = getDozeAlpha(scrim); - float alpha = 1 - (1 - alpha1) * (1 - dozeAlpha); - alpha = Math.max(0, Math.min(1.0f, alpha)); - scrimView.setViewAlpha(alpha); Trace.traceCounter(Trace.TRACE_TAG_APP, scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha", (int) (alpha * 255)); - int dozeTint = Color.TRANSPARENT; - - boolean dozing = mAnimatingDozeUnlock || mDozing; - boolean frontScrimDozing = mWakingUpFromAodInProgress; - if (dozing || frontScrimDozing && scrim == mScrimInFront) { - dozeTint = Color.BLACK; - } Trace.traceCounter(Trace.TRACE_TAG_APP, scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint", - dozeTint == Color.BLACK ? 1 : 0); + Color.alpha(tint)); - scrimView.setTint(dozeTint); + scrimView.setTint(tint); + scrimView.setViewAlpha(alpha); } else { - scrim.setAlpha(alpha1); + scrim.setAlpha(alpha); } dispatchScrimsVisible(); } - private void startScrimAnimation(final View scrim, float target) { - float current = getCurrentScrimAlpha(scrim); - ValueAnimator anim = ValueAnimator.ofFloat(current, target); + private int getCurrentScrimTint(View scrim) { + return scrim == mScrimInFront ? mCurrentInFrontTint : mCurrentBehindTint; + } + + private void startScrimAnimation(final View scrim, float current, float target) { + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() : + Color.TRANSPARENT; anim.addUpdateListener(animation -> { - float alpha = (float) animation.getAnimatedValue(); - setCurrentScrimAlpha(scrim, alpha); - updateScrimColor(scrim); + final float animAmount = (float) animation.getAnimatedValue(); + final int finalScrimTint = scrim == mScrimInFront ? + mCurrentInFrontTint : mCurrentBehindTint; + float alpha = MathUtils.lerp(current, target, animAmount); + int tint = ColorUtils.blendARGB(initialScrimTint, finalScrimTint, animAmount); + updateScrimColor(scrim, alpha, tint); dispatchScrimsVisible(); }); anim.setInterpolator(getInterpolator()); anim.setStartDelay(mAnimationDelay); - anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); + anim.setDuration(mAnimationDuration); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - if (!mDeferFinishedListener && mOnAnimationFinished != null) { - mOnAnimationFinished.run(); - mOnAnimationFinished = null; - } if (mKeyguardFadingOutInProgress) { mKeyguardFadeoutAnimation = null; mKeyguardFadingOutInProgress = false; - mAnimatingDozeUnlock = false; - } - if (mWakingUpFromAodAnimationRunning && !mDeferFinishedListener) { - mWakingUpFromAodAnimationRunning = false; - mWakingUpFromAodInProgress = false; } + onFinished(); + scrim.setTag(TAG_KEY_ANIM, null); scrim.setTag(TAG_KEY_ANIM_TARGET, null); dispatchScrimsVisible(); + + if (!mDeferFinishedListener && mOnAnimationFinished != null) { + mOnAnimationFinished.run(); + mOnAnimationFinished = null; + } } }); anim.start(); @@ -558,12 +498,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mKeyguardFadingOutInProgress = true; mKeyguardFadeoutAnimation = anim; } - if (mWakingUpFromAodInProgress) { - mWakingUpFromAodAnimationRunning = true; - } - if (mSkipFirstFrame) { - anim.setCurrentPlayTime(16); - } scrim.setTag(TAG_KEY_ANIM, anim); scrim.setTag(TAG_KEY_ANIM_TARGET, target); } @@ -582,19 +516,33 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public boolean onPreDraw() { mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); mUpdatePending = false; - if (mDontAnimateBouncerChanges) { - mDontAnimateBouncerChanges = false; + if (mCallback != null) { + mCallback.onStart(); } updateScrims(); - mDurationOverride = -1; - mAnimationDelay = 0; - mSkipFirstFrame = false; // Make sure that we always call the listener even if we didn't start an animation. endAnimateKeyguardFadingOut(false /* force */); return true; } + private void onFinished() { + if (mWakeLockHeld) { + mWakeLock.release(); + mWakeLockHeld = false; + } + if (mCallback != null) { + mCallback.onFinished(); + mCallback = null; + } + // When unlocking with fingerprint, we'll fade the scrims from black to transparent. + // At the end of the animation we need to remove the tint. + if (mState == ScrimState.UNLOCKED) { + mCurrentInFrontTint = Color.TRANSPARENT; + mCurrentBehindTint = Color.TRANSPARENT; + } + } + private void endAnimateKeyguardFadingOut(boolean force) { mAnimateKeyguardFadingOut = false; if (force || (!isAnimating(mScrimInFront) && !isAnimating(mScrimBehind))) { @@ -603,8 +551,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mOnAnimationFinished = null; } mKeyguardFadingOutInProgress = false; - if (!mWakeAndUnlocking || force) - mAnimatingDozeUnlock = false; } } @@ -641,16 +587,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } private void updateHeadsUpScrim(boolean animate) { - updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha(), mCurrentHeadsUpAlpha); + updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha()); } - private void updateScrim(boolean animate, View scrim, float alpha, float currentAlpha) { - if (mKeyguardFadingOutInProgress && mKeyguardFadeoutAnimation.getCurrentPlayTime() != 0) { - return; - } + @VisibleForTesting + void setOnAnimationFinished(Runnable onAnimationFinished) { + mOnAnimationFinished = onAnimationFinished; + } + + private void updateScrim(boolean animate, View scrim, float alpha) { + final float currentAlpha = scrim instanceof ScrimView ? ((ScrimView) scrim).getViewAlpha() + : scrim.getAlpha(); - ValueAnimator previousAnimator = ViewState.getChildTag(scrim, - TAG_KEY_ANIM); + ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM); float animEndValue = -1; if (previousAnimator != null) { if (animate || alpha == currentAlpha) { @@ -664,9 +613,32 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA); } } - if (alpha != currentAlpha && alpha != animEndValue) { + + final boolean blankingInProgress = mScrimInFront.getTag(TAG_KEY_ANIM_BLANK) != null; + if (mBlankScreen || blankingInProgress) { + if (!blankingInProgress) { + blankDisplay(); + } + return; + } else if (!mScreenBlankingCallbackCalled) { + // Not blanking the screen. Letting the callback know that we're ready + // to replace what was on the screen before. + if (mCallback != null) { + mCallback.onDisplayBlanked(); + mScreenBlankingCallbackCalled = true; + } + } + + final ScrimView scrimView = scrim instanceof ScrimView ? (ScrimView) scrim : null; + final boolean wantsAlphaUpdate = alpha != currentAlpha && alpha != animEndValue; + final boolean wantsTintUpdate = scrimView != null + && scrimView.getTint() != getCurrentScrimTint(scrimView); + + if (wantsAlphaUpdate || wantsTintUpdate) { if (animate) { - startScrimAnimation(scrim, alpha); + final float fromAlpha = scrimView == null ? scrim.getAlpha() + : scrimView.getViewAlpha(); + startScrimAnimation(scrim, fromAlpha, alpha); scrim.setTag(TAG_START_ALPHA, currentAlpha); scrim.setTag(TAG_END_ALPHA, alpha); } else { @@ -685,13 +657,62 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); } else { // update the alpha directly - setCurrentScrimAlpha(scrim, alpha); - updateScrimColor(scrim); + updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); + onFinished(); } } + } else { + onFinished(); } } + private void blankDisplay() { + final float initialAlpha = mScrimInFront.getViewAlpha(); + final int initialTint = mScrimInFront.getTint(); + ValueAnimator anim = ValueAnimator.ofFloat(0, 1); + anim.addUpdateListener(animation -> { + final float amount = (float) animation.getAnimatedValue(); + float animAlpha = MathUtils.lerp(initialAlpha, 1, amount); + int animTint = ColorUtils.blendARGB(initialTint, Color.BLACK, amount); + updateScrimColor(mScrimInFront, animAlpha, animTint); + dispatchScrimsVisible(); + }); + anim.setInterpolator(getInterpolator()); + anim.setDuration(mDozeParameters.getPulseInDuration()); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mCallback != null) { + mCallback.onDisplayBlanked(); + mScreenBlankingCallbackCalled = true; + } + Runnable blankingCallback = () -> { + mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, null); + mBlankScreen = false; + // Try again. + updateScrims(); + }; + + // Setting power states can happen after we push out the frame. Make sure we + // stay fully opaque until the power state request reaches the lower levels. + getHandler().postDelayed(blankingCallback, 100); + + } + }); + anim.start(); + mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, anim); + + // Finish animation if we're already at its final state + if (initialAlpha == 1 && mScrimInFront.getTint() == Color.BLACK) { + anim.end(); + } + } + + @VisibleForTesting + protected Handler getHandler() { + return Handler.getMain(); + } + /** * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means * the heads up is in its resting space and 1 means it's fully dragged out. @@ -719,23 +740,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, return alpha * expandFactor; } - public void forceHideScrims(boolean hide, boolean animated) { - mForceHideScrims = hide; - mAnimateChange = animated; - scheduleUpdate(); - } - - public void dontAnimateBouncerChangesUntilNextFrame() { - mDontAnimateBouncerChanges = true; - } - public void setExcludedBackgroundArea(Rect area) { mScrimBehind.setExcludedArea(area); } public int getBackgroundColor() { int color = mLockColors.getMainColor(); - return Color.argb((int) (mScrimBehind.getAlpha() * Color.alpha(color)), + return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)), Color.red(color), Color.green(color), Color.blue(color)); } @@ -764,27 +775,41 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, - ColorExtractor.TYPE_DARK, mKeyguardShowing); + ColorExtractor.TYPE_DARK, mState != ScrimState.UNLOCKED); mNeedsDrawableColorUpdate = true; scheduleUpdate(); } } - public void dump(PrintWriter pw) { - pw.println(" ScrimController:"); + @VisibleForTesting + protected WakeLock createWakeLock() { + return new DelayedWakeLock(getHandler(), + WakeLock.createPartial(mContext, "Doze")); + } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(" ScrimController:"); + pw.print(" state:"); pw.println(mState); pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha()); pw.print(" alpha="); pw.print(mCurrentInFrontAlpha); - pw.print(" dozeAlpha="); pw.print(mDozeInFrontAlpha); pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint())); pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha()); pw.print(" alpha="); pw.print(mCurrentBehindAlpha); - pw.print(" dozeAlpha="); pw.print(mDozeBehindAlpha); pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint())); - pw.print(" mBouncerShowing="); pw.println(mBouncerShowing); pw.print(" mTracking="); pw.println(mTracking); - pw.print(" mForceHideScrims="); pw.println(mForceHideScrims); + } + + public interface Callback { + default void onStart() { + } + default void onDisplayBlanked() { + } + default void onFinished() { + } + default void onCancelled() { + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java new file mode 100644 index 000000000000..0db98f372561 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2017 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.statusbar.phone; + +import android.graphics.Color; +import android.os.Trace; + +import com.android.systemui.statusbar.ScrimView; + +/** + * Possible states of the ScrimController state machine. + */ +public enum ScrimState { + + /** + * Initial state. + */ + UNINITIALIZED, + + /** + * On the lock screen. + */ + KEYGUARD { + + @Override + public void prepare(ScrimState previousState) { + // DisplayPowerManager will blank the screen, we'll just + // set our scrim to black in this frame to avoid flickering and + // fade it out afterwards. + mBlankScreen = previousState == ScrimState.AOD; + if (previousState == ScrimState.AOD) { + updateScrimColor(mScrimInFront, 1, Color.BLACK); + } + mCurrentBehindAlpha = mScrimBehindAlphaKeyguard; + mCurrentInFrontAlpha = 0; + } + }, + + /** + * Showing password challenge. + */ + BOUNCER { + @Override + public void prepare(ScrimState previousState) { + mCurrentBehindAlpha = ScrimController.SCRIM_BEHIND_ALPHA_UNLOCKING; + mCurrentInFrontAlpha = ScrimController.SCRIM_IN_FRONT_ALPHA_LOCKED; + } + }, + + /** + * Changing screen brightness from quick settings. + */ + BRIGHTNESS_MIRROR { + @Override + public void prepare(ScrimState previousState) { + mCurrentBehindAlpha = 0; + mCurrentInFrontAlpha = 0; + } + }, + + /** + * Always on display or screen off. + */ + AOD { + @Override + public void prepare(ScrimState previousState) { + if (previousState == ScrimState.PULSING) { + updateScrimColor(mScrimInFront, 1, Color.BLACK); + } + final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn(); + mBlankScreen = previousState == ScrimState.PULSING; + mCurrentBehindAlpha = 1; + mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f; + mCurrentInFrontTint = Color.BLACK; + mCurrentBehindTint = Color.BLACK; + // DisplayPowerManager will blank the screen for us, we just need + // to set our state. + mAnimateChange = false; + } + }, + + /** + * When phone wakes up because you received a notification. + */ + PULSING { + @Override + public void prepare(ScrimState previousState) { + mCurrentBehindAlpha = 1; + mCurrentInFrontAlpha = 0; + mCurrentInFrontTint = Color.BLACK; + mCurrentBehindTint = Color.BLACK; + mBlankScreen = true; + updateScrimColor(mScrimInFront, 1, Color.BLACK); + } + }, + + /** + * Unlocked on top of an app (launcher or any other activity.) + */ + UNLOCKED { + @Override + public void prepare(ScrimState previousState) { + mCurrentBehindAlpha = 0; + mCurrentInFrontAlpha = 0; + mAnimationDuration = StatusBar.FADE_KEYGUARD_DURATION; + + if (previousState == ScrimState.AOD) { + // Fade from black to transparent when coming directly from AOD + updateScrimColor(mScrimInFront, 1, Color.BLACK); + updateScrimColor(mScrimBehind, 1, Color.BLACK); + // Scrims should still be black at the end of the transition. + mCurrentInFrontTint = Color.BLACK; + mCurrentBehindTint = Color.BLACK; + mBlankScreen = true; + } else { + // Scrims should still be black at the end of the transition. + mCurrentInFrontTint = Color.TRANSPARENT; + mCurrentBehindTint = Color.TRANSPARENT; + mBlankScreen = false; + } + } + }; + + boolean mBlankScreen = false; + long mAnimationDuration = ScrimController.ANIMATION_DURATION; + int mCurrentInFrontTint = Color.TRANSPARENT; + int mCurrentBehindTint = Color.TRANSPARENT; + boolean mAnimateChange = true; + float mCurrentInFrontAlpha; + float mCurrentBehindAlpha; + float mAodFrontScrimAlpha; + float mScrimBehindAlphaKeyguard; + ScrimView mScrimInFront; + ScrimView mScrimBehind; + DozeParameters mDozeParameters; + + public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters) { + mScrimInFront = scrimInFront; + mScrimBehind = scrimBehind; + mDozeParameters = dozeParameters; + } + + public void prepare(ScrimState previousState) { + } + + public float getFrontAlpha() { + return mCurrentInFrontAlpha; + } + + public float getBehindAlpha() { + return mCurrentBehindAlpha; + } + + public int getFrontTint() { + return mCurrentInFrontTint; + } + + public int getBehindTint() { + return mCurrentBehindTint; + } + + public long getAnimationDuration() { + return mAnimationDuration; + } + + public boolean getBlanksScreen() { + return mBlankScreen; + } + + public void updateScrimColor(ScrimView scrim, float alpha, int tint) { + Trace.traceCounter(Trace.TRACE_TAG_APP, + scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha", + (int) (alpha * 255)); + + Trace.traceCounter(Trace.TRACE_TAG_APP, + scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint", + Color.alpha(tint)); + + scrim.setTint(tint); + scrim.setViewAlpha(alpha); + } + + public boolean getAnimateChange() { + return mAnimateChange; + } + + public void setAodFrontScrimAlpha(float aodFrontScrimAlpha) { + mAodFrontScrimAlpha = aodFrontScrimAlpha; + } + + public void setScrimBehindAlphaKeyguard(float scrimBehindAlphaKeyguard) { + mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard; + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 43cc0f771a97..a5609da78abb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -238,6 +238,7 @@ import com.android.systemui.statusbar.stack.NotificationStackScrollLayout .OnChildLocationsChangedListener; import com.android.systemui.util.NotificationChannels; import com.android.systemui.util.leak.LeakDetector; +import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.volume.VolumeComponent; import java.io.FileDescriptor; @@ -386,6 +387,7 @@ public class StatusBar extends SystemUI implements DemoMode, private VolumeComponent mVolumeComponent; private BrightnessMirrorController mBrightnessMirrorController; + private boolean mBrightnessMirrorVisible; protected FingerprintUnlockController mFingerprintUnlockController; private LightBarController mLightBarController; protected LockscreenWallpaper mLockscreenWallpaper; @@ -646,6 +648,31 @@ public class StatusBar extends SystemUI implements DemoMode, } }; + // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over, + // this animation is tied to the scrim for historic reasons. + // TODO: notify when keyguard has faded away instead of the scrim. + private final ScrimController.Callback mUnlockScrimCallback = new ScrimController + .Callback() { + @Override + public void onFinished() { + notifyKeyguardState(); + } + + @Override + public void onCancelled() { + notifyKeyguardState(); + } + + private void notifyKeyguardState() { + if (mStatusBarKeyguardViewManager == null) { + Log.w(TAG, "Tried to notify keyguard visibility when " + + "mStatusBarKeyguardViewManager was null"); + return; + } + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + } + }; + private NotificationMessagingUtil mMessagingUtil; private KeyguardUserSwitcher mKeyguardUserSwitcher; private UserSwitcherController mUserSwitcherController; @@ -1045,7 +1072,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarWindowManager != null) { mStatusBarWindowManager.setScrimsVisible(scrimsVisible); } - }); + }, new DozeParameters(mContext)); if (mScrimSrcModeEnabled) { Runnable runnable = () -> { boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE; @@ -1081,7 +1108,10 @@ public class StatusBar extends SystemUI implements DemoMode, final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this, mIconController); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow, - mScrimController); + (visible) -> { + mBrightnessMirrorVisible = visible; + updateScrimController(); + }); fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> { QS qs = (QS) f; if (qs instanceof QSFragment) { @@ -1457,8 +1487,7 @@ public class StatusBar extends SystemUI implements DemoMode, mDozeScrimController, keyguardViewMediator, mScrimController, this, UnlockMethodCache.getInstance(mContext)); mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, - getBouncerContainer(), mScrimController, - mFingerprintUnlockController); + getBouncerContainer(), mFingerprintUnlockController); mKeyguardIndicationController .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); @@ -2640,7 +2669,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean isPulsing() { - return mDozeScrimController.isPulsing(); + return mDozeScrimController != null && mDozeScrimController.isPulsing(); } @Override @@ -3348,7 +3377,7 @@ public class StatusBar extends SystemUI implements DemoMode, } if (mScrimController != null) { - mScrimController.dump(pw); + mScrimController.dump(fd, pw, args); } if (DUMPTRUCK) { @@ -4081,7 +4110,6 @@ public class StatusBar extends SystemUI implements DemoMode, releaseGestureWakeLock(); runLaunchTransitionEndRunnable(); mLaunchTransitionFadingAway = false; - mScrimController.forceHideScrims(false /* hide */, false /* animated */); updateMediaMetaData(true /* metaDataChanged */, true); } @@ -4114,7 +4142,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (beforeFading != null) { beforeFading.run(); } - mScrimController.forceHideScrims(true /* hide */, false /* animated */); + updateScrimController(); updateMediaMetaData(false, true); mNotificationPanel.setAlpha(1); mStackScroller.setParentNotFullyVisible(true); @@ -4145,6 +4173,13 @@ public class StatusBar extends SystemUI implements DemoMode, .setStartDelay(0) .setDuration(FADE_KEYGUARD_DURATION_PULSING) .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + hideKeyguard(); + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + } + }) .start(); } @@ -4152,7 +4187,6 @@ public class StatusBar extends SystemUI implements DemoMode, * Plays the animation when an activity that was occluding Keyguard goes away. */ public void animateKeyguardUnoccluding() { - mScrimController.animateKeyguardUnoccluding(500); mNotificationPanel.setExpandedFraction(0f); animateExpandNotificationsPanel(); } @@ -4340,11 +4374,6 @@ public class StatusBar extends SystemUI implements DemoMode, mAmbientIndicationContainer.setVisibility(View.INVISIBLE); } } - if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { - mScrimController.setKeyguardShowing(true); - } else { - mScrimController.setKeyguardShowing(false); - } mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade); updateTheme(); updateDozingState(); @@ -4352,6 +4381,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateStackScrollerState(goingToFullShade, fromShadeLocked); updateNotifications(); checkBarModes(); + updateScrimController(); updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), mUnlockMethodCache.isMethodSecure(), @@ -4415,11 +4445,10 @@ public class StatusBar extends SystemUI implements DemoMode, boolean animate = !mDozing && mDozeServiceHost.shouldAnimateWakeup(); mNotificationPanel.setDozing(mDozing, animate); mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation); - mScrimController.setDozing(mDozing); + mDozeScrimController.setDozing(mDozing); mKeyguardIndicationController.setDozing(mDozing); mNotificationPanel.setDark(mDozing, animate); updateQsExpansionEnabled(); - mDozeScrimController.setDozing(mDozing, animate); updateRowStates(); Trace.endSection(); } @@ -4914,6 +4943,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); updateHideIconsForBouncer(true /* animate */); recomputeDisableFlags(true /* animate */); + updateScrimController(); } public void cancelCurrentTouch() { @@ -4965,12 +4995,10 @@ public class StatusBar extends SystemUI implements DemoMode, mStackScroller.setAnimationsEnabled(true); mVisualStabilityManager.setScreenOn(true); mNotificationPanel.setTouchDisabled(false); - - maybePrepareWakeUpFromAod(); - mDozeServiceHost.stopDozing(); updateVisibleToUser(); updateIsKeyguard(); + updateScrimController(); } }; @@ -4980,18 +5008,16 @@ public class StatusBar extends SystemUI implements DemoMode, mFalsingManager.onScreenTurningOn(); mNotificationPanel.onScreenTurningOn(); - maybePrepareWakeUpFromAod(); - if (mLaunchCameraOnScreenTurningOn) { mNotificationPanel.launchCamera(false, mLastCameraLaunchSource); mLaunchCameraOnScreenTurningOn = false; } + + updateScrimController(); } @Override public void onScreenTurnedOn() { - mScrimController.wakeUpFromAod(); - mDozeScrimController.onScreenTurnedOn(); } @Override @@ -5009,13 +5035,6 @@ public class StatusBar extends SystemUI implements DemoMode, return mWakefulnessLifecycle.getWakefulness(); } - private void maybePrepareWakeUpFromAod() { - int wakefulness = mWakefulnessLifecycle.getWakefulness(); - if (mDozing && wakefulness == WAKEFULNESS_WAKING && !isPulsing()) { - mScrimController.prepareWakeUpFromAod(); - } - } - private void vibrateForCameraGesture() { // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep. mVibrator.vibrate(mCameraLaunchGestureVibePattern, -1 /* repeat */); @@ -5098,12 +5117,12 @@ public class StatusBar extends SystemUI implements DemoMode, if (!mDeviceInteractive) { // Avoid flickering of the scrim when we instant launch the camera and the bouncer // comes on. - mScrimController.dontAnimateBouncerChangesUntilNextFrame(); mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L); } if (isScreenTurningOnOrOn()) { if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera"); mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source); + updateScrimController(); } else { // We need to defer the camera launch until the screen comes on, since otherwise // we will dismiss us too early since we are waiting on an activity to be drawn and @@ -5145,15 +5164,16 @@ public class StatusBar extends SystemUI implements DemoMode, private void updateDozing() { Trace.beginSection("StatusBar#updateDozing"); // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. - mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD + boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD || mFingerprintUnlockController.getMode() == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; // When in wake-and-unlock we may not have received a change to mState // but we still should not be dozing, manually set to false. if (mFingerprintUnlockController.getMode() == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) { - mDozing = false; + dozing = false; } + mDozing = dozing; mStatusBarWindowManager.setDozing(mDozing); mStatusBarKeyguardViewManager.setDozing(mDozing); if (mAmbientIndicationContainer instanceof DozeReceiver) { @@ -5163,6 +5183,24 @@ public class StatusBar extends SystemUI implements DemoMode, Trace.endSection(); } + public void updateScrimController() { + if (mBouncerShowing) { + mScrimController.transitionTo(ScrimState.BOUNCER); + } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) { + mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); + } else if (mBrightnessMirrorVisible) { + mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); + } else if (isPulsing()) { + // Handled in DozeScrimController#setPulsing + } else if (mDozing) { + mScrimController.transitionTo(ScrimState.AOD); + } else if (mIsKeyguard) { + mScrimController.transitionTo(ScrimState.KEYGUARD); + } else { + mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); + } + } + public boolean isKeyguardShowing() { if (mStatusBarKeyguardViewManager == null) { Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true"); @@ -5222,7 +5260,6 @@ public class StatusBar extends SystemUI implements DemoMode, } mDozeScrimController.pulse(new PulseCallback() { - @Override public void onPulseStarted() { callback.onPulseStarted(); @@ -5306,11 +5343,6 @@ public class StatusBar extends SystemUI implements DemoMode, StatusBar.this.startPendingIntentDismissingKeyguard(intent); } - @Override - public void abortPulsing() { - mDozeScrimController.abortPulsing(); - } - @Override public void extendPulse() { mDozeScrimController.extendPulse(); @@ -5347,7 +5379,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void setAodDimmingScrim(float scrimOpacity) { - mDozeScrimController.setAodDimmingScrim(scrimOpacity); + ScrimState.AOD.setAodFrontScrimAlpha(scrimOpacity); } public void dispatchDoubleTap(float viewX, float viewY) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index bcde556a5954..ef05bbb4fe3d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -24,7 +24,6 @@ import android.content.ComponentCallbacks2; import android.content.Context; import android.os.Bundle; import android.os.SystemClock; -import android.os.Trace; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -71,17 +70,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected final Context mContext; private final StatusBarWindowManager mStatusBarWindowManager; - private final boolean mDisplayBlanksAfterDoze; protected LockPatternUtils mLockPatternUtils; protected ViewMediatorCallback mViewMediatorCallback; protected StatusBar mStatusBar; - private ScrimController mScrimController; private FingerprintUnlockController mFingerprintUnlockController; private ViewGroup mContainer; - private boolean mScreenTurnedOn; protected KeyguardBouncer mBouncer; protected boolean mShowing; protected boolean mOccluded; @@ -95,12 +91,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastBouncerDismissible; protected boolean mLastRemoteInputActive; private boolean mLastDozing; - private boolean mLastDeferScrimFadeOut; private int mLastFpMode; private OnDismissAction mAfterKeyguardGoneAction; private final ArrayList mAfterKeyguardGoneRunnables = new ArrayList<>(); - private boolean mDeferScrimFadeOut; // Dismiss action to be launched when we stop dozing or the keyguard is gone. private DismissWithActionRequest mPendingWakeupAction; @@ -125,18 +119,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mLockPatternUtils = lockPatternUtils; mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class); KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback); - mDisplayBlanksAfterDoze = context.getResources().getBoolean( - com.android.internal.R.bool.config_displayBlanksAfterDoze); } public void registerStatusBar(StatusBar statusBar, ViewGroup container, - ScrimController scrimController, FingerprintUnlockController fingerprintUnlockController, DismissCallbackRegistry dismissCallbackRegistry) { mStatusBar = statusBar; mContainer = container; - mScrimController = scrimController; mFingerprintUnlockController = fingerprintUnlockController; mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry); @@ -149,7 +139,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void show(Bundle options) { mShowing = true; mStatusBarWindowManager.setKeyguardShowing(true); - mScrimController.abortKeyguardFadingOut(); reset(true /* hideBouncerWhenShowing */); } @@ -253,15 +242,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public void onScreenTurnedOn() { - Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurnedOn"); - mScreenTurnedOn = true; - if (mDeferScrimFadeOut) { - mDeferScrimFadeOut = false; - animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS, - true /* skipFirstFrame */); - updateStates(); - } - Trace.endSection(); + // TODO: remove } @Override @@ -285,7 +266,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public void onScreenTurnedOff() { - mScreenTurnedOn = false; + // TODO: remove } public void notifyDeviceWakeUpRequested() { @@ -374,10 +355,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBarWindowManager.setKeyguardFadingAway(true); hideBouncer(true /* destroyView */); updateStates(); - mScrimController.animateKeyguardFadingOut( - StatusBar.FADE_KEYGUARD_START_DELAY, - StatusBar.FADE_KEYGUARD_DURATION, null, - false /* skipFirstFrame */); } }, new Runnable() { @Override @@ -400,36 +377,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mFingerprintUnlockController.startKeyguardFadingAway(); hideBouncer(true /* destroyView */); if (wakeUnlockPulsing) { - mStatusBarWindowManager.setKeyguardFadingAway(true); mStatusBar.fadeKeyguardWhilePulsing(); - animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration, - mStatusBar::hideKeyguard, false /* skipFirstFrame */); + wakeAndUnlockDejank(); } else { mFingerprintUnlockController.startKeyguardFadingAway(); mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration); boolean staying = mStatusBar.hideKeyguard(); if (!staying) { mStatusBarWindowManager.setKeyguardFadingAway(true); - if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK) { - boolean turnedOnSinceAuth = - mFingerprintUnlockController.hasScreenTurnedOnSinceAuthenticating(); - if (!mScreenTurnedOn || mDisplayBlanksAfterDoze && !turnedOnSinceAuth) { - // Not ready to animate yet; either because the screen is not on yet, - // or it is on but will turn off before waking out of doze. - mDeferScrimFadeOut = true; - } else { - - // Screen is already on, don't defer with fading out. - animateScrimControllerKeyguardFadingOut(0, - WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS, - true /* skipFirstFrame */); - } - } else { - animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration, - false /* skipFirstFrame */); - } + wakeAndUnlockDejank(); } else { - mScrimController.animateGoingToFullShade(delay, fadeoutDuration); mStatusBar.finishKeyguardFadingAway(); mFingerprintUnlockController.finishKeyguardFadingAway(); } @@ -449,30 +406,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer.prepare(); } - private void animateScrimControllerKeyguardFadingOut(long delay, long duration, - boolean skipFirstFrame) { - animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */, - skipFirstFrame); + public void onKeyguardFadedAway() { + mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false), + 100); + mStatusBar.finishKeyguardFadingAway(); + mFingerprintUnlockController.finishKeyguardFadingAway(); + WindowManagerGlobal.getInstance().trimMemory( + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); + } - private void animateScrimControllerKeyguardFadingOut(long delay, long duration, - final Runnable endRunnable, boolean skipFirstFrame) { - Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0); - mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() { - @Override - public void run() { - if (endRunnable != null) { - endRunnable.run(); - } - mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false), - 100); - mStatusBar.finishKeyguardFadingAway(); - mFingerprintUnlockController.finishKeyguardFadingAway(); - WindowManagerGlobal.getInstance().trimMemory( - ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); - Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0); - } - }, skipFirstFrame); + private void wakeAndUnlockDejank() { if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK && LatencyTracker.isEnabled(mContext)) { DejankUtils.postAfterTraversal(() -> @@ -593,7 +537,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { mStatusBarWindowManager.setBouncerShowing(bouncerShowing); mStatusBar.setBouncerShowing(bouncerShowing); - mScrimController.setBouncerShowing(bouncerShowing); } KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); @@ -611,7 +554,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mLastBouncerDismissible = bouncerDismissible; mLastRemoteInputActive = remoteInputActive; mLastDozing = mDozing; - mLastDeferScrimFadeOut = mDeferScrimFadeOut; mLastFpMode = mFingerprintUnlockController.getMode(); mStatusBar.onKeyguardViewManagerStatesUpdated(); } @@ -624,7 +566,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean keyguardShowing = mShowing && !mOccluded; boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING; return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing() - || mRemoteInputActive) && !mDeferScrimFadeOut; + || mRemoteInputActive); } /** @@ -634,7 +576,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean keyguardShowing = mLastShowing && !mLastOccluded; boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING; return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing - || mLastRemoteInputActive) && !mLastDeferScrimFadeOut; + || mLastRemoteInputActive); } public boolean shouldDismissOnMenuPressed() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index b0553d72dd49..a011952f1476 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy; +import android.annotation.NonNull; import android.util.ArraySet; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -26,46 +27,47 @@ import android.widget.FrameLayout; import com.android.internal.util.Preconditions; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarWindowView; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; +import java.util.function.Consumer; + /** * Controls showing and hiding of the brightness mirror. */ public class BrightnessMirrorController implements CallbackController { - private final NotificationStackScrollLayout mStackScroller; - public long TRANSITION_DURATION_OUT = 150; - public long TRANSITION_DURATION_IN = 200; + private final static long TRANSITION_DURATION_OUT = 150; + private final static long TRANSITION_DURATION_IN = 200; private final StatusBarWindowView mStatusBarWindow; - private final ScrimController mScrimController; + private final NotificationStackScrollLayout mStackScroller; + private final Consumer mVisibilityCallback; private final View mNotificationPanel; private final ArraySet mBrightnessMirrorListeners = new ArraySet<>(); private final int[] mInt2Cache = new int[2]; private View mBrightnessMirror; public BrightnessMirrorController(StatusBarWindowView statusBarWindow, - ScrimController scrimController) { + @NonNull Consumer visibilityCallback) { mStatusBarWindow = statusBarWindow; mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror); mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel); mStackScroller = statusBarWindow.findViewById(R.id.notification_stack_scroller); - mScrimController = scrimController; + mVisibilityCallback = visibilityCallback; } public void showMirror() { mBrightnessMirror.setVisibility(View.VISIBLE); mStackScroller.setFadingOut(true); - mScrimController.forceHideScrims(true /* hide */, true /* animated */); + mVisibilityCallback.accept(true); outAnimation(mNotificationPanel.animate()) .withLayer(); } public void hideMirror() { - mScrimController.forceHideScrims(false /* hide */, true /* animated */); + mVisibilityCallback.accept(false); inAnimation(mNotificationPanel.animate()) .withLayer() .withEndAction(() -> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java index b0c9f32873ff..1c104cffc3c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java @@ -19,12 +19,13 @@ package com.android.systemui.doze; import android.annotation.NonNull; import android.app.PendingIntent; +import com.android.systemui.util.wakelock.WakeLock; + /** * A rudimentary fake for DozeHost. */ class DozeHostFake implements DozeHost { Callback callback; - boolean pulseAborted; boolean pulseExtended; boolean animateWakeup; boolean dozing; @@ -91,11 +92,6 @@ class DozeHostFake implements DozeHost { public void onIgnoreTouchWhilePulsing(boolean ignore) { } - @Override - public void abortPulsing() { - pulseAborted = true; - } - @Override public void extendPulse() { pulseExtended = true; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java index 4c3bf1081792..a4bcc69c0e10 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java @@ -86,9 +86,9 @@ public class ScrimViewTest extends LeakCheckedTest { @Test public void testSetViewAlpha_propagatesToDrawable() { - float alpha = 0.5f; + final float alpha = 0.5f; mView.setViewAlpha(alpha); - assertEquals(mView.getViewAlpha(), alpha); + assertEquals("View alpha did not propagate to drawable", alpha, mView.getViewAlpha()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java new file mode 100644 index 000000000000..ca2f713d2b6f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 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.statusbar.phone; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.os.Debug; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.doze.DozeHost; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class DozeScrimControllerTest extends SysuiTestCase { + + private ScrimController mScrimController; + private DozeScrimController mDozeScrimController; + + @Before + public void setup() { + mScrimController = mock(ScrimController.class); + // Make sure callbacks will be invoked to complete the lifecycle. + doAnswer(invocationOnMock -> { + ScrimController.Callback callback = invocationOnMock.getArgument(1); + callback.onStart(); + callback.onDisplayBlanked(); + callback.onFinished(); + return null; + }).when(mScrimController).transitionTo(any(ScrimState.class), + any(ScrimController.Callback.class)); + + mDozeScrimController = new DozeScrimController(mScrimController, getContext()); + mDozeScrimController.setDozing(true); + } + + @Test + public void changesScrimControllerState() { + mDozeScrimController.pulse(mock(DozeHost.PulseCallback.class), 0); + verify(mScrimController).transitionTo(eq(ScrimState.PULSING), + any(ScrimController.Callback.class)); + } + + @Test + public void callsPulseCallback() { + DozeHost.PulseCallback callback = mock(DozeHost.PulseCallback.class); + mDozeScrimController.pulse(callback, 0); + + verify(callback).onPulseStarted(); + mDozeScrimController.pulseOutNow(); + verify(callback).onPulseFinished(); + } + + @Test + public void secondPulseIsSuppressed() { + DozeHost.PulseCallback callback1 = mock(DozeHost.PulseCallback.class); + DozeHost.PulseCallback callback2 = mock(DozeHost.PulseCallback.class); + mDozeScrimController.pulse(callback1, 0); + mDozeScrimController.pulse(callback2, 0); + + verify(callback1, never()).onPulseFinished(); + verify(callback2).onPulseFinished(); + } + + @Test + public void suppressesPulseIfNotDozing() { + mDozeScrimController.setDozing(false); + DozeHost.PulseCallback callback = mock(DozeHost.PulseCallback.class); + mDozeScrimController.pulse(callback, 0); + + verify(callback).onPulseFinished(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java new file mode 100644 index 000000000000..b9f695be90cf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2017 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.statusbar.phone; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.animation.Animator; +import android.graphics.Color; +import android.os.Handler; +import android.os.Looper; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.util.wakelock.WakeLock; +import com.android.systemui.utils.os.FakeHandler; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class ScrimControllerTest extends SysuiTestCase { + + private SynchronousScrimController mScrimController; + private ScrimView mScrimBehind; + private ScrimView mScrimInFront; + private View mHeadsUpScrim; + private Consumer mScrimVisibilityCallback; + private Boolean mScrimVisibile; + private LightBarController mLightBarController; + private DozeParameters mDozeParamenters; + private WakeLock mWakeLock; + private boolean mAlwaysOnEnabled; + + @Before + public void setup() { + mLightBarController = mock(LightBarController.class); + mScrimBehind = new ScrimView(getContext()); + mScrimInFront = new ScrimView(getContext()); + mHeadsUpScrim = mock(View.class); + mWakeLock = mock(WakeLock.class); + mAlwaysOnEnabled = true; + mScrimVisibilityCallback = (Boolean visible) -> mScrimVisibile = visible; + mDozeParamenters = mock(DozeParameters.class); + when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); + mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind, + mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters); + } + + @Test + public void initialState() { + Assert.assertEquals("ScrimController should start initialized", + mScrimController.getState(), ScrimState.UNINITIALIZED); + } + + @Test + public void transitionToKeyguard() { + mScrimController.transitionTo(ScrimState.KEYGUARD); + mScrimController.finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be visible without tint + assertScrimVisibility(false /* front */, true /* behind */); + assertScrimTint(mScrimBehind, false /* tinted */); + } + + @Test + public void transitionToAod() { + mScrimController.transitionTo(ScrimState.AOD); + mScrimController.finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be visible with tint + assertScrimVisibility(false /* front */, true /* behind */); + assertScrimTint(mScrimBehind, true /* tinted */); + assertScrimTint(mScrimInFront, true /* tinted */); + } + + @Test + public void transitionToPulsing() { + mScrimController.transitionTo(ScrimState.PULSING); + mScrimController.finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be visible with tint + // Pulse callback should have been invoked + assertScrimVisibility(false /* front */, true /* behind */); + assertScrimTint(mScrimBehind, true /* tinted */); + } + + @Test + public void transitionToBouncer() { + mScrimController.transitionTo(ScrimState.BOUNCER); + mScrimController.finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be visible without tint + assertScrimVisibility(true /* front */, true /* behind */); + assertScrimTint(mScrimBehind, false /* tinted */); + } + + @Test + public void transitionToUnlocked() { + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be transparent + assertScrimVisibility(false /* front */, false /* behind */); + assertScrimTint(mScrimBehind, false /* tinted */); + assertScrimTint(mScrimInFront, false /* tinted */); + + // Back scrim should be visible after start dragging + mScrimController.setPanelExpansion(0.5f); + assertScrimVisibility(false /* front */, true /* behind */); + } + + @Test + public void transitionToUnlockedFromAod() { + // Simulate unlock with fingerprint + mScrimController.transitionTo(ScrimState.AOD); + mScrimController.finishAnimationsImmediately(); + mScrimController.transitionTo(ScrimState.UNLOCKED); + // Immediately tinted after the transition starts + assertScrimTint(mScrimInFront, true /* tinted */); + assertScrimTint(mScrimBehind, true /* tinted */); + mScrimController.finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be transparent + // Neither scrims should be tinted anymore after the animation. + assertScrimVisibility(false /* front */, false /* behind */); + assertScrimTint(mScrimInFront, false /* tinted */); + assertScrimTint(mScrimBehind, false /* tinted */); + } + + @Test + public void scrimBlanksBeforeLeavingAoD() { + // Simulate unlock with fingerprint + mScrimController.transitionTo(ScrimState.AOD); + mScrimController.finishAnimationsImmediately(); + mScrimController.transitionTo(ScrimState.UNLOCKED, + new ScrimController.Callback() { + @Override + public void onDisplayBlanked() { + // Front scrim should be black in the middle of the transition + Assert.assertTrue("Scrim should be visible during transition. Alpha: " + + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0); + assertScrimTint(mScrimInFront, true /* tinted */); + Assert.assertTrue("Scrim should be visible during transition.", + mScrimVisibile); + } + }); + mScrimController.finishAnimationsImmediately(); + } + + @Test + public void testScrimCallback() { + int[] callOrder = {0, 0, 0}; + int[] currentCall = {0}; + mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() { + @Override + public void onStart() { + callOrder[0] = ++currentCall[0]; + } + + @Override + public void onDisplayBlanked() { + callOrder[1] = ++currentCall[0]; + } + + @Override + public void onFinished() { + callOrder[2] = ++currentCall[0]; + } + }); + mScrimController.finishAnimationsImmediately(); + Assert.assertEquals("onStart called in wrong order", 1, callOrder[0]); + Assert.assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]); + Assert.assertEquals("onFinished called in wrong order", 3, callOrder[2]); + } + + @Test + public void testScrimCallbacksWithoutAmbientDisplay() { + mAlwaysOnEnabled = false; + testScrimCallback(); + } + + @Test + public void testScrimCallbackCancelled() { + boolean[] cancelledCalled = {false}; + mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() { + @Override + public void onCancelled() { + cancelledCalled[0] = true; + } + }); + mScrimController.transitionTo(ScrimState.PULSING); + Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]); + } + + @Test + public void testHoldsWakeLock() { + mScrimController.transitionTo(ScrimState.AOD); + verify(mWakeLock, times(1)).acquire(); + verify(mWakeLock, never()).release(); + mScrimController.finishAnimationsImmediately(); + verify(mWakeLock, times(1)).release(); + } + + private void assertScrimTint(ScrimView scrimView, boolean tinted) { + final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT; + final String name = scrimView == mScrimInFront ? "front" : "back"; + Assert.assertEquals("Tint test failed at state " + mScrimController.getState() + +" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()), + tinted, viewIsTinted); + } + + private void assertScrimVisibility(boolean inFront, boolean behind) { + Assert.assertEquals("Unexpected front scrim visibility. Alpha is " + + mScrimInFront.getViewAlpha(), inFront, mScrimInFront.getViewAlpha() > 0); + Assert.assertEquals("Unexpected back scrim visibility. Alpha is " + + mScrimBehind.getViewAlpha(), behind, mScrimBehind.getViewAlpha() > 0); + Assert.assertEquals("Invalid visibility.", inFront || behind, mScrimVisibile); + } + + /** + * Special version of ScrimController where animations have 0 duration for test purposes. + */ + private class SynchronousScrimController extends ScrimController { + + private FakeHandler mHandler; + + public SynchronousScrimController(LightBarController lightBarController, + ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim, + Consumer scrimVisibleListener, DozeParameters dozeParameters) { + super(lightBarController, scrimBehind, scrimInFront, headsUpScrim, + scrimVisibleListener, dozeParameters); + mHandler = new FakeHandler(Looper.myLooper()); + } + + public void finishAnimationsImmediately() { + boolean[] animationFinished = {false}; + setOnAnimationFinished(()-> animationFinished[0] = true); + + // Execute code that will trigger animations. + onPreDraw(); + + // Force finish screen blanking. + endAnimation(mScrimInFront, TAG_KEY_ANIM_BLANK); + mHandler.dispatchQueuedMessages(); + // Force finish all animations. + endAnimation(mScrimBehind, TAG_KEY_ANIM); + endAnimation(mScrimInFront, TAG_KEY_ANIM); + + if (!animationFinished[0]) { + throw new IllegalStateException("Animation never finished"); + } + } + + private void endAnimation(ScrimView scrimView, int tag) { + Animator animator = (Animator) scrimView.getTag(tag); + if (animator != null) { + animator.end(); + } + } + + @Override + protected Handler getHandler() { + return mHandler; + } + + @Override + protected WakeLock createWakeLock() { + return mWakeLock; + } + } + +} -- cgit v1.2.3-59-g8ed1b