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.ValueAnimator; 22 import android.content.Context; 23 import android.graphics.Color; 24 import android.view.View; 25 import android.view.ViewTreeObserver; 26 import android.view.animation.AnimationUtils; 27 import android.view.animation.DecelerateInterpolator; 28 import android.view.animation.Interpolator; 29 30 import com.android.systemui.R; 31 import com.android.systemui.statusbar.BackDropView; 32 import com.android.systemui.statusbar.ScrimView; 33 34 /** 35 * Controls both the scrim behind the notifications and in front of the notifications (when a 36 * security method gets shown). 37 */ 38 public class ScrimController implements ViewTreeObserver.OnPreDrawListener { 39 public static final long ANIMATION_DURATION = 220; 40 41 private static final float SCRIM_BEHIND_ALPHA = 0.62f; 42 private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.55f; 43 private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; 44 private static final float SCRIM_IN_FRONT_ALPHA = 0.75f; 45 private static final int TAG_KEY_ANIM = R.id.scrim; 46 47 private final ScrimView mScrimBehind; 48 private final ScrimView mScrimInFront; 49 private final UnlockMethodCache mUnlockMethodCache; 50 51 private boolean mKeyguardShowing; 52 private float mFraction; 53 54 private boolean mDarkenWhileDragging; 55 private boolean mBouncerShowing; 56 private boolean mAnimateChange; 57 private boolean mUpdatePending; 58 private boolean mExpanding; 59 private boolean mAnimateKeyguardFadingOut; 60 private long mDurationOverride = -1; 61 private long mAnimationDelay; 62 private Runnable mOnAnimationFinished; 63 private boolean mAnimationStarted; 64 private final Interpolator mInterpolator = new DecelerateInterpolator(); 65 private final Interpolator mLinearOutSlowInInterpolator; 66 private BackDropView mBackDropView; 67 private boolean mScrimSrcEnabled; 68 private boolean mDozing; 69 private float mDozeInFrontAlpha; 70 private float mDozeBehindAlpha; 71 private float mCurrentInFrontAlpha; 72 private float mCurrentBehindAlpha; 73 ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, boolean scrimSrcEnabled)74 public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, boolean scrimSrcEnabled) { 75 mScrimBehind = scrimBehind; 76 mScrimInFront = scrimInFront; 77 final Context context = scrimBehind.getContext(); 78 mUnlockMethodCache = UnlockMethodCache.getInstance(context); 79 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 80 android.R.interpolator.linear_out_slow_in); 81 mScrimSrcEnabled = scrimSrcEnabled; 82 } 83 setKeyguardShowing(boolean showing)84 public void setKeyguardShowing(boolean showing) { 85 mKeyguardShowing = showing; 86 scheduleUpdate(); 87 } 88 onTrackingStarted()89 public void onTrackingStarted() { 90 mExpanding = true; 91 mDarkenWhileDragging = !mUnlockMethodCache.isCurrentlyInsecure(); 92 } 93 onExpandingFinished()94 public void onExpandingFinished() { 95 mExpanding = false; 96 } 97 setPanelExpansion(float fraction)98 public void setPanelExpansion(float fraction) { 99 if (mFraction != fraction) { 100 mFraction = fraction; 101 scheduleUpdate(); 102 } 103 } 104 setBouncerShowing(boolean showing)105 public void setBouncerShowing(boolean showing) { 106 mBouncerShowing = showing; 107 mAnimateChange = !mExpanding; 108 scheduleUpdate(); 109 } 110 animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished)111 public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) { 112 mAnimateKeyguardFadingOut = true; 113 mDurationOverride = duration; 114 mAnimationDelay = delay; 115 mAnimateChange = true; 116 mOnAnimationFinished = onAnimationFinished; 117 scheduleUpdate(); 118 } 119 animateGoingToFullShade(long delay, long duration)120 public void animateGoingToFullShade(long delay, long duration) { 121 mDurationOverride = duration; 122 mAnimationDelay = delay; 123 mAnimateChange = true; 124 scheduleUpdate(); 125 } 126 setDozing(boolean dozing)127 public void setDozing(boolean dozing) { 128 mDozing = dozing; 129 scheduleUpdate(); 130 } 131 setDozeInFrontAlpha(float alpha)132 public void setDozeInFrontAlpha(float alpha) { 133 mDozeInFrontAlpha = alpha; 134 updateScrimColor(mScrimInFront); 135 } 136 setDozeBehindAlpha(float alpha)137 public void setDozeBehindAlpha(float alpha) { 138 mDozeBehindAlpha = alpha; 139 updateScrimColor(mScrimBehind); 140 } 141 getDozeBehindAlpha()142 public float getDozeBehindAlpha() { 143 return mDozeBehindAlpha; 144 } 145 getDozeInFrontAlpha()146 public float getDozeInFrontAlpha() { 147 return mDozeInFrontAlpha; 148 } 149 scheduleUpdate()150 private void scheduleUpdate() { 151 if (mUpdatePending) return; 152 153 // Make sure that a frame gets scheduled. 154 mScrimBehind.invalidate(); 155 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 156 mUpdatePending = true; 157 } 158 updateScrims()159 private void updateScrims() { 160 if (mAnimateKeyguardFadingOut) { 161 setScrimInFrontColor(0f); 162 setScrimBehindColor(0f); 163 } else if (!mKeyguardShowing && !mBouncerShowing) { 164 updateScrimNormal(); 165 setScrimInFrontColor(0); 166 } else { 167 updateScrimKeyguard(); 168 } 169 mAnimateChange = false; 170 } 171 updateScrimKeyguard()172 private void updateScrimKeyguard() { 173 if (mExpanding && mDarkenWhileDragging) { 174 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 175 float fraction = 1 - behindFraction; 176 fraction = (float) Math.pow(fraction, 0.8f); 177 behindFraction = (float) Math.pow(behindFraction, 0.8f); 178 setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); 179 setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); 180 } else if (mBouncerShowing) { 181 setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); 182 setScrimBehindColor(0f); 183 } else { 184 float fraction = Math.max(0, Math.min(mFraction, 1)); 185 setScrimInFrontColor(0f); 186 setScrimBehindColor(fraction 187 * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING) 188 + SCRIM_BEHIND_ALPHA_UNLOCKING); 189 } 190 } 191 updateScrimNormal()192 private void updateScrimNormal() { 193 float frac = mFraction; 194 // let's start this 20% of the way down the screen 195 frac = frac * 1.2f - 0.2f; 196 if (frac <= 0) { 197 setScrimBehindColor(0); 198 } else { 199 // woo, special effects 200 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 201 setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); 202 } 203 } 204 setScrimBehindColor(float alpha)205 private void setScrimBehindColor(float alpha) { 206 setScrimColor(mScrimBehind, alpha); 207 } 208 setScrimInFrontColor(float alpha)209 private void setScrimInFrontColor(float alpha) { 210 setScrimColor(mScrimInFront, alpha); 211 if (alpha == 0f) { 212 mScrimInFront.setClickable(false); 213 } else { 214 215 // Eat touch events (unless dozing). 216 mScrimInFront.setClickable(!mDozing); 217 } 218 } 219 setScrimColor(ScrimView scrim, float alpha)220 private void setScrimColor(ScrimView scrim, float alpha) { 221 Object runningAnim = scrim.getTag(TAG_KEY_ANIM); 222 if (runningAnim instanceof ValueAnimator) { 223 ((ValueAnimator) runningAnim).cancel(); 224 scrim.setTag(TAG_KEY_ANIM, null); 225 } 226 if (mAnimateChange) { 227 startScrimAnimation(scrim, alpha); 228 } else { 229 setCurrentScrimAlpha(scrim, alpha); 230 updateScrimColor(scrim); 231 } 232 } 233 getDozeAlpha(View scrim)234 private float getDozeAlpha(View scrim) { 235 return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha; 236 } 237 getCurrentScrimAlpha(View scrim)238 private float getCurrentScrimAlpha(View scrim) { 239 return scrim == mScrimBehind ? mCurrentBehindAlpha : mCurrentInFrontAlpha; 240 } 241 setCurrentScrimAlpha(View scrim, float alpha)242 private void setCurrentScrimAlpha(View scrim, float alpha) { 243 if (scrim == mScrimBehind) { 244 mCurrentBehindAlpha = alpha; 245 } else { 246 mCurrentInFrontAlpha = alpha; 247 } 248 } 249 updateScrimColor(ScrimView scrim)250 private void updateScrimColor(ScrimView scrim) { 251 float alpha1 = getCurrentScrimAlpha(scrim); 252 float alpha2 = getDozeAlpha(scrim); 253 float alpha = 1 - (1 - alpha1) * (1 - alpha2); 254 scrim.setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0)); 255 } 256 startScrimAnimation(final ScrimView scrim, float target)257 private void startScrimAnimation(final ScrimView scrim, float target) { 258 float current = getCurrentScrimAlpha(scrim); 259 ValueAnimator anim = ValueAnimator.ofFloat(current, target); 260 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 261 @Override 262 public void onAnimationUpdate(ValueAnimator animation) { 263 float alpha = (float) animation.getAnimatedValue(); 264 setCurrentScrimAlpha(scrim, alpha); 265 updateScrimColor(scrim); 266 } 267 }); 268 anim.setInterpolator(getInterpolator()); 269 anim.setStartDelay(mAnimationDelay); 270 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 271 anim.addListener(new AnimatorListenerAdapter() { 272 @Override 273 public void onAnimationEnd(Animator animation) { 274 if (mOnAnimationFinished != null) { 275 mOnAnimationFinished.run(); 276 mOnAnimationFinished = null; 277 } 278 scrim.setTag(TAG_KEY_ANIM, null); 279 } 280 }); 281 anim.start(); 282 scrim.setTag(TAG_KEY_ANIM, anim); 283 mAnimationStarted = true; 284 } 285 getInterpolator()286 private Interpolator getInterpolator() { 287 return mAnimateKeyguardFadingOut ? mLinearOutSlowInInterpolator : mInterpolator; 288 } 289 290 @Override onPreDraw()291 public boolean onPreDraw() { 292 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 293 mUpdatePending = false; 294 updateScrims(); 295 mAnimateKeyguardFadingOut = false; 296 mDurationOverride = -1; 297 mAnimationDelay = 0; 298 299 // Make sure that we always call the listener even if we didn't start an animation. 300 if (!mAnimationStarted && mOnAnimationFinished != null) { 301 mOnAnimationFinished.run(); 302 mOnAnimationFinished = null; 303 } 304 mAnimationStarted = false; 305 return true; 306 } 307 setBackDropView(BackDropView backDropView)308 public void setBackDropView(BackDropView backDropView) { 309 mBackDropView = backDropView; 310 mBackDropView.setOnVisibilityChangedRunnable(new Runnable() { 311 @Override 312 public void run() { 313 updateScrimBehindDrawingMode(); 314 } 315 }); 316 updateScrimBehindDrawingMode(); 317 } 318 updateScrimBehindDrawingMode()319 private void updateScrimBehindDrawingMode() { 320 boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled; 321 mScrimBehind.setDrawAsSrc(asSrc); 322 } 323 } 324