ScrimController.java revision ed69bd661f5152cdcb2d8359efb592234a45fc49
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 75 private final Interpolator mInterpolator = new DecelerateInterpolator(); 76 77 public ScrimController(View scrimBehind, View scrimInFront) { 78 mScrimBehind = scrimBehind; 79 mScrimInFront = scrimInFront; 80 mUnlockMethodCache = UnlockMethodCache.getInstance(scrimBehind.getContext()); 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 setDozing(boolean dozing) { 120 if (mDozing == dozing) return; 121 mDozing = dozing; 122 if (!mDozing) { 123 cancelTeasing(); 124 } 125 scheduleUpdate(); 126 } 127 128 /** When dozing, fade screen contents in and out a few times using the front scrim. */ 129 public long tease() { 130 if (!mDozing) return 0; 131 mTeasesRemaining = NUM_TEASES; 132 mScrimInFront.postDelayed(mTeaseIn, PRE_TEASE_DELAY); 133 return PRE_TEASE_DELAY + NUM_TEASES * TEASE_DURATION; 134 } 135 136 private void cancelTeasing() { 137 mTeasesRemaining = 0; 138 mScrimInFront.removeCallbacks(mTeaseIn); 139 mScrimInFront.removeCallbacks(mTeaseOut); 140 } 141 142 private void scheduleUpdate() { 143 if (mUpdatePending) return; 144 145 // Make sure that a frame gets scheduled. 146 mScrimBehind.invalidate(); 147 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 148 mUpdatePending = true; 149 } 150 151 private void updateScrims() { 152 if ((!mKeyguardShowing && !mBouncerShowing) || mAnimateKeyguardFadingOut) { 153 updateScrimNormal(); 154 setScrimInFrontColor(0); 155 } else { 156 updateScrimKeyguard(); 157 } 158 mAnimateChange = false; 159 } 160 161 private void updateScrimKeyguard() { 162 if (mExpanding && mDarkenWhileDragging) { 163 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 164 float fraction = 1 - behindFraction; 165 setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); 166 setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); 167 } else if (mBouncerShowing) { 168 setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); 169 setScrimBehindColor(0f); 170 } else if (mDozing) { 171 setScrimInFrontColor(1); 172 } else { 173 setScrimInFrontColor(0f); 174 setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD); 175 } 176 } 177 178 private void updateScrimNormal() { 179 float frac = mFraction; 180 // let's start this 20% of the way down the screen 181 frac = frac * 1.2f - 0.2f; 182 if (frac <= 0) { 183 setScrimBehindColor(0); 184 } else { 185 // woo, special effects 186 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 187 setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); 188 } 189 } 190 191 private void setScrimBehindColor(float alpha) { 192 setScrimColor(mScrimBehind, alpha); 193 } 194 195 private void setScrimInFrontColor(float alpha) { 196 setScrimColor(mScrimInFront, alpha); 197 if (alpha == 0f) { 198 mScrimInFront.setClickable(false); 199 } else { 200 201 // Eat touch events. 202 mScrimInFront.setClickable(true); 203 } 204 } 205 206 private void setScrimColor(View scrim, float alpha) { 207 int color = Color.argb((int) (alpha * 255), 0, 0, 0); 208 if (mAnimateChange) { 209 startScrimAnimation(scrim, color); 210 } else { 211 scrim.setBackgroundColor(color); 212 } 213 } 214 215 private void startScrimAnimation(final View scrim, int targetColor) { 216 int current = getBackgroundAlpha(scrim); 217 int target = Color.alpha(targetColor); 218 if (current == targetColor) { 219 return; 220 } 221 Object runningAnim = scrim.getTag(TAG_KEY_ANIM); 222 if (runningAnim instanceof ValueAnimator) { 223 ((ValueAnimator) runningAnim).cancel(); 224 } 225 ValueAnimator anim = ValueAnimator.ofInt(current, target); 226 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 227 @Override 228 public void onAnimationUpdate(ValueAnimator animation) { 229 int value = (int) animation.getAnimatedValue(); 230 scrim.setBackgroundColor(Color.argb(value, 0, 0, 0)); 231 } 232 }); 233 anim.setInterpolator(mInterpolator); 234 anim.setStartDelay(mAnimationDelay); 235 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 236 anim.addListener(new AnimatorListenerAdapter() { 237 238 @Override 239 public void onAnimationEnd(Animator animation) { 240 if (mOnAnimationFinished != null) { 241 mOnAnimationFinished.run(); 242 mOnAnimationFinished = null; 243 } 244 scrim.setTag(TAG_KEY_ANIM, null); 245 } 246 }); 247 anim.start(); 248 scrim.setTag(TAG_KEY_ANIM, anim); 249 mAnimationStarted = true; 250 } 251 252 private int getBackgroundAlpha(View scrim) { 253 if (scrim.getBackground() instanceof ColorDrawable) { 254 ColorDrawable drawable = (ColorDrawable) scrim.getBackground(); 255 return Color.alpha(drawable.getColor()); 256 } else { 257 return 0; 258 } 259 } 260 261 @Override 262 public boolean onPreDraw() { 263 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 264 mUpdatePending = false; 265 updateScrims(); 266 mAnimateKeyguardFadingOut = false; 267 mDurationOverride = -1; 268 mAnimationDelay = 0; 269 270 // Make sure that we always call the listener even if we didn't start an animation. 271 if (!mAnimationStarted && mOnAnimationFinished != null) { 272 mOnAnimationFinished.run(); 273 mOnAnimationFinished = null; 274 } 275 mAnimationStarted = false; 276 return true; 277 } 278 279 private final Runnable mTeaseIn = new Runnable() { 280 @Override 281 public void run() { 282 if (DEBUG) Log.d(TAG, "Tease in, mDozing=" + mDozing 283 + " mTeasesRemaining=" + mTeasesRemaining); 284 if (!mDozing || mTeasesRemaining == 0) return; 285 mTeasesRemaining--; 286 mDurationOverride = TEASE_IN_ANIMATION_DURATION; 287 mAnimationDelay = 0; 288 mAnimateChange = true; 289 mOnAnimationFinished = mTeaseInFinished; 290 setScrimColor(mScrimInFront, 0); 291 } 292 }; 293 294 private final Runnable mTeaseInFinished = new Runnable() { 295 @Override 296 public void run() { 297 if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing); 298 if (!mDozing) return; 299 mScrimInFront.postDelayed(mTeaseOut, TEASE_VISIBLE_DURATION); 300 } 301 }; 302 303 private final Runnable mTeaseOut = new Runnable() { 304 @Override 305 public void run() { 306 if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing); 307 if (!mDozing) return; 308 mDurationOverride = TEASE_OUT_ANIMATION_DURATION; 309 mAnimationDelay = 0; 310 mAnimateChange = true; 311 mOnAnimationFinished = mTeaseOutFinished; 312 setScrimColor(mScrimInFront, 1); 313 } 314 }; 315 316 private final Runnable mTeaseOutFinished = new Runnable() { 317 @Override 318 public void run() { 319 if (DEBUG) Log.d(TAG, "Tease out finished, mTeasesRemaining=" + mTeasesRemaining); 320 if (mTeasesRemaining > 0) { 321 mScrimInFront.postDelayed(mTeaseIn, TEASE_INVISIBLE_DURATION); 322 } 323 } 324 }; 325} 326