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

Commit 42197263 authored by John Spurlock's avatar John Spurlock
Browse files

Optimize system bar background drawable.

Out with TransitionDrawable.

The new background drawable knows about all possible
background styles, and optimizes the transitions
between them - including picking up from the current
state, force finishing on screen off, and using
SystemClock.elapsedRealtime() for timing.

Bug:11254317
Change-Id: Ice83dc966f6674ef97f7008f2a1b62d67ec59e7d
parent a81736a7
Loading
Loading
Loading
Loading
+133 −81
Original line number Diff line number Diff line
@@ -16,16 +16,20 @@

package com.android.systemui.statusbar.phone;

import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.animation.TimeInterpolator;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.android.systemui.R;

@@ -45,43 +49,16 @@ public class BarTransitions {
    private final String mTag;
    private final View mView;
    private final boolean mSupportsTransitions = ActivityManager.isHighEndGfx();

    private final int mOpaque;
    private final int mSemiTransparent;
    private final BarBackgroundDrawable mBarBackground;

    private int mMode;
    private ValueAnimator mColorDrawableAnimator;
    private boolean mColorDrawableShowing;

    private final ColorDrawable mColorDrawable;
    private final TransitionDrawable mTransitionDrawable;
    private final AnimatorUpdateListener mAnimatorListener = new AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animator) {
            mColorDrawable.setColor((Integer) animator.getAnimatedValue());
        }
    };

    public BarTransitions(View view, int gradientResourceId) {
        mTag = "BarTransitions." + view.getClass().getSimpleName();
        mView = view;
        final Resources res = mView.getContext().getResources();

        if (DEBUG_COLORS) {
            mOpaque = 0xff0000ff;
            mSemiTransparent = 0x7f0000ff;
        } else {
            mOpaque = res.getColor(R.color.system_bar_background_opaque);
            mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent);
        }

        mColorDrawable = new ColorDrawable(mOpaque);
        mTransitionDrawable = new TransitionDrawable(
                new Drawable[] { res.getDrawable(gradientResourceId), mColorDrawable });
        mTransitionDrawable.setCrossFadeEnabled(true);
        mTransitionDrawable.resetTransition();
        mBarBackground = new BarBackgroundDrawable(mView.getContext(), gradientResourceId);
        if (mSupportsTransitions) {
            mView.setBackground(mTransitionDrawable);
            mView.setBackground(mBarBackground);
        }
    }

