Merge "Add myself to launcher OWNERS" into main
diff --git a/quickstep/res/drawable/bg_bubble_expanded_view_drop_target.xml b/quickstep/res/drawable/bg_bubble_expanded_view_drop_target.xml
new file mode 100644
index 0000000..98aab67
--- /dev/null
+++ b/quickstep/res/drawable/bg_bubble_expanded_view_drop_target.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/bubble_expanded_view_drop_target_corner_radius" />
+ <solid android:color="@color/bubblebar_drop_target_bg_color" />
+ <stroke
+ android:width="1dp"
+ android:color="?androidprv:attr/materialColorPrimaryContainer" />
+</shape>
diff --git a/quickstep/res/layout/bubble_expanded_view_drop_target.xml b/quickstep/res/layout/bubble_expanded_view_drop_target.xml
new file mode 100644
index 0000000..15ec49a
--- /dev/null
+++ b/quickstep/res/layout/bubble_expanded_view_drop_target.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- TODO(b/330585402): replace 600dp height with calculated value -->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/bubble_expanded_view_drop_target_width"
+ android:layout_height="600dp"
+ android:layout_margin="@dimen/bubble_expanded_view_drop_target_margin"
+ android:background="@drawable/bg_bubble_expanded_view_drop_target"
+ android:elevation="@dimen/bubblebar_elevation" />
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b862d7c..c5f25ad 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -456,6 +456,9 @@
<!-- Bubble bar drop target -->
<dimen name="bubblebar_drop_target_corner_radius">36dp</dimen>
+ <dimen name="bubble_expanded_view_drop_target_corner_radius">16dp</dimen>
+ <dimen name="bubble_expanded_view_drop_target_width">412dp</dimen>
+ <dimen name="bubble_expanded_view_drop_target_margin">16dp</dimen>
<!-- Launcher splash screen -->
<!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml -->
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index a4b6ad0..e5396ee 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -106,6 +106,7 @@
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.taskbar.bubbles.BubbleDismissController;
import com.android.launcher3.taskbar.bubbles.BubbleDragController;
+import com.android.launcher3.taskbar.bubbles.BubblePinController;
import com.android.launcher3.taskbar.bubbles.BubbleStashController;
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController;
import com.android.launcher3.taskbar.navbutton.NearestTouchFrame;
@@ -259,6 +260,8 @@
new BubbleDragController(this),
new BubbleDismissController(this, mDragLayer),
new BubbleBarPinController(this, mDragLayer,
+ () -> getDeviceProfile().getDisplayInfo().currentSize),
+ new BubblePinController(this, mDragLayer,
() -> getDeviceProfile().getDisplayInfo().currentSize)
));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index ec47c4f..90c3ea7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -131,21 +131,15 @@
// Draw background.
val radius = backgroundHeight / 2f
- val left = if (anchorLeft) 0f else bounds.width().toFloat() - width
- val right = if (anchorLeft) width else bounds.width().toFloat()
- canvas.drawRoundRect(
- left,
- pointerVisibleHeight,
- right,
- bounds.height().toFloat(),
- radius,
- radius,
- paint
- )
+ val left = bounds.left + (if (anchorLeft) 0f else bounds.width().toFloat() - width)
+ val right = bounds.left + (if (anchorLeft) width else bounds.width().toFloat())
+ val top = bounds.top + pointerVisibleHeight
+ val bottom = bounds.top + bounds.height().toFloat()
+ canvas.drawRoundRect(left, top, right, bottom, radius, radius, paint)
if (showingArrow) {
// Draw arrow.
- val transX = arrowPositionX - pointerWidth / 2f
+ val transX = bounds.left + arrowPositionX - pointerWidth / 2f
canvas.translate(transX, 0f)
arrowDrawable.draw(canvas)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 60e8abe..de93ba5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar.bubbles;
import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import android.animation.Animator;
@@ -29,6 +30,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.util.LayoutDirection;
import android.util.Log;
import android.view.Gravity;
@@ -79,6 +81,7 @@
// TODO: (b/273594744) calculate the amount of space we have and base the max on that
// if it's smaller than 5.
private static final int MAX_BUBBLES = 5;
+ private static final int MAX_VISIBLE_BUBBLES_COLLAPSED = 2;
private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200;
private static final int WIDTH_ANIMATION_DURATION_MS = 200;
@@ -94,6 +97,40 @@
// During fade in animation we shift the bubble bar 1/60th of the screen width
private static final float FADE_IN_ANIM_POSITION_SHIFT = 1 / 60f;
+ /**
+ * Custom property to set translationX value for the bar view while a bubble is being dragged.
+ * Skips applying translation to the dragged bubble.
+ */
+ private static final FloatProperty<BubbleBarView> BUBBLE_DRAG_TRANSLATION_X =
+ new FloatProperty<>("bubbleDragTranslationX") {
+ @Override
+ public void setValue(BubbleBarView bubbleBarView, float translationX) {
+ bubbleBarView.setTranslationXDuringBubbleDrag(translationX);
+ }
+
+ @Override
+ public Float get(BubbleBarView bubbleBarView) {
+ return bubbleBarView.mTranslationXDuringDrag;
+ }
+ };
+
+ /**
+ * Custom property to set alpha value for the bar view while a bubble is being dragged.
+ * Skips applying alpha to the dragged bubble.
+ */
+ private static final FloatProperty<BubbleBarView> BUBBLE_DRAG_ALPHA =
+ new FloatProperty<>("bubbleDragAlpha") {
+ @Override
+ public void setValue(BubbleBarView bubbleBarView, float alpha) {
+ bubbleBarView.setAlphaDuringBubbleDrag(alpha);
+ }
+
+ @Override
+ public Float get(BubbleBarView bubbleBarView) {
+ return bubbleBarView.mAlphaDuringDrag;
+ }
+ };
+
private final BubbleBarBackground mBubbleBarBackground;
private boolean mIsAnimatingNewBubble = false;
@@ -117,6 +154,8 @@
private final float mDragElevation;
private final int mPointerSize;
+ private final Rect mTempBackgroundBounds = new Rect();
+
// Whether the bar is expanded (i.e. the bubble activity is being displayed).
private boolean mIsBarExpanded = false;
// The currently selected bubble view.
@@ -150,6 +189,8 @@
@Nullable
private BubbleView mDraggedBubbleView;
+ private float mTranslationXDuringDrag = 0f;
+ private float mAlphaDuringDrag = 0f;
private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
@@ -259,8 +300,10 @@
setPivotX(mRelativePivotX * getWidth());
setPivotY(mRelativePivotY * getHeight());
- // Position the views
- updateChildrenRenderNodeProperties(mBubbleBarLocation);
+ if (!mDragging) {
+ // Position the views when not dragging
+ updateChildrenRenderNodeProperties(mBubbleBarLocation);
+ }
}
@Override
@@ -302,17 +345,15 @@
mBubbleBarLocationAnimator.cancel();
mBubbleBarLocationAnimator = null;
}
- setTranslationX(0f);
- setAlpha(1f);
+ resetDragAnimation();
if (bubbleBarLocation != mBubbleBarLocation) {
mBubbleBarLocation = bubbleBarLocation;
onBubbleBarLocationChanged();
- invalidate();
}
}
/**
- * Set whether this view is being currently being dragged
+ * Set whether this view is currently being dragged
*/
public void setIsDragging(boolean dragging) {
if (mDragging == dragging) {
@@ -326,7 +367,7 @@
* Get translation for bubble bar when drag is released and it needs to animate back to the
* resting position.
* Resting position is based on the supplied location. If the supplied location is different
- * from the internal location that was used to lay out the bubble bar, translation values are
+ * from the internal location that was used during bubble bar layout, translation values are
* calculated to position the bar at the desired location.
*
* @param initialTranslation initial bubble bar translation at the start of drag
@@ -353,6 +394,30 @@
return dragEndTranslation;
}
+ /**
+ * Get translation for a bubble when drag is released and it needs to animate back to the
+ * resting position.
+ * Resting position is based on the supplied location. If the supplied location is different
+ * from the internal location that was used during bubble bar layout, translation values are
+ * calculated to position the bar at the desired location.
+ *
+ * @param initialTranslation initial bubble bar translation at the start of drag
+ * @param location desired location of the bubble bar when drag is released
+ * @return point with x and y values representing translation on x and y-axis
+ */
+ public PointF getDraggedBubbleReleaseTranslation(PointF initialTranslation,
+ BubbleBarLocation location) {
+ // Start with bubble bar translation
+ final PointF dragEndTranslation = new PointF(
+ getBubbleBarDragReleaseTranslation(initialTranslation, location));
+ // Apply individual bubble translation, as the order may have changed
+ int viewIndex = indexOfChild(mDraggedBubbleView);
+ dragEndTranslation.x += getExpandedBubbleTranslationX(viewIndex,
+ getChildCount(),
+ location.isOnLeft(isLayoutRtl()));
+ return dragEndTranslation;
+ }
+
private float getDistanceFromOtherSide() {
// Calculate the shift needed to position the bubble bar on the other side
int displayWidth = getResources().getDisplayMetrics().widthPixels;
@@ -393,17 +458,18 @@
mBubbleBarLocationAnimator.start();
}
- private Animator getLocationUpdateFadeOutAnimator(BubbleBarLocation bubbleBarLocation) {
+ private Animator getLocationUpdateFadeOutAnimator(BubbleBarLocation newLocation) {
+ final FloatProperty<? super BubbleBarView> txProp = getLocationAnimTranslationXProperty();
final float shift =
getResources().getDisplayMetrics().widthPixels * FADE_OUT_ANIM_POSITION_SHIFT;
- final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl());
- final float tx = getTranslationX() + (onLeft ? shift : -shift);
+ final boolean onLeft = newLocation.isOnLeft(isLayoutRtl());
+ final float tx = txProp.get(this) + (onLeft ? -shift : shift);
- ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, TRANSLATION_X, tx)
- .setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS);
+ ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, txProp, tx).setDuration(
+ FADE_OUT_ANIM_POSITION_DURATION_MS);
positionAnim.setInterpolator(EMPHASIZED_ACCELERATE);
- ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, ALPHA, 0f)
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, getLocationAnimAlphaProperty(), 0f)
.setDuration(FADE_OUT_ANIM_ALPHA_DURATION_MS);
alphaAnim.setStartDelay(FADE_OUT_ANIM_ALPHA_DELAY_MS);
@@ -412,14 +478,14 @@
return animatorSet;
}
- private Animator getLocationUpdateFadeInAnimator(BubbleBarLocation animatedLocation) {
+ private Animator getLocationUpdateFadeInAnimator(BubbleBarLocation newLocation) {
final float shift =
getResources().getDisplayMetrics().widthPixels * FADE_IN_ANIM_POSITION_SHIFT;
- final boolean onLeft = animatedLocation.isOnLeft(isLayoutRtl());
+ final boolean onLeft = newLocation.isOnLeft(isLayoutRtl());
final float startTx;
final float finalTx;
- if (animatedLocation == mBubbleBarLocation) {
+ if (newLocation == mBubbleBarLocation) {
// Animated location matches layout location.
finalTx = 0;
} else {
@@ -439,9 +505,9 @@
.setEndValue(finalTx)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
.setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS)
- .build(this, VIEW_TRANSLATE_X);
+ .build(this, getLocationAnimTranslationXProperty());
- ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, ALPHA, 1f)
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, getLocationAnimAlphaProperty(), 1f)
.setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);
AnimatorSet animatorSet = new AnimatorSet();
@@ -450,6 +516,84 @@
}
/**
+ * Get property that can be used to animate the translation-x value for the bar.
+ * When a bubble is being dragged, uses {@link #BUBBLE_DRAG_TRANSLATION_X}.
+ * Falls back to {@link com.android.launcher3.LauncherAnimUtils#VIEW_TRANSLATE_X} otherwise.
+ */
+ private FloatProperty<? super BubbleBarView> getLocationAnimTranslationXProperty() {
+ return mDraggedBubbleView == null ? VIEW_TRANSLATE_X : BUBBLE_DRAG_TRANSLATION_X;
+ }
+
+ /**
+ * Get property that can be used to animate the alpha value for the bar.
+ * When a bubble is being dragged, uses {@link #BUBBLE_DRAG_ALPHA}.
+ * Falls back to {@link com.android.launcher3.LauncherAnimUtils#VIEW_ALPHA} otherwise.
+ */
+ private FloatProperty<? super BubbleBarView> getLocationAnimAlphaProperty() {
+ return mDraggedBubbleView == null ? VIEW_ALPHA : BUBBLE_DRAG_ALPHA;
+ }
+
+ /**
+ * Set translation-x value for the bar while a bubble is being dragged.
+ * We can not update translation on the bar directly because the dragged bubble would be
+ * affected as well. As it is a child view.
+ * Instead, while a bubble is being dragged, set translation on each child view, that is not the
+ * dragged view. And set a translation on the background.
+ * This allows for the dragged bubble view to remain in position while the bar moves during
+ * animation.
+ */
+ private void setTranslationXDuringBubbleDrag(float translationX) {
+ mTranslationXDuringDrag = translationX;
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ BubbleView view = (BubbleView) getChildAt(i);
+ if (view != mDraggedBubbleView) {
+ view.setBubbleBarTranslationX(translationX);
+ }
+ }
+ if (mBubbleBarBackground != null) {
+ mTempBackgroundBounds.set(mBubbleBarBackground.getBounds());
+ mTempBackgroundBounds.offsetTo((int) translationX, 0);
+ mBubbleBarBackground.setBounds(mTempBackgroundBounds);
+ }
+ }
+
+ /**
+ * Set alpha value for the bar while a bubble is being dragged.
+ * We can not update the alpha on the bar directly because the dragged bubble would be affected
+ * as well. As it is a child view.
+ * Instead, while a bubble is being dragged, set alpha on each child view, that is not the
+ * dragged view. And set an alpha on the background.
+ * This allows for the dragged bubble to remain visible while the bar is hidden during
+ * animation.
+ */
+ private void setAlphaDuringBubbleDrag(float alpha) {
+ mAlphaDuringDrag = alpha;
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View view = getChildAt(i);
+ if (view != mDraggedBubbleView) {
+ view.setAlpha(alpha);
+ }
+ }
+ if (mBubbleBarBackground != null) {
+ mBubbleBarBackground.setAlpha((int) (255 * alpha));
+ }
+ }
+
+ private void resetDragAnimation() {
+ if (mBubbleBarLocationAnimator != null) {
+ mBubbleBarLocationAnimator.removeAllListeners();
+ mBubbleBarLocationAnimator.cancel();
+ mBubbleBarLocationAnimator = null;
+ }
+ setTranslationXDuringBubbleDrag(0f);
+ setAlphaDuringBubbleDrag(1f);
+ setTranslationX(0f);
+ setAlpha(1f);
+ }
+
+ /**
* Updates the bounds with translation that may have been applied and returns the result.
*/
public Rect getBubbleBarBounds() {
@@ -558,30 +702,21 @@
float elevationState = (1 - widthState);
for (int i = 0; i < bubbleCount; i++) {
BubbleView bv = (BubbleView) getChildAt(i);
+ if (bv == mDraggedBubbleView) {
+ // Skip the dragged bubble. Its translation is managed by the drag controller.
+ continue;
+ }
bv.setTranslationY(ty);
// the position of the bubble when the bar is fully expanded
- final float expandedX;
+ final float expandedX = getExpandedBubbleTranslationX(i, bubbleCount, onLeft);
// the position of the bubble when the bar is fully collapsed
- final float collapsedX;
- if (onLeft) {
- // If bar is on the left, bubbles are ordered right to left
- expandedX = (bubbleCount - i - 1) * (mIconSize + mExpandedBarIconsSpacing);
- // Shift the first bubble only if there are more bubbles in addition to overflow
- collapsedX = i == 0 && bubbleCount > 2 ? mIconOverlapAmount : 0;
- } else {
- // Bubbles ordered left to right, don't move the first bubble
- expandedX = i * (mIconSize + mExpandedBarIconsSpacing);
- collapsedX = i == 0 ? 0 : mIconOverlapAmount;
- }
- if (bv == mDraggedBubbleView) {
- // if bubble is dragged set the elevation to bubble drag elevation
- bv.setZ(mDragElevation);
- } else {
- // otherwise slowly animate elevation while keeping correct Z ordering
- float fullElevationForChild = (MAX_BUBBLES * mBubbleElevation) - i;
- bv.setZ(fullElevationForChild * elevationState);
- }
+ final float collapsedX = getCollapsedBubbleTranslationX(i, bubbleCount, onLeft);
+
+ // slowly animate elevation while keeping correct Z ordering
+ float fullElevationForChild = (MAX_BUBBLES * mBubbleElevation) - i;
+ bv.setZ(fullElevationForChild * elevationState);
+
if (mIsBarExpanded) {
// If bar is on the right, account for bubble bar expanding and shifting left
final float expandedBarShift = onLeft ? 0 : currentWidth - expandedWidth;
@@ -601,9 +736,10 @@
// If we're fully collapsed, hide all bubbles except for the first 2. If there are
// only 2 bubbles, hide the second bubble as well because it's the overflow.
if (widthState == 0) {
- if (i > 1) {
+ if (i > MAX_VISIBLE_BUBBLES_COLLAPSED - 1) {
bv.setAlpha(0);
- } else if (i == 1 && bubbleCount == 2) {
+ } else if (i == MAX_VISIBLE_BUBBLES_COLLAPSED - 1
+ && bubbleCount == MAX_VISIBLE_BUBBLES_COLLAPSED) {
bv.setAlpha(0);
}
}
@@ -636,6 +772,34 @@
mBubbleBarBackground.setWidth(interpolatedWidth);
}
+ private float getExpandedBubbleTranslationX(int bubbleIndex, int bubbleCount,
+ boolean onLeft) {
+ if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) {
+ return 0;
+ }
+ if (onLeft) {
+ // If bar is on the left, bubbles are ordered right to left
+ return (bubbleCount - bubbleIndex - 1) * (mIconSize + mExpandedBarIconsSpacing);
+ } else {
+ // Bubbles ordered left to right, don't move the first bubble
+ return bubbleIndex * (mIconSize + mExpandedBarIconsSpacing);
+ }
+ }
+
+ private float getCollapsedBubbleTranslationX(int bubbleIndex, int bubbleCount,
+ boolean onLeft) {
+ if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) {
+ return 0;
+ }
+ if (onLeft) {
+ // Shift the first bubble only if there are more bubbles in addition to overflow
+ return bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED
+ ? mIconOverlapAmount : 0;
+ } else {
+ return bubbleIndex == 0 ? 0 : mIconOverlapAmount;
+ }
+ }
+
/**
* Reorders the views to match the provided list.
*/
@@ -685,8 +849,18 @@
* Sets the dragged bubble view to correctly apply Z order. Dragged view should appear on top
*/
public void setDraggedBubble(@Nullable BubbleView view) {
+ if (mDraggedBubbleView != null) {
+ mDraggedBubbleView.setZ(0);
+ if (view == null) {
+ // We are clearing the dragged bubble, reset drag
+ resetDragAnimation();
+ }
+ }
mDraggedBubbleView = view;
- requestLayout();
+ if (view != null) {
+ view.setZ(mDragElevation);
+ }
+ setIsDragging(view != null);
}
/**
@@ -745,7 +919,7 @@
if (bubbleBarLocation.isOnLeft(isLayoutRtl())) {
// Bubble positions are reversed. First bubble may be shifted, if there are more
// bubbles than the current bubble and overflow.
- bubblePosition = index == 0 && getChildCount() > 2 ? 1 : 0;
+ bubblePosition = index == 0 && getChildCount() > MAX_VISIBLE_BUBBLES_COLLAPSED ? 1 : 0;
} else {
bubblePosition = index;
}
@@ -807,7 +981,7 @@
final int horizontalPadding = getPaddingStart() + getPaddingEnd();
// If there are more than 2 bubbles, the first 2 should be visible when collapsed.
// Otherwise just the first bubble should be visible because we don't show the overflow.
- return childCount > 2
+ return childCount > MAX_VISIBLE_BUBBLES_COLLAPSED
? mIconSize + mIconOverlapAmount + horizontalPadding
: mIconSize + horizontalPadding;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index dc48a66..95dd24b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -20,6 +20,7 @@
import android.content.res.Resources;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -468,7 +469,8 @@
*/
public void onDragStart(@NonNull BubbleView bubbleView) {
if (bubbleView.getBubble() == null) return;
- mSystemUiProxy.onBubbleDrag(bubbleView.getBubble().getKey(), /* isBeingDragged = */ true);
+
+ mSystemUiProxy.startBubbleDrag(bubbleView.getBubble().getKey());
mBarView.setDraggedBubble(bubbleView);
}
@@ -476,19 +478,46 @@
* Notifies SystemUI to expand the selected bubble when the bubble is released.
* @param bubbleView dragged bubble view
*/
- public void onDragRelease(@NonNull BubbleView bubbleView) {
+ public void onDragRelease(@NonNull BubbleView bubbleView, BubbleBarLocation location) {
if (bubbleView.getBubble() == null) return;
- mSystemUiProxy.onBubbleDrag(bubbleView.getBubble().getKey(), /* isBeingDragged = */ false);
+ // TODO(b/330585402): send new bubble bar bounds to shell for the animation
+ mSystemUiProxy.stopBubbleDrag(bubbleView.getBubble().getKey(), location);
}
/**
- * Removes the dragged bubble view in the bubble bar view
+ * Notifies {@link BubbleBarView} that drag and all animations are finished.
*/
public void onDragEnd() {
mBarView.setDraggedBubble(null);
}
/**
+ * Get translation for bubble bar when drag is released.
+ *
+ * @see BubbleBarView#getBubbleBarDragReleaseTranslation(PointF, BubbleBarLocation)
+ */
+ public PointF getBubbleBarDragReleaseTranslation(PointF initialTranslation,
+ BubbleBarLocation location) {
+ if (location == mBarView.getBubbleBarLocation()) {
+ return initialTranslation;
+ }
+ return mBarView.getBubbleBarDragReleaseTranslation(initialTranslation, location);
+ }
+
+ /**
+ * Get translation for bubble view when drag is released.
+ *
+ * @see BubbleBarView#getDraggedBubbleReleaseTranslation(PointF, BubbleBarLocation)
+ */
+ public PointF getDraggedBubbleReleaseTranslation(PointF initialTranslation,
+ BubbleBarLocation location) {
+ if (location == mBarView.getBubbleBarLocation()) {
+ return initialTranslation;
+ }
+ return mBarView.getDraggedBubbleReleaseTranslation(initialTranslation, location);
+ }
+
+ /**
* Called when bubble was dragged into the dismiss target. Notifies System
* @param bubble dismissed bubble item
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index 90f1be3..295477c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -30,6 +30,7 @@
public final BubbleDragController bubbleDragController;
public final BubbleDismissController bubbleDismissController;
public final BubbleBarPinController bubbleBarPinController;
+ public final BubblePinController bubblePinController;
private final RunnableList mPostInitRunnables = new RunnableList();
@@ -45,7 +46,8 @@
BubbleStashedHandleViewController bubbleStashedHandleViewController,
BubbleDragController bubbleDragController,
BubbleDismissController bubbleDismissController,
- BubbleBarPinController bubbleBarPinController) {
+ BubbleBarPinController bubbleBarPinController,
+ BubblePinController bubblePinController) {
this.bubbleBarController = bubbleBarController;
this.bubbleBarViewController = bubbleBarViewController;
this.bubbleStashController = bubbleStashController;
@@ -53,6 +55,7 @@
this.bubbleDragController = bubbleDragController;
this.bubbleDismissController = bubbleDismissController;
this.bubbleBarPinController = bubbleBarPinController;
+ this.bubblePinController = bubblePinController;
}
/**
@@ -68,6 +71,7 @@
bubbleDragController.init(/* bubbleControllers = */ this);
bubbleDismissController.init(/* bubbleControllers = */ this);
bubbleBarPinController.init(this);
+ bubblePinController.init(this);
mPostInitRunnables.executeAllAndDestroy();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index d1c9da7..1764f75 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -43,6 +43,7 @@
private BubbleBarViewController mBubbleBarViewController;
private BubbleDismissController mBubbleDismissController;
private BubbleBarPinController mBubbleBarPinController;
+ private BubblePinController mBubblePinController;
public BubbleDragController(TaskbarActivityContext activity) {
mActivity = activity;
@@ -58,8 +59,12 @@
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleDismissController = bubbleControllers.bubbleDismissController;
mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
+ mBubblePinController = bubbleControllers.bubblePinController;
mBubbleDismissController.setListener(
- stuck -> mBubbleBarPinController.setDropTargetHidden(stuck));
+ stuck -> {
+ mBubbleBarPinController.setDropTargetHidden(stuck);
+ mBubblePinController.setDropTargetHidden(stuck);
+ });
}
/**
@@ -73,19 +78,58 @@
}
bubbleView.setOnTouchListener(new BubbleTouchListener() {
+
+ private BubbleBarLocation mReleasedLocation = BubbleBarLocation.DEFAULT;
+
+ private final LocationChangeListener mLocationChangeListener =
+ new LocationChangeListener() {
+ @Override
+ public void onChange(@NonNull BubbleBarLocation location) {
+ mBubbleBarController.animateBubbleBarLocation(location);
+ }
+
+ @Override
+ public void onRelease(@NonNull BubbleBarLocation location) {
+ mReleasedLocation = location;
+ }
+ };
+
@Override
void onDragStart() {
+ mBubblePinController.setListener(mLocationChangeListener);
mBubbleBarViewController.onDragStart(bubbleView);
+ mBubblePinController.onDragStart(
+ mBubbleBarViewController.getBubbleBarLocation().isOnLeft(
+ bubbleView.isLayoutRtl()));
}
@Override
- void onDragEnd() {
- mBubbleBarViewController.onDragEnd();
+ protected void onDragUpdate(float x, float y) {
+ mBubblePinController.onDragUpdate(x, y);
}
@Override
protected void onDragRelease() {
- mBubbleBarViewController.onDragRelease(bubbleView);
+ mBubblePinController.onDragEnd();
+ mBubbleBarViewController.onDragRelease(bubbleView, mReleasedLocation);
+ }
+
+ @Override
+ protected void onDragDismiss() {
+ mBubblePinController.onDragEnd();
+ }
+
+ @Override
+ void onDragEnd() {
+ mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
+ mBubbleBarViewController.onDragEnd();
+ mBubblePinController.setListener(null);
+ }
+
+ @Override
+ protected PointF getRestingPosition() {
+ return mBubbleBarViewController.getDraggedBubbleReleaseTranslation(
+ getInitialPosition(), mReleasedLocation);
}
});
}
@@ -98,8 +142,7 @@
PointF initialRelativePivot = new PointF();
bubbleBarView.setOnTouchListener(new BubbleTouchListener() {
- @Nullable
- private BubbleBarLocation mReleasedLocation;
+ private BubbleBarLocation mReleasedLocation = BubbleBarLocation.DEFAULT;
private final LocationChangeListener mLocationChangeListener =
new LocationChangeListener() {
@@ -145,20 +188,18 @@
@Override
void onDragEnd() {
+ // Make sure to update location as the first thing. Pivot update causes a relayout
+ mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
+ bubbleBarView.setIsDragging(false);
// Restoring the initial pivot for the bubble bar view
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
- bubbleBarView.setIsDragging(false);
- mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
+ mBubbleBarPinController.setListener(null);
}
@Override
protected PointF getRestingPosition() {
- if (mReleasedLocation == null
- || mReleasedLocation == bubbleBarView.getBubbleBarLocation()) {
- return getInitialPosition();
- }
- return bubbleBarView.getBubbleBarDragReleaseTranslation(getInitialPosition(),
- mReleasedLocation);
+ return mBubbleBarViewController.getBubbleBarDragReleaseTranslation(
+ getInitialPosition(), mReleasedLocation);
}
});
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
new file mode 100644
index 0000000..fef7fa1
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Point
+import android.view.Gravity.BOTTOM
+import android.view.Gravity.LEFT
+import android.view.Gravity.RIGHT
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.core.view.updateLayoutParams
+import com.android.launcher3.R
+import com.android.wm.shell.common.bubbles.BaseBubblePinController
+import com.android.wm.shell.common.bubbles.BubbleBarLocation
+
+/** Controller to manage pinning bubble bar to left or right when dragging starts from a bubble */
+class BubblePinController(
+ private val context: Context,
+ private val container: FrameLayout,
+ screenSizeProvider: () -> Point
+) : BaseBubblePinController(screenSizeProvider) {
+
+ private lateinit var bubbleBarViewController: BubbleBarViewController
+ private lateinit var bubbleStashController: BubbleStashController
+ private var exclRectWidth: Float = 0f
+ private var exclRectHeight: Float = 0f
+
+ private var dropTargetView: View? = null
+ private var dropTargetMargin: Int = 0
+
+ fun init(bubbleControllers: BubbleControllers) {
+ bubbleBarViewController = bubbleControllers.bubbleBarViewController
+ bubbleStashController = bubbleControllers.bubbleStashController
+ exclRectWidth = context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_width)
+ exclRectHeight = context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_height)
+ dropTargetMargin =
+ context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_drop_target_margin)
+ }
+
+ override fun getExclusionRectWidth(): Float {
+ return exclRectWidth
+ }
+
+ override fun getExclusionRectHeight(): Float {
+ return exclRectHeight
+ }
+
+ override fun getDropTargetView(): View? {
+ return dropTargetView
+ }
+
+ override fun removeDropTargetView(view: View) {
+ container.removeView(view)
+ dropTargetView = null
+ }
+
+ override fun createDropTargetView(): View {
+ return LayoutInflater.from(context)
+ .inflate(R.layout.bubble_expanded_view_drop_target, container, false)
+ .also { view ->
+ // TODO(b/330585402): dynamic height for the drop target based on actual height
+ dropTargetView = view
+ container.addView(view)
+ }
+ }
+
+ @SuppressLint("RtlHardcoded")
+ override fun updateLocation(location: BubbleBarLocation) {
+ val onLeft = location.isOnLeft(container.isLayoutRtl)
+
+ val bubbleBarBounds = bubbleBarViewController.bubbleBarBounds
+ dropTargetView?.updateLayoutParams<FrameLayout.LayoutParams> {
+ gravity = BOTTOM or (if (onLeft) LEFT else RIGHT)
+ bottomMargin =
+ -bubbleStashController.bubbleBarTranslationY.toInt() +
+ bubbleBarBounds.height() +
+ dropTargetMargin
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index bcdc718..3dc4ebc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -66,6 +66,9 @@
private final ImageView mAppIcon;
private final int mBubbleSize;
+ private float mBubbleBarTranslationX = 0f;
+ private float mTranslationX = 0f;
+
private DotRenderer mDotRenderer;
private DotRenderer.DrawParams mDrawParams;
private int mDotColor;
@@ -127,6 +130,35 @@
}
@Override
+ public void setTranslationX(float translationX) {
+ // Overriding setting translationX as it can be a combination of the parent translation
+ // and current view translation.
+ // When a BubbleView is being dragged to pin the bubble bar to other side, we animate the
+ // bar to the new location during the drag.
+ // One part of the animation is updating the translation of the bubble bar. But doing
+ // that also updates the translation for the child views, like the dragged bubble.
+ // To get around that, we instead apply translation on each child view of bubble bar. It
+ // is applied as bubble bar translation. This results in BubbleView's translation being a
+ // sum of the translation it has and the parent bubble bar translation.
+ mTranslationX = translationX;
+ applyTranslation();
+ }
+
+ /**
+ * Translation of the bubble bar that hosts this bubble.
+ * Is applied together with translation applied on the view through
+ * {@link #setTranslationX(float)}.
+ */
+ void setBubbleBarTranslationX(float translationX) {
+ mBubbleBarTranslationX = translationX;
+ applyTranslation();
+ }
+
+ private void applyTranslation() {
+ super.setTranslationX(mTranslationX + mBubbleBarTranslationX);
+ }
+
+ @Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index fcf5ffc..0ad60b7 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -800,15 +800,29 @@
/**
* Tells SysUI when the bubble is being dragged.
* Should be called only when the bubble bar is expanded.
- * @param bubbleKey the key of the bubble to collapse/expand
- * @param isBeingDragged whether the bubble is being dragged
+ * @param bubbleKey key of the bubble being dragged
*/
- public void onBubbleDrag(@Nullable String bubbleKey, boolean isBeingDragged) {
+ public void startBubbleDrag(@Nullable String bubbleKey) {
if (mBubbles == null) return;
try {
- mBubbles.onBubbleDrag(bubbleKey, isBeingDragged);
+ mBubbles.startBubbleDrag(bubbleKey);
} catch (RemoteException e) {
- Log.w(TAG, "Failed call onBubbleDrag");
+ Log.w(TAG, "Failed call startBubbleDrag");
+ }
+ }
+
+ /**
+ * Tells SysUI when the bubble stops being dragged.
+ * Should be called only when the bubble bar is expanded.
+ * @param bubbleKey key of the bubble being dragged
+ * @param location location of the bubble bar
+ */
+ public void stopBubbleDrag(@Nullable String bubbleKey, BubbleBarLocation location) {
+ if (mBubbles == null) return;
+ try {
+ mBubbles.stopBubbleDrag(bubbleKey, location);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopBubbleDrag");
}
}