diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index 1f3c4839ab..c1c76d0a01 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -220,7 +220,7 @@ public class BubbleBarController extends IBubblesListener.Stub { mBubbleStashedHandleViewController.setHiddenForBubbles( !sBubbleBarEnabled || mBubbles.isEmpty()); mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse( - key -> setSelectedBubble(mBubbles.get(key))); + key -> setSelectedBubbleInternal(mBubbles.get(key))); }); } @@ -390,7 +390,7 @@ public class BubbleBarController extends IBubblesListener.Stub { } } if (bubbleToSelect != null) { - setSelectedBubble(bubbleToSelect); + setSelectedBubbleInternal(bubbleToSelect); if (previouslySelectedBubble == null) { mBubbleStashController.animateToInitialState(update.expanded); } @@ -409,8 +409,7 @@ public class BubbleBarController extends IBubblesListener.Stub { if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) { // Animate when receiving updates. Skip it if we received the initial state. boolean animate = !update.initialState; - mBubbleBarViewController.setBubbleBarLocation(update.bubbleBarLocation, animate); - mBubbleStashController.setBubbleBarLocation(update.bubbleBarLocation); + updateBubbleBarLocationInternal(update.bubbleBarLocation, animate); } } } @@ -436,7 +435,7 @@ public class BubbleBarController extends IBubblesListener.Stub { /** Updates the currently selected bubble for launcher views and tells WMShell to show it. */ public void showAndSelectBubble(BubbleBarItem b) { if (DEBUG) Log.w(TAG, "showingSelectedBubble: " + b.getKey()); - setSelectedBubble(b); + setSelectedBubbleInternal(b); showSelectedBubble(); } @@ -445,7 +444,7 @@ public class BubbleBarController extends IBubblesListener.Stub { * WMShell that the selection has changed, that should go through either * {@link #showSelectedBubble()} or {@link #showAndSelectBubble(BubbleBarItem)}. */ - private void setSelectedBubble(BubbleBarItem b) { + private void setSelectedBubbleInternal(BubbleBarItem b) { if (!Objects.equals(b, mSelectedBubble)) { if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey()); mSelectedBubble = b; @@ -464,6 +463,21 @@ public class BubbleBarController extends IBubblesListener.Stub { return null; } + /** + * Set a new bubble bar location. + *

+ * Updates the value locally in Launcher and in WMShell. + */ + public void updateBubbleBarLocation(BubbleBarLocation location) { + updateBubbleBarLocationInternal(location, false /* animate */); + mSystemUiProxy.setBubbleBarLocation(location); + } + + private void updateBubbleBarLocationInternal(BubbleBarLocation location, boolean animate) { + mBubbleBarViewController.setBubbleBarLocation(location, animate); + mBubbleStashController.setBubbleBarLocation(location); + } + // // Loading data for the bubbles // diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 4ca7c89127..c27e9f1287 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -24,7 +24,9 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.content.Context; +import android.graphics.PointF; import android.graphics.Rect; import android.util.AttributeSet; import android.util.LayoutDirection; @@ -108,6 +110,7 @@ public class BubbleBarView extends FrameLayout { private final float mIconSize; // The elevation of the bubbles within the bar private final float mBubbleElevation; + private final float mDragElevation; private final int mPointerSize; // Whether the bar is expanded (i.e. the bubble activity is being displayed). @@ -138,11 +141,15 @@ public class BubbleBarView extends FrameLayout { @Nullable private Consumer mUpdateSelectedBubbleAfterCollapse; + private boolean mDragging; + @Nullable private BubbleView mDraggedBubbleView; private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED; + private boolean mLocationChangePending; + public BubbleBarView(Context context) { this(context, null); } @@ -163,6 +170,7 @@ public class BubbleBarView extends FrameLayout { mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing); mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size); mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation); + mDragElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_drag_elevation); mPointerSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_pointer_size); setClipToPadding(false); @@ -237,12 +245,13 @@ public class BubbleBarView extends FrameLayout { } } + @SuppressLint("RtlHardcoded") private void onBubbleBarLocationChanged() { + mLocationChangePending = false; final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl()); mBubbleBarBackground.setAnchorLeft(onLeft); mRelativePivotX = onLeft ? 0f : 1f; - ViewGroup.LayoutParams layoutParams = getLayoutParams(); - if (layoutParams instanceof LayoutParams lp) { + if (getLayoutParams() instanceof LayoutParams lp) { lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT); setLayoutParams(lp); } @@ -267,11 +276,82 @@ public class BubbleBarView extends FrameLayout { } } + /** + * Set whether this view is being currently being dragged + */ + public void setIsDragging(boolean dragging) { + if (mDragging == dragging) { + return; + } + mDragging = dragging; + setElevation(dragging ? mDragElevation : mBubbleElevation); + if (!dragging && mLocationChangePending) { + // During drag finish animation we may update the translation x value to shift the + // bubble to the new drop target. Clear the translation here. + setTranslationX(0f); + onBubbleBarLocationChanged(); + } + } + + /** + * Adjust resting position for the bubble bar while it is being dragged. + *

+ * Bubble bar is laid out on left or right side of the screen. When it is being dragged to + * the opposite side, the resting position should be on that side. Calculate any additional + * translation that may be required to move the bubble bar to the new side. + * + * @param restingPosition relative resting position of the bubble bar from the laid out position + */ + @SuppressLint("RtlHardcoded") + void adjustRelativeRestingPosition(PointF restingPosition) { + final boolean locationOnLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl()); + // Bubble bar is placed left or right with gravity. Check where it is currently. + final int absoluteGravity = Gravity.getAbsoluteGravity( + ((LayoutParams) getLayoutParams()).gravity, getLayoutDirection()); + final boolean gravityOnLeft = + (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT; + + // Bubble bar is pinned to the same side per gravity and the desired location. + // Resting translation does not need to be adjusted. + if (locationOnLeft == gravityOnLeft) { + return; + } + + // Bubble bar is laid out on left or right side of the screen. And the desired new + // location is on the other side. Calculate x translation value required to shift the + // bubble bar from one side to the other. + float x = getDistanceFromOtherSide(); + if (locationOnLeft) { + // New location is on the left, shift left + // before -> |......ooo.| after -> |.ooo......| + restingPosition.x = -x; + } else { + // New location is on the right, shift right + // before -> |.ooo......| after -> |......ooo.| + restingPosition.x = x; + } + } + + private float getDistanceFromOtherSide() { + // Calculate the shift needed to position the bubble bar on the other side + int displayWidth = getResources().getDisplayMetrics().widthPixels; + int margin = 0; + if (getLayoutParams() instanceof MarginLayoutParams lp) { + margin += lp.leftMargin; + margin += lp.rightMargin; + } + return (float) (displayWidth - getWidth() - margin); + } + private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) { if (bubbleBarLocation != mBubbleBarLocation) { mBubbleBarLocation = bubbleBarLocation; - onBubbleBarLocationChanged(); - invalidate(); + if (mDragging) { + mLocationChangePending = true; + } else { + onBubbleBarLocationChanged(); + invalidate(); + } } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java index dab7d9d07a..5ffc6d830a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java @@ -25,7 +25,6 @@ import android.view.ViewConfiguration; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.R; import com.android.launcher3.taskbar.TaskbarActivityContext; /** @@ -55,9 +54,8 @@ public class BubbleDragController { mBubbleBarViewController = bubbleControllers.bubbleBarViewController; mBubbleDismissController = bubbleControllers.bubbleDismissController; mBubbleBarPinController = bubbleControllers.bubbleBarPinController; - mBubbleBarPinController.setListener(location -> { - // TODO(b/330585397): update bubble bar location in shell - }); + mBubbleBarPinController.setListener( + bubbleControllers.bubbleBarController::updateBubbleBarLocation); mBubbleDismissController.setListener( stuck -> mBubbleBarPinController.setDropTargetHidden(stuck)); } @@ -96,11 +94,8 @@ public class BubbleDragController { @SuppressLint("ClickableViewAccessibility") public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) { PointF initialRelativePivot = new PointF(); - final int restingElevation = bubbleBarView.getResources().getDimensionPixelSize( - R.dimen.bubblebar_elevation); - final int dragElevation = bubbleBarView.getResources().getDimensionPixelSize( - R.dimen.bubblebar_drag_elevation); bubbleBarView.setOnTouchListener(new BubbleTouchListener() { + @Override protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) { if (bubbleBarView.isExpanded()) return false; @@ -114,7 +109,7 @@ public class BubbleDragController { // By default the bubble bar view pivot is in bottom right corner, while dragging // it should be centered in order to align it with the dismiss target view bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f); - bubbleBarView.setElevation(dragElevation); + bubbleBarView.setIsDragging(true); mBubbleBarPinController.onDragStart( bubbleBarView.getBubbleBarLocation().isOnLeft(bubbleBarView.isLayoutRtl())); } @@ -138,7 +133,14 @@ public class BubbleDragController { void onDragEnd() { // Restoring the initial pivot for the bubble bar view bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y); - bubbleBarView.setElevation(restingElevation); + bubbleBarView.setIsDragging(false); + } + + @Override + protected PointF getRestingPosition() { + PointF restingPosition = super.getRestingPosition(); + bubbleBarView.adjustRelativeRestingPosition(restingPosition); + return restingPosition; } }); } @@ -226,6 +228,13 @@ public class BubbleDragController { protected void onDragDismiss() { } + /** + * Get the resting position of the view when drag is released + */ + protected PointF getRestingPosition() { + return mViewInitialPosition; + } + @Override @SuppressLint("ClickableViewAccessibility") public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) { @@ -353,7 +362,7 @@ public class BubbleDragController { mAnimator.animateDismiss(mViewInitialPosition, onComplete); } else { onDragRelease(); - mAnimator.animateToInitialState(mViewInitialPosition, getCurrentVelocity(), + mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(), onComplete); } mBubbleDismissController.hideDismissView(); diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index b6272dad87..3c3307ac59 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -82,6 +82,7 @@ import com.android.systemui.unfold.progress.IUnfoldTransitionListener; import com.android.wm.shell.back.IBackAnimation; import com.android.wm.shell.bubbles.IBubbles; import com.android.wm.shell.bubbles.IBubblesListener; +import com.android.wm.shell.common.bubbles.BubbleBarLocation; import com.android.wm.shell.common.pip.IPip; import com.android.wm.shell.common.pip.IPipAnimationListener; import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; @@ -808,6 +809,18 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { } } + /** + * Tells SysUI to update the bubble bar location to the new location. + * @param location new location for the bubble bar + */ + public void setBubbleBarLocation(BubbleBarLocation location) { + try { + mBubbles.setBubbleBarLocation(location); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setBubbleBarLocation"); + } + } + // // Splitscreen //