OSDN Git Service

c45c05e14a22a5ac2563d60f2f82509b63332486
[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
58     public DozeScrimController(ScrimController scrimController, Context context) {
59         mContext = context;
60         mScrimController = scrimController;
61         mDozeParameters = new DozeParameters(context);
62     }
63
64     public void setDozing(boolean dozing, boolean animate) {
65         if (mDozing == dozing) return;
66         mDozing = dozing;
67         mWakeAndUnlocking = false;
68         if (mDozing) {
69             mDozingAborted = false;
70             abortAnimations();
71             mScrimController.setDozeBehindAlpha(1f);
72             mScrimController.setDozeInFrontAlpha(
73                     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                 mScrimController.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             mScrimController.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             mScrimController.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         mScrimController.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             mScrimController.setDozeInFrontAlpha(
142                     mDozeParameters.getAlwaysOn() && !mDozingAborted ?
143                             mAodFrontScrimOpacity : 1f);
144         }
145     }
146
147     /**
148      * Aborts dozing immediately.
149      */
150     public void abortDoze() {
151         mDozingAborted = true;
152         abortPulsing();
153     }
154
155     public void pulseOutNow() {
156         if (mPulseCallback != null && mFullyPulsing) {
157             mPulseOut.run();
158         }
159     }
160
161     public void onScreenTurnedOn() {
162         if (isPulsing()) {
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,
168                     mPulseInFinished);
169         }
170     }
171
172     public boolean isPulsing() {
173         return mPulseCallback != null;
174     }
175
176     public boolean isDozing() {
177         return mDozing;
178     }
179
180     public void extendPulse() {
181         mHandler.removeCallbacks(mPulseOut);
182     }
183
184     private void cancelPulsing() {
185         if (DEBUG) Log.d(TAG, "Cancel pulsing");
186
187         if (mPulseCallback != null) {
188             mFullyPulsing = false;
189             mHandler.removeCallbacks(mPulseIn);
190             mHandler.removeCallbacks(mPulseOut);
191             mHandler.removeCallbacks(mPulseOutExtended);
192             pulseFinished();
193         }
194     }
195
196     private void pulseStarted() {
197         if (mPulseCallback != null) {
198             mPulseCallback.onPulseStarted();
199         }
200     }
201
202     private void pulseFinished() {
203         if (mPulseCallback != null) {
204             mPulseCallback.onPulseFinished();
205             mPulseCallback = null;
206         }
207     }
208
209     private void abortAnimations() {
210         if (mInFrontAnimator != null) {
211             mInFrontAnimator.cancel();
212         }
213         if (mBehindAnimator != null) {
214             mBehindAnimator.cancel();
215         }
216     }
217
218     private void startScrimAnimation(final boolean inFront, float target, long duration,
219             Interpolator interpolator) {
220         startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
221     }
222
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) {
229                 return;
230             }
231             current.cancel();
232         }
233         ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
234         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
235             @Override
236             public void onAnimationUpdate(ValueAnimator animation) {
237                 float value = (float) animation.getAnimatedValue();
238                 setDozeAlpha(inFront, value);
239             }
240         });
241         anim.setInterpolator(interpolator);
242         anim.setDuration(duration);
243         anim.addListener(new AnimatorListenerAdapter() {
244             @Override
245             public void onAnimationEnd(Animator animation) {
246                 setCurrentAnimator(inFront, null);
247                 if (endRunnable != null) {
248                     endRunnable.run();
249                 }
250             }
251         });
252         anim.start();
253         setCurrentAnimator(inFront, anim);
254         setCurrentTarget(inFront, target);
255     }
256
257     private float getCurrentTarget(boolean inFront) {
258         return inFront ? mInFrontTarget : mBehindTarget;
259     }
260
261     private void setCurrentTarget(boolean inFront, float target) {
262         if (inFront) {
263             mInFrontTarget = target;
264         } else {
265             mBehindTarget = target;
266         }
267     }
268
269     private Animator getCurrentAnimator(boolean inFront) {
270         return inFront ? mInFrontAnimator : mBehindAnimator;
271     }
272
273     private void setCurrentAnimator(boolean inFront, Animator animator) {
274         if (inFront) {
275             mInFrontAnimator = animator;
276         } else {
277             mBehindAnimator = animator;
278         }
279     }
280
281     private void setDozeAlpha(boolean inFront, float alpha) {
282         if (mWakeAndUnlocking) {
283             return;
284         }
285         if (inFront) {
286             mScrimController.setDozeInFrontAlpha(alpha);
287         } else {
288             mScrimController.setDozeBehindAlpha(alpha);
289         }
290     }
291
292     private float getDozeAlpha(boolean inFront) {
293         return inFront
294                 ? mScrimController.getDozeInFrontAlpha()
295                 : mScrimController.getDozeBehindAlpha();
296     }
297
298     private final Runnable mPulseIn = new Runnable() {
299         @Override
300         public void run() {
301             if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
302                     + DozeLog.pulseReasonToString(mPulseReason));
303             if (!mDozing) return;
304             DozeLog.tracePulseStart(mPulseReason);
305
306             // Signal that the pulse is ready to turn the screen on and draw.
307             pulseStarted();
308         }
309     };
310
311     private final Runnable mPulseInFinished = new Runnable() {
312         @Override
313         public void run() {
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;
320         }
321     };
322
323     private final Runnable mPulseOutExtended = new Runnable() {
324         @Override
325         public void run() {
326             mHandler.removeCallbacks(mPulseOut);
327             mPulseOut.run();
328         }
329     };
330
331     private final Runnable mPulseOut = new Runnable() {
332         @Override
333         public void run() {
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);
342         }
343     };
344
345     private final Runnable mPulseOutFinishing = new Runnable() {
346         @Override
347         public void run() {
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
353                 // framebuffer.
354                 mHandler.postDelayed(mPulseOutFinished, 30);
355             } else {
356                 mPulseOutFinished.run();
357             }
358         }
359     };
360
361     private final Runnable mPulseOutFinished = new Runnable() {
362         @Override
363         public void run() {
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);
368             }
369         }
370     };
371 }