From cba1b858d5eb5b88182e1af3c0463d67b91f2fe5 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Fri, 13 Aug 2021 17:07:18 -0700 Subject: [PATCH 1/3] Shift bubbles for IME on large screens * Incorporate IME state when calculating the x/y position of the bubbles -- if the IME is up, shift the bubbles so they don't overlap it (if possible). * When the IME visibility changes: - positioner saves the state - stackview animates the bubbles to their new position Test: manual - open the IME with bubbles on a large screen - check that the IME pushes the bubbles up so they are not overlapping - check that IME on phone portrait & landscape works as it did before Bug: 193911220 Change-Id: I3c93e472353cde5c938e33edf2d1ae7a4141db0e --- .../wm/shell/bubbles/BubbleController.java | 4 +- .../wm/shell/bubbles/BubblePositioner.java | 92 +++++++++++++++++-- .../wm/shell/bubbles/BubbleStackView.java | 49 +++++++--- .../ExpandedAnimationController.java | 52 ++++------- .../animation/StackAnimationController.java | 22 ++--- 5 files changed, 147 insertions(+), 72 deletions(-) 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; } From 56b1b2d2d5ba822189a41e8dbd2f4ad2beca6382 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Mon, 13 Sep 2021 14:38:23 -0700 Subject: [PATCH 2/3] Hide the overflow when the IME comes up sometimes If there isn't enough space to show all the bubbles when the IME is up on large screens, then hide the overflow. Really the overflow isn't hidden, it just doesn't get shifted up above the IME like the actual bubbles. Made a little helper class to hold stack state info and pass that around. Test: manual - on large screen that can't fit max # of bubbles when IME is up, select the last bubble & check that IME shifts all the bubbles but the overflow Bug: 193911220 Change-Id: I74b0d66cedf70cca40a36b07933ebf6466da63b7 --- .../wm/shell/bubbles/BubblePositioner.java | 53 ++++++++----------- .../wm/shell/bubbles/BubbleStackView.java | 43 ++++++++++----- .../ExpandedAnimationController.java | 21 ++++---- .../ExpandedAnimationControllerTest.java | 22 ++++++-- 4 files changed, 76 insertions(+), 63 deletions(-) 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 ff819429ffe46..127d5a8a9966e 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 @@ -482,13 +482,12 @@ public class BubblePositioner { * Returns the position of the bubble on-screen when the stack is expanded. * * @param index the index of the bubble in the stack. - * @param numberOfBubbles the total number of bubbles in the stack. - * @param onLeftEdge whether the stack would rest on the left edge of the screen when collapsed. - * @return the x, y position of the bubble on-screen when the stack is expanded. + * @param state state information about the stack to help with calculations. + * @return the position of the bubble on-screen when the stack is expanded. */ - public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) { + public PointF getExpandedBubbleXY(int index, BubbleStackView.StackViewState state) { final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles); - final float expandedStackSize = getExpandedStackSize(numberOfBubbles); + final float expandedStackSize = getExpandedStackSize(state.numberOfBubbles); final float centerPosition = showBubblesVertically() ? mPositionRect.centerY() : mPositionRect.centerX(); @@ -504,7 +503,7 @@ public class BubblePositioner { int right = mIsLargeScreen ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding : mPositionRect.right - mBubbleSize; - x = onLeftEdge + x = state.onLeft ? left : right; } else { @@ -513,12 +512,11 @@ public class BubblePositioner { } if (showBubblesVertically() && mImeVisible) { - return new PointF(x, getExpandedBubbleYForIme(index, numberOfBubbles)); + return new PointF(x, getExpandedBubbleYForIme(index, state.numberOfBubbles)); } return new PointF(x, y); } - /** * Returns the position of the bubble on-screen when the stack is expanded and the IME * is showing. @@ -533,44 +531,35 @@ public class BubblePositioner { // 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; + // Showing vertically: might need to translate the bubbles above the IME. + // Subtract spacing here to provide a margin between top of IME and bottom of bubble row. + final float bottomInset = getImeHeight() + mInsets.bottom - (mSpacingBetweenBubbles * 2); 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); - + float rowTopForIme = rowTop; if (rowBottom > bottomInset) { // We overlap with IME, must shift the bubbles float translationY = rowBottom - bottomInset; + rowTopForIme = Math.max(rowTop - translationY, top); 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. + // Hide the overflow for a lil more space: + final float expandedStackSizeNoO = getExpandedStackSize(numberOfBubbles - 1); + final float centerPositionNoO = showBubblesVertically() + ? mPositionRect.centerY() + : mPositionRect.centerX(); + final float rowBottomNoO = centerPositionNoO + (expandedStackSizeNoO / 2f); + final float rowTopNoO = centerPositionNoO - (expandedStackSizeNoO / 2f); + translationY = rowBottomNoO - bottomInset; + rowTopForIme = rowTopNoO - translationY; } - return translationY; } - return 0; + return rowTopForIme + (index * (mBubbleSize + mSpacingBetweenBubbles)); } /** 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 a12c545b43b24..b04c4c231eae6 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 @@ -190,6 +190,7 @@ public class BubbleStackView extends FrameLayout }; private final BubbleController mBubbleController; private final BubbleData mBubbleData; + private StackViewState mStackViewState = new StackViewState(); private final ValueAnimator mDismissBubbleAnimator; @@ -779,7 +780,7 @@ public class BubbleStackView extends FrameLayout this::animateShadows /* onStackAnimationFinished */, mPositioner); mExpandedAnimationController = new ExpandedAnimationController(mPositioner, - onBubbleAnimatedOut); + onBubbleAnimatedOut, this); mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or @@ -1877,8 +1878,7 @@ public class BubbleStackView extends FrameLayout } else { index = getBubbleIndex(mExpandedBubble); } - PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(), - mStackOnLeftOrWillBe); + PointF p = mPositioner.getExpandedBubbleXY(index, getState()); final float translationY = mPositioner.getExpandedViewY(mExpandedBubble, mPositioner.showBubblesVertically() ? p.y : p.x); mExpandedViewContainer.setTranslationX(0f); @@ -2012,8 +2012,7 @@ public class BubbleStackView extends FrameLayout index = mBubbleData.getBubbles().indexOf(mExpandedBubble); } // Value the bubble is animating from (back into the stack). - final PointF p = mPositioner.getExpandedBubbleXY(index, - mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe); + final PointF p = mPositioner.getExpandedBubbleXY(index, getState()); if (mPositioner.showBubblesVertically()) { float pivotX; float pivotY = p.y + mBubbleSize / 2f; @@ -2110,7 +2109,7 @@ public class BubbleStackView extends FrameLayout PointF p = mPositioner.getExpandedBubbleXY(isOverflow ? mBubbleContainer.getChildCount() - 1 : mBubbleData.getBubbles().indexOf(mExpandedBubble), - mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe); + getState()); mExpandedViewContainer.setAlpha(1f); mExpandedViewContainer.setVisibility(View.VISIBLE); @@ -2223,12 +2222,10 @@ public class BubbleStackView extends FrameLayout } 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++) { + for (int i = 0; i < mBubbleContainer.getChildCount(); i++) { View child = mBubbleContainer.getChildAt(i); - float transY = mPositioner.getExpandedBubbleXY( - i, count, mStackOnLeftOrWillBe).y; + float transY = mPositioner.getExpandedBubbleXY(i, getState()).y; ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY); animList.add(anim); } @@ -2797,7 +2794,7 @@ public class BubbleStackView extends FrameLayout } if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble), - mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe); + getState()); mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble, mPositioner.showBubblesVertically() ? p.y : p.x)); mExpandedViewContainer.setTranslationX(0f); @@ -2882,9 +2879,7 @@ public class BubbleStackView extends FrameLayout if (index == -1) { return; } - PointF position = mPositioner.getExpandedBubbleXY(index, - mBubbleContainer.getChildCount(), - mStackOnLeftOrWillBe); + PointF position = mPositioner.getExpandedBubbleXY(index, getState()); float bubblePosition = mPositioner.showBubblesVertically() ? position.y : position.x; @@ -2972,6 +2967,26 @@ public class BubbleStackView extends FrameLayout return bubbles; } + /** @return the current stack state. */ + public StackViewState getState() { + mStackViewState.numberOfBubbles = mBubbleContainer.getChildCount(); + mStackViewState.selectedIndex = getBubbleIndex(mExpandedBubble); + mStackViewState.onLeft = mStackOnLeftOrWillBe; + return mStackViewState; + } + + /** + * Holds some commonly queried information about the stack. + */ + public static class StackViewState { + // Number of bubbles (including the overflow itself) in the stack. + public int numberOfBubbles; + // The selected index if the stack is expanded. + public int selectedIndex; + // Whether the stack is resting on the left or right side of the screen when collapsed. + public boolean onLeft; + } + /** * Representation of stack position that uses relative properties rather than absolute * coordinates. This is used to maintain similar stack positions across configuration changes. 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 d781e987b4be8..f0f78748e343a 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 @@ -32,6 +32,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.bubbles.BubblePositioner; +import com.android.wm.shell.bubbles.BubbleStackView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.google.android.collect.Sets; @@ -123,12 +124,15 @@ public class ExpandedAnimationController private BubblePositioner mPositioner; + private BubbleStackView mBubbleStackView; + public ExpandedAnimationController(BubblePositioner positioner, - Runnable onBubbleAnimatedOutAction) { + Runnable onBubbleAnimatedOutAction, BubbleStackView stackView) { mPositioner = positioner; updateResources(); mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction; mCollapsePoint = mPositioner.getDefaultStartPosition(); + mBubbleStackView = stackView; } /** @@ -238,10 +242,7 @@ public class ExpandedAnimationController final Path path = new Path(); path.moveTo(bubble.getTranslationX(), bubble.getTranslationY()); - boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint); - final PointF p = mPositioner.getExpandedBubbleXY(index, - mLayout.getChildCount(), - onLeft); + final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState()); if (expanding) { // If we're expanding, first draw a line from the bubble's current position to where // it'll end up @@ -409,8 +410,7 @@ public class ExpandedAnimationController return; } final int index = mLayout.indexOfChild(bubbleView); - final PointF p = mPositioner.getExpandedBubbleXY(index, mLayout.getChildCount(), - mPositioner.isStackOnLeft(mCollapsePoint)); + final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState()); animationForChildAtIndex(index) .position(p.x, p.y) .withPositionStartVelocities(velX, velY) @@ -485,9 +485,7 @@ public class ExpandedAnimationController startOrUpdatePathAnimation(false /* expanding */); } else { boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint); - final PointF p = mPositioner.getExpandedBubbleXY(index, - mLayout.getChildCount(), - onLeft); + final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState()); if (mPositioner.showBubblesVertically()) { child.setTranslationY(p.y); } else { @@ -560,7 +558,6 @@ public class ExpandedAnimationController if (mAnimatingExpand || mAnimatingCollapse) { return; } - boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint); for (int i = 0; i < mLayout.getChildCount(); i++) { final View bubble = mLayout.getChildAt(i); @@ -570,7 +567,7 @@ public class ExpandedAnimationController return; } - final PointF p = mPositioner.getExpandedBubbleXY(i, mLayout.getChildCount(), onLeft); + final PointF p = mPositioner.getExpandedBubbleXY(i, mBubbleStackView.getState()); animationForChild(bubble) .translationX(p.x) .translationY(p.y) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java index 9732a8890e0ef..2b9bdce45a6c0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java @@ -16,10 +16,12 @@ package com.android.wm.shell.bubbles.animation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static org.mockito.Mockito.when; import android.annotation.SuppressLint; import android.content.res.Configuration; @@ -37,6 +39,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.BubblePositioner; +import com.android.wm.shell.bubbles.BubbleStackView; import org.junit.Before; import org.junit.Ignore; @@ -56,18 +59,22 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC private int mStackOffset; private PointF mExpansionPoint; private BubblePositioner mPositioner; + private BubbleStackView.StackViewState mStackViewState; @SuppressLint("VisibleForTests") @Before public void setUp() throws Exception { super.setUp(); + BubbleStackView stackView = mock(BubbleStackView.class); + when(stackView.getState()).thenReturn(getStackViewState()); mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class)); mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT, Insets.of(0, 0, 0, 0), new Rect(0, 0, mDisplayWidth, mDisplayHeight)); mExpandedController = new ExpandedAnimationController(mPositioner, - mOnBubbleAnimatedOutAction); + mOnBubbleAnimatedOutAction, + stackView); spyOn(mExpandedController); addOneMoreThanBubbleLimitBubbles(); @@ -78,6 +85,13 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC mExpansionPoint = new PointF(100, 100); } + public BubbleStackView.StackViewState getStackViewState() { + mStackViewState.numberOfBubbles = mLayout.getChildCount(); + mStackViewState.selectedIndex = 0; + mStackViewState.onLeft = mPositioner.isStackOnLeft(mExpansionPoint); + return mStackViewState; + } + @Test @Ignore public void testExpansionAndCollapse() throws InterruptedException { @@ -141,12 +155,10 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC /** Check that children are in the correct positions for being expanded. */ private void testBubblesInCorrectExpandedPositions() { - boolean onLeft = mPositioner.isStackOnLeft(mExpansionPoint); // Check all the visible bubbles to see if they're in the right place. for (int i = 0; i < mLayout.getChildCount(); i++) { PointF expectedPosition = mPositioner.getExpandedBubbleXY(i, - mLayout.getChildCount(), - onLeft); + getStackViewState()); assertEquals(expectedPosition.x, mLayout.getChildAt(i).getTranslationX(), 2f); From ec77b60fe63c1e334a3e0b435d730a815c7e851b Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Fri, 10 Sep 2021 14:41:08 -0700 Subject: [PATCH 3/3] Animate the pointer when IME comes up * Removes some unnecessary methods off Bubble, these can be called directly on the expanded view. * Add some code to animate the pointer * Fade the pointer with the TaskView Test: manual - check that the pointer animates with the bubble when the IME comes in / out Bug: 193911220 Change-Id: I477f34de3611eea79f933958ec6e09f68eb93179 --- .../com/android/wm/shell/bubbles/Bubble.java | 8 ----- .../wm/shell/bubbles/BubbleExpandedView.java | 23 +++++++++------ .../wm/shell/bubbles/BubbleOverflow.kt | 4 --- .../wm/shell/bubbles/BubbleStackView.java | 29 ++++++++++++------- .../wm/shell/bubbles/BubbleViewProvider.java | 6 ---- 5 files changed, 32 insertions(+), 38 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 9d65d28b21b47..05ebbba4e955a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -422,14 +422,6 @@ public class Bubble implements BubbleViewProvider { } } - @Override - public void setExpandedContentAlpha(float alpha) { - if (mExpandedView != null) { - mExpandedView.setAlpha(alpha); - mExpandedView.setTaskViewAlpha(alpha); - } - } - /** * Set visibility of bubble in the expanded state. * diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 7d7bfb2a92a33..a87aad4261a69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -398,6 +398,7 @@ public class BubbleExpandedView extends LinearLayout { updatePointerView(); } + /** Updates the size and visuals of the pointer. **/ private void updatePointerView() { LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams(); if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) { @@ -524,9 +525,8 @@ public class BubbleExpandedView extends LinearLayout { if (mTaskView != null) { mTaskView.setAlpha(alpha); } - if (mManageButton != null && mManageButton.getVisibility() == View.VISIBLE) { - mManageButton.setAlpha(alpha); - } + mPointerView.setAlpha(alpha); + setAlpha(alpha); } /** @@ -545,6 +545,7 @@ public class BubbleExpandedView extends LinearLayout { mIsContentVisible = visibility; if (mTaskView != null && !mIsAlphaAnimating) { mTaskView.setAlpha(visibility ? 1f : 0f); + mPointerView.setAlpha(visibility ? 1f : 0f); } } @@ -689,7 +690,7 @@ public class BubbleExpandedView extends LinearLayout { * the bubble if showing vertically. * @param onLeft whether the stack was on the left side of the screen when expanded. */ - public void setPointerPosition(float bubblePosition, boolean onLeft) { + public void setPointerPosition(float bubblePosition, boolean onLeft, boolean animate) { // Pointer gets drawn in the padding final boolean showVertically = mPositioner.showBubblesVertically(); final float paddingLeft = (showVertically && onLeft) @@ -710,6 +711,8 @@ public class BubbleExpandedView extends LinearLayout { : pointerPosition; // Post because we need the width of the view post(() -> { + mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer; + updatePointerView(); float pointerY; float pointerX; if (showVertically) { @@ -721,11 +724,13 @@ public class BubbleExpandedView extends LinearLayout { pointerY = mPointerOverlap; pointerX = bubbleCenter - (mPointerWidth / 2f); } - mPointerView.setTranslationY(pointerY); - mPointerView.setTranslationX(pointerX); - mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer; - updatePointerView(); - mPointerView.setVisibility(VISIBLE); + if (animate) { + mPointerView.animate().translationX(pointerX).translationY(pointerY).start(); + } else { + mPointerView.setTranslationY(pointerY); + mPointerView.setTranslationX(pointerX); + mPointerView.setVisibility(VISIBLE); + } }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index 705a12a5e65b5..0c3a6b2dbd842 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -154,10 +154,6 @@ class BubbleOverflow( return dotPath } - override fun setExpandedContentAlpha(alpha: Float) { - expandedView?.alpha = alpha - } - override fun setTaskViewVisibility(visible: Boolean) { // Overflow does not have a TaskView. } 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 b04c4c231eae6..9d0dd3c7fcdb6 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 @@ -891,7 +891,7 @@ public class BubbleStackView extends FrameLayout // Re-draw bubble row and pointer for new orientation. beforeExpandedViewAnimation(); updateOverflowVisibility(); - updatePointerPosition(); + updatePointerPosition(false /* forIme */); mExpandedAnimationController.expandFromStack(() -> { afterExpandedViewAnimation(); } /* after */); @@ -969,7 +969,8 @@ public class BubbleStackView extends FrameLayout }); mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> { if (mExpandedBubble != null) { - mExpandedBubble.setExpandedContentAlpha((float) valueAnimator.getAnimatedValue()); + mExpandedBubble.getExpandedView().setTaskViewAlpha( + (float) valueAnimator.getAnimatedValue()); } }); @@ -1559,7 +1560,7 @@ public class BubbleStackView extends FrameLayout } else { bubble.cleanupViews(); } - updatePointerPosition(); + updatePointerPosition(false /* forIme */); updateExpandedView(); logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); return; @@ -1600,7 +1601,7 @@ public class BubbleStackView extends FrameLayout .map(b -> b.getIconView()).collect(Collectors.toList()); mStackAnimationController.animateReorder(bubbleViews, reorder); } - updatePointerPosition(); + updatePointerPosition(false /* forIme */); } /** @@ -1671,7 +1672,6 @@ public class BubbleStackView extends FrameLayout private void showNewlySelectedBubble(BubbleViewProvider bubbleToSelect) { final BubbleViewProvider previouslySelected = mExpandedBubble; mExpandedBubble = bubbleToSelect; - updatePointerPosition(); if (mIsExpanded) { hideCurrentInputMethod(); @@ -1866,7 +1866,7 @@ public class BubbleStackView extends FrameLayout updateBadges(false /* setBadgeForCollapsedStack */); mBubbleContainer.setActiveController(mExpandedAnimationController); updateOverflowVisibility(); - updatePointerPosition(); + updatePointerPosition(false /* forIme */); mExpandedAnimationController.expandFromStack(() -> { if (mIsExpanded && mExpandedBubble.getExpandedView() != null) { maybeShowManageEdu(); @@ -1929,7 +1929,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); if (mExpandedBubble.getExpandedView() != null) { - mExpandedBubble.setExpandedContentAlpha(0f); + mExpandedBubble.getExpandedView().setTaskViewAlpha(0f); // We'll be starting the alpha animation after a slight delay, so set this flag early // here. @@ -2196,7 +2196,7 @@ public class BubbleStackView extends FrameLayout if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) { // This will update the animation so the bubbles move to position for the IME mExpandedAnimationController.expandFromStack(() -> { - updatePointerPosition(); + updatePointerPosition(false /* forIme */); afterExpandedViewAnimation(); } /* after */); return; @@ -2229,6 +2229,7 @@ public class BubbleStackView extends FrameLayout ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY); animList.add(anim); } + updatePointerPosition(true /* forIme */); AnimatorSet set = new AnimatorSet(); set.playTogether(animList); set.start(); @@ -2800,7 +2801,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setTranslationX(0f); mExpandedBubble.getExpandedView().updateView( mExpandedViewContainer.getLocationOnScreen()); - updatePointerPosition(); + updatePointerPosition(false /* forIme */); } mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide(); @@ -2871,7 +2872,13 @@ public class BubbleStackView extends FrameLayout } } - private void updatePointerPosition() { + /** + * Updates the position of the pointer based on the expanded bubble. + * + * @param forIme whether the position is being updated due to the ime appearing, in this case + * the pointer is animated to the location. + */ + private void updatePointerPosition(boolean forIme) { if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) { return; } @@ -2884,7 +2891,7 @@ public class BubbleStackView extends FrameLayout ? position.y : position.x; mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition, - mStackOnLeftOrWillBe); + mStackOnLeftOrWillBe, forIme /* animate */); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java index 38b3ba9dfda00..7e552826e94ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java @@ -28,12 +28,6 @@ import androidx.annotation.Nullable; public interface BubbleViewProvider { @Nullable BubbleExpandedView getExpandedView(); - /** - * Sets the alpha of the expanded view content. This will be applied to both the expanded view - * container itself (the manage button, etc.) as well as the TaskView within it. - */ - void setExpandedContentAlpha(float alpha); - /** * Sets whether the contents of the bubble's TaskView should be visible. */