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