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;
58 public DozeScrimController(ScrimController scrimController, Context context) {
60 mScrimController = scrimController;
61 mDozeParameters = new DozeParameters(context);
64 public void setDozing(boolean dozing, boolean animate) {
65 if (mDozing == dozing) return;
67 mWakeAndUnlocking = false;
69 mDozingAborted = false;
71 mScrimController.setDozeBehindAlpha(1f);
72 mScrimController.setDozeInFrontAlpha(
73 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 mScrimController.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 mScrimController.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 mScrimController.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 mScrimController.setDozeInFrontAlpha(1f);
131 mHandler.post(mPulseIn);
135 * Aborts pulsing immediately.
137 public void abortPulsing() {
139 if (mDozing && !mWakeAndUnlocking) {
140 mScrimController.setDozeBehindAlpha(1f);
141 mScrimController.setDozeInFrontAlpha(
142 mDozeParameters.getAlwaysOn() && !mDozingAborted ?
143 mAodFrontScrimOpacity : 1f);
148 * Aborts dozing immediately.
150 public void abortDoze() {
151 mDozingAborted = true;
155 public void pulseOutNow() {
156 if (mPulseCallback != null && mFullyPulsing) {
161 public void onScreenTurnedOn() {
163 final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
164 || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
165 startScrimAnimation(true /* inFront */, 0f,
166 mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
167 pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
172 public boolean isPulsing() {
173 return mPulseCallback != null;
176 public boolean isDozing() {
180 public void extendPulse() {
181 mHandler.removeCallbacks(mPulseOut);
184 private void cancelPulsing() {
185 if (DEBUG) Log.d(TAG, "Cancel pulsing");
187 if (mPulseCallback != null) {
188 mFullyPulsing = false;
189 mHandler.removeCallbacks(mPulseIn);
190 mHandler.removeCallbacks(mPulseOut);
191 mHandler.removeCallbacks(mPulseOutExtended);
196 private void pulseStarted() {
197 if (mPulseCallback != null) {
198 mPulseCallback.onPulseStarted();
202 private void pulseFinished() {
203 if (mPulseCallback != null) {
204 mPulseCallback.onPulseFinished();
205 mPulseCallback = null;
209 private void abortAnimations() {
210 if (mInFrontAnimator != null) {
211 mInFrontAnimator.cancel();
213 if (mBehindAnimator != null) {
214 mBehindAnimator.cancel();
218 private void startScrimAnimation(final boolean inFront, float target, long duration,
219 Interpolator interpolator) {
220 startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
223 private void startScrimAnimation(final boolean inFront, float target, long duration,
224 Interpolator interpolator, final Runnable endRunnable) {
225 Animator current = getCurrentAnimator(inFront);
226 if (current != null) {
227 float currentTarget = getCurrentTarget(inFront);
228 if (currentTarget == target) {
233 ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
234 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
236 public void onAnimationUpdate(ValueAnimator animation) {
237 float value = (float) animation.getAnimatedValue();
238 setDozeAlpha(inFront, value);
241 anim.setInterpolator(interpolator);
242 anim.setDuration(duration);
243 anim.addListener(new AnimatorListenerAdapter() {
245 public void onAnimationEnd(Animator animation) {
246 setCurrentAnimator(inFront, null);
247 if (endRunnable != null) {
253 setCurrentAnimator(inFront, anim);
254 setCurrentTarget(inFront, target);
257 private float getCurrentTarget(boolean inFront) {
258 return inFront ? mInFrontTarget : mBehindTarget;
261 private void setCurrentTarget(boolean inFront, float target) {
263 mInFrontTarget = target;
265 mBehindTarget = target;
269 private Animator getCurrentAnimator(boolean inFront) {
270 return inFront ? mInFrontAnimator : mBehindAnimator;
273 private void setCurrentAnimator(boolean inFront, Animator animator) {
275 mInFrontAnimator = animator;
277 mBehindAnimator = animator;
281 private void setDozeAlpha(boolean inFront, float alpha) {
282 if (mWakeAndUnlocking) {
286 mScrimController.setDozeInFrontAlpha(alpha);
288 mScrimController.setDozeBehindAlpha(alpha);
292 private float getDozeAlpha(boolean inFront) {
294 ? mScrimController.getDozeInFrontAlpha()
295 : mScrimController.getDozeBehindAlpha();
298 private final Runnable mPulseIn = new Runnable() {
301 if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
302 + DozeLog.pulseReasonToString(mPulseReason));
303 if (!mDozing) return;
304 DozeLog.tracePulseStart(mPulseReason);
306 // Signal that the pulse is ready to turn the screen on and draw.
311 private final Runnable mPulseInFinished = new Runnable() {
314 if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
315 if (!mDozing) return;
316 mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
317 mHandler.postDelayed(mPulseOutExtended,
318 mDozeParameters.getPulseVisibleDurationExtended());
319 mFullyPulsing = true;
323 private final Runnable mPulseOutExtended = new Runnable() {
326 mHandler.removeCallbacks(mPulseOut);
331 private final Runnable mPulseOut = new Runnable() {
334 mFullyPulsing = false;
335 mHandler.removeCallbacks(mPulseOut);
336 mHandler.removeCallbacks(mPulseOutExtended);
337 if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
338 if (!mDozing) return;
339 startScrimAnimation(true /* inFront */, 1,
340 mDozeParameters.getPulseOutDuration(),
341 Interpolators.ALPHA_IN, mPulseOutFinishing);
345 private final Runnable mPulseOutFinishing = new Runnable() {
348 if (DEBUG) Log.d(TAG, "Pulse out finished");
349 DozeLog.tracePulseFinish();
350 if (mDozeParameters.getAlwaysOn() && mDozing) {
351 // Setting power states can block rendering. For AOD, delay finishing the pulse and
352 // setting the power state until the fully black scrim had time to hit the
354 mHandler.postDelayed(mPulseOutFinished, 30);
356 mPulseOutFinished.run();
361 private final Runnable mPulseOutFinished = new Runnable() {
364 // Signal that the pulse is all finished so we can turn the screen off now.
365 DozeScrimController.this.pulseFinished();
366 if (mDozeParameters.getAlwaysOn()) {
367 mScrimController.setDozeInFrontAlpha(mAodFrontScrimOpacity);