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