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

Commit 36b1b2ce authored by Joshua Tsuji's avatar Joshua Tsuji
Browse files

Adjust the flyout sizing and appearance to match the design spec.

Test: atest SystemUITests
Bug: 130234901
Change-Id: Ib7cb6ef58d27f7d296b2dd22a41749c5f3bfe67b
parent b962712d
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@
  ~ limitations under the License
  -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <!-- TODO: Add the triangle pointing to the bubble stack. -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="?android:attr/colorBackgroundFloating" />
@@ -22,8 +21,10 @@
                android:bottomLeftRadius="?android:attr/dialogCornerRadius"
                android:topLeftRadius="?android:attr/dialogCornerRadius"
                android:bottomRightRadius="?android:attr/dialogCornerRadius"
                android:topRightRadius="?android:attr/dialogCornerRadius"
            />
                android:topRightRadius="?android:attr/dialogCornerRadius" />
            <padding
                android:left="@dimen/bubble_flyout_pointer_size"
                android:right="@dimen/bubble_flyout_pointer_size" />
        </shape>
    </item>
</layer-list>
 No newline at end of file
+22 −11
Original line number Diff line number Diff line
@@ -15,19 +15,30 @@
  -->
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingLeft="@dimen/bubble_flyout_pointer_size"
    android:paddingRight="@dimen/bubble_flyout_pointer_size">

    <FrameLayout
        android:id="@+id/bubble_flyout"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:background="@drawable/bubble_flyout"
    android:padding="@dimen/bubble_flyout_padding"
        android:paddingLeft="@dimen/bubble_flyout_padding_x"
        android:paddingRight="@dimen/bubble_flyout_padding_x"
        android:paddingTop="@dimen/bubble_flyout_padding_y"
        android:paddingBottom="@dimen/bubble_flyout_padding_y"
        android:translationZ="@dimen/bubble_flyout_elevation">

        <TextView
            android:id="@+id/bubble_flyout_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="@*android:string/config_bodyFontFamily"
            android:maxLines="2"
        android:maxWidth="@dimen/bubble_flyout_maxwidth"
        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" />
            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>

    </FrameLayout>

</FrameLayout>
 No newline at end of file
+9 −5
Original line number Diff line number Diff line
@@ -1064,16 +1064,20 @@
    <dimen name="bubble_elevation">1dp</dimen>
    <!-- How much the bubble flyout text container is elevated. -->
    <dimen name="bubble_flyout_elevation">4dp</dimen>
    <!-- How much padding is around the flyout text. -->
    <dimen name="bubble_flyout_padding">16dp</dimen>
    <!-- The maximum width of a bubble flyout. -->
    <dimen name="bubble_flyout_maxwidth">200dp</dimen>
    <!-- How much padding is around the left and right sides of the flyout text. -->
    <dimen name="bubble_flyout_padding_x">16dp</dimen>
    <!-- How much padding is around the top and bottom of the flyout text. -->
    <dimen name="bubble_flyout_padding_y">8dp</dimen>
    <!-- Size of the triangle that points from the flyout to the bubble stack. -->
    <dimen name="bubble_flyout_pointer_size">6dp</dimen>
    <!-- How much space to leave between the flyout (tip of the arrow) and the bubble stack. -->
    <dimen name="bubble_flyout_space_from_bubble">8dp</dimen>
    <!-- Padding around a collapsed bubble -->
    <dimen name="bubble_view_padding">0dp</dimen>
    <!-- Padding between bubbles when displayed in expanded state -->
    <dimen name="bubble_padding">8dp</dimen>
    <!-- Size of individual bubbles. -->
    <dimen name="individual_bubble_size">56dp</dimen>
    <dimen name="individual_bubble_size">52dp</dimen>
    <!-- How much to inset the icon in the circle -->
    <dimen name="bubble_icon_inset">16dp</dimen>
    <!-- Padding around the view displayed when the bubble is expanded -->
+134 −22
Original line number Diff line number Diff line
@@ -22,16 +22,21 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.StatsLog;
import android.view.Choreographer;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -56,6 +61,7 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;

