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