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

Commit f44347f9 authored by Joshua Tsuji's avatar Joshua Tsuji
Browse files

Fixes positioning issues with the bubble stack.

- Waits for layout before moving the stack to the initial position, so that width/height are correct. This was not caught by tests because the TestablePhysicsAnimationLayout posts all of its controller dispatch methods to a Handler, which caused them to wait for layout only in the tests. This seems unavoidable, unfortunately.
- Uses the status bar height resource value, rather than top inset, which is actually always zero.

Test: atest SystemUITests
Bug: 123635140
Change-Id: I2357445a7b1ab374e3a835884639e3b227207de3
parent 63be76e1
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
    private int mBubblePadding;
    private int mExpandedAnimateXDistance;
    private int mExpandedAnimateYDistance;
    private int mStatusBarHeight;

    private boolean mIsExpanded;
    private int mExpandedBubbleHeight;
@@ -153,6 +154,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
                res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
        mExpandedAnimateYDistance =
                res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance);
        mStatusBarHeight =
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);

        mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
        mDisplaySize = new Point();
@@ -639,7 +642,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
        if (getRootWindowInsets() != null) {
            WindowInsets insets = getRootWindowInsets();
            return Math.max(
                    insets.getSystemWindowInsetTop(),
                    mStatusBarHeight,
                    insets.getDisplayCutout() != null
                            ? insets.getDisplayCutout().getSafeInsetTop()
                            : 0);
+11 −7
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.bubbles.animation;

import android.content.res.Resources;
import android.graphics.PointF;
import android.view.View;
import android.view.WindowInsets;
@@ -55,16 +56,19 @@ public class ExpandedAnimationController
    private float mBubblePaddingPx;
    /** Size of each bubble. */
    private float mBubbleSizePx;
    /** Height of the status bar. */
    private float mStatusBarHeight;

    @Override
    protected void setLayout(PhysicsAnimationLayout layout) {
        super.setLayout(layout);
        mStackOffsetPx = layout.getResources().getDimensionPixelSize(
                R.dimen.bubble_stack_offset);
        mBubblePaddingPx = layout.getResources().getDimensionPixelSize(
                R.dimen.bubble_padding);
        mBubbleSizePx = layout.getResources().getDimensionPixelSize(
                R.dimen.individual_bubble_size);

        final Resources res = layout.getResources();
        mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
        mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding);
        mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
        mStatusBarHeight =
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
    }

    /**
@@ -103,7 +107,7 @@ public class ExpandedAnimationController
        final WindowInsets insets = mLayout.getRootWindowInsets();
        if (insets != null) {
            return mBubblePaddingPx + Math.max(
                    insets.getSystemWindowInsetTop(),
                    mStatusBarHeight,
                    insets.getDisplayCutout() != null
                            ? insets.getDisplayCutout().getSafeInsetTop()
                            : 0);
+53 −51
Original line number Diff line number Diff line
@@ -16,15 +16,12 @@

package com.android.systemui.bubbles.animation;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;

import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FlingAnimation;
@@ -88,9 +85,8 @@ public class StackAnimationController extends
    private int mBubbleOffscreen;
    /** How far down the screen the stack starts, when there is no pre-existing location. */
    private int mStackStartingVerticalOffset;

    private Point mDisplaySize;
    private RectF mAllowableStackPositionRegion;
    /** Height of the status bar. */
    private float mStatusBarHeight;

    @Override
    protected void setLayout(PhysicsAnimationLayout layout) {
@@ -103,11 +99,8 @@ public class StackAnimationController extends
        mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
        mStackStartingVerticalOffset =
                res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);

        mDisplaySize = new Point();
        WindowManager wm =
                (WindowManager) layout.getContext().getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getSize(mDisplaySize);
        mStatusBarHeight =
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
    }

    /**
@@ -203,10 +196,9 @@ public class StackAnimationController extends
     */
    public RectF getAllowableStackPositionRegion() {
        final WindowInsets insets = mLayout.getRootWindowInsets();
        mAllowableStackPositionRegion = new RectF();

        final RectF allowableRegion = new RectF();
        if (insets != null) {
            mAllowableStackPositionRegion.left =
            allowableRegion.left =
                    -mBubbleOffscreen
                            - mBubblePadding
                            + Math.max(
@@ -214,7 +206,7 @@ public class StackAnimationController extends
                            insets.getDisplayCutout() != null
                                    ? insets.getDisplayCutout().getSafeInsetLeft()
                                    : 0);
            mAllowableStackPositionRegion.right =
            allowableRegion.right =
                    mLayout.getWidth()
                            - mIndividualBubbleSize
                            + mBubbleOffscreen
@@ -225,14 +217,14 @@ public class StackAnimationController extends
                                    ? insets.getDisplayCutout().getSafeInsetRight()
                                    : 0);

            mAllowableStackPositionRegion.top =
            allowableRegion.top =
                    mBubblePadding
                            + Math.max(
                            insets.getSystemWindowInsetTop(),
                            mStatusBarHeight,
                            insets.getDisplayCutout() != null
                                    ? insets.getDisplayCutout().getSafeInsetTop()
                                    : 0);
            mAllowableStackPositionRegion.bottom =
            allowableRegion.bottom =
                    mLayout.getHeight()
                            - mIndividualBubbleSize
                            - mBubblePadding
@@ -243,7 +235,7 @@ public class StackAnimationController extends
                                    : 0);
        }

        return mAllowableStackPositionRegion;
        return allowableRegion;
    }

    @Override
