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 }