Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56b1b2d2 authored by Mady Mellor's avatar Mady Mellor
Browse files

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
parent cba1b858
Loading
Loading
Loading
Loading
+21 −32
Original line number Diff line number Diff line
@@ -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));
    }

    /**
+29 −14
Original line number Diff line number Diff line
@@ -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<Animator> 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.
+9 −12
Original line number Diff line number Diff line
@@ -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)
+17 −5
Original line number Diff line number Diff line
@@ -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);