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