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