Loading quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java +28 −27 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); } Loading @@ -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, Loading quickstep/src/com/android/quickstep/util/BorderAnimator.java +188 −116 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading @@ -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(); } /** Loading @@ -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); } Loading @@ -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); Loading @@ -172,7 +142,7 @@ public final class BorderAnimator { mRunningBorderAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { setViewScales(); mBorderAnimationParams.onShowBorder(); } }); mRunningBorderAnimation.addListener( Loading @@ -181,7 +151,7 @@ public final class BorderAnimator { if (isAppearing) { return; } resetViewScales(); mBorderAnimationParams.onHideBorder(); })); return mRunningBorderAnimation; Loading @@ -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(); } } } quickstep/src/com/android/quickstep/views/TaskView.java +5 −4 Original line number Diff line number Diff line Loading @@ -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(); } Loading Loading
quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java +28 −27 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); } Loading @@ -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, Loading
quickstep/src/com/android/quickstep/util/BorderAnimator.java +188 −116 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading @@ -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(); } /** Loading @@ -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); } Loading @@ -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); Loading @@ -172,7 +142,7 @@ public final class BorderAnimator { mRunningBorderAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { setViewScales(); mBorderAnimationParams.onShowBorder(); } }); mRunningBorderAnimation.addListener( Loading @@ -181,7 +151,7 @@ public final class BorderAnimator { if (isAppearing) { return; } resetViewScales(); mBorderAnimationParams.onHideBorder(); })); return mRunningBorderAnimation; Loading @@ -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(); } } }
quickstep/src/com/android/quickstep/views/TaskView.java +5 −4 Original line number Diff line number Diff line Loading @@ -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(); } Loading