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