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