ScrimController.java revision 190d026167401ed593924dd12ab6e134e2d08e94
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.content.Context; 23import android.graphics.Color; 24import android.graphics.drawable.ColorDrawable; 25import android.util.Log; 26import android.view.View; 27import android.view.ViewTreeObserver; 28import android.view.animation.AnimationUtils; 29import android.view.animation.DecelerateInterpolator; 30import android.view.animation.Interpolator; 31 32import com.android.systemui.R; 33 34/** 35 * Controls both the scrim behind the notifications and in front of the notifications (when a 36 * security method gets shown). 37 */ 38public class ScrimController implements ViewTreeObserver.OnPreDrawListener { 39 private static final String TAG = "ScrimController"; 40 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 41 42 public static final long ANIMATION_DURATION = 220; 43 44 private static final float SCRIM_BEHIND_ALPHA = 0.62f; 45 private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.55f; 46 private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; 47 private static final float SCRIM_IN_FRONT_ALPHA = 0.75f; 48 private static final int TAG_KEY_ANIM = R.id.scrim; 49 50 private final View mScrimBehind; 51 private final View mScrimInFront; 52 private final UnlockMethodCache mUnlockMethodCache; 53 private final DozeParameters mDozeParameters; 54 55 private boolean mKeyguardShowing; 56 private float mFraction; 57 58 private boolean mDarkenWhileDragging; 59 private boolean mBouncerShowing; 60 private boolean mAnimateChange; 61 private boolean mUpdatePending; 62 private boolean mExpanding; 63 private boolean mAnimateKeyguardFadingOut; 64 private long mDurationOverride = -1; 65 private long mAnimationDelay; 66 private Runnable mOnAnimationFinished; 67 private boolean mAnimationStarted; 68 private boolean mDozing; 69 private long mPulseEndTime; 70 private final Interpolator mInterpolator = new DecelerateInterpolator(); 71 private final Interpolator mLinearOutSlowInInterpolator; 72 73 public ScrimController(View scrimBehind, View scrimInFront) { 74 mScrimBehind = scrimBehind; 75 mScrimInFront = scrimInFront; 76 final Context context = scrimBehind.getContext(); 77 mUnlockMethodCache = UnlockMethodCache.getInstance(context); 78 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 79 android.R.interpolator.linear_out_slow_in); 80 mDozeParameters = new DozeParameters(context); 81 } 82 83 public void setKeyguardShowing(boolean showing) { 84 mKeyguardShowing = showing; 85 scheduleUpdate(); 86 } 87 88 public void onTrackingStarted() { 89 mExpanding = true; 90 mDarkenWhileDragging = !mUnlockMethodCache.isMethodInsecure(); 91 } 92 93 public void onExpandingFinished() { 94 mExpanding = false; 95 } 96 97 public void setPanelExpansion(float fraction) { 98 if (mFraction != fraction) { 99 mFraction = fraction; 100 scheduleUpdate(); 101 } 102 } 103 104 public void setBouncerShowing(boolean showing) { 105 mBouncerShowing = showing; 106 mAnimateChange = !mExpanding; 107 scheduleUpdate(); 108 } 109 110 public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) { 111 mAnimateKeyguardFadingOut = true; 112 mDurationOverride = duration; 113 mAnimationDelay = delay; 114 mAnimateChange = true; 115 mOnAnimationFinished = onAnimationFinished; 116 scheduleUpdate(); 117 } 118 119 public void animateGoingToFullShade(long delay, long duration) { 120 mDurationOverride = duration; 121 mAnimationDelay = delay; 122 mAnimateChange = true; 123 scheduleUpdate(); 124 } 125 126 public void setDozing(boolean dozing) { 127 if (mDozing == dozing) return; 128 mDozing = dozing; 129 if (!mDozing) { 130 cancelPulsing(); 131 mAnimateChange = true; 132 } else { 133 mAnimateChange = false; 134 } 135 scheduleUpdate(); 136 } 137 138 /** When dozing, fade screen contents in and out using the front scrim. */ 139 public long pulse() { 140 if (!mDozing) return 0; 141 final long now = System.currentTimeMillis(); 142 if (DEBUG) Log.d(TAG, "pulse mPulseEndTime=" + mPulseEndTime + " now=" + now); 143 if (mPulseEndTime != 0 && mPulseEndTime > now) return mPulseEndTime - now; 144 mScrimInFront.post(mPulseIn); 145 mPulseEndTime = now + mDozeParameters.getPulseDuration(); 146 return mPulseEndTime - now; 147 } 148 149 public boolean isPulsing() { 150 return mDozing && mPulseEndTime != 0; 151 } 152 153 private void cancelPulsing() { 154 if (DEBUG) Log.d(TAG, "Cancel pulsing"); 155 mScrimInFront.removeCallbacks(mPulseIn); 156 mScrimInFront.removeCallbacks(mPulseOut); 157 mPulseEndTime = 0; 158 } 159 160 private void scheduleUpdate() { 161 if (mUpdatePending) return; 162 163 // Make sure that a frame gets scheduled. 164 mScrimBehind.invalidate(); 165 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 166 mUpdatePending = true; 167 } 168 169 private void updateScrims() { 170 if (mAnimateKeyguardFadingOut) { 171 setScrimInFrontColor(0f); 172 setScrimBehindColor(0f); 173 } else if (!mKeyguardShowing && !mBouncerShowing) { 174 updateScrimNormal(); 175 setScrimInFrontColor(0); 176 } else { 177 updateScrimKeyguard(); 178 } 179 mAnimateChange = false; 180 } 181 182 private void updateScrimKeyguard() { 183 if (mExpanding && mDarkenWhileDragging) { 184 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 185 float fraction = 1 - behindFraction; 186 fraction = (float) Math.pow(fraction, 0.8f); 187 behindFraction = (float) Math.pow(behindFraction, 0.8f); 188 setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); 189 setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); 190 } else if (mBouncerShowing) { 191 setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); 192 setScrimBehindColor(0f); 193 } else if (mDozing) { 194 setScrimInFrontColor(1); 195 } else { 196 float fraction = Math.max(0, Math.min(mFraction, 1)); 197 setScrimInFrontColor(0f); 198 setScrimBehindColor(fraction 199 * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING) 200 + SCRIM_BEHIND_ALPHA_UNLOCKING); 201 } 202 } 203 204 private void updateScrimNormal() { 205 float frac = mFraction; 206 // let's start this 20% of the way down the screen 207 frac = frac * 1.2f - 0.2f; 208 if (frac <= 0) { 209 setScrimBehindColor(0); 210 } else { 211 // woo, special effects 212 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 213 setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); 214 } 215 } 216 217 private void setScrimBehindColor(float alpha) { 218 setScrimColor(mScrimBehind, alpha); 219 } 220 221 private void setScrimInFrontColor(float alpha) { 222 setScrimColor(mScrimInFront, alpha); 223 if (alpha == 0f) { 224 mScrimInFront.setClickable(false); 225 } else { 226 227 // Eat touch events (unless dozing). 228 mScrimInFront.setClickable(!mDozing); 229 } 230 } 231 232 private void setScrimColor(View scrim, float alpha) { 233 int color = Color.argb((int) (alpha * 255), 0, 0, 0); 234 if (mAnimateChange) { 235 startScrimAnimation(scrim, color); 236 } else { 237 scrim.setBackgroundColor(color); 238 } 239 } 240 241 private void startScrimAnimation(final View scrim, int targetColor) { 242 int current = getBackgroundAlpha(scrim); 243 int target = Color.alpha(targetColor); 244 if (current == targetColor) { 245 return; 246 } 247 Object runningAnim = scrim.getTag(TAG_KEY_ANIM); 248 if (runningAnim instanceof ValueAnimator) { 249 ((ValueAnimator) runningAnim).cancel(); 250 } 251 ValueAnimator anim = ValueAnimator.ofInt(current, target); 252 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 253 @Override 254 public void onAnimationUpdate(ValueAnimator animation) { 255 int value = (int) animation.getAnimatedValue(); 256 scrim.setBackgroundColor(Color.argb(value, 0, 0, 0)); 257 } 258 }); 259 anim.setInterpolator(mAnimateKeyguardFadingOut 260 ? mLinearOutSlowInInterpolator 261 : mInterpolator); 262 anim.setStartDelay(mAnimationDelay); 263 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 264 anim.addListener(new AnimatorListenerAdapter() { 265 266 @Override 267 public void onAnimationEnd(Animator animation) { 268 if (mOnAnimationFinished != null) { 269 mOnAnimationFinished.run(); 270 mOnAnimationFinished = null; 271 } 272 scrim.setTag(TAG_KEY_ANIM, null); 273 } 274 }); 275 anim.start(); 276 scrim.setTag(TAG_KEY_ANIM, anim); 277 mAnimationStarted = true; 278 } 279 280 private int getBackgroundAlpha(View scrim) { 281 if (scrim.getBackground() instanceof ColorDrawable) { 282 ColorDrawable drawable = (ColorDrawable) scrim.getBackground(); 283 return Color.alpha(drawable.getColor()); 284 } else { 285 return 0; 286 } 287 } 288 289 @Override 290 public boolean onPreDraw() { 291 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 292 mUpdatePending = false; 293 updateScrims(); 294 mAnimateKeyguardFadingOut = false; 295 mDurationOverride = -1; 296 mAnimationDelay = 0; 297 298 // Make sure that we always call the listener even if we didn't start an animation. 299 if (!mAnimationStarted && mOnAnimationFinished != null) { 300 mOnAnimationFinished.run(); 301 mOnAnimationFinished = null; 302 } 303 mAnimationStarted = false; 304 return true; 305 } 306 307 private final Runnable mPulseIn = new Runnable() { 308 @Override 309 public void run() { 310 if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing); 311 if (!mDozing) return; 312 mDurationOverride = mDozeParameters.getPulseInDuration(); 313 mAnimationDelay = 0; 314 mAnimateChange = true; 315 mOnAnimationFinished = mPulseInFinished; 316 setScrimColor(mScrimInFront, 0); 317 } 318 }; 319 320 private final Runnable mPulseInFinished = new Runnable() { 321 @Override 322 public void run() { 323 if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing); 324 if (!mDozing) return; 325 mScrimInFront.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); 326 } 327 }; 328 329 private final Runnable mPulseOut = new Runnable() { 330 @Override 331 public void run() { 332 if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing); 333 if (!mDozing) return; 334 mDurationOverride = mDozeParameters.getPulseOutDuration(); 335 mAnimationDelay = 0; 336 mAnimateChange = true; 337 mOnAnimationFinished = mPulseOutFinished; 338 setScrimColor(mScrimInFront, 1); 339 } 340 }; 341 342 private final Runnable mPulseOutFinished = new Runnable() { 343 @Override 344 public void run() { 345 if (DEBUG) Log.d(TAG, "Pulse out finished"); 346 mPulseEndTime = 0; 347 } 348 }; 349} 350