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

Commit b323851e authored by Schneider Victor-tulias's avatar Schneider Victor-tulias Committed by Android (Google) Code Review
Browse files

Merge "Update BorderAnimator to work with layout updates" into udc-dev

parents a2de64b8 7dee27f4
Loading
Loading
Loading
Loading
+28 −27
Original line number Diff line number Diff line
@@ -27,11 +27,13 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;

import com.android.launcher3.R;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.BorderAnimator;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -43,7 +45,9 @@ import java.util.function.Consumer;
 */
public class KeyboardQuickSwitchTaskView extends ConstraintLayout {

    @NonNull private final BorderAnimator mBorderAnimator;
    @ColorInt private final int mBorderColor;

    @Nullable private BorderAnimator mBorderAnimator;

    @Nullable private ImageView mThumbnailView1;
    @Nullable private ImageView mThumbnailView2;
@@ -74,29 +78,9 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
                attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);

        setWillNotDraw(false);
        Resources resources = context.getResources();
        mBorderAnimator = new BorderAnimator(
                /* borderBoundsBuilder= */ bounds -> bounds.set(0, 0, getWidth(), getHeight()),
                /* borderWidthPx= */ resources.getDimensionPixelSize(
                        R.dimen.keyboard_quick_switch_border_width),
                /* borderRadiusPx= */ resources.getDimensionPixelSize(
                        R.dimen.keyboard_quick_switch_task_view_radius),
                /* borderColor= */ ta.getColor(
                        R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR),
                /* invalidateViewCallback= */ KeyboardQuickSwitchTaskView.this::invalidate,
                /* viewScaleTargetProvider= */ new BorderAnimator.ViewScaleTargetProvider() {
                    @NonNull
                    @Override
                    public View getContainerView() {
                        return KeyboardQuickSwitchTaskView.this;
                    }

                    @NonNull
                    @Override
                    public View getContentView() {
                        return mContent;
                    }
                });
        mBorderColor = ta.getColor(
                R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR);
        ta.recycle();
    }

