DozeScrimController.java revision eab28e660223b6e02f2fbd8dc31c8bde3ce5b22d
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.annotation.NonNull;
23import android.content.Context;
24import android.graphics.Color;
25import android.os.Handler;
26import android.util.Log;
27import android.view.animation.Animation;
28import android.view.animation.AnimationUtils;
29import android.view.animation.Interpolator;
30
31import com.android.systemui.doze.DozeHost;
32import com.android.systemui.doze.DozeLog;
33
34/**
35 * Controller which handles all the doze animations of the scrims.
36 */
37public class DozeScrimController {
38    private static final String TAG = "DozeScrimController";
39    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
40
41    private final DozeParameters mDozeParameters;
42    private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT;
43    private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN;
44    private final Interpolator mDozeAnimationInterpolator;
45    private final Handler mHandler = new Handler();
46    private final ScrimController mScrimController;
47
48    private boolean mDozing;
49    private DozeHost.PulseCallback mPulseCallback;
50    private int mPulseReason;
51    private Animator mInFrontAnimator;
52    private Animator mBehindAnimator;
53    private float mInFrontTarget;
54    private float mBehindTarget;
55
56    public DozeScrimController(ScrimController scrimController, Context context) {
57        mScrimController = scrimController;
58        mDozeParameters = new DozeParameters(context);
59        mDozeAnimationInterpolator = AnimationUtils.loadInterpolator(context,
60                android.R.interpolator.linear_out_slow_in);
61    }
62
63    public void setDozing(boolean dozing, boolean animate) {
64        if (mDozing == dozing) return;
65        mDozing = dozing;
66        if (mDozing) {
67            abortAnimations();
68            mScrimController.setDozeBehindAlpha(1f);
69            mScrimController.setDozeInFrontAlpha(1f);
70        } else {
71            cancelPulsing();
72            if (animate) {
73                startScrimAnimation(false /* inFront */, 0f /* target */,
74                        NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator);
75                startScrimAnimation(true /* inFront */, 0f /* target */,
76                        NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator);
77            } else {
78                abortAnimations();
79                mScrimController.setDozeBehindAlpha(0f);
80                mScrimController.setDozeInFrontAlpha(0f);
81            }
82        }
83    }
84
85    /** When dozing, fade screen contents in and out using the front scrim. */
86    public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) {
87        if (callback == null) {
88            throw new IllegalArgumentException("callback must not be null");
89        }
90
91        if (!mDozing || mPulseCallback != null) {
92            // Pulse suppressed.
93            callback.onPulseFinished();
94            return;
95        }
96
97        // Begin pulse.  Note that it's very important that the pulse finished callback
98        // be invoked when we're done so that the caller can drop the pulse wakelock.
99        mPulseCallback = callback;
100        mPulseReason = reason;
101        mHandler.post(mPulseIn);
102    }
103
104    public boolean isPulsing() {
105        return mPulseCallback != null;
106    }
107
108    private void cancelPulsing() {
109        if (DEBUG) Log.d(TAG, "Cancel pulsing");
110
111        if (mPulseCallback != null) {
112            mHandler.removeCallbacks(mPulseIn);
113            mHandler.removeCallbacks(mPulseOut);
114            pulseFinished();
115        }
116    }
117
118    private void pulseStarted() {
119        if (mPulseCallback != null) {
120            mPulseCallback.onPulseStarted();
121        }
122    }
123
124    private void pulseFinished() {
125        if (mPulseCallback != null) {
126            mPulseCallback.onPulseFinished();
127            mPulseCallback = null;
128        }
129    }
130
131    private void abortAnimations() {
132        if (mInFrontAnimator != null) {
133            mInFrontAnimator.cancel();
134        }
135        if (mBehindAnimator != null) {
136            mBehindAnimator.cancel();
137        }
138    }
139
140    private void startScrimAnimation(final boolean inFront, float target, long duration,
141            Interpolator interpolator) {
142        startScrimAnimation(inFront, target, duration, interpolator, 0 /* delay */,
143                null /* endRunnable */);
144    }
145
146    private void startScrimAnimation(final boolean inFront, float target, long duration,
147            Interpolator interpolator, long delay, final Runnable endRunnable) {
148        Animator current = getCurrentAnimator(inFront);
149        if (current != null) {
150            float currentTarget = getCurrentTarget(inFront);
151            if (currentTarget == target) {
152                return;
153            }
154            current.cancel();
155        }
156        ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
157        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
158            @Override
159            public void onAnimationUpdate(ValueAnimator animation) {
160                float value = (float) animation.getAnimatedValue();
161                setDozeAlpha(inFront, value);
162            }
163        });
164        anim.setInterpolator(interpolator);
165        anim.setDuration(duration);
166        anim.setStartDelay(delay);
167        anim.addListener(new AnimatorListenerAdapter() {
168            @Override
169            public void onAnimationEnd(Animator animation) {
170                setCurrentAnimator(inFront, null);
171                if (endRunnable != null) {
172                    endRunnable.run();
173                }
174            }
175        });
176        anim.start();
177        setCurrentAnimator(inFront, anim);
178        setCurrentTarget(inFront, target);
179    }
180
181    private float getCurrentTarget(boolean inFront) {
182        return inFront ? mInFrontTarget : mBehindTarget;
183    }
184
185    private void setCurrentTarget(boolean inFront, float target) {
186        if (inFront) {
187            mInFrontTarget = target;
188        } else {
189            mBehindTarget = target;
190        }
191    }
192
193    private Animator getCurrentAnimator(boolean inFront) {
194        return inFront ? mInFrontAnimator : mBehindAnimator;
195    }
196
197    private void setCurrentAnimator(boolean inFront, Animator animator) {
198        if (inFront) {
199            mInFrontAnimator = animator;
200        } else {
201            mBehindAnimator = animator;
202        }
203    }
204
205    private void setDozeAlpha(boolean inFront, float alpha) {
206        if (inFront) {
207            mScrimController.setDozeInFrontAlpha(alpha);
208        } else {
209            mScrimController.setDozeBehindAlpha(alpha);
210        }
211    }
212
213    private float getDozeAlpha(boolean inFront) {
214        return inFront
215                ? mScrimController.getDozeInFrontAlpha()
216                : mScrimController.getDozeBehindAlpha();
217    }
218
219    private final Runnable mPulseIn = new Runnable() {
220        @Override
221        public void run() {
222            if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing);
223            if (!mDozing) return;
224            DozeLog.tracePulseStart(mPulseReason);
225            startScrimAnimation(true /* inFront */, 0f, mDozeParameters.getPulseInDuration(),
226                    mPulseInInterpolator, mDozeParameters.getPulseInDelay(), mPulseInFinished);
227
228            // Signal that the pulse is ready to turn the screen on and draw.
229            pulseStarted();
230        }
231    };
232
233    private final Runnable mPulseInFinished = new Runnable() {
234        @Override
235        public void run() {
236            if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
237            if (!mDozing) return;
238            mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
239        }
240    };
241
242    private final Runnable mPulseOut = new Runnable() {
243        @Override
244        public void run() {
245            if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
246            if (!mDozing) return;
247            startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
248                    mPulseOutInterpolator, 0 /* delay */, mPulseOutFinished);
249        }
250    };
251
252    private final Runnable mPulseOutFinished = new Runnable() {
253        @Override
254        public void run() {
255            if (DEBUG) Log.d(TAG, "Pulse out finished");
256            DozeLog.tracePulseFinish();
257
258            // Signal that the pulse is all finished so we can turn the screen off now.
259            pulseFinished();
260        }
261    };
262}
263