From dca47432fcce4cbb91a29d0e3e0deef6460a7cc8 Mon Sep 17 00:00:00 2001 From: Alina Zaidi Date: Tue, 8 Jun 2021 14:35:22 +0100 Subject: [PATCH] Flip ArrowTipView when it goes beyond screen height. Bug: 185354491 Test: Manually tested tips in widget picker, all apps, and reconfigurable widgets tip. Change-Id: I5d1c9e4b8bc3ae89834bc198016ffa409a2b28ad --- .../launcher3/AppWidgetResizeFrame.java | 23 ++-- .../android/launcher3/views/ArrowTipView.java | 123 ++++++++++++++---- 2 files changed, 111 insertions(+), 35 deletions(-) diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index ac582cdcbf..9e21e1a0f1 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -24,6 +24,9 @@ import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; +import androidx.annotation.Nullable; +import androidx.annotation.Px; + import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.logging.InstanceId; @@ -692,15 +695,19 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O || keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN); } - private ArrowTipView showReconfigurableWidgetEducationTip() { - int[] coords = new int[2]; - mReconfigureButton.getLocationOnScreen(coords); - int tipTopMargin = mLauncher.getResources() + @Nullable private ArrowTipView showReconfigurableWidgetEducationTip() { + Rect rect = new Rect(); + if (!mReconfigureButton.getGlobalVisibleRect(rect)) { + return null; + } + @Px int tipMargin = mLauncher.getResources() .getDimensionPixelSize(R.dimen.widget_reconfigure_tip_top_margin); - return new ArrowTipView(mLauncher, /* isPointingUp= */ true).showAtLocation( - getContext().getString(R.string.reconfigurable_widget_education_tip), - /* arrowXCoord= */ coords[0] + mReconfigureButton.getWidth() / 2, - /* yCoord= */ coords[1] + mReconfigureButton.getHeight() + tipTopMargin); + return new ArrowTipView(mLauncher, /* isPointingUp= */ true) + .showAroundRect( + getContext().getString(R.string.reconfigurable_widget_education_tip), + /* arrowXCoord= */ rect.left + mReconfigureButton.getWidth() / 2, + /* rect= */ rect, + /* margin= */ tipMargin); } private boolean hasSeenReconfigurableWidgetEducationTip() { diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java index f3068a7d43..e449a4bfbf 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -17,8 +17,10 @@ package com.android.launcher3.views; import android.content.Context; +import android.content.res.Configuration; import android.graphics.CornerPathEffect; import android.graphics.Paint; +import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.Handler; import android.util.Log; @@ -53,9 +55,10 @@ public class ArrowTipView extends AbstractFloatingView { protected final BaseDraggingActivity mActivity; private final Handler mHandler = new Handler(); - private final boolean mIsPointingUp; private final int mArrowWidth; + private boolean mIsPointingUp; private Runnable mOnClosed; + private View mArrowView; public ArrowTipView(Context context) { this(context, false); @@ -109,24 +112,8 @@ public class ArrowTipView extends AbstractFloatingView { inflate(context, R.layout.arrow_toast, this); setOrientation(LinearLayout.VERTICAL); - View arrowView = findViewById(R.id.arrow); - ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams(); - ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create( - arrowLp.width, arrowLp.height, mIsPointingUp)); - 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.setPathEffect(new CornerPathEffect(arrowTipRadius)); - arrowView.setBackground(arrowDrawable); - // Add negative margin so that the rounded corners on base of arrow are not visible. - if (mIsPointingUp) { - removeView(arrowView); - addView(arrowView, 0); - ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, 0, 0, -1 * arrowTipRadius); - } else { - ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, -1 * arrowTipRadius, 0, 0); - } + mArrowView = findViewById(R.id.arrow); + updateArrowTipInView(); } /** @@ -152,8 +139,7 @@ public class ArrowTipView extends AbstractFloatingView { DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams(); params.gravity = gravity; - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) findViewById( - R.id.arrow).getLayoutParams(); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mArrowView.getLayoutParams(); lp.gravity = gravity; if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { @@ -184,7 +170,8 @@ public class ArrowTipView extends AbstractFloatingView { } /** - * Show the ArrowTipView (tooltip) custom aligned. + * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it + * cannot fit on screen in the requested orientation. * * @param text The text to be shown in the tooltip. * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the @@ -193,8 +180,51 @@ public class ArrowTipView extends AbstractFloatingView { * @return The tool tip view. {@code null} if the tip can not be shown. */ @Nullable public ArrowTipView showAtLocation(String text, @Px int arrowXCoord, @Px int yCoord) { + return showAtLocation( + text, + arrowXCoord, + /* yCoordDownPointingTip= */ yCoord, + /* yCoordUpPointingTip= */ yCoord); + } + + /** + * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it + * cannot fit on screen in the requested orientation. + * + * @param text The text to be shown in the tooltip. + * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the + * center of tooltip unless the tooltip goes beyond screen margin. + * @param rect The coordinates of the view which requests the tooltip to be shown. + * @param margin The margin between {@param rect} and the tooltip. + * @return The tool tip view. {@code null} if the tip can not be shown. + */ + @Nullable public ArrowTipView showAroundRect( + String text, @Px int arrowXCoord, Rect rect, @Px int margin) { + return showAtLocation( + text, + arrowXCoord, + /* yCoordDownPointingTip= */ rect.top - margin, + /* yCoordUpPointingTip= */ rect.bottom + margin); + } + + /** + * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it + * cannot fit on screen in the requested orientation. + * + * @param text The text to be shown in the tooltip. + * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the + * center of tooltip unless the tooltip goes beyond screen margin. + * @param yCoordDownPointingTip The Y coordinate of the pointed tip end of the tooltip when the + * tooltip is placed pointing downwards. + * @param yCoordUpPointingTip The Y coordinate of the pointed tip end of the tooltip when the + * tooltip is placed pointing upwards. + * @return The tool tip view. {@code null} if the tip can not be shown. + */ + @Nullable private ArrowTipView showAtLocation(String text, @Px int arrowXCoord, + @Px int yCoordDownPointingTip, @Px int yCoordUpPointingTip) { ViewGroup parent = mActivity.getDragLayer(); @Px int parentViewWidth = parent.getWidth(); + @Px int parentViewHeight = parent.getHeight(); @Px int maxTextViewWidth = getContext().getResources() .getDimensionPixelSize(R.dimen.widget_picker_education_tip_max_width); @Px int minViewMargin = getContext().getResources() @@ -211,6 +241,7 @@ public class ArrowTipView extends AbstractFloatingView { requestLayout(); post(() -> { + // Adjust the tooltip horizontally. float halfWidth = getWidth() / 2f; float xCoord; if (arrowXCoord - halfWidth < minViewMargin) { @@ -226,13 +257,24 @@ public class ArrowTipView extends AbstractFloatingView { xCoord = arrowXCoord - halfWidth; } setX(xCoord); - // Place the tooltip such that its top is at yCoord if arrow is pointing upwards, - // otherwise place it such that its bottom is at yCoord. - setY(mIsPointingUp ? yCoord : yCoord - getHeight()); + + // Adjust the tooltip vertically. + @Px int viewHeight = getHeight(); + if (mIsPointingUp + ? (yCoordUpPointingTip + viewHeight > parentViewHeight) + : (yCoordDownPointingTip - viewHeight < 0)) { + // Flip the view if it exceeds the vertical bounds of screen. + mIsPointingUp = !mIsPointingUp; + updateArrowTipInView(); + } + // Place the tooltip such that its top is at yCoordUpPointingTip if arrow is displayed + // pointing upwards, otherwise place it such that its bottom is at + // yCoordDownPointingTip. + setY(mIsPointingUp ? yCoordUpPointingTip : yCoordDownPointingTip - viewHeight); + // Adjust the arrow's relative position on tooltip to make sure the actual position of // arrow's pointed tip is always at arrowXCoord. - View arrowView = findViewById(R.id.arrow); - arrowView.setX(arrowXCoord - xCoord - arrowView.getWidth() / 2f); + mArrowView.setX(arrowXCoord - xCoord - mArrowView.getWidth() / 2f); requestLayout(); }); @@ -249,6 +291,27 @@ public class ArrowTipView extends AbstractFloatingView { return this; } + private void updateArrowTipInView() { + ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams(); + ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create( + arrowLp.width, arrowLp.height, mIsPointingUp)); + 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.setPathEffect(new CornerPathEffect(arrowTipRadius)); + mArrowView.setBackground(arrowDrawable); + // Add negative margin so that the rounded corners on base of arrow are not visible. + removeView(mArrowView); + if (mIsPointingUp) { + addView(mArrowView, 0); + ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, 0, 0, -1 * arrowTipRadius); + } else { + addView(mArrowView, 1); + ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, -1 * arrowTipRadius, 0, 0); + } + } + /** * Register a callback fired when toast is hidden */ @@ -256,4 +319,10 @@ public class ArrowTipView extends AbstractFloatingView { mOnClosed = runnable; return this; } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + close(/* animate= */ false); + } }