import java.math.BigDecimal;
@@ -73,6 +79,9 @@ public class BubbleStackView extends FrameLayout {
    /** Duration of the flyout alpha animations. */
    private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;

    /** Max width of the flyout, in terms of percent of the screen width. */
    private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;

    /** How long to wait, in milliseconds, before hiding the flyout. */
    @VisibleForTesting
    static final int FLYOUT_HIDE_AFTER = 5000;
@@ -126,13 +135,17 @@ public class BubbleStackView extends FrameLayout {

    private FrameLayout mExpandedViewContainer;

    private View mFlyout;
    private FrameLayout mFlyoutContainer;
    private FrameLayout mFlyout;
    private TextView mFlyoutText;
    private ShapeDrawable mLeftFlyoutTriangle;
    private ShapeDrawable mRightFlyoutTriangle;
    /** Spring animation for the flyout. */
    private SpringAnimation mFlyoutSpring;
    /** Runnable that fades out the flyout and then sets it to GONE. */
    private Runnable mHideFlyout =
            () -> mFlyout.animate().alpha(0f).withEndAction(() -> mFlyout.setVisibility(GONE));
            () -> mFlyoutContainer.animate().alpha(0f).withEndAction(
                    () -> mFlyoutContainer.setVisibility(GONE));

    /** Layout change listener that moves the stack to the nearest valid position on rotation. */
    private OnLayoutChangeListener mMoveStackToValidPositionOnLayoutListener;
@@ -146,6 +159,9 @@ public class BubbleStackView extends FrameLayout {

    private int mBubbleSize;
    private int mBubblePadding;
    private int mFlyoutPadding;
    private int mFlyoutSpaceFromBubble;
    private int mPointerSize;
    private int mExpandedAnimateXDistance;
    private int mExpandedAnimateYDistance;
    private int mStatusBarHeight;
@@ -218,6 +234,9 @@ public class BubbleStackView extends FrameLayout {
        Resources res = getResources();
        mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
        mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
        mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
        mFlyoutSpaceFromBubble = res.getDimensionPixelSize(R.dimen.bubble_flyout_space_from_bubble);
        mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size);
        mExpandedAnimateXDistance =
                res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
        mExpandedAnimateYDistance =
@@ -244,7 +263,6 @@ public class BubbleStackView extends FrameLayout {
                getResources().getInteger(R.integer.bubbles_max_rendered));
        mBubbleContainer.setController(mStackAnimationController);
        mBubbleContainer.setElevation(elevation);
        mBubbleContainer.setPadding(padding, 0, padding, 0);
        mBubbleContainer.setClipChildren(false);
        addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));

@@ -254,16 +272,17 @@ public class BubbleStackView extends FrameLayout {
        mExpandedViewContainer.setClipChildren(false);
        addView(mExpandedViewContainer);

        mFlyout = mInflater.inflate(R.layout.bubble_flyout, this, false);
        mFlyout.setVisibility(GONE);
        mFlyout.animate()
        mFlyoutContainer = (FrameLayout) mInflater.inflate(R.layout.bubble_flyout, this, false);
        mFlyoutContainer.setVisibility(GONE);
        mFlyoutContainer.setClipToPadding(false);
        mFlyoutContainer.setClipChildren(false);
        mFlyoutContainer.animate()
                .setDuration(FLYOUT_ALPHA_ANIMATION_DURATION)
                .setInterpolator(new AccelerateDecelerateInterpolator());
        addView(mFlyout);

        mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);

        mFlyoutSpring = new SpringAnimation(mFlyout, DynamicAnimation.TRANSLATION_X);
        mFlyout = mFlyoutContainer.findViewById(R.id.bubble_flyout);
        addView(mFlyoutContainer);
        setupFlyout();

        mExpandedViewXAnim =
                new SpringAnimation(mExpandedViewContainer, DynamicAnimation.TRANSLATION_X);
@@ -592,7 +611,7 @@ public class BubbleStackView extends FrameLayout {
            }
            // Outside parts of view we care about.
            return null;
        } else if (isIntersecting(mFlyout, x, y)) {
        } else if (mFlyoutContainer.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
            return mFlyout;
        }

