Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a77a37c4 authored by Jorim Jaggi's avatar Jorim Jaggi Committed by Android (Google) Code Review
Browse files

Merge "Fix doze jank by removing a fullscreen layer of overdraw" into lmp-mr1-dev

parents d0d23c80 048af1f7
Loading
Loading
Loading
Loading
+260 −0
Original line number Diff line number Diff line
/*
 * 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.graphics.Color;
import android.os.Handler;
import android.util.Log;
import android.view.animation.Animation;
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 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 Animator mInFrontAnimator;
    private Animator mBehindAnimator;
    private float mInFrontTarget;
    private float mBehindTarget;

    public DozeScrimController(ScrimController scrimController, Context context) {
        mScrimController = scrimController;
        mDozeParameters = new DozeParameters(context);
        mDozeAnimationInterpolator = 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) {
        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;
        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);
            if (!mDozing) return;
            DozeLog.tracePulseStart();
            startScrimAnimation(true /* inFront */, 0f, mDozeParameters.getPulseInDuration(),
                    mPulseInInterpolator, mDozeParameters.getPulseInDelay(), 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();
        }
    };
}
+0 −53
Original line number Diff line number Diff line
@@ -23,10 +23,7 @@ import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.util.LayoutDirection;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -63,8 +60,6 @@ public class NotificationPanelView extends PanelView implements
    private static final float HEADER_RUBBERBAND_FACTOR = 2.05f;
    private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;

    private static final int DOZE_BACKGROUND_COLOR = 0xff000000;
    private static final int TAG_KEY_ANIM = R.id.scrim;
    public static final long DOZE_ANIMATION_DURATION = 700;

    private KeyguardAffordanceHelper mAfforanceHelper;
