Merge "open grid task menu to the right of icon" into sc-v2-dev am: bd1aec6721
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/16248618
Change-Id: I5552cc4a7222a22191553f1b88ad23fc3ef3f25c
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index e48c9e8..4e6b7b9 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -28,6 +28,7 @@
<dimen name="task_menu_item_corner_radius">4dp</dimen>
<dimen name="task_menu_spacing">2dp</dimen>
<dimen name="task_menu_width_grid">234dp</dimen>
+ <dimen name="task_menu_horizontal_padding">8dp</dimen>
<dimen name="overview_proactive_row_height">48dp</dimen>
<dimen name="overview_proactive_row_bottom_margin">16dp</dimen>
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
index 57dad08..a768ef5 100644
--- a/quickstep/src/com/android/quickstep/KtR.java
+++ b/quickstep/src/com/android/quickstep/KtR.java
@@ -30,6 +30,7 @@
public static final class dimen {
public static int task_menu_spacing = R.dimen.task_menu_spacing;
+ public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
}
public static final class layout {
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index 5059f8b..179fd68 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -23,14 +23,18 @@
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RectShape
import android.util.AttributeSet
+import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import android.widget.FrameLayout
import android.widget.LinearLayout
import com.android.launcher3.BaseDraggingActivity
import com.android.launcher3.DeviceProfile
+import com.android.launcher3.InsettableFrameLayout
import com.android.launcher3.R
import com.android.launcher3.popup.ArrowPopup
+import com.android.launcher3.popup.RoundedArrowDrawable
import com.android.launcher3.popup.SystemShortcut
import com.android.launcher3.util.Themes
import com.android.quickstep.KtR
@@ -43,9 +47,13 @@
fun showForTask(taskContainer: TaskIdAttributeContainer): Boolean {
val activity = BaseDraggingActivity
- .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
+ .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
val taskMenuViewWithArrow = activity.layoutInflater
- .inflate(KtR.layout.task_menu_with_arrow, activity.dragLayer, false) as TaskMenuViewWithArrow<*>
+ .inflate(
+ KtR.layout.task_menu_with_arrow,
+ activity.dragLayer,
+ false
+ ) as TaskMenuViewWithArrow<*>
return taskMenuViewWithArrow.populateAndShowForTask(taskContainer)
}
@@ -53,10 +61,21 @@
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
- constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
+ constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
+ context,
+ attrs,
+ defStyleAttr
+ )
init {
clipToOutline = true
+
+ shouldScaleArrow = true
+ // This synchronizes the arrow and menu to open at the same time
+ OPEN_CHILD_FADE_START_DELAY = OPEN_FADE_START_DELAY
+ OPEN_CHILD_FADE_DURATION = OPEN_FADE_DURATION
+ CLOSE_FADE_START_DELAY = CLOSE_CHILD_FADE_START_DELAY
+ CLOSE_FADE_DURATION = CLOSE_CHILD_FADE_DURATION
}
private val menuWidth = context.resources.getDimensionPixelSize(R.dimen.task_menu_width_grid)
@@ -65,6 +84,13 @@
private lateinit var optionLayout: LinearLayout
private lateinit var taskContainer: TaskIdAttributeContainer
+ private var optionMeasuredHeight = 0
+ private val arrowHorizontalPadding: Int
+ get() = if (taskView.isFocusedTask)
+ resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding)
+ else
+ 0
+
override fun isOfType(type: Int): Boolean = type and TYPE_TASK_MENU != 0
override fun getTargetObjectLocation(outPos: Rect?) {
@@ -147,7 +173,10 @@
}
override fun assignMarginsAndBackgrounds(viewGroup: ViewGroup) {
- assignMarginsAndBackgrounds(this, Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface))
+ assignMarginsAndBackgrounds(
+ this,
+ Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface)
+ )
}
override fun onCreateOpenAnimation(anim: AnimatorSet) {
@@ -164,4 +193,90 @@
ObjectAnimator.ofFloat(taskContainer.thumbnailView, TaskThumbnailView.DIM_ALPHA, 0f)
)
}
+
+ /**
+ * Orients this container to the left or right of the given icon, aligning with the first option
+ * or second.
+ *
+ * These are the preferred orientations, in order (RTL prefers right-aligned over left):
+ * - Right and first option aligned
+ * - Right and second option aligned
+ * - Left and first option aligned
+ * - Left and second option aligned
+ *
+ * So we always align right if there is enough horizontal space
+ */
+ override fun orientAboutObject() {
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+ // Needed for offsets later
+ optionMeasuredHeight = optionLayout.getChildAt(0).measuredHeight
+ val extraHorizontalSpace = (mArrowHeight + mArrowOffsetVertical + arrowHorizontalPadding)
+
+ val widthWithArrow = measuredWidth + paddingLeft + paddingRight + extraHorizontalSpace
+ getTargetObjectLocation(mTempRect)
+ val dragLayer: InsettableFrameLayout = popupContainer
+ val insets = dragLayer.insets
+
+ // Put to the right of the icon if there is space, which means left aligned with the menu
+ val rightAlignedMenuStartX = mTempRect.left - widthWithArrow
+ val leftAlignedMenuStartX = mTempRect.right + extraHorizontalSpace
+ mIsLeftAligned = if (mIsRtl) {
+ rightAlignedMenuStartX + insets.left < 0
+ } else {
+ leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left <
+ dragLayer.width - insets.right
+ }
+
+ var menuStartX = if (mIsLeftAligned) leftAlignedMenuStartX else rightAlignedMenuStartX
+
+ // Offset y so that the arrow and first row are center-aligned with the original icon.
+ val iconHeight = mTempRect.height()
+ val optionHeight = optionMeasuredHeight
+ val yOffset = (optionHeight - iconHeight) / 2
+ var menuStartY = mTempRect.top - yOffset
+
+ // Insets are added later, so subtract them now.
+ menuStartX -= insets.left
+ menuStartY -= insets.top
+
+ setX(menuStartX.toFloat())
+ setY(menuStartY.toFloat())
+
+ val lp = layoutParams as FrameLayout.LayoutParams
+ val arrowLp = mArrow.layoutParams as FrameLayout.LayoutParams
+ lp.gravity = Gravity.TOP
+ arrowLp.gravity = lp.gravity
+ }
+
+ override fun addArrow() {
+ popupContainer.addView(mArrow)
+ mArrow.x = getArrowX()
+ mArrow.y = y + (optionMeasuredHeight / 2) - (mArrowHeight / 2)
+
+ updateArrowColor()
+
+ // This is inverted (x = height, y = width) because the arrow is rotated
+ mArrow.pivotX = if (mIsLeftAligned) 0f else mArrowHeight.toFloat()
+ mArrow.pivotY = 0f
+ }
+
+ private fun getArrowX(): Float {
+ return if (mIsLeftAligned)
+ x - mArrowHeight
+ else
+ x + measuredWidth + mArrowOffsetVertical
+ }
+
+ override fun updateArrowColor() {
+ mArrow.background = RoundedArrowDrawable(
+ mArrowWidth.toFloat(),
+ mArrowHeight.toFloat(),
+ mArrowPointRadius.toFloat(),
+ mIsLeftAligned,
+ mArrowColor
+ )
+ elevation = mElevation
+ mArrow.elevation = mElevation
+ }
+
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 5a1e4bf..b1a4109 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -77,44 +77,45 @@
extends AbstractFloatingView {
// Duration values (ms) for popup open and close animations.
- private static final int OPEN_DURATION = 276;
- private static final int OPEN_FADE_START_DELAY = 0;
- private static final int OPEN_FADE_DURATION = 38;
- private static final int OPEN_CHILD_FADE_START_DELAY = 38;
- private static final int OPEN_CHILD_FADE_DURATION = 76;
+ protected int OPEN_DURATION = 276;
+ protected int OPEN_FADE_START_DELAY = 0;
+ protected int OPEN_FADE_DURATION = 38;
+ protected int OPEN_CHILD_FADE_START_DELAY = 38;
+ protected int OPEN_CHILD_FADE_DURATION = 76;
- private static final int CLOSE_DURATION = 200;
- private static final int CLOSE_FADE_START_DELAY = 140;
- private static final int CLOSE_FADE_DURATION = 50;
- private static final int CLOSE_CHILD_FADE_START_DELAY = 0;
- private static final int CLOSE_CHILD_FADE_DURATION = 140;
+ protected int CLOSE_DURATION = 200;
+ protected int CLOSE_FADE_START_DELAY = 140;
+ protected int CLOSE_FADE_DURATION = 50;
+ protected int CLOSE_CHILD_FADE_START_DELAY = 0;
+ protected int CLOSE_CHILD_FADE_DURATION = 140;
// Index used to get background color when using local wallpaper color extraction,
private static final int DARK_COLOR_EXTRACTION_INDEX = android.R.color.system_neutral2_800;
private static final int LIGHT_COLOR_EXTRACTION_INDEX = android.R.color.system_accent2_50;
- private final Rect mTempRect = new Rect();
+ protected final Rect mTempRect = new Rect();
protected final LayoutInflater mInflater;
- private final float mOutlineRadius;
+ protected final float mOutlineRadius;
protected final T mActivityContext;
protected final boolean mIsRtl;
- private final int mArrowOffsetVertical;
- private final int mArrowOffsetHorizontal;
- private final int mArrowWidth;
- private final int mArrowHeight;
- private final int mArrowPointRadius;
- private final View mArrow;
+ protected final int mArrowOffsetVertical;
+ protected final int mArrowOffsetHorizontal;
+ protected final int mArrowWidth;
+ protected final int mArrowHeight;
+ protected final int mArrowPointRadius;
+ protected final View mArrow;
private final int mMargin;
protected boolean mIsLeftAligned;
protected boolean mIsAboveIcon;
- private int mGravity;
+ protected int mGravity;
protected AnimatorSet mOpenCloseAnimator;
protected boolean mDeferContainerRemoval;
+ protected boolean shouldScaleArrow = false;
private final GradientDrawable mRoundedTop;
private final GradientDrawable mRoundedBottom;
@@ -122,10 +123,10 @@
private Runnable mOnCloseCallback = () -> { };
// The rect string of the view that the arrow is attached to, in screen reference frame.
- private int mArrowColor;
+ protected int mArrowColor;
protected final List<LocalColorExtractor> mColorExtractors;
- private final float mElevation;
+ protected final float mElevation;
private final int mBackgroundColor;
private final String mIterateChildrenTag;
@@ -729,6 +730,14 @@
scale.setInterpolator(interpolator);
animatorSet.play(scale);
+ if (shouldScaleArrow) {
+ Animator arrowScaleAnimator = ObjectAnimator.ofFloat(mArrow, View.SCALE_Y,
+ scaleValues);
+ arrowScaleAnimator.setDuration(totalDuration);
+ arrowScaleAnimator.setInterpolator(interpolator);
+ animatorSet.play(arrowScaleAnimator);
+ }
+
fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet);
return animatorSet;
diff --git a/src/com/android/launcher3/popup/RoundedArrowDrawable.java b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
index e662d5c..436aa51 100644
--- a/src/com/android/launcher3/popup/RoundedArrowDrawable.java
+++ b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
@@ -78,6 +78,32 @@
mPath.transform(pathTransform);
}
+ /**
+ * Constructor for an arrow that points to the left or right.
+ *
+ * @param width of the arrow.
+ * @param height of the arrow.
+ * @param radius of the tip of the arrow.
+ * @param isPointingLeft or not.
+ * @param color to draw the triangle.
+ */
+ public RoundedArrowDrawable(float width, float height, float radius, boolean isPointingLeft,
+ int color) {
+ mPath = new Path();
+ mPaint = new Paint();
+ mPaint.setColor(color);
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setAntiAlias(true);
+
+ // Make the drawable with the triangle pointing down...
+ addDownPointingRoundedTriangleToPath(width, height, radius, mPath);
+
+ // ... then rotate it to the side it needs to point.
+ Matrix pathTransform = new Matrix();
+ pathTransform.setRotate(isPointingLeft ? 90 : -90, width * 0.5f, height * 0.5f);
+ mPath.transform(pathTransform);
+ }
+
@Override
public void draw(Canvas canvas) {
canvas.drawPath(mPath, mPaint);