@@ -287,31 +279,14 @@ public class StackAnimationController extends

    @Override
    void onChildAdded(View child, int index) {
        // If this is the first child added, position the stack in its starting position.
        if (mLayout.getChildCount() == 1) {
            moveStackToStartPosition();
        }

        if (mLayout.indexOfChild(child) == 0) {
            child.setTranslationY(mStackPosition.y);

            // Pop in the new bubble.
            child.setScaleX(ANIMATE_IN_STARTING_SCALE);
            child.setScaleY(ANIMATE_IN_STARTING_SCALE);
            mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
            mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);

            // Fade in the new bubble.
            child.setAlpha(0);
            mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);

            // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
            // animate in from this position. Since the animations are chained, when the new bubble
            // flies in from the side, it will push the other ones out of the way.
            float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
            child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
            mLayout.animateValueForChildAtIndex(
                    DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
            // If this is the first child added, position the stack in its starting position before
            // animating in.
            moveStackToStartPosition(() -> animateInBubble(child));
        } else if (mLayout.indexOfChild(child) == 0) {
            // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble
            // to the back of the stack, it'll be largely invisible so don't bother animating it in.
            animateInBubble(child);
        }
    }

@@ -334,10 +309,14 @@ public class StackAnimationController extends
    }

    /** Moves the stack, without any animation, to the starting position. */
    private void moveStackToStartPosition() {
        mLayout.post(() -> setStackPosition(
    private void moveStackToStartPosition(Runnable after) {
        // Post to ensure that the layout's width and height have been calculated.
        mLayout.post(() -> {
            setStackPosition(
                    getAllowableStackPositionRegion().right,
                getAllowableStackPositionRegion().top + mStackStartingVerticalOffset));
                    getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
            after.run();
        });
    }

    /**
@@ -379,6 +358,29 @@ public class StackAnimationController extends
        }
    }

    /** Animates in the given bubble. */
    private void animateInBubble(View child) {
        child.setTranslationY(mStackPosition.y);

        // Pop in the new bubble.
        child.setScaleX(ANIMATE_IN_STARTING_SCALE);
        child.setScaleY(ANIMATE_IN_STARTING_SCALE);
        mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
        mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);

        // Fade in the new bubble.
        child.setAlpha(0);
        mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);

        // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
        // animate in from this position. Since the animations are chained, when the new bubble
        // flies in from the side, it will push the other ones out of the way.
        float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
        child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
        mLayout.animateValueForChildAtIndex(
                DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
    }

    /**
     * Springs the first bubble to the given final position, with the rest of the stack 'following'.
     */