ScrimController.java revision e8bae6288bf0f241f0cea70f2c5e8294f930d4d8
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.PropertyValuesHolder; 22import android.animation.ValueAnimator; 23import android.content.Context; 24import android.graphics.Color; 25import android.view.View; 26import android.view.ViewTreeObserver; 27import android.view.animation.AnimationUtils; 28import android.view.animation.DecelerateInterpolator; 29import android.view.animation.Interpolator; 30 31import com.android.systemui.R; 32import com.android.systemui.statusbar.BackDropView; 33import com.android.systemui.statusbar.ExpandableNotificationRow; 34import com.android.systemui.statusbar.NotificationData; 35import com.android.systemui.statusbar.ScrimView; 36import com.android.systemui.statusbar.policy.HeadsUpManager; 37import com.android.systemui.statusbar.stack.StackStateAnimator; 38 39/** 40 * Controls both the scrim behind the notifications and in front of the notifications (when a 41 * security method gets shown). 42 */ 43public class ScrimController implements ViewTreeObserver.OnPreDrawListener, 44 HeadsUpManager.OnHeadsUpChangedListener { 45 public static final long ANIMATION_DURATION = 220; 46 47 private static final float SCRIM_BEHIND_ALPHA = 0.62f; 48 private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f; 49 private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; 50 private static final float SCRIM_IN_FRONT_ALPHA = 0.75f; 51 private static final int TAG_KEY_ANIM = R.id.scrim; 52 private static final int TAG_HUN_START_ALPHA = R.id.hun_scrim_alpha_start; 53 private static final int TAG_HUN_END_ALPHA = R.id.hun_scrim_alpha_end; 54 55 private final ScrimView mScrimBehind; 56 private final ScrimView mScrimInFront; 57 private final UnlockMethodCache mUnlockMethodCache; 58 private final View mHeadsUpScrim; 59 60 private boolean mKeyguardShowing; 61 private float mFraction; 62 63 private boolean mDarkenWhileDragging; 64 private boolean mBouncerShowing; 65 private boolean mAnimateChange; 66 private boolean mUpdatePending; 67 private boolean mExpanding; 68 private boolean mAnimateKeyguardFadingOut; 69 private long mDurationOverride = -1; 70 private long mAnimationDelay; 71 private Runnable mOnAnimationFinished; 72 private boolean mAnimationStarted; 73 private final Interpolator mInterpolator = new DecelerateInterpolator(); 74 private final Interpolator mLinearOutSlowInInterpolator; 75 private BackDropView mBackDropView; 76 private boolean mScrimSrcEnabled; 77 private boolean mDozing; 78 private float mDozeInFrontAlpha; 79 private float mDozeBehindAlpha; 80 private float mCurrentInFrontAlpha; 81 private float mCurrentBehindAlpha; 82 private float mCurrentHeadsUpAlpha = 1; 83 private int mPinnedHeadsUpCount; 84 private float mTopHeadsUpDragAmount; 85 private View mDraggedHeadsUpView; 86 private boolean mForceHideScrims; 87 88 public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim, 89 boolean scrimSrcEnabled) { 90 mScrimBehind = scrimBehind; 91 mScrimInFront = scrimInFront; 92 mHeadsUpScrim = headsUpScrim; 93 final Context context = scrimBehind.getContext(); 94 mUnlockMethodCache = UnlockMethodCache.getInstance(context); 95 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 96 android.R.interpolator.linear_out_slow_in); 97 mScrimSrcEnabled = scrimSrcEnabled; 98 updateHeadsUpScrim(false); 99 } 100 101 public void setKeyguardShowing(boolean showing) { 102 mKeyguardShowing = showing; 103 scheduleUpdate(); 104 } 105 106 public void onTrackingStarted() { 107 mExpanding = true; 108 mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer(); 109 } 110 111 public void onExpandingFinished() { 112 mExpanding = false; 113 } 114 115 public void setPanelExpansion(float fraction) { 116 if (mFraction != fraction) { 117 mFraction = fraction; 118 scheduleUpdate(); 119 if (mPinnedHeadsUpCount != 0) { 120 updateHeadsUpScrim(false); 121 } 122 } 123 } 124 125 public void setBouncerShowing(boolean showing) { 126 mBouncerShowing = showing; 127 mAnimateChange = !mExpanding; 128 scheduleUpdate(); 129 } 130 131 public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) { 132 mAnimateKeyguardFadingOut = true; 133 mDurationOverride = duration; 134 mAnimationDelay = delay; 135 mAnimateChange = true; 136 mOnAnimationFinished = onAnimationFinished; 137 scheduleUpdate(); 138 } 139 140 public void abortKeyguardFadingOut() { 141 if (mAnimateKeyguardFadingOut) { 142 endAnimateKeyguardFadingOut(); 143 } 144 } 145 146 public void animateGoingToFullShade(long delay, long duration) { 147 mDurationOverride = duration; 148 mAnimationDelay = delay; 149 mAnimateChange = true; 150 scheduleUpdate(); 151 } 152 153 public void setDozing(boolean dozing) { 154 mDozing = dozing; 155 scheduleUpdate(); 156 } 157 158 public void setDozeInFrontAlpha(float alpha) { 159 mDozeInFrontAlpha = alpha; 160 updateScrimColor(mScrimInFront); 161 } 162 163 public void setDozeBehindAlpha(float alpha) { 164 mDozeBehindAlpha = alpha; 165 updateScrimColor(mScrimBehind); 166 } 167 168 public float getDozeBehindAlpha() { 169 return mDozeBehindAlpha; 170 } 171 172 public float getDozeInFrontAlpha() { 173 return mDozeInFrontAlpha; 174 } 175 176 private void scheduleUpdate() { 177 if (mUpdatePending) return; 178 179 // Make sure that a frame gets scheduled. 180 mScrimBehind.invalidate(); 181 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 182 mUpdatePending = true; 183 } 184 185 private void updateScrims() { 186 if (mAnimateKeyguardFadingOut || mForceHideScrims) { 187 setScrimInFrontColor(0f); 188 setScrimBehindColor(0f); 189 } else if (!mKeyguardShowing && !mBouncerShowing) { 190 updateScrimNormal(); 191 setScrimInFrontColor(0); 192 } else { 193 updateScrimKeyguard(); 194 } 195 mAnimateChange = false; 196 } 197 198 private void updateScrimKeyguard() { 199 if (mExpanding && mDarkenWhileDragging) { 200 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 201 float fraction = 1 - behindFraction; 202 fraction = (float) Math.pow(fraction, 0.8f); 203 behindFraction = (float) Math.pow(behindFraction, 0.8f); 204 setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); 205 setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); 206 } else if (mBouncerShowing) { 207 setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); 208 setScrimBehindColor(0f); 209 } else { 210 float fraction = Math.max(0, Math.min(mFraction, 1)); 211 setScrimInFrontColor(0f); 212 setScrimBehindColor(fraction 213 * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING) 214 + SCRIM_BEHIND_ALPHA_UNLOCKING); 215 } 216 } 217 218 private void updateScrimNormal() { 219 float frac = mFraction; 220 // let's start this 20% of the way down the screen 221 frac = frac * 1.2f - 0.2f; 222 if (frac <= 0) { 223 setScrimBehindColor(0); 224 } else { 225 // woo, special effects 226 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 227 setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); 228 } 229 } 230 231 private void setScrimBehindColor(float alpha) { 232 setScrimColor(mScrimBehind, alpha); 233 } 234 235 private void setScrimInFrontColor(float alpha) { 236 setScrimColor(mScrimInFront, alpha); 237 if (alpha == 0f) { 238 mScrimInFront.setClickable(false); 239 } else { 240 241 // Eat touch events (unless dozing). 242 mScrimInFront.setClickable(!mDozing); 243 } 244 } 245 246 private void setScrimColor(View scrim, float alpha) { 247 Object runningAnim = scrim.getTag(TAG_KEY_ANIM); 248 if (runningAnim instanceof ValueAnimator) { 249 ((ValueAnimator) runningAnim).cancel(); 250 scrim.setTag(TAG_KEY_ANIM, null); 251 } 252 if (mAnimateChange) { 253 startScrimAnimation(scrim, alpha); 254 } else { 255 setCurrentScrimAlpha(scrim, alpha); 256 updateScrimColor(scrim); 257 } 258 } 259 260 private float getDozeAlpha(View scrim) { 261 return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha; 262 } 263 264 private float getCurrentScrimAlpha(View scrim) { 265 return scrim == mScrimBehind ? mCurrentBehindAlpha 266 : scrim == mScrimInFront ? mCurrentInFrontAlpha 267 : mCurrentHeadsUpAlpha; 268 } 269 270 private void setCurrentScrimAlpha(View scrim, float alpha) { 271 if (scrim == mScrimBehind) { 272 mCurrentBehindAlpha = alpha; 273 } else if (scrim == mScrimInFront) { 274 mCurrentInFrontAlpha = alpha; 275 } else { 276 alpha = Math.max(0.0f, Math.min(1.0f, alpha)); 277 mCurrentHeadsUpAlpha = alpha; 278 } 279 } 280 281 private void updateScrimColor(View scrim) { 282 float alpha1 = getCurrentScrimAlpha(scrim); 283 if (scrim instanceof ScrimView) { 284 float alpha2 = getDozeAlpha(scrim); 285 float alpha = 1 - (1 - alpha1) * (1 - alpha2); 286 ((ScrimView) scrim).setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0)); 287 } else { 288 scrim.setAlpha(alpha1); 289 } 290 } 291 292 private void startScrimAnimation(final View scrim, float target) { 293 float current = getCurrentScrimAlpha(scrim); 294 ValueAnimator anim = ValueAnimator.ofFloat(current, target); 295 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 296 @Override 297 public void onAnimationUpdate(ValueAnimator animation) { 298 float alpha = (float) animation.getAnimatedValue(); 299 setCurrentScrimAlpha(scrim, alpha); 300 updateScrimColor(scrim); 301 } 302 }); 303 anim.setInterpolator(getInterpolator()); 304 anim.setStartDelay(mAnimationDelay); 305 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 306 anim.addListener(new AnimatorListenerAdapter() { 307 @Override 308 public void onAnimationEnd(Animator animation) { 309 if (mOnAnimationFinished != null) { 310 mOnAnimationFinished.run(); 311 mOnAnimationFinished = null; 312 } 313 scrim.setTag(TAG_KEY_ANIM, null); 314 } 315 }); 316 anim.start(); 317 scrim.setTag(TAG_KEY_ANIM, anim); 318 mAnimationStarted = true; 319 } 320 321 private Interpolator getInterpolator() { 322 return mAnimateKeyguardFadingOut ? mLinearOutSlowInInterpolator : mInterpolator; 323 } 324 325 @Override 326 public boolean onPreDraw() { 327 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 328 mUpdatePending = false; 329 updateScrims(); 330 mDurationOverride = -1; 331 mAnimationDelay = 0; 332 333 // Make sure that we always call the listener even if we didn't start an animation. 334 endAnimateKeyguardFadingOut(); 335 mAnimationStarted = false; 336 return true; 337 } 338 339 private void endAnimateKeyguardFadingOut() { 340 mAnimateKeyguardFadingOut = false; 341 if (!mAnimationStarted && mOnAnimationFinished != null) { 342 mOnAnimationFinished.run(); 343 mOnAnimationFinished = null; 344 } 345 } 346 347 public void setBackDropView(BackDropView backDropView) { 348 mBackDropView = backDropView; 349 mBackDropView.setOnVisibilityChangedRunnable(new Runnable() { 350 @Override 351 public void run() { 352 updateScrimBehindDrawingMode(); 353 } 354 }); 355 updateScrimBehindDrawingMode(); 356 } 357 358 private void updateScrimBehindDrawingMode() { 359 boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled; 360 mScrimBehind.setDrawAsSrc(asSrc); 361 } 362 363 @Override 364 public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { 365 } 366 367 @Override 368 public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { 369 mPinnedHeadsUpCount++; 370 updateHeadsUpScrim(true); 371 } 372 373 @Override 374 public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { 375 mPinnedHeadsUpCount--; 376 if (headsUp == mDraggedHeadsUpView) { 377 mDraggedHeadsUpView = null; 378 mTopHeadsUpDragAmount = 0.0f; 379 } 380 updateHeadsUpScrim(true); 381 } 382 383 @Override 384 public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { 385 } 386 387 private void updateHeadsUpScrim(boolean animate) { 388 float alpha = calculateHeadsUpAlpha(); 389 ValueAnimator previousAnimator = StackStateAnimator.getChildTag(mHeadsUpScrim, 390 TAG_KEY_ANIM); 391 float animEndValue = -1; 392 if (previousAnimator != null) { 393 if (animate || alpha == mCurrentHeadsUpAlpha) { 394 previousAnimator.cancel(); 395 } else { 396 animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_END_ALPHA); 397 } 398 } 399 if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) { 400 if (animate) { 401 startScrimAnimation(mHeadsUpScrim, alpha); 402 mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, mCurrentHeadsUpAlpha); 403 mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha); 404 } else { 405 if (previousAnimator != null) { 406 float previousStartValue = StackStateAnimator.getChildTag(mHeadsUpScrim, 407 TAG_HUN_START_ALPHA); 408 float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, 409 TAG_HUN_END_ALPHA); 410 // we need to increase all animation keyframes of the previous animator by the 411 // relative change to the end value 412 PropertyValuesHolder[] values = previousAnimator.getValues(); 413 float relativeDiff = alpha - previousEndValue; 414 float newStartValue = previousStartValue + relativeDiff; 415 values[0].setFloatValues(newStartValue, alpha); 416 mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, newStartValue); 417 mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha); 418 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 419 } else { 420 // update the alpha directly 421 setCurrentScrimAlpha(mHeadsUpScrim, alpha); 422 updateScrimColor(mHeadsUpScrim); 423 } 424 } 425 } 426 } 427 428 /** 429 * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means 430 * the heads up is in its resting space and 1 means it's fully dragged out. 431 * 432 * @param draggedHeadsUpView the dragged view 433 * @param topHeadsUpDragAmount how far is it dragged 434 */ 435 public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) { 436 mTopHeadsUpDragAmount = topHeadsUpDragAmount; 437 mDraggedHeadsUpView = draggedHeadsUpView; 438 updateHeadsUpScrim(false); 439 } 440 441 private float calculateHeadsUpAlpha() { 442 float alpha; 443 if (mPinnedHeadsUpCount >= 2) { 444 alpha = 1.0f; 445 } else if (mPinnedHeadsUpCount == 0) { 446 alpha = 0.0f; 447 } else { 448 alpha = 1.0f - mTopHeadsUpDragAmount; 449 } 450 float expandFactor = (1.0f - mFraction); 451 expandFactor = Math.max(expandFactor, 0.0f); 452 return alpha * expandFactor; 453 } 454 455 public void forceHideScrims(boolean hide) { 456 mForceHideScrims = hide; 457 mAnimateChange = false; 458 scheduleUpdate(); 459 } 460} 461