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);