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

Commit 5d8f140c authored by Mady Mellor's avatar Mady Mellor
Browse files

Translate bubbles with IME

* When IME appears, translate the expanded state + row of bubbles into
  correct position
* Moves the logic to apply the forwarded insets into BubbleStackView;
  also applies the insets after all the animation / translation
* Don't update the height of the expanded view when the IME is visible
  (this causes config change which loses IME -- instead we'll update the
  height when IME goes away
* Clears out the insets when the view goes away

What's left to do: if the expanded view is too big the top portion of
it gets shifted off screen, along with the header, this should actually
be clipped + header should be visible; this will come in follow up

Test: manual - have a bubble that uses IME, focus the IME
               => note the row of bubbles + expanded state animate up
             - have multple bubbles of small, medium, large all that
               focus the IME, expand and tap between them while focusing
               the IME
               => make sure things move to the right places
             - via BubbleSettings apk, toggle 'show bubbles at top', have
               some very large bubbles that focus IME, tap between them
               and focus IME, then tap between them without focusing
               => make sure the inset doesn't remain
             - have a bubble that uses IME, open it + focus the IME, update
               its height
               => note that the height doesn't change, once IME goes
                  away height is updated

Fixes: 128007159
Bug: 123544535
Change-Id: I8d613370d46e85f2779069f9c321e88f96a4ff36
parent 40e9a201
Loading
Loading
Loading
Loading
+47 −16
Original line number Diff line number Diff line
@@ -94,6 +94,9 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
    private boolean mActivityViewReady = false;
    private PendingIntent mBubbleIntent;

    private boolean mKeyboardVisible;
    private boolean mNeedsNewHeight;

    private int mMinHeight;
    private int mHeaderHeight;
    private int mBubbleHeight;
@@ -227,21 +230,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
                true /* singleTaskInstance */);
        addView(mActivityView);

        mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
            ActivityView activityView = (ActivityView) view;
            // Here we assume that the position of the ActivityView on the screen
            // remains regardless of IME status. When we move ActivityView, the
            // forwardedInsets should be computed not against the current location
            // and size, but against the post-moved location and size.
            Point displaySize = new Point();
            view.getContext().getDisplay().getSize(displaySize);
            int[] windowLocation = view.getLocationOnScreen();
            final int windowBottom = windowLocation[1] + view.getHeight();
        setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
            // Keep track of IME displaying because we should not make any adjustments that might
            // cause a config change while the IME is displayed otherwise it'll loose focus.
            final int keyboardHeight = insets.getSystemWindowInsetBottom()
                    - insets.getStableInsetBottom();
            final int insetsBottom = Math.max(0,
                    windowBottom + keyboardHeight - displaySize.y);
            activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
            mKeyboardVisible = keyboardHeight != 0;
            if (!mKeyboardVisible && mNeedsNewHeight) {
                updateHeight();
            }
            return view.onApplyWindowInsets(insets);
        });

@@ -258,6 +255,34 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mKeyboardVisible = false;
        mNeedsNewHeight = false;
        if (mActivityView != null) {
            mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
        }
    }

    /**
     * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
     * This should be done post-move and post-animation.
     */
    void updateInsets(WindowInsets insets) {
        if (usingActivityView()) {
            Point displaySize = new Point();
            mActivityView.getContext().getDisplay().getSize(displaySize);
            int[] windowLocation = mActivityView.getLocationOnScreen();
            final int windowBottom = windowLocation[1] + mActivityView.getHeight();
            final int keyboardHeight = insets.getSystemWindowInsetBottom()
                    - insets.getStableInsetBottom();
            final int insetsBottom = Math.max(0,
                    windowBottom + keyboardHeight - displaySize.y);
            mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
        }
    }

    /**
     * Creates a background with corners rounded based on how the view is configured to display
     */
@@ -441,9 +466,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
            int height = Math.min(desiredHeight, max);
            height = Math.max(height, mMinHeight);
            LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
            mNeedsNewHeight =  lp.height != height;
            if (!mKeyboardVisible) {
                // If the keyboard is visible... don't adjust the height because that will cause
                // a configuration change and the keyboard will be lost.
                lp.height = height;
                mBubbleHeight = height;
                mActivityView.setLayoutParams(lp);
                mNeedsNewHeight = false;
            }
        } else {
            mBubbleHeight = mNotifRow != null ? mNotifRow.getIntrinsicHeight() : mMinHeight;
        }
