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