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 pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP; 113 startScrimAnimation(true /* inFront */, 0f, 114 mDozeParameters.getPulseInDuration(pickup), 115 pickup ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT, 116 mPulseInFinished); 117 } 118 } 119 120 public boolean isPulsing() { 121 return mPulseCallback != null; 122 } 123 124 public boolean isDozing() { 125 return mDozing; 126 } 127 128 private void cancelPulsing() { 129 if (DEBUG) Log.d(TAG, "Cancel pulsing"); 130 131 if (mPulseCallback != null) { 132 mHandler.removeCallbacks(mPulseIn); 133 mHandler.removeCallbacks(mPulseOut); 134 pulseFinished(); 135 } 136 } 137 138 private void pulseStarted() { 139 if (mPulseCallback != null) { 140 mPulseCallback.onPulseStarted(); 141 } 142 } 143 144 private void pulseFinished() { 145 if (mPulseCallback != null) { 146 mPulseCallback.onPulseFinished(); 147 mPulseCallback = null; 148 } 149 } 150 151 private void abortAnimations() { 152 if (mInFrontAnimator != null) { 153 mInFrontAnimator.cancel(); 154 } 155 if (mBehindAnimator != null) { 156 mBehindAnimator.cancel(); 157 } 158 } 159 160 private void startScrimAnimation(final boolean inFront, float target, long duration, 161 Interpolator interpolator) { 162 startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */); 163 } 164 165 private void startScrimAnimation(final boolean inFront, float target, long duration, 166 Interpolator interpolator, final Runnable endRunnable) { 167 Animator current = getCurrentAnimator(inFront); 168 if (current != null) { 169 float currentTarget = getCurrentTarget(inFront); 170 if (currentTarget == target) { 171 return; 172 } 173 current.cancel(); 174 } 175 ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target); 176 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 177 @Override 178 public void onAnimationUpdate(ValueAnimator animation) { 179 float value = (float) animation.getAnimatedValue(); 180 setDozeAlpha(inFront, value); 181 } 182 }); 183 anim.setInterpolator(interpolator); 184 anim.setDuration(duration); 185 anim.addListener(new AnimatorListenerAdapter() { 186 @Override 187 public void onAnimationEnd(Animator animation) { 188 setCurrentAnimator(inFront, null); 189 if (endRunnable != null) { 190 endRunnable.run(); 191 } 192 } 193 }); 194 anim.start(); 195 setCurrentAnimator(inFront, anim); 196 setCurrentTarget(inFront, target); 197 } 198 199 private float getCurrentTarget(boolean inFront) { 200 return inFront ? mInFrontTarget : mBehindTarget; 201 } 202 203 private void setCurrentTarget(boolean inFront, float target) { 204 if (inFront) { 205 mInFrontTarget = target; 206 } else { 207 mBehindTarget = target; 208 } 209 } 210 211 private Animator getCurrentAnimator(boolean inFront) { 212 return inFront ? mInFrontAnimator : mBehindAnimator; 213 } 214 215 private void setCurrentAnimator(boolean inFront, Animator animator) { 216 if (inFront) { 217 mInFrontAnimator = animator; 218 } else { 219 mBehindAnimator = animator; 220 } 221 } 222 223 private void setDozeAlpha(boolean inFront, float alpha) { 224 if (inFront) { 225 mScrimController.setDozeInFrontAlpha(alpha); 226 } else { 227 mScrimController.setDozeBehindAlpha(alpha); 228 } 229 } 230 231 private float getDozeAlpha(boolean inFront) { 232 return inFront 233 ? mScrimController.getDozeInFrontAlpha() 234 : mScrimController.getDozeBehindAlpha(); 235 } 236 237 private final Runnable mPulseIn = new Runnable() { 238 @Override 239 public void run() { 240 if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason=" 241 + DozeLog.pulseReasonToString(mPulseReason)); 242 if (!mDozing) return; 243 DozeLog.tracePulseStart(mPulseReason); 244 245 // Signal that the pulse is ready to turn the screen on and draw. 246 pulseStarted(); 247 } 248 }; 249 250 private final Runnable mPulseInFinished = new Runnable() { 251 @Override 252 public void run() { 253 if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing); 254 if (!mDozing) return; 255 mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); 256 } 257 }; 258 259 private final Runnable mPulseOut = new Runnable() { 260 @Override 261 public void run() { 262 if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing); 263 if (!mDozing) return; 264 startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(), 265 Interpolators.ALPHA_IN, mPulseOutFinished); 266 } 267 }; 268 269 private final Runnable mPulseOutFinished = new Runnable() { 270 @Override 271 public void run() { 272 if (DEBUG) Log.d(TAG, "Pulse out finished"); 273 DozeLog.tracePulseFinish(); 274 275 // Signal that the pulse is all finished so we can turn the screen off now. 276 pulseFinished(); 277 } 278 }; 279} 280