OSDN Git Service

AOD: Delay removing AOD scrim after pulse
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / statusbar / phone / DozeScrimController.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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
15  */
16
17 package com.android.systemui.statusbar.phone;
18
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;
27
28 import com.android.systemui.Interpolators;
29 import com.android.systemui.doze.DozeHost;
30 import com.android.systemui.doze.DozeLog;
31
32 /**
33  * Controller which handles all the doze animations of the scrims.
34  */
35 public class DozeScrimController {
36     private static final String TAG = "DozeScrimController";
37     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
38
39     private final DozeParameters mDozeParameters;
40     private final Handler mHandler = new Handler();
41     private final ScrimController mScrimController;
42
43     private final Context mContext;
44
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;
55
56     private float mAodFrontScrimOpacity = 0;
57     private Runnable mSetDozeInFrontAlphaDelayed;
58
59     public DozeScrimController(ScrimController scrimController, Context context) {
60         mContext = context;
61         mScrimController = scrimController;
62         mDozeParameters = new DozeParameters(context);
63     }
64
65     public void setDozing(boolean dozing, boolean animate) {
66         if (mDozing == dozing) return;
67         mDozing = dozing;
68         mWakeAndUnlocking = false;
69         if (mDozing) {
70             mDozingAborted = false;
71             abortAnimations();
72             mScrimController.setDozeBehindAlpha(1f);
73             setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? mAodFrontScrimOpacity : 1f);
74         } else {
75             cancelPulsing();
76             if (animate) {
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);
83             } else {
84                 abortAnimations();
85                 mScrimController.setDozeBehindAlpha(0f);
86                 setDozeInFrontAlpha(0f);
87             }
88         }
89     }
90
91     /**
92      * Set the opacity of the front scrim when showing AOD1
93      *
94      * Used to emulate lower brightness values than the hardware supports natively.
95      */
96     public void setAodDimmingScrim(float scrimOpacity) {
97         mAodFrontScrimOpacity = scrimOpacity;
98         if (mDozing && !isPulsing() && !mDozingAborted && !mWakeAndUnlocking
99                 && mDozeParameters.getAlwaysOn()) {
100             setDozeInFrontAlpha(mAodFrontScrimOpacity);
101         }
102     }
103
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);
111         }
112     }
113
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");
118         }
119
120         if (!mDozing || mPulseCallback != null) {
121             // Pulse suppressed.
122             callback.onPulseFinished();
123             return;
124         }
125
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);
132     }
133
134     /**
135      * Aborts pulsing immediately.
136      */
137     public void abortPulsing() {
138         cancelPulsing();
139         if (mDozing && !mWakeAndUnlocking) {
140             mScrimController.setDozeBehindAlpha(1f);
141             setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() && !mDozingAborted
142                     ? mAodFrontScrimOpacity : 1f);
143         }
144     }
145
146     /**
147      * Aborts dozing immediately.
148      */
149     public void abortDoze() {
150         mDozingAborted = true;
151         abortPulsing();
152     }
153
154     public void pulseOutNow() {
155         if (mPulseCallback != null && mFullyPulsing) {
156             mPulseOut.run();
157         }
158     }
159
160     public void onScreenTurnedOn() {
161         if (isPulsing()) {
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,
167                     mPulseInFinished);
168         }
169     }
170
171     public boolean isPulsing() {
172         return mPulseCallback != null;
173     }
174
175     public boolean isDozing() {
176         return mDozing;
177     }
178
179     public void extendPulse() {
180         mHandler.removeCallbacks(mPulseOut);
181     }
182
183     private void cancelPulsing() {
184         if (DEBUG) Log.d(TAG, "Cancel pulsing");
185
186         if (mPulseCallback != null) {
187             mFullyPulsing = false;
188             mHandler.removeCallbacks(mPulseIn);
189             mHandler.removeCallbacks(mPulseOut);
190             mHandler.removeCallbacks(mPulseOutExtended);
191             pulseFinished();
192         }
193     }
194
195     private void pulseStarted() {
196         if (mPulseCallback != null) {
197             mPulseCallback.onPulseStarted();
198         }
199     }
200
201     private void pulseFinished() {
202         if (mPulseCallback != null) {
203             mPulseCallback.onPulseFinished();
204             mPulseCallback = null;
205         }
206     }
207
208     private void abortAnimations() {
209         if (mInFrontAnimator != null) {
210             mInFrontAnimator.cancel();
211         }
212         if (mBehindAnimator != null) {
213             mBehindAnimator.cancel();
214         }
215     }
216
217     private void startScrimAnimation(final boolean inFront, float target, long duration,
218             Interpolator interpolator) {
219         startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
220     }
221
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) {
228                 return;
229             }
230             current.cancel();
231         }
232         ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
233         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
234             @Override
235             public void onAnimationUpdate(ValueAnimator animation) {
236                 float value = (float) animation.getAnimatedValue();
237                 setDozeAlpha(inFront, value);
238             }
239         });
240         anim.setInterpolator(interpolator);
241         anim.setDuration(duration);
242         anim.addListener(new AnimatorListenerAdapter() {
243             @Override
244             public void onAnimationEnd(Animator animation) {
245                 setCurrentAnimator(inFront, null);
246                 if (endRunnable != null) {
247                     endRunnable.run();
248                 }
249             }
250         });
251         anim.start();
252         setCurrentAnimator(inFront, anim);
253         setCurrentTarget(inFront, target);
254     }
255
256     private float getCurrentTarget(boolean inFront) {
257         return inFront ? mInFrontTarget : mBehindTarget;
258     }
259
260     private void setCurrentTarget(boolean inFront, float target) {
261         if (inFront) {
262             mInFrontTarget = target;
263         } else {
264             mBehindTarget = target;
265         }
266     }
267
268     private Animator getCurrentAnimator(boolean inFront) {
269         return inFront ? mInFrontAnimator : mBehindAnimator;
270     }
271
272     private void setCurrentAnimator(boolean inFront, Animator animator) {
273         if (inFront) {
274             mInFrontAnimator = animator;
275         } else {
276             mBehindAnimator = animator;
277         }
278     }
279
280     private void setDozeAlpha(boolean inFront, float alpha) {
281         if (mWakeAndUnlocking) {
282             return;
283         }
284         if (inFront) {
285             mScrimController.setDozeInFrontAlpha(alpha);
286         } else {
287             mScrimController.setDozeBehindAlpha(alpha);
288         }
289     }
290
291     private float getDozeAlpha(boolean inFront) {
292         return inFront
293                 ? mScrimController.getDozeInFrontAlpha()
294                 : mScrimController.getDozeBehindAlpha();
295     }
296
297     private void setDozeInFrontAlpha(float opacity) {
298         setDozeInFrontAlphaDelayed(opacity, 0 /* delay */);
299
300     }
301
302     private void setDozeInFrontAlphaDelayed(float opacity, long delayMs) {
303         if (mSetDozeInFrontAlphaDelayed != null) {
304             mHandler.removeCallbacks(mSetDozeInFrontAlphaDelayed);
305             mSetDozeInFrontAlphaDelayed = null;
306         }
307         if (delayMs < 0) {
308             mScrimController.setDozeInFrontAlpha(opacity);
309         } else {
310             mHandler.postDelayed(mSetDozeInFrontAlphaDelayed = () -> {
311                 setDozeInFrontAlpha(opacity);
312             }, delayMs);
313         }
314     }
315
316     private final Runnable mPulseIn = new Runnable() {
317         @Override
318         public void run() {
319             if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
320                     + DozeLog.pulseReasonToString(mPulseReason));
321             if (!mDozing) return;
322             DozeLog.tracePulseStart(mPulseReason);
323
324             // Signal that the pulse is ready to turn the screen on and draw.
325             pulseStarted();
326         }
327     };
328
329     private final Runnable mPulseInFinished = new Runnable() {
330         @Override
331         public void run() {
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;
338         }
339     };
340
341     private final Runnable mPulseOutExtended = new Runnable() {
342         @Override
343         public void run() {
344             mHandler.removeCallbacks(mPulseOut);
345             mPulseOut.run();
346         }
347     };
348
349     private final Runnable mPulseOut = new Runnable() {
350         @Override
351         public void run() {
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);
360         }
361     };
362
363     private final Runnable mPulseOutFinishing = new Runnable() {
364         @Override
365         public void run() {
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
371                 // framebuffer.
372                 mHandler.postDelayed(mPulseOutFinished, 30);
373             } else {
374                 mPulseOutFinished.run();
375             }
376         }
377     };
378
379     private final Runnable mPulseOutFinished = new Runnable() {
380         @Override
381         public void run() {
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);
388             }
389         }
390     };
391 }