ScrimController.java revision e29b2dbc762bfa66093d76f5a65f55328d8753c9
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.ValueAnimator; 22import android.graphics.Color; 23import android.graphics.drawable.ColorDrawable; 24import android.view.View; 25import android.view.ViewTreeObserver; 26import android.view.animation.DecelerateInterpolator; 27import android.view.animation.Interpolator; 28 29/** 30 * Controls both the scrim behind the notifications and in front of the notifications (when a 31 * security method gets shown). 32 */ 33public class ScrimController implements ViewTreeObserver.OnPreDrawListener { 34 35 private static final float SCRIM_BEHIND_ALPHA = 0.62f; 36 private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.5f; 37 private static final float SCRIM_IN_FRONT_ALPHA = 0.75f; 38 private static final long ANIMATION_DURATION = 220; 39 40 private final View mScrimBehind; 41 private final View mScrimInFront; 42 private final UnlockMethodCache mUnlockMethodCache; 43 44 private boolean mKeyguardShowing; 45 private float mFraction; 46 47 private boolean mDarkenWhileDragging; 48 private boolean mBouncerShowing; 49 private boolean mAnimateChange; 50 private boolean mUpdatePending; 51 private boolean mExpanding; 52 private boolean mAnimateKeyguardFadingOut; 53 private long mDurationOverride = -1; 54 private long mAnimationDelay; 55 private Runnable mOnAnimationFinished; 56 private boolean mAnimationStarted; 57 58 private final Interpolator mInterpolator = new DecelerateInterpolator(); 59 60 public ScrimController(View scrimBehind, View scrimInFront) { 61 mScrimBehind = scrimBehind; 62 mScrimInFront = scrimInFront; 63 mUnlockMethodCache = UnlockMethodCache.getInstance(scrimBehind.getContext()); 64 } 65 66 public void setKeyguardShowing(boolean showing) { 67 mKeyguardShowing = showing; 68 scheduleUpdate(); 69 } 70 71 public void onTrackingStarted() { 72 mExpanding = true; 73 mDarkenWhileDragging = !mUnlockMethodCache.isMethodInsecure(); 74 } 75 76 public void onExpandingFinished() { 77 mExpanding = false; 78 } 79 80 public void setPanelExpansion(float fraction) { 81 mFraction = fraction; 82 scheduleUpdate(); 83 } 84 85 public void setBouncerShowing(boolean showing) { 86 mBouncerShowing = showing; 87 mAnimateChange = !mExpanding; 88 scheduleUpdate(); 89 } 90 91 public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) { 92 mAnimateKeyguardFadingOut = true; 93 mDurationOverride = duration; 94 mAnimationDelay = delay; 95 mAnimateChange = true; 96 mOnAnimationFinished = onAnimationFinished; 97 scheduleUpdate(); 98 } 99 100 private void scheduleUpdate() { 101 if (mUpdatePending) return; 102 103 // Make sure that a frame gets scheduled. 104 mScrimBehind.invalidate(); 105 mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); 106 mUpdatePending = true; 107 } 108 109 private void updateScrims() { 110 if (!mKeyguardShowing || mAnimateKeyguardFadingOut) { 111 updateScrimNormal(); 112 setScrimInFrontColor(0); 113 } else { 114 updateScrimKeyguard(); 115 } 116 mAnimateChange = false; 117 } 118 119 private void updateScrimKeyguard() { 120 if (mExpanding && mDarkenWhileDragging) { 121 float behindFraction = Math.max(0, Math.min(mFraction, 1)); 122 float fraction = 1 - behindFraction; 123 setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); 124 setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); 125 } else if (mBouncerShowing) { 126 setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); 127 setScrimBehindColor(0f); 128 } else { 129 setScrimInFrontColor(0f); 130 setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD); 131 } 132 } 133 134 private void updateScrimNormal() { 135 float frac = mFraction; 136 // let's start this 20% of the way down the screen 137 frac = frac * 1.2f - 0.2f; 138 if (frac <= 0) { 139 setScrimBehindColor(0); 140 } else { 141 // woo, special effects 142 final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); 143 setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); 144 } 145 } 146 147 private void setScrimBehindColor(float alpha) { 148 setScrimColor(mScrimBehind, alpha); 149 } 150 151 private void setScrimInFrontColor(float alpha) { 152 setScrimColor(mScrimInFront, alpha); 153 if (alpha == 0f) { 154 mScrimInFront.setClickable(false); 155 } else { 156 157 // Eat touch events. 158 mScrimInFront.setClickable(true); 159 } 160 } 161 162 private void setScrimColor(View scrim, float alpha) { 163 int color = Color.argb((int) (alpha * 255), 0, 0, 0); 164 if (mAnimateChange) { 165 startScrimAnimation(scrim, color); 166 } else { 167 scrim.setBackgroundColor(color); 168 } 169 } 170 171 private void startScrimAnimation(final View scrim, int targetColor) { 172 int current = getBackgroundAlpha(scrim); 173 int target = Color.alpha(targetColor); 174 if (current == targetColor) { 175 return; 176 } 177 ValueAnimator anim = ValueAnimator.ofInt(current, target); 178 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 179 @Override 180 public void onAnimationUpdate(ValueAnimator animation) { 181 int value = (int) animation.getAnimatedValue(); 182 scrim.setBackgroundColor(Color.argb(value, 0, 0, 0)); 183 } 184 }); 185 anim.setInterpolator(mInterpolator); 186 anim.setStartDelay(mAnimationDelay); 187 anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); 188 anim.addListener(new AnimatorListenerAdapter() { 189 190 @Override 191 public void onAnimationEnd(Animator animation) { 192 if (mOnAnimationFinished != null) { 193 mOnAnimationFinished.run(); 194 mOnAnimationFinished = null; 195 } 196 } 197 }); 198 anim.start(); 199 mAnimationStarted = true; 200 } 201 202 private int getBackgroundAlpha(View scrim) { 203 if (scrim.getBackground() instanceof ColorDrawable) { 204 ColorDrawable drawable = (ColorDrawable) scrim.getBackground(); 205 return Color.alpha(drawable.getColor()); 206 } else { 207 return 0; 208 } 209 } 210 211 @Override 212 public boolean onPreDraw() { 213 mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); 214 mUpdatePending = false; 215 updateScrims(); 216 mAnimateKeyguardFadingOut = false; 217 mDurationOverride = -1; 218 mAnimationDelay = 0; 219 220 // Make sure that we always call the listener even if we didn't start an animation. 221 if (!mAnimationStarted && mOnAnimationFinished != null) { 222 mOnAnimationFinished.run(); 223 mOnAnimationFinished = null; 224 } 225 mAnimationStarted = false; 226 return true; 227 } 228} 229