@@ -108,18 +92,35 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
        mIcon1 = findViewById(R.id.icon1);
        mIcon2 = findViewById(R.id.icon2);
        mContent = findViewById(R.id.content);

        Resources resources = mContext.getResources();

        Preconditions.assertNotNull(mContent);
        mBorderAnimator = new BorderAnimator(
                /* borderRadiusPx= */ resources.getDimensionPixelSize(
                        R.dimen.keyboard_quick_switch_task_view_radius),
                /* borderColor= */ mBorderColor,
                /* borderAnimationParams= */ new BorderAnimator.ScalingParams(
                        /* borderWidthPx= */ resources.getDimensionPixelSize(
                                R.dimen.keyboard_quick_switch_border_width),
                        /* boundsBuilder= */ bounds -> bounds.set(
                                0, 0, getWidth(), getHeight()),
                        /* targetView= */ this,
                        /* contentView= */ mContent));
    }

    @NonNull
    @Nullable
    protected Animator getFocusAnimator(boolean focused) {
        return mBorderAnimator.buildAnimator(focused);
        return mBorderAnimator == null ? null : mBorderAnimator.buildAnimator(focused);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (mBorderAnimator != null) {
            mBorderAnimator.drawBorder(canvas);
        }
    }

    protected void setThumbnails(
            @NonNull Task task1,
+188 −116
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter;
import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
@@ -37,8 +38,8 @@ import com.android.launcher3.anim.Interpolators;
 * <p>
 * To use this class:
 * 1. Create an instance in the target view. NOTE: The border will animate outwards from the
 *      provided border bounds. If the border will not be visible outside of those bounds, then a
 *      {@link ViewScaleTargetProvider} must be provided in the constructor.
 *      provided border bounds. See {@link SimpleParams} and {@link ScalingParams} to determine
 *      which would be best for your target view.
 * 2. Override the target view's {@link android.view.View#draw(Canvas)} method and call
 *      {@link BorderAnimator#drawBorder(Canvas)} after {@code super.draw(canvas)}.
 * 3. Call {@link BorderAnimator#buildAnimator(boolean)} and start the animation or call
@@ -46,7 +47,7 @@ import com.android.launcher3.anim.Interpolators;
 */
public final class BorderAnimator {

    public static final int DEFAULT_BORDER_COLOR = 0xffffffff;
    public static final int DEFAULT_BORDER_COLOR = Color.WHITE;

    private static final long DEFAULT_APPEARANCE_ANIMATION_DURATION_MS = 300;
    private static final long DEFAULT_DISAPPEARANCE_ANIMATION_DURATION_MS = 133;
@@ -54,68 +55,44 @@ public final class BorderAnimator {

    @NonNull private final AnimatedFloat mBorderAnimationProgress = new AnimatedFloat(
            this::updateOutline);
    @NonNull private final Rect mBorderBounds = new Rect();
    @NonNull private final BorderBoundsBuilder mBorderBoundsBuilder;
    @Px private final int mBorderWidthPx;
    @Px private final int mBorderRadiusPx;
    @NonNull private final Runnable mInvalidateViewCallback;
    @Nullable private final ViewScaleTargetProvider mViewScaleTargetProvider;
    @NonNull private final BorderAnimationParams mBorderAnimationParams;
    private final long mAppearanceDurationMs;
    private final long mDisappearanceDurationMs;
    @NonNull private final Interpolator mInterpolator;
    @NonNull private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private float mAlignmentAdjustment;

    @Nullable private Animator mRunningBorderAnimation;

    public BorderAnimator(
            @NonNull BorderBoundsBuilder borderBoundsBuilder,
            int borderWidthPx,
            int borderRadiusPx,
            @ColorInt int borderColor,
            @NonNull Runnable invalidateViewCallback) {
        this(borderBoundsBuilder,
                borderWidthPx,
                borderRadiusPx,
                borderColor,
                invalidateViewCallback,
                /* viewScaleTargetProvider= */ null);
    }

    public BorderAnimator(
            @NonNull BorderBoundsBuilder borderBoundsBuilder,
            int borderWidthPx,
            int borderRadiusPx,
            @Px int borderRadiusPx,
            @ColorInt int borderColor,
            @NonNull Runnable invalidateViewCallback,
            @Nullable ViewScaleTargetProvider viewScaleTargetProvider) {
        this(borderBoundsBuilder,
                borderWidthPx,
                borderRadiusPx,
            @NonNull BorderAnimationParams borderAnimationParams) {
        this(borderRadiusPx,
                borderColor,
                invalidateViewCallback,
                viewScaleTargetProvider,
                borderAnimationParams,
                DEFAULT_APPEARANCE_ANIMATION_DURATION_MS,
                DEFAULT_DISAPPEARANCE_ANIMATION_DURATION_MS,
                DEFAULT_INTERPOLATOR);
    }

    /**
     * @param borderRadiusPx the radius of the border's corners, in pixels
     * @param borderColor the border's color
     * @param borderAnimationParams params for handling different target view layout situation.
     * @param appearanceDurationMs appearance animation duration, in milliseconds
     * @param disappearanceDurationMs disappearance animation duration, in milliseconds
     * @param interpolator animation interpolator
     */
    public BorderAnimator(
            @NonNull BorderBoundsBuilder borderBoundsBuilder,
            int borderWidthPx,
            int borderRadiusPx,
            @Px int borderRadiusPx,
            @ColorInt int borderColor,
            @NonNull Runnable invalidateViewCallback,
            @Nullable ViewScaleTargetProvider viewScaleTargetProvider,
            @NonNull BorderAnimationParams borderAnimationParams,
            long appearanceDurationMs,
            long disappearanceDurationMs,
            @NonNull Interpolator interpolator) {
        mBorderBoundsBuilder = borderBoundsBuilder;
        mBorderWidthPx = borderWidthPx;
        mBorderRadiusPx = borderRadiusPx;
        mInvalidateViewCallback = invalidateViewCallback;
        mViewScaleTargetProvider = viewScaleTargetProvider;
        mBorderAnimationParams = borderAnimationParams;
        mAppearanceDurationMs = appearanceDurationMs;
        mDisappearanceDurationMs = disappearanceDurationMs;
        mInterpolator = interpolator;
@@ -128,15 +105,11 @@ public final class BorderAnimator {
    private void updateOutline() {
        float interpolatedProgress = mInterpolator.getInterpolation(
                mBorderAnimationProgress.value);
        float borderWidth = mBorderWidthPx * interpolatedProgress;
        // Outset the border by half the width to create an outwards-growth animation
        mAlignmentAdjustment = (-borderWidth / 2f)
                // Inset the border if we are scaling the container up
                + (mViewScaleTargetProvider == null ? 0 : mBorderWidthPx);

        mBorderAnimationParams.setProgress(interpolatedProgress);
        mBorderPaint.setAlpha(Math.round(255 * interpolatedProgress));
        mBorderPaint.setStrokeWidth(borderWidth);
        mInvalidateViewCallback.run();
        mBorderPaint.setStrokeWidth(mBorderAnimationParams.getBorderWidth());
        mBorderAnimationParams.mTargetView.invalidate();
    }

    /**
@@ -146,16 +119,14 @@ public final class BorderAnimator {
     * calling super.
     */
    public void drawBorder(Canvas canvas) {
        // Increase the radius if we are scaling the container up
        float radiusAdjustment = mViewScaleTargetProvider == null
                ? -mAlignmentAdjustment : mAlignmentAdjustment;
        float alignmentAdjustment = mBorderAnimationParams.getAlignmentAdjustment();
        canvas.drawRoundRect(
                /* left= */ mBorderBounds.left + mAlignmentAdjustment,
                /* top= */ mBorderBounds.top + mAlignmentAdjustment,
                /* right= */ mBorderBounds.right - mAlignmentAdjustment,
                /* bottom= */ mBorderBounds.bottom - mAlignmentAdjustment,
                /* rx= */ mBorderRadiusPx + radiusAdjustment,
                /* ry= */ mBorderRadiusPx + radiusAdjustment,
                /* left= */ mBorderAnimationParams.mBorderBounds.left + alignmentAdjustment,
                /* top= */ mBorderAnimationParams.mBorderBounds.top + alignmentAdjustment,
                /* right= */ mBorderAnimationParams.mBorderBounds.right - alignmentAdjustment,
                /* bottom= */ mBorderAnimationParams.mBorderBounds.bottom - alignmentAdjustment,
                /* rx= */ mBorderRadiusPx + mBorderAnimationParams.getRadiusAdjustment(),
                /* ry= */ mBorderRadiusPx + mBorderAnimationParams.getRadiusAdjustment(),
                /* paint= */ mBorderPaint);
    }

@@ -164,7 +135,6 @@ public final class BorderAnimator {
     */
    @NonNull
    public Animator buildAnimator(boolean isAppearing) {
        mBorderBoundsBuilder.updateBorderBounds(mBorderBounds);
        mRunningBorderAnimation = mBorderAnimationProgress.animateToValue(isAppearing ? 1f : 0f);
        mRunningBorderAnimation.setDuration(
                isAppearing ? mAppearanceDurationMs : mDisappearanceDurationMs);
@@ -172,7 +142,7 @@ public final class BorderAnimator {
        mRunningBorderAnimation.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                setViewScales();
                mBorderAnimationParams.onShowBorder();
            }
        });
        mRunningBorderAnimation.addListener(
@@ -181,7 +151,7 @@ public final class BorderAnimator {
                    if (isAppearing) {
                        return;
                    }
                    resetViewScales();
                    mBorderAnimationParams.onHideBorder();
                }));

        return mRunningBorderAnimation;
@@ -196,85 +166,187 @@ public final class BorderAnimator {
        if (mRunningBorderAnimation != null) {
            mRunningBorderAnimation.end();
        }
        mBorderBoundsBuilder.updateBorderBounds(mBorderBounds);
        if (visible) {
            setViewScales();
            mBorderAnimationParams.onShowBorder();
        }
        mBorderAnimationProgress.updateValue(visible ? 1f : 0f);
        if (!visible) {
            resetViewScales();
            mBorderAnimationParams.onHideBorder();
        }
    }

    private void setViewScales() {
        if (mViewScaleTargetProvider == null) {
            return;
    /**
     * Callback to update the border bounds when building this animation.
     */
    public interface BorderBoundsBuilder {

        /**
         * Sets the given rect to the most up-to-date bounds.
         */
        void updateBorderBounds(Rect rect);
    }
        View container = mViewScaleTargetProvider.getContainerView();
        float width = container.getWidth();
        float height = container.getHeight();
        // scale up just enough to make room for the border
        float scaleX = 1f + ((2 * mBorderWidthPx) / width);
        float scaleY = 1f + ((2 * mBorderWidthPx) / height);

        container.setPivotX(width / 2);
        container.setPivotY(height / 2);
        container.setScaleX(scaleX);
        container.setScaleY(scaleY);
    /**
     * Params for handling different target view layout situation.
     */
    private abstract static class BorderAnimationParams {

        @NonNull private final Rect mBorderBounds = new Rect();
        @NonNull private final BorderBoundsBuilder mBoundsBuilder;

        @NonNull final View mTargetView;
        @Px final int mBorderWidthPx;

        private float mAnimationProgress = 0f;
        @Nullable private View.OnLayoutChangeListener mLayoutChangeListener;

        View contentView = mViewScaleTargetProvider.getContentView();
        contentView.setPivotX(contentView.getWidth() / 2f);
        contentView.setPivotY(contentView.getHeight() / 2f);
        contentView.setScaleX(1f / scaleX);
        contentView.setScaleY(1f / scaleY);
        /**
         * @param borderWidthPx the width of the border, in pixels
         * @param boundsBuilder callback to update the border bounds
         * @param targetView the view that will be drawing the border
         */
        private BorderAnimationParams(
                @Px int borderWidthPx,
                @NonNull BorderBoundsBuilder boundsBuilder,
                @NonNull View targetView) {
            mBorderWidthPx = borderWidthPx;
            mBoundsBuilder = boundsBuilder;
            mTargetView = targetView;
        }

    private void resetViewScales() {
        if (mViewScaleTargetProvider == null) {
            return;
        private void setProgress(float progress) {
            mAnimationProgress = progress;
        }
        View container = mViewScaleTargetProvider.getContainerView();
        container.setPivotX(container.getWidth());
        container.setPivotY(container.getHeight());
        container.setScaleX(1f);
        container.setScaleY(1f);

        View contentView = mViewScaleTargetProvider.getContentView();
        contentView.setPivotX(contentView.getWidth() / 2f);
        contentView.setPivotY(contentView.getHeight() / 2f);
        contentView.setScaleX(1f);
        contentView.setScaleY(1f);
        private float getBorderWidth() {
            return mBorderWidthPx * mAnimationProgress;
        }

    /**
     * Callback to update the border bounds when building this animation.
     */
    public interface BorderBoundsBuilder {
        float getAlignmentAdjustment() {
            // Outset the border by half the width to create an outwards-growth animation
            return (-getBorderWidth() / 2f) + getAlignmentAdjustmentInset();
        }

        /**
         * Sets the given rect to the most up-to-date bounds.
         */
        void updateBorderBounds(Rect rect);

        void onShowBorder() {
            if (mLayoutChangeListener == null) {
                mLayoutChangeListener =
                        (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                            onShowBorder();
                            mTargetView.invalidate();
                        };
                mTargetView.addOnLayoutChangeListener(mLayoutChangeListener);
            }
            mBoundsBuilder.updateBorderBounds(mBorderBounds);
        }

        void onHideBorder() {
            if (mLayoutChangeListener != null) {
                mTargetView.removeOnLayoutChangeListener(mLayoutChangeListener);
                mLayoutChangeListener = null;
            }
        }

        abstract int getAlignmentAdjustmentInset();

        abstract float getRadiusAdjustment();
    }

    /**
     * Provider for scaling target views for the beginning and end of this animation.
     * Use an instance of this {@link BorderAnimationParams} if the border can be drawn outside the
     * target view's bounds without any additional logic.
     */
    public interface ViewScaleTargetProvider {
    public static final class SimpleParams extends BorderAnimationParams {

        public SimpleParams(
                @Px int borderWidthPx,
                @NonNull BorderBoundsBuilder boundsBuilder,
                @NonNull View targetView) {
            super(borderWidthPx, boundsBuilder, targetView);
        }

        @Override
        int getAlignmentAdjustmentInset() {
            return 0;
        }

        @Override
        float getRadiusAdjustment() {
            return -getAlignmentAdjustment();
        }
    }

    /**
         * Returns the content view's container. This view will be scaled up to make room for the
         * border.
     * Use an instance of this {@link BorderAnimationParams} if the border would other be clipped by
     * the target view's bound.
     * <p>
     * Note: using these params will set the scales and pivots of the
     * container and content views, however will only reset the scales back to 1.
     */
        @NonNull
        View getContainerView();
    public static final class ScalingParams extends BorderAnimationParams {

        @NonNull private final View mContentView;

        /**
         * Returns the content view. This view will be scaled down reciprocally to the container's
         * up-scaling to maintain its original size. This should be the view containing all of the
         * content being surrounded by the border.
         * @param targetView the view that will be drawing the border. this view will be scaled up
         *                   to make room for the border
         * @param contentView the view around which the border will be drawn. this view will be
         *                    scaled down reciprocally to keep its original size and location.
         */
        @NonNull
        View getContentView();
        public ScalingParams(
                @Px int borderWidthPx,
                @NonNull BorderBoundsBuilder boundsBuilder,
                @NonNull View targetView,
                @NonNull View contentView) {
            super(borderWidthPx, boundsBuilder, targetView);
            mContentView = contentView;
        }

        @Override
        void onShowBorder() {
            super.onShowBorder();
            float width = mTargetView.getWidth();
            float height = mTargetView.getHeight();
            // Scale up just enough to make room for the border. Fail fast and fix the scaling
            // onLayout.
            float scaleX = width == 0 ? 1f : 1f + ((2 * mBorderWidthPx) / width);
            float scaleY = height == 0 ? 1f : 1f + ((2 * mBorderWidthPx) / height);

            mTargetView.setPivotX(width / 2);
            mTargetView.setPivotY(height / 2);
            mTargetView.setScaleX(scaleX);
            mTargetView.setScaleY(scaleY);

            mContentView.setPivotX(mContentView.getWidth() / 2f);
            mContentView.setPivotY(mContentView.getHeight() / 2f);
            mContentView.setScaleX(1f / scaleX);
            mContentView.setScaleY(1f / scaleY);
        }

        @Override
        void onHideBorder() {
            super.onHideBorder();
            mTargetView.setPivotX(mTargetView.getWidth());
            mTargetView.setPivotY(mTargetView.getHeight());
            mTargetView.setScaleX(1f);
            mTargetView.setScaleY(1f);

            mContentView.setPivotX(mContentView.getWidth() / 2f);
            mContentView.setPivotY(mContentView.getHeight() / 2f);
            mContentView.setScaleX(1f);
            mContentView.setScaleY(1f);
        }

        @Override
        int getAlignmentAdjustmentInset() {
            // Inset the border since we are scaling the container up
            return mBorderWidthPx;
        }

        @Override
        float getRadiusAdjustment() {
            // Increase the radius since we are scaling the container up
            return getAlignmentAdjustment();
        }
    }
}
+5 −4
Original line number Diff line number Diff line
@@ -445,13 +445,14 @@ public class TaskView extends FrameLayout implements Reusable {
        mBorderAnimator = !keyboardFocusHighlightEnabled
                ? null
                : new BorderAnimator(
                        /* borderBoundsBuilder= */ this::updateBorderBounds,
                        /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
                                R.dimen.keyboard_quick_switch_border_width),
                        /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
                        /* borderColor= */ ta.getColor(
                                R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR),
                        /* invalidateViewCallback= */ TaskView.this::invalidate);
                        /* borderAnimationParams= */ new BorderAnimator.SimpleParams(
                                /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
                                        R.dimen.keyboard_quick_switch_border_width),
                                /* boundsBuilder= */ this::updateBorderBounds,
                                /* targetView= */ this));
        ta.recycle();
    }