Adjust the flyout sizing and appearance to match the design spec.
Test: atest SystemUITests Bug: 130234901 Change-Id: Ib7cb6ef58d27f7d296b2dd22a41749c5f3bfe67b
This commit is contained in:
@@ -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>
|
||||
@@ -15,19 +15,30 @@
|
||||
-->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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:translationZ="@dimen/bubble_flyout_elevation">
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/bubble_flyout_pointer_size"
|
||||
android:paddingRight="@dimen/bubble_flyout_pointer_size">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bubble_flyout_text"
|
||||
android:layout_width="wrap_content"
|
||||
<FrameLayout
|
||||
android:id="@+id/bubble_flyout"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="2"
|
||||
android:maxWidth="@dimen/bubble_flyout_maxwidth"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" />
|
||||
android:layout_width="wrap_content"
|
||||
android:background="@drawable/bubble_flyout"
|
||||
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:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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();
|
||||
|
||||
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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user