diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 9dafefebf62b0..ea04fb6daa9fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -144,7 +144,6 @@ public class BubbleController { private BubbleLogger mLogger; private BubbleData mBubbleData; - private View mBubbleScrim; @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; private BubblePositioner mBubblePositioner; @@ -1372,8 +1371,9 @@ public class BubbleController { private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + mBubblePositioner.setImeVisible(imeVisible, imeHeight); if (mStackView != null) { - mStackView.onImeVisibilityChanged(imeVisible, imeHeight); + mStackView.animateForIme(imeVisible); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 306224bd316c8..ff819429ffe46 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -70,13 +70,16 @@ public class BubblePositioner { private Context mContext; private WindowManager mWindowManager; - private Rect mPositionRect; private Rect mScreenRect; private @Surface.Rotation int mRotation = Surface.ROTATION_0; private Insets mInsets; + private boolean mImeVisible; + private int mImeHeight; + private boolean mIsLargeScreen; + + private Rect mPositionRect; private int mDefaultMaxBubbles; private int mMaxBubbles; - private int mBubbleSize; private int mSpacingBetweenBubbles; @@ -98,7 +101,6 @@ public class BubblePositioner { private PointF mRestingStackPosition; private int[] mPaddings = new int[4]; - private boolean mIsLargeScreen; private boolean mShowingInTaskbar; private @TaskbarPosition int mTaskbarPosition = TASKBAR_POSITION_NONE; private int mTaskbarIconSize; @@ -302,6 +304,17 @@ public class BubblePositioner { return mMaxBubbles; } + /** The height for the IME if it's visible. **/ + public int getImeHeight() { + return mImeVisible ? mImeHeight : 0; + } + + /** Sets whether the IME is visible. **/ + public void setImeVisible(boolean visible, int height) { + mImeVisible = visible; + mImeHeight = height; + } + /** * Calculates the padding for the bubble expanded view. * @@ -357,7 +370,7 @@ public class BubblePositioner { } /** Gets the y position of the expanded view if it was top-aligned. */ - private float getExpandedViewYTopAligned() { + public float getExpandedViewYTopAligned() { final int top = getAvailableRect().top; if (showBubblesVertically()) { return top - mPointerWidth + mExpandedViewPadding; @@ -366,10 +379,6 @@ public class BubblePositioner { } } - public float getExpandedBubblesY() { - return getAvailableRect().top + mExpandedViewPadding; - } - /** * Calculate the maximum height the expanded view can be depending on where it's placed on * the screen and the size of the elements around it (e.g. padding, pointer, manage button). @@ -464,6 +473,11 @@ public class BubblePositioner { : bubblePosition + (normalizedSize / 2f) - mPointerWidth; } + private int getExpandedStackSize(int numberOfBubbles) { + return (numberOfBubbles * mBubbleSize) + + ((numberOfBubbles - 1) * mSpacingBetweenBubbles); + } + /** * Returns the position of the bubble on-screen when the stack is expanded. * @@ -474,8 +488,7 @@ public class BubblePositioner { */ public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) { final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles); - final float expandedStackSize = (numberOfBubbles * mBubbleSize) - + ((numberOfBubbles - 1) * mSpacingBetweenBubbles); + final float expandedStackSize = getExpandedStackSize(numberOfBubbles); final float centerPosition = showBubblesVertically() ? mPositionRect.centerY() : mPositionRect.centerX(); @@ -498,9 +511,68 @@ public class BubblePositioner { y = mPositionRect.top + mExpandedViewPadding; x = rowStart + positionInRow; } + + if (showBubblesVertically() && mImeVisible) { + return new PointF(x, getExpandedBubbleYForIme(index, numberOfBubbles)); + } return new PointF(x, y); } + + /** + * Returns the position of the bubble on-screen when the stack is expanded and the IME + * is showing. + * + * @param index the index of the bubble in the stack. + * @param numberOfBubbles the total number of bubbles in the stack. + * @return y position of the bubble on-screen when the stack is expanded. + */ + private float getExpandedBubbleYForIme(int index, int numberOfBubbles) { + final float top = getAvailableRect().top + mExpandedViewPadding; + if (!showBubblesVertically()) { + // Showing horizontally: align to top + return top; + } + // Showing vertically: align to edges, check if there's overlap with the IME and adjust. + final float expandedStackSize = getExpandedStackSize(numberOfBubbles); + final float centerPosition = showBubblesVertically() + ? mPositionRect.centerY() + : mPositionRect.centerX(); + final float rowTop = centerPosition - (expandedStackSize / 2f); + float rowTopAdjusted = Math.max(rowTop - getTranslationForIme(0, numberOfBubbles), top); + return rowTopAdjusted + (index * (mBubbleSize + mSpacingBetweenBubbles)); + } + + private float getTranslationForIme(int selectedIndex, int numberOfBubbles) { + if (!showBubblesVertically()) { + // Showing at the top, no need to adjust for IME. + return 0; + } + // Showing vertically: if there are enough bubbles, need to translate + final float top = getAvailableRect().top + mExpandedViewPadding; + final float bottomInset = getImeHeight() + mInsets.bottom - mExpandedViewPadding; + final float expandedStackSize = getExpandedStackSize(numberOfBubbles); + final float centerPosition = showBubblesVertically() + ? mPositionRect.centerY() + : mPositionRect.centerX(); + final float rowBottom = centerPosition + (expandedStackSize / 2f); + final float rowTop = centerPosition - (expandedStackSize / 2f); + + if (rowBottom > bottomInset) { + // We overlap with IME, must shift the bubbles + float translationY = rowBottom - bottomInset; + if (rowTop - translationY < top) { + // Even if we shift the bubbles, they will still overlap with the IME. + + // TODO: in the case that the selected bubble is the one that overlaps with + // the IME, we should allow the bubbles to shift further "up" and potentially + // go offscreen so that the selected one is visible. + } + return translationY; + } + return 0; + } + /** * @return the width of the bubble flyout (message originating from the bubble). */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 5bc6128d6c9ec..a12c545b43b24 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -28,6 +28,8 @@ import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RES import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.ContentResolver; @@ -246,7 +248,6 @@ public class BubbleStackView extends FrameLayout private int mBubbleTouchPadding; private int mExpandedViewPadding; private int mCornerRadius; - private int mImeOffset; @Nullable private BubbleViewProvider mExpandedBubble; private boolean mIsExpanded; @@ -757,7 +758,6 @@ public class BubbleStackView extends FrameLayout mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size); mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding); - mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); @@ -1762,6 +1762,7 @@ public class BubbleStackView extends FrameLayout * not. */ void hideCurrentInputMethod() { + mPositioner.setImeVisible(false, 0); mBubbleController.hideCurrentInputMethod(); } @@ -2187,9 +2188,20 @@ public class BubbleStackView extends FrameLayout } } - /** Moves the bubbles out of the way if they're going to be over the keyboard. */ - public void onImeVisibilityChanged(boolean visible, int height) { - mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0); + /** + * Updates the stack based for IME changes. When collapsed it'll move the stack if it + * overlaps where they IME would be. When expanded it'll shift the expanded bubbles + * if they might overlap with the IME (this only happens for large screens). + */ + public void animateForIme(boolean visible) { + if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) { + // This will update the animation so the bubbles move to position for the IME + mExpandedAnimationController.expandFromStack(() -> { + updatePointerPosition(); + afterExpandedViewAnimation(); + } /* after */); + return; + } if (!mIsExpanded && getBubbleCount() > 0) { final float stackDestinationY = @@ -2208,9 +2220,21 @@ public class BubbleStackView extends FrameLayout FLYOUT_IME_ANIMATION_SPRING_CONFIG) .start(); } - } else if (mIsExpanded && mExpandedBubble != null - && mExpandedBubble.getExpandedView() != null) { + } else if (mPositioner.showBubblesVertically() && mIsExpanded + && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { mExpandedBubble.getExpandedView().setImeVisible(visible); + final int count = mBubbleContainer.getChildCount(); + List animList = new ArrayList(); + for (int i = 0; i < count; i++) { + View child = mBubbleContainer.getChildAt(i); + float transY = mPositioner.getExpandedBubbleXY( + i, count, mStackOnLeftOrWillBe).y; + ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY); + animList.add(anim); + } + AnimatorSet set = new AnimatorSet(); + set.playTogether(animList); + set.start(); } } @@ -2514,7 +2538,7 @@ public class BubbleStackView extends FrameLayout // Account for the IME in the touchable region so that the touchable region of the // Bubble window doesn't obscure the IME. The touchable region affects which areas // of the screen can be excluded by lower windows (IME is just above the embedded task) - outRect.bottom -= (int) mStackAnimationController.getImeHeight(); + outRect.bottom -= mPositioner.getImeHeight(); } if (mFlyout.getVisibility() == View.VISIBLE) { @@ -2858,12 +2882,13 @@ public class BubbleStackView extends FrameLayout if (index == -1) { return; } - PointF bubblePosition = mPositioner.getExpandedBubbleXY(index, + PointF position = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe); - mExpandedBubble.getExpandedView().setPointerPosition(mPositioner.showBubblesVertically() - ? bubblePosition.y - : bubblePosition.x, + float bubblePosition = mPositioner.showBubblesVertically() + ? position.y + : position.x; + mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition, mStackOnLeftOrWillBe); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index c32be98866cf7..d781e987b4be8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -21,7 +21,6 @@ import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RES import android.content.res.Resources; import android.graphics.Path; import android.graphics.PointF; -import android.graphics.Rect; import android.view.View; import androidx.annotation.NonNull; @@ -364,7 +363,7 @@ public class ExpandedAnimationController bubbleView.setTranslationY(y); } - final float expandedY = mPositioner.getExpandedBubblesY(); + final float expandedY = mPositioner.getExpandedViewYTopAligned(); final boolean draggedOutEnough = y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx; if (draggedOutEnough != mBubbleDraggedOutEnough) { @@ -429,16 +428,6 @@ public class ExpandedAnimationController updateBubblePositions(); } - /** - * Animates the bubbles to the y position. Used in response to IME showing. - */ - public void updateYPosition(Runnable after) { - if (mLayout == null) return; - animationsForChildrenFromIndex( - 0, (i, anim) -> anim.translationY(mPositioner.getExpandedBubblesY())) - .startAll(after); - } - /** Description of current animation controller state. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("ExpandedAnimationController state:"); @@ -504,28 +493,27 @@ public class ExpandedAnimationController } else { child.setTranslationX(p.x); } - if (!mPreparingToCollapse) { - // Only animate if we're not collapsing as that animation will handle placing the + + if (mPreparingToCollapse) { + // Don't animate if we're collapsing, as that animation will handle placing the // new bubble in the stacked position. - if (mPositioner.showBubblesVertically()) { - Rect availableRect = mPositioner.getAvailableRect(); - float fromX = onLeft - ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR - : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; - animationForChild(child) - .translationX(fromX, p.y) - .start(); - } else { - // Only animate if we're not collapsing as that animation will handle placing - // the new bubble in the stacked position. - float fromY = mPositioner.getExpandedBubblesY() - mBubbleSizePx - * ANIMATE_TRANSLATION_FACTOR; - animationForChild(child) - .translationY(fromY, p.y) - .start(); - } - updateBubblePositions(); + return; } + + if (mPositioner.showBubblesVertically()) { + float fromX = onLeft + ? p.x - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR + : p.x + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; + animationForChild(child) + .translationX(fromX, p.y) + .start(); + } else { + float fromY = p.y - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; + animationForChild(child) + .translationY(fromY, p.y) + .start(); + } + updateBubblePositions(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java index 9a08190675b63..60b64333114e4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java @@ -127,9 +127,6 @@ public class StackAnimationController extends /** Whether or not the stack's start position has been set. */ private boolean mStackMovedToStartPosition = false; - /** The height of the most recently visible IME. */ - private float mImeHeight = 0f; - /** * The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the * IME is not visible or the user moved the stack since the IME became visible. @@ -173,7 +170,7 @@ public class StackAnimationController extends */ private boolean mSpringToTouchOnNextMotionEvent = false; - /** Horizontal offset of bubbles in the stack. */ + /** Offset of bubbles in the stack (i.e. how much they overlap). */ private float mStackOffset; /** Offset between stack y and animation y for bubble swap. */ private float mSwapAnimationOffset; @@ -521,16 +518,6 @@ public class StackAnimationController extends removeEndActionForProperty(DynamicAnimation.TRANSLATION_Y); } - /** Save the current IME height so that we know where the stack bounds should be. */ - public void setImeHeight(int imeHeight) { - mImeHeight = imeHeight; - } - - /** Returns the current IME height that the stack is offset by. */ - public float getImeHeight() { - return mImeHeight; - } - /** * Animates the stack either away from the newly visible IME, or back to its original position * due to the IME going away. @@ -589,11 +576,14 @@ public class StackAnimationController extends */ public RectF getAllowableStackPositionRegion() { final RectF allowableRegion = new RectF(mPositioner.getAvailableRect()); + final int imeHeight = mPositioner.getImeHeight(); + final float bottomPadding = getBubbleCount() > 1 + ? mBubblePaddingTop + mStackOffset + : mBubblePaddingTop; allowableRegion.left -= mBubbleOffscreen; allowableRegion.top += mBubblePaddingTop; allowableRegion.right += mBubbleOffscreen - mBubbleSize; - allowableRegion.bottom -= mBubblePaddingTop + mBubbleSize - + (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f); + allowableRegion.bottom -= imeHeight + bottomPadding + mBubbleSize; return allowableRegion; }