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.annotation.NonNull;
20 import android.os.Handler;
21 
22 import com.android.internal.annotations.VisibleForTesting;
23 import com.android.systemui.dagger.SysUISingleton;
24 import com.android.systemui.doze.DozeHost;
25 import com.android.systemui.doze.DozeLog;
26 import com.android.systemui.plugins.statusbar.StatusBarStateController;
27 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
28 
29 import javax.inject.Inject;
30 
31 /**
32  * Controller which handles all the doze animations of the scrims.
33  */
34 @SysUISingleton
35 public class DozeScrimController implements StateListener {
36     private final DozeLog mDozeLog;
37     private final DozeParameters mDozeParameters;
38     private final Handler mHandler = new Handler();
39 
40     private boolean mDozing;
41     private DozeHost.PulseCallback mPulseCallback;
42     private int mPulseReason;
43 
44     private final ScrimController.Callback mScrimCallback = new ScrimController.Callback() {
45         @Override
46         public void onDisplayBlanked() {
47             if (!mDozing) {
48                 mDozeLog.tracePulseDropped("onDisplayBlanked - not dozing");
49                 return;
50             }
51 
52             if (mPulseCallback != null) {
53                 // Signal that the pulse is ready to turn the screen on and draw.
54                 mDozeLog.tracePulseStart(mPulseReason);
55                 mPulseCallback.onPulseStarted();
56             }
57         }
58 
59         @Override
60         public void onFinished() {
61             mDozeLog.tracePulseEvent("scrimCallback-onFinished", mDozing, mPulseReason);
62 
63             if (!mDozing) {
64                 return;
65             }
66             // Notifications should time out on their own.  Pulses due to notifications should
67             // instead be managed externally based off the notification's lifetime.
68             // Dock also controls the time out by self.
69             if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION
70                     && mPulseReason != DozeLog.PULSE_REASON_DOCKING) {
71                 mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
72                 mHandler.postDelayed(mPulseOutExtended,
73                         mDozeParameters.getPulseVisibleDurationExtended());
74             }
75         }
76 
77         /**
78          * Transition was aborted before it was over.
79          */
80         @Override
81         public void onCancelled() {
82             pulseFinished();
83         }
84     };
85 
86     @Inject
DozeScrimController( DozeParameters dozeParameters, DozeLog dozeLog, StatusBarStateController statusBarStateController )87     public DozeScrimController(
88             DozeParameters dozeParameters,
89             DozeLog dozeLog,
90             StatusBarStateController statusBarStateController
91     ) {
92         mDozeParameters = dozeParameters;
93         // Never expected to be destroyed
94         statusBarStateController.addCallback(this);
95         mDozeLog = dozeLog;
96     }
97 
98     @VisibleForTesting
setDozing(boolean dozing)99     public void setDozing(boolean dozing) {
100         if (mDozing == dozing) return;
101         mDozing = dozing;
102         if (!mDozing) {
103             cancelPulsing();
104         }
105     }
106 
107     /** When dozing, fade screen contents in and out using the front scrim. */
pulse(@onNull DozeHost.PulseCallback callback, int reason)108     public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) {
109         if (callback == null) {
110             throw new IllegalArgumentException("callback must not be null");
111         }
112 
113         if (!mDozing || mPulseCallback != null) {
114             // Pulse suppressed.
115             callback.onPulseFinished();
116             if (!mDozing) {
117                 mDozeLog.tracePulseDropped("pulse - device isn't dozing");
118             } else {
119                 mDozeLog.tracePulseDropped("pulse - already has pulse callback mPulseCallback="
120                         + mPulseCallback);
121             }
122             return;
123         }
124 
125         // Begin pulse. Note that it's very important that the pulse finished callback
126         // be invoked when we're done so that the caller can drop the pulse wakelock.
127         mPulseCallback = callback;
128         mPulseReason = reason;
129     }
130 
pulseOutNow()131     public void pulseOutNow() {
132         mPulseOut.run();
133     }
134 
isPulsing()135     public boolean isPulsing() {
136         return mPulseCallback != null;
137     }
138 
isDozing()139     public boolean isDozing() {
140         return mDozing;
141     }
142 
extendPulse()143     public void extendPulse() {
144         mHandler.removeCallbacks(mPulseOut);
145     }
146 
147     /**
148      * When pulsing, cancel any timeouts that would take you out of the pulsing state.
149      */
cancelPendingPulseTimeout()150     public void cancelPendingPulseTimeout() {
151         mHandler.removeCallbacks(mPulseOut);
152         mHandler.removeCallbacks(mPulseOutExtended);
153     }
154 
cancelPulsing()155     private void cancelPulsing() {
156         if (mPulseCallback != null) {
157             mDozeLog.tracePulseEvent("cancel", mDozing, mPulseReason);
158             mHandler.removeCallbacks(mPulseOut);
159             mHandler.removeCallbacks(mPulseOutExtended);
160             pulseFinished();
161         }
162     }
163 
pulseFinished()164     private void pulseFinished() {
165         if (mPulseCallback != null) {
166             mDozeLog.tracePulseFinish();
167             mPulseCallback.onPulseFinished();
168             mPulseCallback = null;
169         }
170     }
171 
172     private final Runnable mPulseOutExtended = new Runnable() {
173         @Override
174         public void run() {
175             mHandler.removeCallbacks(mPulseOut);
176             mPulseOut.run();
177         }
178     };
179 
180     private final Runnable mPulseOut = new Runnable() {
181         @Override
182         public void run() {
183             mHandler.removeCallbacks(mPulseOut);
184             mHandler.removeCallbacks(mPulseOutExtended);
185             mDozeLog.tracePulseEvent("out", mDozing, mPulseReason);
186             if (!mDozing) return;
187             pulseFinished();
188         }
189     };
190 
getScrimCallback()191     public ScrimController.Callback getScrimCallback() {
192         return mScrimCallback;
193     }
194 
195     @Override
onStateChanged(int newState)196     public void onStateChanged(int newState) {
197         // don't care
198     }
199 
200     @Override
onDozingChanged(boolean isDozing)201     public void onDozingChanged(boolean isDozing) {
202         if (mDozing != isDozing) {
203             mDozeLog.traceDozingChanged(isDozing);
204         }
205 
206         setDozing(isDozing);
207     }
208 }