@@ -1824,11 +1819,9 @@ public class NotificationPanelView extends PanelView implements
        if (dozing == mDozing) return;
        mDozing = dozing;
        if (mDozing) {
            setBackgroundColorAlpha(DOZE_BACKGROUND_COLOR, 0xff, false /*animate*/);
            mKeyguardStatusBar.setVisibility(View.INVISIBLE);
            mKeyguardBottomArea.setVisibility(View.INVISIBLE);
        } else {
            setBackgroundColorAlpha(DOZE_BACKGROUND_COLOR, 0, animate);
            mKeyguardBottomArea.setVisibility(View.VISIBLE);
            mKeyguardStatusBar.setVisibility(View.VISIBLE);
            if (animate) {
@@ -1843,52 +1836,6 @@ public class NotificationPanelView extends PanelView implements
        return mDozing;
    }

    private void setBackgroundColorAlpha(int rgb, int targetAlpha,
            boolean animate) {
        int currentAlpha = getBackgroundAlpha(this);
        if (currentAlpha == targetAlpha) {
            return;
        }
        final int r = Color.red(rgb);
        final int g = Color.green(rgb);
        final int b = Color.blue(rgb);
        Object runningAnim = getTag(TAG_KEY_ANIM);
        if (runningAnim instanceof ValueAnimator) {
            ((ValueAnimator) runningAnim).cancel();
        }
        if (!animate) {
            setBackgroundColor(Color.argb(targetAlpha, r, g, b));
            return;
        }
        ValueAnimator anim = ValueAnimator.ofInt(currentAlpha, targetAlpha);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                setBackgroundColor(Color.argb(value, r, g, b));
            }
        });
        anim.setInterpolator(mDozeAnimationInterpolator);
        anim.setDuration(DOZE_ANIMATION_DURATION);
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                setTag(TAG_KEY_ANIM, null);
            }
        });
        anim.start();
        setTag(TAG_KEY_ANIM, anim);
    }

    private static int getBackgroundAlpha(View view) {
        if (view.getBackground() instanceof ColorDrawable) {
            ColorDrawable drawable = (ColorDrawable) view.getBackground();
            return Color.alpha(drawable.getColor());
        } else {
            return 0;
        }
    }

    public void setShadeEmpty(boolean shadeEmpty) {
        mShadeEmpty = shadeEmpty;
        updateEmptyShadeView();
+7 −4
Original line number Diff line number Diff line
@@ -414,6 +414,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,

    private ViewMediatorCallback mKeyguardViewMediatorCallback;
    private ScrimController mScrimController;
    private DozeScrimController mDozeScrimController;

    private final Runnable mAutohide = new Runnable() {
        @Override
@@ -741,6 +742,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
        mScrimController = new ScrimController(scrimBehind, scrimInFront, mScrimSrcModeEnabled);
        mScrimController.setBackDropView(mBackdrop);
        mStatusBarView.setScrimController(mScrimController);
        mDozeScrimController = new DozeScrimController(mScrimController, context);

        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
        mHeader.setActivityStarter(this);
@@ -3677,13 +3679,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
        if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
            return;
        }
        mNotificationPanel.setDozing(mDozing, mScrimController.isPulsing() /*animate*/);
        mNotificationPanel.setDozing(mDozing, mDozeScrimController.isPulsing() /*animate*/);
        if (mDozing) {
            mStackScroller.setDark(true, false /*animate*/);
        } else {
            mStackScroller.setDark(false, false /*animate*/);
        }
        mScrimController.setDozing(mDozing, mScrimController.isPulsing() /*animate*/);
        mScrimController.setDozing(mDozing);
        mDozeScrimController.setDozing(mDozing, mDozeScrimController.isPulsing() /* animate */);
    }

    public void updateStackScrollerState(boolean goingToFullShade) {
@@ -4060,7 +4063,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
    }

    public void wakeUpIfDozing(long time, boolean fromTouch) {
        if (mDozing && mScrimController.isPulsing()) {
        if (mDozing && mDozeScrimController.isPulsing()) {
            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
            pm.wakeUp(time);
            if (fromTouch) {
@@ -4175,7 +4178,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
        }

        private void handlePulseWhileDozing(@NonNull PulseCallback callback) {
            mScrimController.pulse(callback);
            mDozeScrimController.pulse(callback);
        }

        private void handleStopDozing() {
+48 −135
Original line number Diff line number Diff line
@@ -19,20 +19,15 @@ 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.graphics.Color;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import com.android.systemui.R;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.ScrimView;

@@ -41,9 +36,6 @@ import com.android.systemui.statusbar.ScrimView;
 * security method gets shown).
 */
public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
    private static final String TAG = "ScrimController";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    public static final long ANIMATION_DURATION = 220;

    private static final float SCRIM_BEHIND_ALPHA = 0.62f;
@@ -55,7 +47,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
    private final ScrimView mScrimBehind;
    private final ScrimView mScrimInFront;
    private final UnlockMethodCache mUnlockMethodCache;
    private final DozeParameters mDozeParameters;

    private boolean mKeyguardShowing;
    private float mFraction;
@@ -70,15 +61,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
    private long mAnimationDelay;
    private Runnable mOnAnimationFinished;
    private boolean mAnimationStarted;
    private boolean mDozing;
    private boolean mPulsingOut;
    private DozeHost.PulseCallback mPulseCallback;
    private final Interpolator mInterpolator = new DecelerateInterpolator();
    private final Interpolator mLinearOutSlowInInterpolator;
    private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT;
    private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN;
    private BackDropView mBackDropView;
    private boolean mScrimSrcEnabled;
    private boolean mDozing;
    private float mDozeInFrontAlpha;
    private float mDozeBehindAlpha;
    private float mCurrentInFrontAlpha;
    private float mCurrentBehindAlpha;

    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, boolean scrimSrcEnabled) {
        mScrimBehind = scrimBehind;
@@ -87,7 +78,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
        mUnlockMethodCache = UnlockMethodCache.getInstance(context);
        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                android.R.interpolator.linear_out_slow_in);
        mDozeParameters = new DozeParameters(context);
        mScrimSrcEnabled = scrimSrcEnabled;
    }

@@ -134,60 +124,27 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
        scheduleUpdate();
    }

    public void setDozing(boolean dozing, boolean animate) {
        if (mDozing == dozing) return;
    public void setDozing(boolean dozing) {
        mDozing = dozing;
        if (!mDozing) {
            cancelPulsing();
            mAnimateChange = animate;
        }
        scheduleUpdate();
    }

    /** When dozing, fade screen contents in and out using the front scrim. */
    public void pulse(@NonNull DozeHost.PulseCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
    public void setDozeInFrontAlpha(float alpha) {
        mDozeInFrontAlpha = alpha;
        updateScrimColor(mScrimInFront);
    }

        if (!mDozing || mPulseCallback != null) {
            // Pulse suppressed.
            callback.onPulseFinished();
            return;
    public void setDozeBehindAlpha(float alpha) {
        mDozeBehindAlpha = alpha;
        updateScrimColor(mScrimBehind);
    }

        // 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;
        mScrimInFront.post(mPulseIn);
    public float getDozeBehindAlpha() {
        return mDozeBehindAlpha;
    }

    public boolean isPulsing() {
        return mPulseCallback != null;
    }

    private void cancelPulsing() {
        if (DEBUG) Log.d(TAG, "Cancel pulsing");

        if (mPulseCallback != null) {
            mScrimInFront.removeCallbacks(mPulseIn);
            mScrimInFront.removeCallbacks(mPulseOut);
            pulseFinished();
        }
    }

    private void pulseStarted() {
        if (mPulseCallback != null) {
            mPulseCallback.onPulseStarted();
        }
    }

    private void pulseFinished() {
        mPulsingOut = false;
        if (mPulseCallback != null) {
            mPulseCallback.onPulseFinished();
            mPulseCallback = null;
        }
    public float getDozeInFrontAlpha() {
        return mDozeInFrontAlpha;
    }

    private void scheduleUpdate() {
@@ -223,12 +180,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
        } else if (mBouncerShowing) {
            setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
            setScrimBehindColor(0f);
        } else if (mDozing && isPulsing() && !mPulsingOut) {
            setScrimInFrontColor(0);
            setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
        } else if (mDozing) {
            setScrimInFrontColor(1);
            setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
        } else {
            float fraction = Math.max(0, Math.min(mFraction, 1));
            setScrimInFrontColor(0f);
@@ -272,26 +223,46 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
            ((ValueAnimator) runningAnim).cancel();
            scrim.setTag(TAG_KEY_ANIM, null);
        }
        int color = Color.argb((int) (alpha * 255), 0, 0, 0);
        if (mAnimateChange) {
            startScrimAnimation(scrim, color);
            startScrimAnimation(scrim, alpha);
        } else {
            setCurrentScrimAlpha(scrim, alpha);
            updateScrimColor(scrim);
        }
    }

    private float getDozeAlpha(View scrim) {
        return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha;
    }

    private float getCurrentScrimAlpha(View scrim) {
        return scrim == mScrimBehind ? mCurrentBehindAlpha : mCurrentInFrontAlpha;
    }

    private void setCurrentScrimAlpha(View scrim, float alpha) {
        if (scrim == mScrimBehind) {
            mCurrentBehindAlpha = alpha;
        } else {
            scrim.setScrimColor(color);
            mCurrentInFrontAlpha = alpha;
        }
    }

    private void startScrimAnimation(final ScrimView scrim, int targetColor) {
        int current = Color.alpha(scrim.getScrimColor());
        int target = Color.alpha(targetColor);
        if (current == target) {
            return;
    private void updateScrimColor(ScrimView scrim) {
        float alpha1 = getCurrentScrimAlpha(scrim);
        float alpha2 = getDozeAlpha(scrim);
        float alpha = 1 - (1 - alpha1) * (1 - alpha2);
        scrim.setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0));
    }
        ValueAnimator anim = ValueAnimator.ofInt(current, target);

    private void startScrimAnimation(final ScrimView scrim, float target) {
        float current = getCurrentScrimAlpha(scrim);
        ValueAnimator anim = ValueAnimator.ofFloat(current, target);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                scrim.setScrimColor(Color.argb(value, 0, 0, 0));
                float alpha = (float) animation.getAnimatedValue();
                setCurrentScrimAlpha(scrim, alpha);
                updateScrimColor(scrim);
            }
        });
        anim.setInterpolator(getInterpolator());
@@ -313,15 +284,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
    }

    private Interpolator getInterpolator() {
       if (mAnimateKeyguardFadingOut) {
           return mLinearOutSlowInInterpolator;
       } else if (isPulsing() && !mPulsingOut) {
           return mPulseInInterpolator;
       } else if (isPulsing()) {
           return mPulseOutInterpolator;
       } else {
           return mInterpolator;
       }
        return mAnimateKeyguardFadingOut ? mLinearOutSlowInInterpolator : mInterpolator;
    }

    @Override
@@ -342,56 +305,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
        return true;
    }

    private final Runnable mPulseIn = new Runnable() {
        @Override
        public void run() {
            if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing);
            if (!mDozing) return;
            DozeLog.tracePulseStart();
            mDurationOverride = mDozeParameters.getPulseInDuration();
            mAnimationDelay = mDozeParameters.getPulseInDelay();
            mAnimateChange = true;
            mOnAnimationFinished = mPulseInFinished;
            scheduleUpdate();

            // 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;
            mScrimInFront.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;
            mDurationOverride = mDozeParameters.getPulseOutDuration();
            mAnimateChange = true;
            mOnAnimationFinished = mPulseOutFinished;
            mPulsingOut = true;
            scheduleUpdate();
        }
    };

    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();
        }
    };

    public void setBackDropView(BackDropView backDropView) {
        mBackDropView = backDropView;
        mBackDropView.setOnVisibilityChangedRunnable(new Runnable() {