+30 −11
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ public class BubbleStackView extends FrameLayout {

    private Bubble mExpandedBubble;
    private boolean mIsExpanded;
    private boolean mImeVisible;

    private BubbleTouchHandler mTouchHandler;
    private BubbleController.BubbleExpandListener mExpandListener;
@@ -236,6 +237,28 @@ public class BubbleStackView extends FrameLayout {
        setClipChildren(false);
        setFocusable(true);
        mBubbleContainer.bringToFront();

        setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
            final int keyboardHeight = insets.getSystemWindowInsetBottom()
                    - insets.getStableInsetBottom();
            if (!mIsExpanded) {
                return view.onApplyWindowInsets(insets);
            }
            mImeVisible = keyboardHeight != 0;

            float newY = getYPositionForExpandedView();
            if (newY < 0) {
                // TODO: This means our expanded content is too big to fit on screen. Right now
                // we'll let it translate off but we should be clipping it & pushing the header
                // down so that it always remains visible.
            }
            mExpandedViewYAnim.animateToFinalPosition(newY);
            mExpandedAnimationController.updateYPosition(
                    // Update the insets after we're done translating otherwise position
                    // calculation for them won't be correct.
                    () -> mExpandedBubble.expandedView.updateInsets(insets));
            return view.onApplyWindowInsets(insets);
        });
    }

    @Override
@@ -639,15 +662,6 @@ public class BubbleStackView extends FrameLayout {
        }
    }

    /**
     * The width of the collapsed stack of bubbles.
     */
    public int getStackWidth() {
        return mBubblePadding * (mBubbleContainer.getChildCount() - 1)
                + mBubbleSize + mBubbleContainer.getPaddingEnd()
                + mBubbleContainer.getPaddingStart();
    }

    private void notifyExpansionChanged(NotificationEntry entry, boolean expanded) {
        if (mExpandListener != null) {
            mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null);
@@ -836,8 +850,13 @@ public class BubbleStackView extends FrameLayout {
            // calculation is correct)
            mExpandedBubble.expandedView.updateView();
            final float y = getYPositionForExpandedView();
            if (!mExpandedViewYAnim.isRunning()) {
                // We're not animating so set the value
                mExpandedViewContainer.setTranslationY(y);
            // Then update the view so that ActivityView knows we translated
            } else {
                // We are animating so update the value
                mExpandedViewYAnim.animateToFinalPosition(y);
            }
            mExpandedBubble.expandedView.updateView();
        }

+26 −6
Original line number Diff line number Diff line
@@ -197,6 +197,19 @@ public class ExpandedAnimationController
        mBubbleDraggedOutEnough = false;
    }

    /**
     * Animates the bubbles to {@link #getExpandedY()} position. Used in response to IME showing.
     */
    public void updateYPosition(Runnable after) {
        if (mLayout == null) return;

        for (int i = 0; i < mLayout.getChildCount(); i++) {
            boolean isLast = i == mLayout.getChildCount() - 1;
            mLayout.animateValueForChildAtIndex(DynamicAnimation.TRANSLATION_Y, i,
                    getExpandedY(), isLast ? after : null);
        }
    }

    /**
     * Animates the bubbles, starting at the given index, to the left or right by the given number
     * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal
@@ -213,18 +226,25 @@ public class ExpandedAnimationController

    /** The Y value of the row of expanded bubbles. */
    public float getExpandedY() {
        boolean showOnTop = mLayout != null
                && BubbleController.showBubblesAtTop(mLayout.getContext());
        final WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
        if (showOnTop && insets != null) {
        if (mLayout == null || mLayout.getRootWindowInsets() == null) {
            return 0;
        }
        final boolean showOnTop = BubbleController.showBubblesAtTop(mLayout.getContext());
        final WindowInsets insets = mLayout.getRootWindowInsets();
        if (showOnTop) {
            return mBubblePaddingPx + Math.max(
                    mStatusBarHeight,
                    insets.getDisplayCutout() != null
                            ? insets.getDisplayCutout().getSafeInsetTop()
                            : 0);
        } else {
            int bottomInset = insets != null ? insets.getSystemWindowInsetBottom() : 0;
            return mDisplaySize.y - mBubbleSizePx - (mPipDismissHeight - bottomInset);
            int keyboardHeight = insets.getSystemWindowInsetBottom()
                    - insets.getStableInsetBottom();
            float bottomInset = keyboardHeight > 0
                    ? keyboardHeight
                    : (mPipDismissHeight - insets.getStableInsetBottom());
            // Stable insets are excluded from display size, so we must subtract it
            return mDisplaySize.y - mBubbleSizePx - mBubblePaddingPx - bottomInset;
        }
    }