Loading res/values/attrs.xml +4 −0 Original line number Diff line number Diff line Loading @@ -518,4 +518,8 @@ <!-- The icon drawable of a widget category. --> <attr name="sectionDrawable" format="reference" /> </declare-styleable> <declare-styleable name="ArrowTipView"> <attr name="arrowTipBackground" format="color" /> </declare-styleable> </resources> res/values/styles.xml +3 −0 Original line number Diff line number Diff line Loading @@ -378,4 +378,7 @@ <item name="horizontalPadding">16dp</item> </style> <style name="ArrowTipStyle"> <item name="arrowTipBackground">@color/arrow_tip_view_bg</item> </style> </resources> src/com/android/launcher3/views/ArrowTipView.java +79 −43 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 { Loading @@ -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; } } Loading @@ -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; Loading @@ -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); } }); } /** Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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() Loading @@ -288,8 +314,10 @@ public class ArrowTipView extends AbstractFloatingView { TextView textView = findViewById(R.id.text); textView.setText(text); textView.setMaxWidth(maxTextViewWidth); if (parent.indexOfChild(this) < 0) { parent.addView(this); requestLayout(); } post(() -> { // Adjust the tooltip horizontally. Loading Loading @@ -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; } Loading @@ -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. Loading @@ -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; } } Loading
res/values/attrs.xml +4 −0 Original line number Diff line number Diff line Loading @@ -518,4 +518,8 @@ <!-- The icon drawable of a widget category. --> <attr name="sectionDrawable" format="reference" /> </declare-styleable> <declare-styleable name="ArrowTipView"> <attr name="arrowTipBackground" format="color" /> </declare-styleable> </resources>
res/values/styles.xml +3 −0 Original line number Diff line number Diff line Loading @@ -378,4 +378,7 @@ <item name="horizontalPadding">16dp</item> </style> <style name="ArrowTipStyle"> <item name="arrowTipBackground">@color/arrow_tip_view_bg</item> </style> </resources>
src/com/android/launcher3/views/ArrowTipView.java +79 −43 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 { Loading @@ -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; } } Loading @@ -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; Loading @@ -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); } }); } /** Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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() Loading @@ -288,8 +314,10 @@ public class ArrowTipView extends AbstractFloatingView { TextView textView = findViewById(R.id.text); textView.setText(text); textView.setMaxWidth(maxTextViewWidth); if (parent.indexOfChild(this) < 0) { parent.addView(this); requestLayout(); } post(() -> { // Adjust the tooltip horizontally. Loading Loading @@ -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; } Loading @@ -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. Loading @@ -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; } }