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 17package com.android.systemui.statusbar.phone; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ValueAnimator; 22import android.annotation.NonNull; 23import android.content.Context; 24import android.os.Handler; 25import android.util.Log; 26import android.view.animation.Interpolator; 27 28import com.android.systemui.Interpolators; 29import com.android.systemui.doze.DozeHost; 30import com.android.systemui.doze.DozeLog; 31 32/** 33 * Controller which handles all the doze animations of the scrims. 34 */ 35public 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 boolean mDozing; 44 private DozeHost.PulseCallback mPulseCallback; 45 private int mPulseReason; 46 private Animator mInFrontAnimator; 47 private Animator mBehindAnimator; 48 private float mInFrontTarget; 49 private float mBehindTarget; 50 51 public DozeScrimController(ScrimController scrimController, Context context) { 52 mScrimController = scrimController; 53 mDozeParameters = new DozeParameters(context); 54 } 55 56 public void setDozing(boolean dozing, boolean animate) { 57 if (mDozing == dozing) return; 58 mDozing = dozing; 59 if (mDozing) { 60 abortAnimations(); 61 mScrimController.setDozeBehindAlpha(1f); 62 mScrimController.setDozeInFrontAlpha(1f); 63 } else { 64 cancelPulsing(); 65 if (animate) { 66 startScrimAnimation(false /* inFront */, 0f /* target */, 67 NotificationPanelView.DOZE_ANIMATION_DURATION, 68 Interpolators.LINEAR_OUT_SLOW_IN); 69 startScrimAnimation(true /* inFront */, 0f /* target */, 70 NotificationPanelView.DOZE_ANIMATION_DURATION, 71 Interpolators.LINEAR_OUT_SLOW_IN); 72 } else { 73 abortAnimations(); 74 mScrimController.setDozeBehindAlpha(0f); 75 mScrimController.setDozeInFrontAlpha(0f); 76 } 77 } 78 } 79 80 /** When dozing, fade screen contents in and out using the front scrim. */ 81 public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) { 82 if (callback == null) { 83 throw new IllegalArgumentException("callback must not be null"); 84 } 85 86 if (!mDozing || mPulseCallback != null) { 87 // Pulse suppressed. 88 callback.onPulseFinished(); 89 return; 90 } 91 92 // Begin pulse. Note that it's very important that the pulse finished callback 93 // be invoked when we're done so that the caller can drop the pulse wakelock. 94 mPulseCallback = callback; 95 mPulseReason = reason; 96 mHandler.post(mPulseIn); 97 } 98 99 /** 100 * Aborts pulsing immediately. 101 */ 102 public void abortPulsing() { 103 cancelPulsing(); 104 if (mDozing) { 105 mScrimController.setDozeBehindAlpha(1f); 106 mScrimController.setDozeInFrontAlpha(1f); 107 } 108 } 109 110 public void onScreenTurnedOn() { 111 if (isPulsing()) { 112 final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP 113 || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP; 114 startScrimAnimation(true /* inFront */, 0f, 115 mDozeParameters.getPulseInDuration(pickupOrDoubleTap), 116 pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT, 117 mPulseInFinished); 118 } 119 } 120 121 public boolean isPulsing() { 122 return mPulseCallback != null; 123 } 124 125 public boolean isDozing() { 126 return mDozing; 127 } 128 129 private void cancelPulsing() { 130 if (DEBUG) Log.d(TAG, "Cancel pulsing"); 131 132 if (mPulseCallback != null) { 133 mHandler.removeCallbacks(mPulseIn); 134 mHandler.removeCallbacks(mPulseOut); 135 pulseFinished(); 136 } 137 } 138 139 private void pulseStarted() { 140 if (mPulseCallback != null) { 141 mPulseCallback.onPulseStarted(); 142 } 143 } 144 145 private void pulseFinished() { 146 if (mPulseCallback != null) { 147 mPulseCallback.onPulseFinished(); 148 mPulseCallback = null; 149 } 150 } 151 152 private void abortAnimations() { 153 if (mInFrontAnimator != null) { 154 mInFrontAnimator.cancel(); 155 } 156 if (mBehindAnimator != null) { 157 mBehindAnimator.cancel(); 158 } 159 } 160 161 private void startScrimAnimation(final boolean inFront, float target, long duration, 162 Interpolator interpolator) { 163 startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */); 164 } 165 166 private void startScrimAnimation(final boolean inFront, float target, long duration, 167 Interpolator interpolator, final Runnable endRunnable) { 168 Animator current = getCurrentAnimator(inFront); 169 if (current != null) { 170 float currentTarget = getCurrentTarget(inFront); 171 if (currentTarget == target) { 172 return; 173 } 174 current.cancel(); 175 } 176 ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target); 177 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 178 @Override 179 public void onAnimationUpdate(ValueAnimator animation) { 180 float value = (float) animation.getAnimatedValue(); 181 setDozeAlpha(inFront, value); 182 } 183 }); 184 anim.setInterpolator(interpolator); 185 anim.setDuration(duration); 186 anim.addListener(new AnimatorListenerAdapter() { 187 @Override 188 public void onAnimationEnd(Animator animation) { 189 setCurrentAnimator(inFront, null); 190 if (endRunnable != null) { 191 endRunnable.run(); 192 } 193 } 194 }); 195 anim.start(); 196 setCurrentAnimator(inFront, anim); 197 setCurrentTarget(inFront, target); 198 } 199 200 private float getCurrentTarget(boolean inFront) { 201 return inFront ? mInFrontTarget : mBehindTarget; 202 } 203 204 private void setCurrentTarget(boolean inFront, float target) { 205 if (inFront) { 206 mInFrontTarget = target; 207 } else { 208 mBehindTarget = target; 209 } 210 } 211 212 private Animator getCurrentAnimator(boolean inFront) { 213 return inFront ? mInFrontAnimator : mBehindAnimator; 214 } 215 216 private void setCurrentAnimator(boolean inFront, Animator animator) { 217 if (inFront) { 218 mInFrontAnimator = animator; 219 } else { 220 mBehindAnimator = animator; 221 } 222 } 223 224 private void setDozeAlpha(boolean inFront, float alpha) { 225 if (inFront) { 226 mScrimController.setDozeInFrontAlpha(alpha); 227 } else { 228 mScrimController.setDozeBehindAlpha(alpha); 229 } 230 } 231 232 private float getDozeAlpha(boolean inFront) { 233 return inFront 234 ? mScrimController.getDozeInFrontAlpha() 235 : mScrimController.getDozeBehindAlpha(); 236 } 237 238 private final Runnable mPulseIn = new Runnable() { 239 @Override 240 public void run() { 241 if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason=" 242 + DozeLog.pulseReasonToString(mPulseReason)); 243 if (!mDozing) return; 244 DozeLog.tracePulseStart(mPulseReason); 245 246 // Signal that the pulse is ready to turn the screen on and draw. 247 pulseStarted(); 248 } 249 }; 250 251 private final Runnable mPulseInFinished = new Runnable() { 252 @Override 253 public void run() { 254 if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing); 255 if (!mDozing) return; 256 mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); 257 } 258 }; 259 260 private final Runnable mPulseOut = new Runnable() { 261 @Override 262 public void run() { 263 if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing); 264 if (!mDozing) return; 265 startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(), 266 Interpolators.ALPHA_IN, mPulseOutFinished); 267 } 268 }; 269 270 private final Runnable mPulseOutFinished = new Runnable() { 271 @Override 272 public void run() { 273 if (DEBUG) Log.d(TAG, "Pulse out finished"); 274 DozeLog.tracePulseFinish(); 275 276 // Signal that the pulse is all finished so we can turn the screen off now. 277 pulseFinished(); 278 } 279 }; 280} 281