/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.Context; import android.os.Handler; import android.util.Log; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; /** * Controller which handles all the doze animations of the scrims. */ public class DozeScrimController { private static final String TAG = "DozeScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final DozeParameters mDozeParameters; private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT; private final Interpolator mPulseInInterpolatorPickup; private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN; private final Interpolator mDozeAnimationInterpolator; private final Handler mHandler = new Handler(); private final ScrimController mScrimController; private boolean mDozing; private DozeHost.PulseCallback mPulseCallback; private int mPulseReason; private Animator mInFrontAnimator; private Animator mBehindAnimator; private float mInFrontTarget; private float mBehindTarget; public DozeScrimController(ScrimController scrimController, Context context) { mScrimController = scrimController; mDozeParameters = new DozeParameters(context); mDozeAnimationInterpolator = mPulseInInterpolatorPickup = AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); } public void setDozing(boolean dozing, boolean animate) { if (mDozing == dozing) return; mDozing = dozing; if (mDozing) { abortAnimations(); mScrimController.setDozeBehindAlpha(1f); mScrimController.setDozeInFrontAlpha(1f); } else { cancelPulsing(); if (animate) { startScrimAnimation(false /* inFront */, 0f /* target */, NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator); startScrimAnimation(true /* inFront */, 0f /* target */, NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator); } else { abortAnimations(); mScrimController.setDozeBehindAlpha(0f); mScrimController.setDozeInFrontAlpha(0f); } } } /** When dozing, fade screen contents in and out using the front scrim. */ public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } if (!mDozing || mPulseCallback != null) { // Pulse suppressed. callback.onPulseFinished(); return; } // Begin pulse. Note that it's very important that the pulse finished callback // be invoked when we're done so that the caller can drop the pulse wakelock. mPulseCallback = callback; mPulseReason = reason; mHandler.post(mPulseIn); } public boolean isPulsing() { return mPulseCallback != null; } private void cancelPulsing() { if (DEBUG) Log.d(TAG, "Cancel pulsing"); if (mPulseCallback != null) { mHandler.removeCallbacks(mPulseIn); mHandler.removeCallbacks(mPulseOut); pulseFinished(); } } private void pulseStarted() { if (mPulseCallback != null) { mPulseCallback.onPulseStarted(); } } private void pulseFinished() { if (mPulseCallback != null) { mPulseCallback.onPulseFinished(); mPulseCallback = null; } } private void abortAnimations() { if (mInFrontAnimator != null) { mInFrontAnimator.cancel(); } if (mBehindAnimator != null) { mBehindAnimator.cancel(); } } private void startScrimAnimation(final boolean inFront, float target, long duration, Interpolator interpolator) { startScrimAnimation(inFront, target, duration, interpolator, 0 /* delay */, null /* endRunnable */); } private void startScrimAnimation(final boolean inFront, float target, long duration, Interpolator interpolator, long delay, final Runnable endRunnable) { Animator current = getCurrentAnimator(inFront); if (current != null) { float currentTarget = getCurrentTarget(inFront); if (currentTarget == target) { return; } current.cancel(); } ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (float) animation.getAnimatedValue(); setDozeAlpha(inFront, value); } }); anim.setInterpolator(interpolator); anim.setDuration(duration); anim.setStartDelay(delay); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { setCurrentAnimator(inFront, null); if (endRunnable != null) { endRunnable.run(); } } }); anim.start(); setCurrentAnimator(inFront, anim); setCurrentTarget(inFront, target); } private float getCurrentTarget(boolean inFront) { return inFront ? mInFrontTarget : mBehindTarget; } private void setCurrentTarget(boolean inFront, float target) { if (inFront) { mInFrontTarget = target; } else { mBehindTarget = target; } } private Animator getCurrentAnimator(boolean inFront) { return inFront ? mInFrontAnimator : mBehindAnimator; } private void setCurrentAnimator(boolean inFront, Animator animator) { if (inFront) { mInFrontAnimator = animator; } else { mBehindAnimator = animator; } } private void setDozeAlpha(boolean inFront, float alpha) { if (inFront) { mScrimController.setDozeInFrontAlpha(alpha); } else { mScrimController.setDozeBehindAlpha(alpha); } } private float getDozeAlpha(boolean inFront) { return inFront ? mScrimController.getDozeInFrontAlpha() : mScrimController.getDozeBehindAlpha(); } private final Runnable mPulseIn = new Runnable() { @Override public void run() { if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason=" + DozeLog.pulseReasonToString(mPulseReason)); if (!mDozing) return; DozeLog.tracePulseStart(mPulseReason); final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP; startScrimAnimation(true /* inFront */, 0f, mDozeParameters.getPulseInDuration(pickup), pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator, mDozeParameters.getPulseInDelay(pickup), mPulseInFinished); // Signal that the pulse is ready to turn the screen on and draw. pulseStarted(); } }; private final Runnable mPulseInFinished = new Runnable() { @Override public void run() { if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing); if (!mDozing) return; mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); } }; private final Runnable mPulseOut = new Runnable() { @Override public void run() { if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing); if (!mDozing) return; startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(), mPulseOutInterpolator, 0 /* delay */, mPulseOutFinished); } }; private final Runnable mPulseOutFinished = new Runnable() { @Override public void run() { if (DEBUG) Log.d(TAG, "Pulse out finished"); DozeLog.tracePulseFinish(); // Signal that the pulse is all finished so we can turn the screen off now. pulseFinished(); } }; }