From 58b6145feb39fb75a49754770d57526b9efe7d6b Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Fri, 17 Feb 2023 15:03:18 +0000 Subject: [PATCH] Refactor ArrowTipView so it can be customized and used in any drag layer. Allows use in TaskbarDragLayer. Allows customization of properties like text color, background color, and open/close animations. Bug: 250092437 Test: manual. (Will test with TaplTestsTaskbar in follow up CL when implemented.) Change-Id: I056395322e5fef7aa4d9fd7e4c2d73c050eff759 --- res/values/attrs.xml | 4 + res/values/styles.xml | 3 + .../android/launcher3/views/ArrowTipView.java | 122 ++++++++++++------ 3 files changed, 86 insertions(+), 43 deletions(-) diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 2e74d7d7dd..275a3b81a6 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -518,4 +518,8 @@ + + + + diff --git a/res/values/styles.xml b/res/values/styles.xml index 2b37db8ec2..ecff9ec4d9 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -378,4 +378,7 @@ 16dp + diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java index d905aaaadb..8e056501d5 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -16,8 +16,13 @@ package com.android.launcher3.views; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Rect; @@ -33,18 +38,16 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.Px; -import androidx.core.content.ContextCompat; import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.graphics.TriangleShape; /** - * A base class for arrow tip view in launcher + * A base class for arrow tip view in launcher. */ public class ArrowTipView extends AbstractFloatingView { @@ -54,33 +57,46 @@ public class ArrowTipView extends AbstractFloatingView { private static final long SHOW_DURATION_MS = 300; private static final long HIDE_DURATION_MS = 100; - protected final BaseDraggingActivity mActivity; + private final ActivityContext mActivityContext; private final Handler mHandler = new Handler(); - private final int mArrowWidth; - private final int mArrowMinOffset; private boolean mIsPointingUp; private Runnable mOnClosed; private View mArrowView; + private final int mArrowWidth; + private final int mArrowMinOffset; + private final int mArrowViewPaintColor; + + private AnimatorSet mOpenAnimator = new AnimatorSet(); + private AnimatorSet mCloseAnimator = new AnimatorSet(); public ArrowTipView(Context context) { this(context, false); } public ArrowTipView(Context context, boolean isPointingUp) { + this(context, isPointingUp, R.layout.arrow_toast); + } + + public ArrowTipView(Context context, boolean isPointingUp, int layoutId) { super(context, null, 0); - mActivity = BaseDraggingActivity.fromContext(context); + mActivityContext = ActivityContext.lookupContext(context); mIsPointingUp = isPointingUp; - mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width); + mArrowWidth = context.getResources().getDimensionPixelSize( + R.dimen.arrow_toast_arrow_width); mArrowMinOffset = context.getResources().getDimensionPixelSize( R.dimen.dynamic_grid_cell_border_spacing); - init(context); + TypedArray ta = context.obtainStyledAttributes(R.styleable.ArrowTipView); + mArrowViewPaintColor = ta.getColor(R.styleable.ArrowTipView_arrowTipBackground, + context.getColor(R.color.arrow_tip_view_bg)); + ta.recycle(); + init(context, layoutId); } @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { close(true); - if (mActivity.getDragLayer().isEventOverView(this, ev)) { + if (mActivityContext.getDragLayer().isEventOverView(this, ev)) { return true; } } @@ -89,18 +105,15 @@ public class ArrowTipView extends AbstractFloatingView { @Override protected void handleClose(boolean animate) { + if (mOpenAnimator.isStarted()) { + mOpenAnimator.cancel(); + } if (mIsOpen) { if (animate) { - animate().alpha(0f) - .withLayer() - .setStartDelay(0) - .setDuration(HIDE_DURATION_MS) - .setInterpolator(Interpolators.ACCELERATE) - .withEndAction(() -> mActivity.getDragLayer().removeView(this)) - .start(); + mCloseAnimator.start(); } else { - animate().cancel(); - mActivity.getDragLayer().removeView(this); + mCloseAnimator.cancel(); + mActivityContext.getDragLayer().removeView(this); } if (mOnClosed != null) mOnClosed.run(); mIsOpen = false; @@ -112,12 +125,31 @@ public class ArrowTipView extends AbstractFloatingView { return (type & TYPE_ON_BOARD_POPUP) != 0; } - private void init(Context context) { - inflate(context, R.layout.arrow_toast, this); + private void init(Context context, int layoutId) { + inflate(context, layoutId, this); setOrientation(LinearLayout.VERTICAL); mArrowView = findViewById(R.id.arrow); updateArrowTipInView(); + setAlpha(0); + + // Create default open animator. + mOpenAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, 1f)); + mOpenAnimator.setStartDelay(SHOW_DELAY_MS); + mOpenAnimator.setDuration(SHOW_DURATION_MS); + mOpenAnimator.setInterpolator(Interpolators.DECELERATE); + + // Create default close animator. + mCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, 0)); + mCloseAnimator.setStartDelay(0); + mCloseAnimator.setDuration(HIDE_DURATION_MS); + mCloseAnimator.setInterpolator(Interpolators.ACCELERATE); + mCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mActivityContext.getDragLayer().removeView(ArrowTipView.this); + } + }); } /** @@ -153,10 +185,10 @@ public class ArrowTipView extends AbstractFloatingView { public ArrowTipView show( String text, int gravity, int arrowMarginStart, int top, boolean shouldAutoClose) { ((TextView) findViewById(R.id.text)).setText(text); - ViewGroup parent = mActivity.getDragLayer(); + ViewGroup parent = mActivityContext.getDragLayer(); parent.addView(this); - DeviceProfile grid = mActivity.getDeviceProfile(); + DeviceProfile grid = mActivityContext.getDeviceProfile(); DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams(); params.gravity = gravity; @@ -185,14 +217,8 @@ public class ArrowTipView extends AbstractFloatingView { if (shouldAutoClose) { mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS); } - setAlpha(0); - animate() - .alpha(1f) - .withLayer() - .setStartDelay(SHOW_DELAY_MS) - .setDuration(SHOW_DURATION_MS) - .setInterpolator(Interpolators.DECELERATE) - .start(); + + mOpenAnimator.start(); return this; } @@ -273,7 +299,7 @@ public class ArrowTipView extends AbstractFloatingView { */ @Nullable private ArrowTipView showAtLocation(String text, @Px int arrowXCoord, @Px int yCoordDownPointingTip, @Px int yCoordUpPointingTip, boolean shouldAutoClose) { - ViewGroup parent = mActivity.getDragLayer(); + ViewGroup parent = mActivityContext.getDragLayer(); @Px int parentViewWidth = parent.getWidth(); @Px int parentViewHeight = parent.getHeight(); @Px int maxTextViewWidth = getContext().getResources() @@ -288,8 +314,10 @@ public class ArrowTipView extends AbstractFloatingView { TextView textView = findViewById(R.id.text); textView.setText(text); textView.setMaxWidth(maxTextViewWidth); - parent.addView(this); - requestLayout(); + if (parent.indexOfChild(this) < 0) { + parent.addView(this); + requestLayout(); + } post(() -> { // Adjust the tooltip horizontally. @@ -333,14 +361,8 @@ public class ArrowTipView extends AbstractFloatingView { if (shouldAutoClose) { mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS); } - setAlpha(0); - animate() - .alpha(1f) - .withLayer() - .setStartDelay(SHOW_DELAY_MS) - .setDuration(SHOW_DURATION_MS) - .setInterpolator(Interpolators.DECELERATE) - .start(); + + mOpenAnimator.start(); return this; } @@ -351,7 +373,7 @@ public class ArrowTipView extends AbstractFloatingView { Paint arrowPaint = arrowDrawable.getPaint(); @Px int arrowTipRadius = getContext().getResources() .getDimensionPixelSize(R.dimen.arrow_toast_corner_radius); - arrowPaint.setColor(ContextCompat.getColor(getContext(), R.color.arrow_tip_view_bg)); + arrowPaint.setColor(mArrowViewPaintColor); arrowPaint.setPathEffect(new CornerPathEffect(arrowTipRadius)); mArrowView.setBackground(arrowDrawable); // Add negative margin so that the rounded corners on base of arrow are not visible. @@ -378,4 +400,18 @@ public class ArrowTipView extends AbstractFloatingView { super.onConfigurationChanged(newConfig); close(/* animate= */ false); } + + /** + * Sets a custom animation to run on open of the ArrowTipView. + */ + public void setCustomOpenAnimation(AnimatorSet animator) { + mOpenAnimator = animator; + } + + /** + * Sets a custom animation to run on close of the ArrowTipView. + */ + public void setCustomCloseAnimation(AnimatorSet animator) { + mCloseAnimator = animator; + } }