ScrimController.java revision 37c110fa14aed2bb54f685f3855feb30000322fa
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.isCurrentlyInsecure(); 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 animateGoingToFullShade(long delay, long duration) { 141 mDurationOverride = duration; 142 mAnimationDelay = delay; 143 mAnimateChange = true; 144 scheduleUpdate(); 145 } 146 147 public void setDozing(boolean dozing) { 148 mDozing = dozing; 149 scheduleUpdate(); 150 } 151 152 public void setDozeInFrontAlpha(float alpha) { 153 mDozeInFrontAlpha = alpha; 154 updateScrimColor(mScrimInFront); 155 } 156 157 public void setDozeBehindAlpha(float alpha) { 158 mDozeBehindAlpha = alpha; 159 updateScrimColor(mScrimBehind); 160 } 161 162 public float getDozeBehindAlpha() { 163 return mDozeBehindAlpha; 164 } 165 166 public float getDozeInFrontAlpha() { 167 return mDozeInFrontAlpha; 168 } 169 170 private void scheduleUpdate() { 171 if (mUpdatePending) return; 172 173 // Make sure that a frame gets scheduled. 174 mScrimBehind.invalidate(); 175 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 176 mUpdatePending = true; 177 } 178 179 private void updateScrims() { 180 if (mAnimateKeyguardFadingOut || mForceHideScrims) { 181 setScrimInFrontColor(0f); 182 setScrimBehindColor(0f); 183 } else if (!mKeyguardShowing && !mBouncerShowing) { 184 updateScrimNormal(); 185 setScrimInFrontColor(0); 186 } else { 187 updateScrimKeyguard(); 188 } 189 mAnimateChange = false; 190 } 191 192 private void updateScrimKeyguard() { 193 if (mExpanding && mDarkenWhileDragging) { 194 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 195 float fraction = 1 - behindFraction; 196 fraction = (float) Math.pow(fraction, 0.8f); 197 behindFraction = (float) Math.pow(behindFraction, 0.8f); 198 setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); 199 setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); 200 } else if (mBouncerShowing) { 201 setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); 202 setScrimBehindColor(0f); 203 } else { 204 float fraction = Math.max(0, Math.min(mFraction, 1)); 205 setScrimInFrontColor(0f); 206 setScrimBehindColor(fraction 207 * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING) 208 + SCRIM_BEHIND_ALPHA_UNLOCKING); 209 } 210 } 211 212 private void updateScrimNormal() { 213 float frac = mFraction; 214 // let's start this 20% of the way down the screen 215 frac = frac * 1.2f - 0.2f; 216 if (frac <= 0) { 217 setScrimBehindColor(0); 218 } else { 219 // woo, special effects 220 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 221 setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); 222 } 223 } 224 225 private void setScrimBehindColor(float alpha) { 226 setScrimColor(mScrimBehind, alpha); 227 } 228 229 private void setScrimInFrontColor(float alpha) { 230 setScrimColor(mScrimInFront, alpha); 231 if (alpha == 0f) { 232 mScrimInFront.setClickable(false); 233 } else { 234 235 // Eat touch events (unless dozing). 236 mScrimInFront.setClickable(!mDozing); 237 } 238 } 239 240 private void setScrimColor(View scrim, float alpha) { 241 Object runningAnim = scrim.getTag(TAG_KEY_ANIM); 242 if (runningAnim instanceof ValueAnimator) { 243 ((ValueAnimator) runningAnim).cancel(); 244 scrim.setTag(TAG_KEY_ANIM, null); 245 } 246 if (mAnimateChange) { 247 startScrimAnimation(scrim, alpha); 248 } else { 249 setCurrentScrimAlpha(scrim, alpha); 250 updateScrimColor(scrim); 251 } 252 } 253 254 private float getDozeAlpha(View scrim) { 255 return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha; 256 } 257 258 private float getCurrentScrimAlpha(View scrim) { 259 return scrim == mScrimBehind ? mCurrentBehindAlpha 260 : scrim == mScrimInFront ? mCurrentInFrontAlpha 261 : mCurrentHeadsUpAlpha; 262 } 263 264 private void setCurrentScrimAlpha(View scrim, float alpha) { 265 if (scrim == mScrimBehind) { 266 mCurrentBehindAlpha = alpha; 267 } else if (scrim == mScrimInFront) { 268 mCurrentInFrontAlpha = alpha; 269 } else { 270 alpha = Math.max(0.0f, Math.min(1.0f, alpha)); 271 mCurrentHeadsUpAlpha = alpha; 272 } 273 } 274 275 private void updateScrimColor(View scrim) { 276 float alpha1 = getCurrentScrimAlpha(scrim); 277 if (scrim instanceof ScrimView) { 278 float alpha2 = getDozeAlpha(scrim); 279 float alpha = 1 - (1 - alpha1) * (1 - alpha2); 280 ((ScrimView) scrim).setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0)); 281 } else { 282 scrim.setAlpha(alpha1); 283 } 284 } 285 286 private void startScrimAnimation(final View scrim, float target) { 287 float current = getCurrentScrimAlpha(scrim); 288 ValueAnimator anim = ValueAnimator.ofFloat(current, target); 289 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 290 @Override 291 public void onAnimationUpdate(ValueAnimator animation) { 292 float alpha = (float) animation.getAnimatedValue(); 293 setCurrentScrimAlpha(scrim, alpha); 294 updateScrimColor(scrim); 295 } 296 }); 297 anim.setInterpolator(getInterpolator()); 298 anim.setStartDelay(mAnimationDelay); 299 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 300 anim.addListener(new AnimatorListenerAdapter() { 301 @Override 302 public void onAnimationEnd(Animator animation) { 303 if (mOnAnimationFinished != null) { 304 mOnAnimationFinished.run(); 305 mOnAnimationFinished = null; 306 } 307 scrim.setTag(TAG_KEY_ANIM, null); 308 } 309 }); 310 anim.start(); 311 scrim.setTag(TAG_KEY_ANIM, anim); 312 mAnimationStarted = true; 313 } 314 315 private Interpolator getInterpolator() { 316 return mAnimateKeyguardFadingOut ? mLinearOutSlowInInterpolator : mInterpolator; 317 } 318 319 @Override 320 public boolean onPreDraw() { 321 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 322 mUpdatePending = false; 323 updateScrims(); 324 mAnimateKeyguardFadingOut = false; 325 mDurationOverride = -1; 326 mAnimationDelay = 0; 327 328 // Make sure that we always call the listener even if we didn't start an animation. 329 if (!mAnimationStarted && mOnAnimationFinished != null) { 330 mOnAnimationFinished.run(); 331 mOnAnimationFinished = null; 332 } 333 mAnimationStarted = false; 334 return true; 335 } 336 337 public void setBackDropView(BackDropView backDropView) { 338 mBackDropView = backDropView; 339 mBackDropView.setOnVisibilityChangedRunnable(new Runnable() { 340 @Override 341 public void run() { 342 updateScrimBehindDrawingMode(); 343 } 344 }); 345 updateScrimBehindDrawingMode(); 346 } 347 348 private void updateScrimBehindDrawingMode() { 349 boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled; 350 mScrimBehind.setDrawAsSrc(asSrc); 351 } 352 353 @Override 354 public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { 355 } 356 357 @Override 358 public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { 359 mPinnedHeadsUpCount++; 360 updateHeadsUpScrim(true); 361 } 362 363 @Override 364 public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { 365 mPinnedHeadsUpCount--; 366 if (headsUp == mDraggedHeadsUpView) { 367 mDraggedHeadsUpView = null; 368 mTopHeadsUpDragAmount = 0.0f; 369 } 370 updateHeadsUpScrim(true); 371 } 372 373 @Override 374 public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { 375 } 376 377 private void updateHeadsUpScrim(boolean animate) { 378 float alpha = calculateHeadsUpAlpha(); 379 ValueAnimator previousAnimator = StackStateAnimator.getChildTag(mHeadsUpScrim, 380 TAG_KEY_ANIM); 381 float animEndValue = -1; 382 if (previousAnimator != null) { 383 if (animate || alpha == mCurrentHeadsUpAlpha) { 384 previousAnimator.cancel(); 385 } else { 386 animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_END_ALPHA); 387 } 388 } 389 if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) { 390 if (animate) { 391 startScrimAnimation(mHeadsUpScrim, alpha); 392 mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, mCurrentHeadsUpAlpha); 393 mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha); 394 } else { 395 if (previousAnimator != null) { 396 float previousStartValue = StackStateAnimator.getChildTag(mHeadsUpScrim, 397 TAG_HUN_START_ALPHA); 398 float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, 399 TAG_HUN_END_ALPHA); 400 // we need to increase all animation keyframes of the previous animator by the 401 // relative change to the end value 402 PropertyValuesHolder[] values = previousAnimator.getValues(); 403 float relativeDiff = alpha - previousEndValue; 404 float newStartValue = previousStartValue + relativeDiff; 405 values[0].setFloatValues(newStartValue, alpha); 406 mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, newStartValue); 407 mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha); 408 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 409 } else { 410 // update the alpha directly 411 setCurrentScrimAlpha(mHeadsUpScrim, alpha); 412 updateScrimColor(mHeadsUpScrim); 413 } 414 } 415 } 416 } 417 418 /** 419 * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means 420 * the heads up is in its resting space and 1 means it's fully dragged out. 421 * 422 * @param draggedHeadsUpView the dragged view 423 * @param topHeadsUpDragAmount how far is it dragged 424 */ 425 public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) { 426 mTopHeadsUpDragAmount = topHeadsUpDragAmount; 427 mDraggedHeadsUpView = draggedHeadsUpView; 428 updateHeadsUpScrim(false); 429 } 430 431 private float calculateHeadsUpAlpha() { 432 float alpha; 433 if (mPinnedHeadsUpCount >= 2) { 434 alpha = 1.0f; 435 } else if (mPinnedHeadsUpCount == 0) { 436 alpha = 0.0f; 437 } else { 438 alpha = 1.0f - mTopHeadsUpDragAmount; 439 } 440 float expandFactor = (1.0f - mFraction); 441 expandFactor = Math.max(expandFactor, 0.0f); 442 return alpha * expandFactor; 443 } 444 445 public void forceHideScrims(boolean hide) { 446 mForceHideScrims = hide; 447 mAnimateChange = false; 448 scheduleUpdate(); 449 } 450} 451