@@ -100,65 +77,140 @@ public class BarTransitions {
        }
    }

    private Integer getBackgroundColor(int mode) {
        if (mode == MODE_SEMI_TRANSPARENT) return mSemiTransparent;
        if (mode == MODE_OPAQUE) return mOpaque;
        if (mode == MODE_LIGHTS_OUT) return mOpaque;
        return null;
    }

    protected void onTransition(int oldMode, int newMode, boolean animate) {
        applyModeBackground(oldMode, newMode, animate);
    }

    protected void applyModeBackground(int oldMode, int newMode, boolean animate) {
        if (DEBUG) Log.d(mTag, String.format("applyModeBackground %s animate=%s",
                modeToString(newMode), animate));
        cancelColorAnimation();
        Integer oldColor = getBackgroundColor(oldMode);
        Integer newColor = getBackgroundColor(newMode);
        if (newColor != null) {
            if (animate && oldColor != null && !oldColor.equals(newColor)) {
                startColorAnimation(oldColor, newColor);
            } else if (!newColor.equals(mColorDrawable.getColor())) {
                if (DEBUG) Log.d(mTag, String.format("setColor = %08x", newColor));
                mColorDrawable.setColor(newColor);
            }
        }
        if (newColor == null && mColorDrawableShowing) {
            if (DEBUG) Log.d(mTag, "Hide color layer");
            if (animate) {
                mTransitionDrawable.reverseTransition(BACKGROUND_DURATION);
        if (DEBUG) Log.d(mTag, String.format("applyModeBackground oldMode=%s newMode=%s animate=%s",
                modeToString(oldMode), modeToString(newMode), animate));
        mBarBackground.applyModeBackground(oldMode, newMode, animate);
    }

    public static String modeToString(int mode) {
        if (mode == MODE_OPAQUE) return "MODE_OPAQUE";
        if (mode == MODE_SEMI_TRANSPARENT) return "MODE_SEMI_TRANSPARENT";
        if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT";
        if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT";
        throw new IllegalArgumentException("Unknown mode " + mode);
    }

    public void finishAnimations() {
        mBarBackground.finishAnimation();
    }

    private static class BarBackgroundDrawable extends Drawable {
        private final int mOpaque;
        private final int mSemiTransparent;
        private final Drawable mGradient;
        private final TimeInterpolator mInterpolator;

        private int mMode = -1;
        private boolean mAnimating;
        private long mStartTime;
        private long mEndTime;

        private int mGradientAlpha;
        private int mColor;

        private int mGradientAlphaStart;
        private int mColorStart;

        public BarBackgroundDrawable(Context context, int gradientResourceId) {
            final Resources res = context.getResources();
            if (DEBUG_COLORS) {
                mOpaque = 0xff0000ff;
                mSemiTransparent = 0x7f0000ff;
            } else {
                mTransitionDrawable.resetTransition();
                mOpaque = res.getColor(R.color.system_bar_background_opaque);
                mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent);
            }
            mColorDrawableShowing = false;
        } else if (newColor != null && !mColorDrawableShowing) {
            if (DEBUG) Log.d(mTag, "Show color layer");
            mTransitionDrawable.startTransition(animate ? BACKGROUND_DURATION : 0);
            mColorDrawableShowing = true;
            mGradient = res.getDrawable(gradientResourceId);
            mInterpolator = new LinearInterpolator();
        }

        @Override
        public void setAlpha(int alpha) {
            // noop
        }

        @Override
        public void setColorFilter(ColorFilter cf) {
            // noop
        }

    private void startColorAnimation(int from, int to) {
        if (DEBUG) Log.d(mTag, String.format("startColorAnimation %08x -> %08x", from, to));
        mColorDrawableAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), from, to);
        mColorDrawableAnimator.addUpdateListener(mAnimatorListener);
        mColorDrawableAnimator.start();
        @Override
        protected void onBoundsChange(Rect bounds) {
            super.onBoundsChange(bounds);
            mGradient.setBounds(bounds);
        }

    private void cancelColorAnimation() {
        if (mColorDrawableAnimator != null && mColorDrawableAnimator.isStarted()) {
            mColorDrawableAnimator.cancel();
            mColorDrawableAnimator = null;
        public void applyModeBackground(int oldMode, int newMode, boolean animate) {
            if (mMode == newMode) return;
            mMode = newMode;
            mAnimating = animate;
            if (animate) {
                long now = SystemClock.elapsedRealtime();
                mStartTime = now;
                mEndTime = now + BACKGROUND_DURATION;
                mGradientAlphaStart = mGradientAlpha;
                mColorStart = mColor;
            }
            invalidateSelf();
        }

    public static String modeToString(int mode) {
        if (mode == MODE_OPAQUE) return "MODE_OPAQUE";
        if (mode == MODE_SEMI_TRANSPARENT) return "MODE_SEMI_TRANSPARENT";
        if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT";
        if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT";
        throw new IllegalArgumentException("Unknown mode " + mode);
        @Override
        public int getOpacity() {
            return PixelFormat.TRANSLUCENT;
        }

        public void finishAnimation() {
            if (mAnimating) {
                mAnimating = false;
                invalidateSelf();
            }
        }

        @Override
        public void draw(Canvas canvas) {
            int targetGradientAlpha = 0, targetColor = 0;
            if (mMode == MODE_TRANSLUCENT) {
                targetGradientAlpha = 0xff;
            } else if (mMode == MODE_SEMI_TRANSPARENT) {
                targetColor = mSemiTransparent;
            } else {
                targetColor = mOpaque;
            }
            if (!mAnimating) {
                mColor = targetColor;
                mGradientAlpha = targetGradientAlpha;
            } else {
                final long now = SystemClock.elapsedRealtime();
                if (now >= mEndTime) {
                    mAnimating = false;
                    mColor = targetColor;
                    mGradientAlpha = targetGradientAlpha;
                } else {
                    final float t = (now - mStartTime) / (float)(mEndTime - mStartTime);
                    final float v = Math.max(0, Math.min(mInterpolator.getInterpolation(t), 1));
                    mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v));
                    mColor = Color.argb(
                          (int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)),
                          (int)(v * Color.red(targetColor) + Color.red(mColorStart) * (1 - v)),
                          (int)(v * Color.green(targetColor) + Color.green(mColorStart) * (1 - v)),
                          (int)(v * Color.blue(targetColor) + Color.blue(mColorStart) * (1 - v)));
                }
            }
            if (mGradientAlpha > 0) {
                mGradient.setAlpha(mGradientAlpha);
                mGradient.draw(canvas);
            }
            if (Color.alpha(mColor) > 0) {
                canvas.drawColor(mColor);
            }
            if (mAnimating) {
                invalidateSelf();  // keep going
            }
        }
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -1945,6 +1945,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
        transitions.transitionTo(mode, anim);
    }

    private void finishBarAnimations() {
        mStatusBarView.getBarTransitions().finishAnimations();
        if (mNavigationBarView != null) {
            mNavigationBarView.getBarTransitions().finishAnimations();
        }
    }

    private final Runnable mCheckBarModes = new Runnable() {
        @Override
        public void run() {
@@ -2449,6 +2456,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
                makeExpandedInvisible();
                notifyNavigationBarScreenOn(false);
                notifyHeadsUpScreenOn(false);
                finishBarAnimations();
            }
            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
                mScreenOn = true;