2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
17 package com.android.systemui.statusbar.phone;
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ValueAnimator;
22 import android.annotation.NonNull;
23 import android.content.Context;
24 import android.os.Handler;
25 import android.util.Log;
26 import android.view.animation.Interpolator;
28 import com.android.systemui.Interpolators;
29 import com.android.systemui.doze.DozeHost;
30 import com.android.systemui.doze.DozeLog;
33 * Controller which handles all the doze animations of the scrims.
35 public class DozeScrimController {
36 private static final String TAG = "DozeScrimController";
37 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
39 private final DozeParameters mDozeParameters;
40 private final Handler mHandler = new Handler();
41 private final ScrimController mScrimController;
43 private final Context mContext;
45 private boolean mDozing;
46 private DozeHost.PulseCallback mPulseCallback;
47 private int mPulseReason;
48 private Animator mInFrontAnimator;
49 private Animator mBehindAnimator;
50 private float mInFrontTarget;
51 private float mBehindTarget;
52 private boolean mDozingAborted;
53 private boolean mWakeAndUnlocking;
54 private boolean mFullyPulsing;
56 private float mAodFrontScrimOpacity = 0;
57 private Runnable mSetDozeInFrontAlphaDelayed;
59 public DozeScrimController(ScrimController scrimController, Context context) {
61 mScrimController = scrimController;
62 mDozeParameters = new DozeParameters(context);
65 public void setDozing(boolean dozing, boolean animate) {
66 if (mDozing == dozing) return;
68 mWakeAndUnlocking = false;
70 mDozingAborted = false;
72 mScrimController.setDozeBehindAlpha(1f);
73 setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? mAodFrontScrimOpacity : 1f);
77 startScrimAnimation(false /* inFront */, 0f /* target */,
78 NotificationPanelView.DOZE_ANIMATION_DURATION,
79 Interpolators.LINEAR_OUT_SLOW_IN);
80 startScrimAnimation(true /* inFront */, 0f /* target */,
81 NotificationPanelView.DOZE_ANIMATION_DURATION,
82 Interpolators.LINEAR_OUT_SLOW_IN);
85 mScrimController.setDozeBehindAlpha(0f);
86 setDozeInFrontAlpha(0f);
92 * Set the opacity of the front scrim when showing AOD1
94 * Used to emulate lower brightness values than the hardware supports natively.
96 public void setAodDimmingScrim(float scrimOpacity) {
97 mAodFrontScrimOpacity = scrimOpacity;
98 if (mDozing && !isPulsing() && !mDozingAborted && !mWakeAndUnlocking
99 && mDozeParameters.getAlwaysOn()) {
100 setDozeInFrontAlpha(mAodFrontScrimOpacity);
104 public void setWakeAndUnlocking() {
105 // Immediately abort the doze scrims in case of wake-and-unlock
106 // for pulsing so the Keyguard fade-out animation scrim can take over.
107 if (!mWakeAndUnlocking) {
108 mWakeAndUnlocking = true;
109 mScrimController.setDozeBehindAlpha(0f);
110 setDozeInFrontAlpha(0f);
114 /** When dozing, fade screen contents in and out using the front scrim. */
115 public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) {
116 if (callback == null) {
117 throw new IllegalArgumentException("callback must not be null");
120 if (!mDozing || mPulseCallback != null) {
122 callback.onPulseFinished();
126 // Begin pulse. Note that it's very important that the pulse finished callback
127 // be invoked when we're done so that the caller can drop the pulse wakelock.
128 mPulseCallback = callback;
129 mPulseReason = reason;
130 setDozeInFrontAlpha(1f);
131 mHandler.post(mPulseIn);
135 * Aborts pulsing immediately.
137 public void abortPulsing() {
139 if (mDozing && !mWakeAndUnlocking) {
140 mScrimController.setDozeBehindAlpha(1f);
141 setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() && !mDozingAborted
142 ? mAodFrontScrimOpacity : 1f);
147 * Aborts dozing immediately.
149 public void abortDoze() {
150 mDozingAborted = true;
154 public void pulseOutNow() {
155 if (mPulseCallback != null && mFullyPulsing) {
160 public void onScreenTurnedOn() {
162 final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
163 || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
164 startScrimAnimation(true /* inFront */, 0f,
165 mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
166 pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
171 public boolean isPulsing() {
172 return mPulseCallback != null;
175 public boolean isDozing() {
179 public void extendPulse() {
180 mHandler.removeCallbacks(mPulseOut);
183 private void cancelPulsing() {
184 if (DEBUG) Log.d(TAG, "Cancel pulsing");
186 if (mPulseCallback != null) {
187 mFullyPulsing = false;
188 mHandler.removeCallbacks(mPulseIn);
189 mHandler.removeCallbacks(mPulseOut);
190 mHandler.removeCallbacks(mPulseOutExtended);
195 private void pulseStarted() {
196 if (mPulseCallback != null) {
197 mPulseCallback.onPulseStarted();
201 private void pulseFinished() {
202 if (mPulseCallback != null) {
203 mPulseCallback.onPulseFinished();
204 mPulseCallback = null;
208 private void abortAnimations() {
209 if (mInFrontAnimator != null) {
210 mInFrontAnimator.cancel();
212 if (mBehindAnimator != null) {
213 mBehindAnimator.cancel();
217 private void startScrimAnimation(final boolean inFront, float target, long duration,
218 Interpolator interpolator) {
219 startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
222 private void startScrimAnimation(final boolean inFront, float target, long duration,
223 Interpolator interpolator, final Runnable endRunnable) {
224 Animator current = getCurrentAnimator(inFront);
225 if (current != null) {
226 float currentTarget = getCurrentTarget(inFront);
227 if (currentTarget == target) {
232 ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
233 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
235 public void onAnimationUpdate(ValueAnimator animation) {
236 float value = (float) animation.getAnimatedValue();
237 setDozeAlpha(inFront, value);
240 anim.setInterpolator(interpolator);
241 anim.setDuration(duration);
242 anim.addListener(new AnimatorListenerAdapter() {
244 public void onAnimationEnd(Animator animation) {
245 setCurrentAnimator(inFront, null);
246 if (endRunnable != null) {
252 setCurrentAnimator(inFront, anim);
253 setCurrentTarget(inFront, target);
256 private float getCurrentTarget(boolean inFront) {
257 return inFront ? mInFrontTarget : mBehindTarget;
260 private void setCurrentTarget(boolean inFront, float target) {
262 mInFrontTarget = target;
264 mBehindTarget = target;
268 private Animator getCurrentAnimator(boolean inFront) {
269 return inFront ? mInFrontAnimator : mBehindAnimator;
272 private void setCurrentAnimator(boolean inFront, Animator animator) {
274 mInFrontAnimator = animator;
276 mBehindAnimator = animator;
280 private void setDozeAlpha(boolean inFront, float alpha) {
281 if (mWakeAndUnlocking) {
285 mScrimController.setDozeInFrontAlpha(alpha);
287 mScrimController.setDozeBehindAlpha(alpha);
291 private float getDozeAlpha(boolean inFront) {
293 ? mScrimController.getDozeInFrontAlpha()
294 : mScrimController.getDozeBehindAlpha();
297 private void setDozeInFrontAlpha(float opacity) {
298 setDozeInFrontAlphaDelayed(opacity, 0 /* delay */);
302 private void setDozeInFrontAlphaDelayed(float opacity, long delayMs) {
303 if (mSetDozeInFrontAlphaDelayed != null) {
304 mHandler.removeCallbacks(mSetDozeInFrontAlphaDelayed);
305 mSetDozeInFrontAlphaDelayed = null;
308 mScrimController.setDozeInFrontAlpha(opacity);
310 mHandler.postDelayed(mSetDozeInFrontAlphaDelayed = () -> {
311 setDozeInFrontAlpha(opacity);
316 private final Runnable mPulseIn = new Runnable() {
319 if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
320 + DozeLog.pulseReasonToString(mPulseReason));
321 if (!mDozing) return;
322 DozeLog.tracePulseStart(mPulseReason);
324 // Signal that the pulse is ready to turn the screen on and draw.
329 private final Runnable mPulseInFinished = new Runnable() {
332 if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
333 if (!mDozing) return;
334 mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
335 mHandler.postDelayed(mPulseOutExtended,
336 mDozeParameters.getPulseVisibleDurationExtended());
337 mFullyPulsing = true;
341 private final Runnable mPulseOutExtended = new Runnable() {
344 mHandler.removeCallbacks(mPulseOut);
349 private final Runnable mPulseOut = new Runnable() {
352 mFullyPulsing = false;
353 mHandler.removeCallbacks(mPulseOut);
354 mHandler.removeCallbacks(mPulseOutExtended);
355 if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
356 if (!mDozing) return;
357 startScrimAnimation(true /* inFront */, 1,
358 mDozeParameters.getPulseOutDuration(),
359 Interpolators.ALPHA_IN, mPulseOutFinishing);
363 private final Runnable mPulseOutFinishing = new Runnable() {
366 if (DEBUG) Log.d(TAG, "Pulse out finished");
367 DozeLog.tracePulseFinish();
368 if (mDozeParameters.getAlwaysOn() && mDozing) {
369 // Setting power states can block rendering. For AOD, delay finishing the pulse and
370 // setting the power state until the fully black scrim had time to hit the
372 mHandler.postDelayed(mPulseOutFinished, 30);
374 mPulseOutFinished.run();
379 private final Runnable mPulseOutFinished = new Runnable() {
382 // Signal that the pulse is all finished so we can turn the screen off now.
383 DozeScrimController.this.pulseFinished();
384 if (mDozeParameters.getAlwaysOn()) {
385 // Setting power states can happen after we push out the frame. Make sure we
386 // stay fully opaque until the power state request reaches the lower levels.
387 setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 30);