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