ScrimController.java revision aac932591d7aa05bae61d2b47ed7647f35da0001
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.55f; 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 mAmountOfPinnedHeadsUps; 84 private float mTopHeadsUpDragAmount; 85 private View mDraggedHeadsUpView; 86 87 public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim, 88 boolean scrimSrcEnabled) { 89 mScrimBehind = scrimBehind; 90 mScrimInFront = scrimInFront; 91 mHeadsUpScrim = headsUpScrim; 92 final Context context = scrimBehind.getContext(); 93 mUnlockMethodCache = UnlockMethodCache.getInstance(context); 94 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 95 android.R.interpolator.linear_out_slow_in); 96 mScrimSrcEnabled = scrimSrcEnabled; 97 updateHeadsUpScrim(false); 98 } 99 100 public void setKeyguardShowing(boolean showing) { 101 mKeyguardShowing = showing; 102 scheduleUpdate(); 103 } 104 105 public void onTrackingStarted() { 106 mExpanding = true; 107 mDarkenWhileDragging = !mUnlockMethodCache.isCurrentlyInsecure(); 108 } 109 110 public void onExpandingFinished() { 111 mExpanding = false; 112 } 113 114 public void setPanelExpansion(float fraction) { 115 if (mFraction != fraction) { 116 mFraction = fraction; 117 scheduleUpdate(); 118 } 119 } 120 121 public void setBouncerShowing(boolean showing) { 122 mBouncerShowing = showing; 123 mAnimateChange = !mExpanding; 124 scheduleUpdate(); 125 } 126 127 public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) { 128 mAnimateKeyguardFadingOut = true; 129 mDurationOverride = duration; 130 mAnimationDelay = delay; 131 mAnimateChange = true; 132 mOnAnimationFinished = onAnimationFinished; 133 scheduleUpdate(); 134 } 135 136 public void animateGoingToFullShade(long delay, long duration) { 137 mDurationOverride = duration; 138 mAnimationDelay = delay; 139 mAnimateChange = true; 140 scheduleUpdate(); 141 } 142 143 public void setDozing(boolean dozing) { 144 mDozing = dozing; 145 scheduleUpdate(); 146 } 147 148 public void setDozeInFrontAlpha(float alpha) { 149 mDozeInFrontAlpha = alpha; 150 updateScrimColor(mScrimInFront); 151 } 152 153 public void setDozeBehindAlpha(float alpha) { 154 mDozeBehindAlpha = alpha; 155 updateScrimColor(mScrimBehind); 156 } 157 158 public float getDozeBehindAlpha() { 159 return mDozeBehindAlpha; 160 } 161 162 public float getDozeInFrontAlpha() { 163 return mDozeInFrontAlpha; 164 } 165 166 private void scheduleUpdate() { 167 if (mUpdatePending) return; 168 169 // Make sure that a frame gets scheduled. 170 mScrimBehind.invalidate(); 171 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 172 mUpdatePending = true; 173 } 174 175 private void updateScrims() { 176 if (mAnimateKeyguardFadingOut) { 177 setScrimInFrontColor(0f); 178 setScrimBehindColor(0f); 179 } else if (!mKeyguardShowing && !mBouncerShowing) { 180 updateScrimNormal(); 181 setScrimInFrontColor(0); 182 } else { 183 updateScrimKeyguard(); 184 } 185 mAnimateChange = false; 186 } 187 188 private void updateScrimKeyguard() { 189 if (mExpanding && mDarkenWhileDragging) { 190 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 191 float fraction = 1 - behindFraction; 192 fraction = (float) Math.pow(fraction, 0.8f); 193 behindFraction = (float) Math.pow(behindFraction, 0.8f); 194 setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); 195 setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); 196 } else if (mBouncerShowing) { 197 setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); 198 setScrimBehindColor(0f); 199 } else { 200 float fraction = Math.max(0, Math.min(mFraction, 1)); 201 setScrimInFrontColor(0f); 202 setScrimBehindColor(fraction 203 * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING) 204 + SCRIM_BEHIND_ALPHA_UNLOCKING); 205 } 206 } 207 208 private void updateScrimNormal() { 209 float frac = mFraction; 210 // let's start this 20% of the way down the screen 211 frac = frac * 1.2f - 0.2f; 212 if (frac <= 0) { 213 setScrimBehindColor(0); 214 } else { 215 // woo, special effects 216 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 217 setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); 218 } 219 } 220 221 private void setScrimBehindColor(float alpha) { 222 setScrimColor(mScrimBehind, alpha); 223 } 224 225 private void setScrimInFrontColor(float alpha) { 226 setScrimColor(mScrimInFront, alpha); 227 if (alpha == 0f) { 228 mScrimInFront.setClickable(false); 229 } else { 230 231 // Eat touch events (unless dozing). 232 mScrimInFront.setClickable(!mDozing); 233 } 234 } 235 236 private void setScrimColor(View scrim, float alpha) { 237 Object runningAnim = scrim.getTag(TAG_KEY_ANIM); 238 if (runningAnim instanceof ValueAnimator) { 239 ((ValueAnimator) runningAnim).cancel(); 240 scrim.setTag(TAG_KEY_ANIM, null); 241 } 242 if (mAnimateChange) { 243 startScrimAnimation(scrim, alpha); 244 } else { 245 setCurrentScrimAlpha(scrim, alpha); 246 updateScrimColor(scrim); 247 } 248 } 249 250 private float getDozeAlpha(View scrim) { 251 return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha; 252 } 253 254 private float getCurrentScrimAlpha(View scrim) { 255 return scrim == mScrimBehind ? mCurrentBehindAlpha 256 : scrim == mScrimInFront ? mCurrentInFrontAlpha 257 : mCurrentHeadsUpAlpha; 258 } 259 260 private void setCurrentScrimAlpha(View scrim, float alpha) { 261 if (scrim == mScrimBehind) { 262 mCurrentBehindAlpha = alpha; 263 } else if (scrim == mScrimInFront) { 264 mCurrentInFrontAlpha = alpha; 265 } else { 266 alpha = Math.max(0.0f, Math.min(1.0f, alpha)); 267 mCurrentHeadsUpAlpha = alpha; 268 } 269 } 270 271 private void updateScrimColor(View scrim) { 272 float alpha1 = getCurrentScrimAlpha(scrim); 273 if (scrim instanceof ScrimView) { 274 float alpha2 = getDozeAlpha(scrim); 275 float alpha = 1 - (1 - alpha1) * (1 - alpha2); 276 ((ScrimView) scrim).setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0)); 277 } else { 278 scrim.setAlpha(alpha1); 279 } 280 } 281 282 private void startScrimAnimation(final View scrim, float target) { 283 float current = getCurrentScrimAlpha(scrim); 284 ValueAnimator anim = ValueAnimator.ofFloat(current, target); 285 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 286 @Override 287 public void onAnimationUpdate(ValueAnimator animation) { 288 float alpha = (float) animation.getAnimatedValue(); 289 setCurrentScrimAlpha(scrim, alpha); 290 updateScrimColor(scrim); 291 } 292 }); 293 anim.setInterpolator(getInterpolator()); 294 anim.setStartDelay(mAnimationDelay); 295 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 296 anim.addListener(new AnimatorListenerAdapter() { 297 @Override 298 public void onAnimationEnd(Animator animation) { 299 if (mOnAnimationFinished != null) { 300 mOnAnimationFinished.run(); 301 mOnAnimationFinished = null; 302 } 303 scrim.setTag(TAG_KEY_ANIM, null); 304 } 305 }); 306 anim.start(); 307 scrim.setTag(TAG_KEY_ANIM, anim); 308 mAnimationStarted = true; 309 } 310 311 private Interpolator getInterpolator() { 312 return mAnimateKeyguardFadingOut ? mLinearOutSlowInInterpolator : mInterpolator; 313 } 314 315 @Override 316 public boolean onPreDraw() { 317 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 318 mUpdatePending = false; 319 updateScrims(); 320 mAnimateKeyguardFadingOut = false; 321 mDurationOverride = -1; 322 mAnimationDelay = 0; 323 324 // Make sure that we always call the listener even if we didn't start an animation. 325 if (!mAnimationStarted && mOnAnimationFinished != null) { 326 mOnAnimationFinished.run(); 327 mOnAnimationFinished = null; 328 } 329 mAnimationStarted = false; 330 return true; 331 } 332 333 public void setBackDropView(BackDropView backDropView) { 334 mBackDropView = backDropView; 335 mBackDropView.setOnVisibilityChangedRunnable(new Runnable() { 336 @Override 337 public void run() { 338 updateScrimBehindDrawingMode(); 339 } 340 }); 341 updateScrimBehindDrawingMode(); 342 } 343 344 private void updateScrimBehindDrawingMode() { 345 boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled; 346 mScrimBehind.setDrawAsSrc(asSrc); 347 } 348 349 @Override 350 public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) { 351 } 352 353 @Override 354 public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) { 355 if (isHeadsUp) { 356 mAmountOfPinnedHeadsUps++; 357 } else { 358 mAmountOfPinnedHeadsUps--; 359 if (headsUp == mDraggedHeadsUpView) { 360 mDraggedHeadsUpView = null; 361 mTopHeadsUpDragAmount = 0.0f; 362 } 363 } 364 updateHeadsUpScrim(true); 365 } 366 367 @Override 368 public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { 369 } 370 371 private void updateHeadsUpScrim(boolean animate) { 372 float alpha = calculateHeadsUpAlpha(); 373 ValueAnimator previousAnimator = StackStateAnimator.getChildTag(mHeadsUpScrim, 374 TAG_KEY_ANIM); 375 float animEndValue = -1; 376 if (previousAnimator != null) { 377 if ((animate || alpha == mCurrentHeadsUpAlpha)) { 378 // lets cancel any running animators 379 previousAnimator.cancel(); 380 } 381 animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, 382 TAG_HUN_START_ALPHA); 383 } 384 if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) { 385 if (animate) { 386 startScrimAnimation(mHeadsUpScrim, alpha); 387 mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, mCurrentHeadsUpAlpha); 388 mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha); 389 } else { 390 if (previousAnimator != null) { 391 float previousStartValue = StackStateAnimator.getChildTag(mHeadsUpScrim, 392 TAG_HUN_START_ALPHA); 393 float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, 394 TAG_HUN_END_ALPHA); 395 // we need to increase all animation keyframes of the previous animator by the 396 // relative change to the end value 397 PropertyValuesHolder[] values = previousAnimator.getValues(); 398 float relativeDiff = alpha - previousEndValue; 399 float newStartValue = previousStartValue + relativeDiff; 400 values[0].setFloatValues(newStartValue, alpha); 401 mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, newStartValue); 402 mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha); 403 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 404 } else { 405 // update the alpha directly 406 setCurrentScrimAlpha(mHeadsUpScrim, alpha); 407 updateScrimColor(mHeadsUpScrim); 408 } 409 } 410 } 411 } 412 413 public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) { 414 mTopHeadsUpDragAmount = topHeadsUpDragAmount; 415 mDraggedHeadsUpView = draggedHeadsUpView; 416 updateHeadsUpScrim(false); 417 } 418 419 private float calculateHeadsUpAlpha() { 420 if (mAmountOfPinnedHeadsUps >= 2) { 421 return 1.0f; 422 } else if (mAmountOfPinnedHeadsUps == 0) { 423 return 0.0f; 424 } else { 425 return 1.0f - mTopHeadsUpDragAmount; 426 } 427 } 428} 429