@@ -828,27 +847,52 @@ public class BubbleStackView extends FrameLayout {
        if (updateMessage != null && !isExpanded() && !mIsExpansionAnimating && !mIsDragging) {
            final PointF stackPos = mStackAnimationController.getStackPosition();

            mFlyout.setAlpha(0f);
            mFlyout.setVisibility(VISIBLE);
            // Set the flyout TextView's max width in terms of percent, and then subtract out the
            // padding so that the entire flyout view will be the desired width (rather than the
            // TextView being the desired width + extra padding).
            mFlyoutText.setMaxWidth(
                    (int) (getWidth() * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2);

            mFlyoutContainer.setAlpha(0f);
            mFlyoutContainer.setVisibility(VISIBLE);

            mFlyoutText.setText(updateMessage);
            mFlyout.measure(WRAP_CONTENT, WRAP_CONTENT);
            post(() -> {

            final boolean onLeft = mStackAnimationController.isStackOnLeftSide();

            if (onLeft) {
                mLeftFlyoutTriangle.setAlpha(255);
                mRightFlyoutTriangle.setAlpha(0);
            } else {
                mLeftFlyoutTriangle.setAlpha(0);
                mRightFlyoutTriangle.setAlpha(255);
            }

            mFlyoutContainer.post(() -> {
                // Multi line flyouts get top-aligned to the bubble.
                if (mFlyoutText.getLineCount() > 1) {
                    mFlyoutContainer.setTranslationY(stackPos.y);
                } else {
                    // Single line flyouts are vertically centered with respect to the bubble.
                    mFlyoutContainer.setTranslationY(
                            stackPos.y + (mBubbleSize - mFlyout.getHeight()) / 2f);
                }

                final float destinationX = onLeft
                        ? stackPos.x + mBubbleSize + mBubblePadding
                        : stackPos.x - mFlyout.getMeasuredWidth();
                        ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble
                        : stackPos.x - mFlyoutContainer.getWidth() - mFlyoutSpaceFromBubble;

                // Translate towards the stack slightly, then spring out from the stack.
                mFlyout.setTranslationX(destinationX + (onLeft ? -mBubblePadding : mBubblePadding));
                mFlyout.setTranslationY(stackPos.y);
                mFlyoutContainer.setTranslationX(
                        destinationX + (onLeft ? -mBubblePadding : mBubblePadding));

                mFlyout.animate().alpha(1f);
                mFlyoutContainer.animate().alpha(1f);
                mFlyoutSpring.animateToFinalPosition(destinationX);

                mFlyout.removeCallbacks(mHideFlyout);
                mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
            });

            logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
        }
    }
@@ -869,7 +913,7 @@ public class BubbleStackView extends FrameLayout {
            mBubbleContainer.getBoundsOnScreen(outRect);
        }

        if (mFlyout.getVisibility() == View.VISIBLE) {
        if (mFlyoutContainer.getVisibility() == View.VISIBLE) {
            final Rect flyoutBounds = new Rect();
            mFlyout.getBoundsOnScreen(flyoutBounds);
            outRect.union(flyoutBounds);
@@ -923,6 +967,74 @@ public class BubbleStackView extends FrameLayout {
        }
    }

    /** Sets up the flyout views and drawables. */
    private void setupFlyout() {
        // Retrieve the styled floating background color.
        TypedArray ta = mContext.obtainStyledAttributes(
                new int[] {android.R.attr.colorBackgroundFloating});
        final int floatingBackgroundColor = ta.getColor(0, Color.WHITE);
        ta.recycle();

        // Retrieve the flyout background, which is currently a rounded white rectangle with a
        // shadow but no triangular arrow pointing anywhere.
        final LayerDrawable flyoutBackground = (LayerDrawable) mFlyout.getBackground();

        // Create the triangle drawables and set their color.
        mLeftFlyoutTriangle =
                new ShapeDrawable(TriangleShape.createHorizontal(
                        mPointerSize, mPointerSize, true /* isPointingLeft */));
        mRightFlyoutTriangle =
                new ShapeDrawable(TriangleShape.createHorizontal(
                        mPointerSize, mPointerSize, false /* isPointingLeft */));
        mLeftFlyoutTriangle.getPaint().setColor(floatingBackgroundColor);
        mRightFlyoutTriangle.getPaint().setColor(floatingBackgroundColor);

        // Add both triangles to the drawable. We'll show and hide the appropriate ones when we show
        // the flyout.
        final int leftTriangleIndex = flyoutBackground.addLayer(mLeftFlyoutTriangle);
        flyoutBackground.setLayerSize(leftTriangleIndex, mPointerSize, mPointerSize);
        flyoutBackground.setLayerGravity(leftTriangleIndex, Gravity.LEFT | Gravity.CENTER_VERTICAL);
        flyoutBackground.setLayerInsetLeft(leftTriangleIndex, -mPointerSize);

        final int rightTriangleIndex = flyoutBackground.addLayer(mRightFlyoutTriangle);
        flyoutBackground.setLayerSize(rightTriangleIndex, mPointerSize, mPointerSize);
        flyoutBackground.setLayerGravity(
                rightTriangleIndex, Gravity.RIGHT | Gravity.CENTER_VERTICAL);
        flyoutBackground.setLayerInsetRight(rightTriangleIndex, -mPointerSize);

        // Append the appropriate triangle's outline to the view's outline so that the shadows look
        // correct.
        mFlyout.setOutlineProvider(new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                final boolean leftPointing = mStackAnimationController.isStackOnLeftSide();

                // Get the outline from the appropriate triangle.
                final Outline triangleOutline = new Outline();
                if (leftPointing) {
                    mLeftFlyoutTriangle.getOutline(triangleOutline);
                } else {
                    mRightFlyoutTriangle.getOutline(triangleOutline);
                }

                // Offset it to the correct position, since it has no intrinsic position since
                // that is maintained by the parent LayerDrawable.
                triangleOutline.offset(
                        leftPointing ? -mPointerSize : mFlyout.getWidth(),
                        mFlyout.getHeight() / 2 - mPointerSize / 2);

                // Merge the outlines.
                final Outline compoundOutline = new Outline();
                flyoutBackground.getOutline(compoundOutline);
                compoundOutline.mPath.addPath(triangleOutline.mPath);
                outline.set(compoundOutline);
            }
        });

        mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
        mFlyoutSpring = new SpringAnimation(mFlyoutContainer, DynamicAnimation.TRANSLATION_X);
    }

    private void applyCurrentState() {
        Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
        mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+5 −4
Original line number Diff line number Diff line
@@ -120,7 +120,11 @@ public class StackAnimationController extends
    private float mStackOffset;
    /** Diameter of the bubbles themselves. */
    private int mIndividualBubbleSize;
    /** Size of spacing around the bubbles, separating it from the edge of the screen. */
    /**
     * The amount of space to add between the bubbles and certain UI elements, such as the top of
     * the screen or the IME. This does not apply to the left/right sides of the screen since the
     * stack goes offscreen intentionally.
     */
    private int mBubblePadding;
    /** How far offscreen the stack rests. */
    private int mBubbleOffscreen;
@@ -381,7 +385,6 @@ public class StackAnimationController extends
        if (insets != null) {
            allowableRegion.left =
                    -mBubbleOffscreen
                            - mBubblePadding
                            + Math.max(
                            insets.getSystemWindowInsetLeft(),
                            insets.getDisplayCutout() != null
@@ -391,7 +394,6 @@ public class StackAnimationController extends
                    mLayout.getWidth()
                            - mIndividualBubbleSize
                            + mBubbleOffscreen
                            - mBubblePadding
                            - Math.max(
                            insets.getSystemWindowInsetRight(),
                            insets.getDisplayCutout() != null
@@ -521,7 +523,6 @@ public class StackAnimationController extends

        if (mLayout.getChildCount() > 0) {
            property.setValue(mLayout.getChildAt(0), value);

            if (mLayout.getChildCount() > 1) {
                animationForChildAtIndex(1)
                        .property(property, value + getOffsetForChainedPropertyAnimation(property))