Merge "Center bubbles horizontally on large screens" into sc-v2-dev

This commit is contained in:
Mady Mellor
2021-08-18 19:42:38 +00:00
committed by Android (Google) Code Review
6 changed files with 232 additions and 203 deletions

View File

@@ -696,8 +696,10 @@ public class BubbleExpandedView extends LinearLayout {
? mPointerHeight - mPointerOverlap ? mPointerHeight - mPointerOverlap
: 0; : 0;
final float paddingRight = (showVertically && !onLeft) final float paddingRight = (showVertically && !onLeft)
? mPointerHeight - mPointerOverlap : 0; ? mPointerHeight - mPointerOverlap
final float paddingTop = showVertically ? 0 : 0;
final float paddingTop = showVertically
? 0
: mPointerHeight - mPointerOverlap; : mPointerHeight - mPointerOverlap;
setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0); setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0);

View File

@@ -79,14 +79,18 @@ public class BubblePositioner {
private int mBubbleSize; private int mBubbleSize;
private int mSpacingBetweenBubbles; private int mSpacingBetweenBubbles;
private float mExpandedViewLargeScreenWidth;
private int mExpandedViewMinHeight;
private int mExpandedViewLargeScreenWidth;
private int mExpandedViewLargeScreenInset;
private int mOverflowWidth; private int mOverflowWidth;
private int mExpandedViewPadding; private int mExpandedViewPadding;
private int mPointerMargin; private int mPointerMargin;
private int mPointerWidth; private int mPointerWidth;
private int mPointerHeight; private int mPointerHeight;
private int mPointerOverlap;
private int mManageButtonHeight; private int mManageButtonHeight;
private int mExpandedViewMinHeight;
private int mOverflowHeight; private int mOverflowHeight;
private int mMinimumFlyoutWidthLargeScreen; private int mMinimumFlyoutWidthLargeScreen;
@@ -168,15 +172,20 @@ public class BubblePositioner {
mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size); mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
mExpandedViewLargeScreenWidth = bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT; mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
mExpandedViewLargeScreenWidth = (int) (bounds.width()
* EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT);
mExpandedViewLargeScreenInset = mIsLargeScreen
? (bounds.width() - mExpandedViewLargeScreenWidth) / 2
: mExpandedViewPadding;
mOverflowWidth = mIsLargeScreen mOverflowWidth = mIsLargeScreen
? (int) mExpandedViewLargeScreenWidth ? mExpandedViewLargeScreenWidth
: res.getDimensionPixelSize( : res.getDimensionPixelSize(
R.dimen.bubble_expanded_view_phone_landscape_overflow_width); R.dimen.bubble_expanded_view_phone_landscape_overflow_width);
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap);
mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height); mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height);
mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
@@ -294,27 +303,46 @@ public class BubblePositioner {
} }
/** /**
* Calculates the left & right padding for the bubble expanded view. * Calculates the padding for the bubble expanded view.
* *
* On larger screens the width of the expanded view is restricted via this padding. * Some specifics:
* On landscape the bubble overflow expanded view is also restricted via this padding. * On large screens the width of the expanded view is restricted via this padding.
* On phone landscape the bubble overflow expanded view is also restricted via this padding.
* On large screens & landscape no top padding is set, the top position is set via translation.
* On phone portrait top padding is set as the space between the tip of the pointer and the
* bubble.
* When the overflow is shown it doesn't have the manage button to pad out the bottom so
* padding is added.
*/ */
public int[] getExpandedViewPadding(boolean onLeft, boolean isOverflow) { public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) {
final int pointerTotalHeight = mPointerHeight - mPointerOverlap;
if (mIsLargeScreen) {
// [left, top, right, bottom]
mPaddings[0] = onLeft
? mExpandedViewLargeScreenInset - pointerTotalHeight
: mExpandedViewLargeScreenInset;
mPaddings[1] = 0;
mPaddings[2] = onLeft
? mExpandedViewLargeScreenInset
: mExpandedViewLargeScreenInset - pointerTotalHeight;
// Overflow doesn't show manage button / get padding from it so add padding here for it
mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
return mPaddings;
} else {
int leftPadding = mInsets.left + mExpandedViewPadding; int leftPadding = mInsets.left + mExpandedViewPadding;
int rightPadding = mInsets.right + mExpandedViewPadding; int rightPadding = mInsets.right + mExpandedViewPadding;
final boolean isLargeOrOverflow = mIsLargeScreen || isOverflow;
final float expandedViewWidth = isOverflow final float expandedViewWidth = isOverflow
? mOverflowWidth ? mOverflowWidth
: mExpandedViewLargeScreenWidth; : mExpandedViewLargeScreenWidth;
if (showBubblesVertically()) { if (showBubblesVertically()) {
if (!onLeft) { if (!onLeft) {
rightPadding += mBubbleSize - mPointerHeight; rightPadding += mBubbleSize - pointerTotalHeight;
leftPadding += isLargeOrOverflow leftPadding += isOverflow
? (mPositionRect.width() - rightPadding - expandedViewWidth) ? (mPositionRect.width() - rightPadding - expandedViewWidth)
: 0; : 0;
} else { } else {
leftPadding += mBubbleSize - mPointerHeight; leftPadding += mBubbleSize - pointerTotalHeight;
rightPadding += isLargeOrOverflow rightPadding += isOverflow
? (mPositionRect.width() - leftPadding - expandedViewWidth) ? (mPositionRect.width() - leftPadding - expandedViewWidth)
: 0; : 0;
} }
@@ -326,34 +354,42 @@ public class BubblePositioner {
mPaddings[3] = 0; mPaddings[3] = 0;
return mPaddings; return mPaddings;
} }
}
/** Gets the y position of the expanded view if it was top-aligned. */ /** Gets the y position of the expanded view if it was top-aligned. */
private float getExpandedViewYTopAligned() { private float getExpandedViewYTopAligned() {
final int top = getAvailableRect().top; final int top = getAvailableRect().top;
if (showBubblesVertically()) { if (showBubblesVertically()) {
return top - mPointerWidth; return top - mPointerWidth + mExpandedViewPadding;
} else { } else {
return top + mBubbleSize + mPointerMargin; return top + mBubbleSize + mPointerMargin;
} }
} }
/** The maximum height the expanded view can be. */ public float getExpandedBubblesY() {
return getAvailableRect().top + mExpandedViewPadding;
}
/**
* Calculate the maximum height the expanded view can be depending on where it's placed on
* the screen and the size of the elements around it (e.g. padding, pointer, manage button).
*/
public int getMaxExpandedViewHeight(boolean isOverflow) { public int getMaxExpandedViewHeight(boolean isOverflow) {
// Subtract top insets because availableRect.height would account for that
int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
int paddingTop = showBubblesVertically() int paddingTop = showBubblesVertically()
? 0 ? 0
: mPointerHeight; : mPointerHeight;
int settingsHeight = isOverflow ? 0 : mManageButtonHeight;
// Subtract pointer size because it's laid out in LinearLayout with the expanded view. // Subtract pointer size because it's laid out in LinearLayout with the expanded view.
int pointerSize = showBubblesVertically() int pointerSize = showBubblesVertically()
? mPointerWidth ? mPointerWidth
: (mPointerHeight + mPointerMargin); : (mPointerHeight + mPointerMargin);
// Subtract top insets because availableRect.height would account for that int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
return getAvailableRect().height() return getAvailableRect().height()
- expandedContainerY - expandedContainerY
- paddingTop - paddingTop
- settingsHeight - pointerSize
- pointerSize; - bottomPadding;
} }
/** /**
@@ -362,11 +398,14 @@ public class BubblePositioner {
*/ */
public float getExpandedViewHeight(BubbleViewProvider bubble) { public float getExpandedViewHeight(BubbleViewProvider bubble) {
boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey()); boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
if (isOverflow && showBubblesVertically() && !mIsLargeScreen) {
// overflow in landscape on phone is max
return MAX_HEIGHT;
}
float desiredHeight = isOverflow float desiredHeight = isOverflow
? mOverflowHeight ? mOverflowHeight
: ((Bubble) bubble).getDesiredHeight(mContext); : ((Bubble) bubble).getDesiredHeight(mContext);
int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight; desiredHeight = Math.max(desiredHeight, mExpandedViewMinHeight);
desiredHeight = Math.max(manageButtonHeight + desiredHeight, mExpandedViewMinHeight);
if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) { if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) {
return MAX_HEIGHT; return MAX_HEIGHT;
} }
@@ -391,7 +430,7 @@ public class BubblePositioner {
return topAlignment; return topAlignment;
} }
// If we're here, we're showing vertically & developer has made height less than maximum. // If we're here, we're showing vertically & developer has made height less than maximum.
int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight; int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
float pointerPosition = getPointerPosition(bubblePosition); float pointerPosition = getPointerPosition(bubblePosition);
float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight; float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight;
float topIfCentered = pointerPosition - (expandedViewHeight / 2); float topIfCentered = pointerPosition - (expandedViewHeight / 2);
@@ -426,25 +465,40 @@ public class BubblePositioner {
} }
/** /**
* When bubbles are expanded in portrait, they display at the top of the screen in a horizontal * Returns the position of the bubble on-screen when the stack is expanded.
* row. When in landscape or on a large screen, they show at the left or right side in a
* vertical row. This method accounts for screen orientation and will return an x or y value
* for the position of the bubble in the row.
* *
* @param index bubble index in the row. * @param index the index of the bubble in the stack.
* @param numberOfBubbles the number of bubbles (including the overflow) in the row. * @param numberOfBubbles the total number of bubbles in the stack.
* @return the y position of the bubble if showing vertically and the x position if showing * @param onLeftEdge whether the stack would rest on the left edge of the screen when collapsed.
* horizontally. * @return the x, y position of the bubble on-screen when the stack is expanded.
*/ */
public float getBubbleXOrYForOrientation(int index, int numberOfBubbles) { public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) {
final float positionInBar = index * (mBubbleSize + mSpacingBetweenBubbles); final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles);
final float expandedStackSize = (numberOfBubbles * mBubbleSize) final float expandedStackSize = (numberOfBubbles * mBubbleSize)
+ ((numberOfBubbles - 1) * mSpacingBetweenBubbles); + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
final float centerPosition = showBubblesVertically() final float centerPosition = showBubblesVertically()
? mPositionRect.centerY() ? mPositionRect.centerY()
: mPositionRect.centerX(); : mPositionRect.centerX();
// alignment - centered on the edge
final float rowStart = centerPosition - (expandedStackSize / 2f); final float rowStart = centerPosition - (expandedStackSize / 2f);
return rowStart + positionInBar; float x;
float y;
if (showBubblesVertically()) {
y = rowStart + positionInRow;
int left = mIsLargeScreen
? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize
: mPositionRect.left;
int right = mIsLargeScreen
? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding
: mPositionRect.right - mBubbleSize;
x = onLeftEdge
? left
: right;
} else {
y = mPositionRect.top + mExpandedViewPadding;
x = rowStart + positionInRow;
}
return new PointF(x, y);
} }
/** /**
@@ -458,6 +512,17 @@ public class BubblePositioner {
return mScreenRect.width() * FLYOUT_MAX_WIDTH_PERCENT; return mScreenRect.width() * FLYOUT_MAX_WIDTH_PERCENT;
} }
/**
* @return whether the stack is considered on the left side of the screen.
*/
public boolean isStackOnLeft(PointF currentStackPosition) {
if (currentStackPosition == null) {
currentStackPosition = getRestingPosition();
}
final int stackCenter = (int) currentStackPosition.x + mBubbleSize / 2;
return stackCenter < mScreenRect.width() / 2;
}
/** /**
* Sets the stack's most recent position along the edge of the screen. This is saved when the * Sets the stack's most recent position along the edge of the screen. This is saved when the
* last bubble is removed, so that the stack can be restored in its previous position. * last bubble is removed, so that the stack can be restored in its previous position.

View File

@@ -778,8 +778,8 @@ public class BubbleStackView extends FrameLayout
floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut,
this::animateShadows /* onStackAnimationFinished */, mPositioner); this::animateShadows /* onStackAnimationFinished */, mPositioner);
mExpandedAnimationController = new ExpandedAnimationController( mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
mPositioner, mExpandedViewPadding, onBubbleAnimatedOut); onBubbleAnimatedOut);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
// Force LTR by default since most of the Bubbles UI is positioned manually by the user, or // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -1869,28 +1869,28 @@ public class BubbleStackView extends FrameLayout
maybeShowManageEdu(); maybeShowManageEdu();
} }
} /* after */); } /* after */);
final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
getBubbleIndex(mExpandedBubble));
mExpandedViewContainer.setTranslationX(0f);
mExpandedViewContainer.setTranslationY(translationY);
mExpandedViewContainer.setAlpha(1f);
int index; int index;
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) { if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
index = mBubbleData.getBubbles().size(); index = mBubbleData.getBubbles().size();
} else { } else {
index = getBubbleIndex(mExpandedBubble); index = getBubbleIndex(mExpandedBubble);
} }
// Position of the bubble we're expanding, once it's settled in its row. PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(),
final float bubbleWillBeAt = mStackOnLeftOrWillBe);
mExpandedAnimationController.getBubbleXOrYForOrientation(index); final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
mPositioner.showBubblesVertically() ? p.y : p.x);
mExpandedViewContainer.setTranslationX(0f);
mExpandedViewContainer.setTranslationY(translationY);
mExpandedViewContainer.setAlpha(1f);
// How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles
// that are animating farther, so that the expanded view doesn't move as much. // that are animating farther, so that the expanded view doesn't move as much.
final float relevantStackPosition = showVertically final float relevantStackPosition = showVertically
? mStackAnimationController.getStackPosition().y ? mStackAnimationController.getStackPosition().y
: mStackAnimationController.getStackPosition().x; : mStackAnimationController.getStackPosition().x;
final float bubbleWillBeAt = showVertically
? p.y
: p.x;
final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition); final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition);
// Wait for the path animation target to reach its end, and add a small amount of extra time // Wait for the path animation target to reach its end, and add a small amount of extra time
@@ -1907,22 +1907,22 @@ public class BubbleStackView extends FrameLayout
// Set the pivot point for the scale, so the expanded view animates out from the bubble. // Set the pivot point for the scale, so the expanded view animates out from the bubble.
if (showVertically) { if (showVertically) {
float pivotX; float pivotX;
float pivotY = bubbleWillBeAt + mBubbleSize / 2f;
if (mStackOnLeftOrWillBe) { if (mStackOnLeftOrWillBe) {
pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; pivotX = p.x + mBubbleSize + mExpandedViewPadding;
} else { } else {
pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; pivotX = p.x - mExpandedViewPadding;
} }
mExpandedViewContainerMatrix.setScale( mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
pivotX, pivotY); pivotX,
p.y + mBubbleSize / 2f);
} else { } else {
mExpandedViewContainerMatrix.setScale( mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
bubbleWillBeAt + mBubbleSize / 2f, p.x + mBubbleSize / 2f,
translationY); p.y + mBubbleSize + mExpandedViewPadding);
} }
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -1959,6 +1959,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainerMatrix); mExpandedViewContainerMatrix);
}) })
.withEndActions(() -> { .withEndActions(() -> {
mExpandedViewContainer.setAnimationMatrix(null);
afterExpandedViewAnimation(); afterExpandedViewAnimation();
if (mExpandedBubble != null if (mExpandedBubble != null
&& mExpandedBubble.getExpandedView() != null) { && mExpandedBubble.getExpandedView() != null) {
@@ -2009,12 +2010,11 @@ public class BubbleStackView extends FrameLayout
index = mBubbleData.getBubbles().indexOf(mExpandedBubble); index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
} }
// Value the bubble is animating from (back into the stack). // Value the bubble is animating from (back into the stack).
final float expandingFromBubbleAt = final PointF p = mPositioner.getExpandedBubbleXY(index,
mExpandedAnimationController.getBubbleXOrYForOrientation(index); mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
final boolean showVertically = mPositioner.showBubblesVertically();
if (mPositioner.showBubblesVertically()) { if (mPositioner.showBubblesVertically()) {
float pivotX; float pivotX;
float pivotY = expandingFromBubbleAt + mBubbleSize / 2f; float pivotY = p.y + mBubbleSize / 2f;
if (mStackOnLeftOrWillBe) { if (mStackOnLeftOrWillBe) {
pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
} else { } else {
@@ -2026,8 +2026,8 @@ public class BubbleStackView extends FrameLayout
} else { } else {
mExpandedViewContainerMatrix.setScale( mExpandedViewContainerMatrix.setScale(
1f, 1f, 1f, 1f,
expandingFromBubbleAt + mBubbleSize / 2f, p.x + mBubbleSize / 2f,
mPositioner.getExpandedViewY(mExpandedBubble, index)); p.y + mBubbleSize + mExpandedViewPadding);
} }
mExpandedViewAlphaAnimator.reverse(); mExpandedViewAlphaAnimator.reverse();
@@ -2105,32 +2105,31 @@ public class BubbleStackView extends FrameLayout
boolean isOverflow = mExpandedBubble != null boolean isOverflow = mExpandedBubble != null
&& mExpandedBubble.getKey().equals(BubbleOverflow.KEY); && mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
float expandingFromBubbleDestination = PointF p = mPositioner.getExpandedBubbleXY(isOverflow
mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow ? mBubbleContainer.getChildCount() - 1
? getBubbleCount() : mBubbleData.getBubbles().indexOf(mExpandedBubble),
: mBubbleData.getBubbles().indexOf(mExpandedBubble)); mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
mExpandedViewContainer.setAlpha(1f); mExpandedViewContainer.setAlpha(1f);
mExpandedViewContainer.setVisibility(View.VISIBLE); mExpandedViewContainer.setVisibility(View.VISIBLE);
if (mPositioner.showBubblesVertically()) { if (mPositioner.showBubblesVertically()) {
float pivotX; float pivotX;
float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f; float pivotY = p.y + mBubbleSize / 2f;
if (mStackOnLeftOrWillBe) { if (mStackOnLeftOrWillBe) {
pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; pivotX = p.x + mBubbleSize + mExpandedViewPadding;
} else { } else {
pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; pivotX = p.x - mExpandedViewPadding;
} }
mExpandedViewContainerMatrix.setScale( mExpandedViewContainerMatrix.setScale(
0f, 0f, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
pivotX, pivotY); pivotX, pivotY);
} else { } else {
mExpandedViewContainerMatrix.setScale( mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
expandingFromBubbleDestination + mBubbleSize / 2f, p.x + mBubbleSize / 2f,
mPositioner.getExpandedViewY(mExpandedBubble, expandingFromBubbleDestination)); p.y + mBubbleSize + mExpandedViewPadding);
} }
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -2155,6 +2154,7 @@ public class BubbleStackView extends FrameLayout
.withEndActions(() -> { .withEndActions(() -> {
mExpandedViewTemporarilyHidden = false; mExpandedViewTemporarilyHidden = false;
mIsBubbleSwitchAnimating = false; mIsBubbleSwitchAnimating = false;
mExpandedViewContainer.setAnimationMatrix(null);
}) })
.start(); .start();
}, 25); }, 25);
@@ -2764,16 +2764,17 @@ public class BubbleStackView extends FrameLayout
} }
boolean isOverflowExpanded = mExpandedBubble != null boolean isOverflowExpanded = mExpandedBubble != null
&& BubbleOverflow.KEY.equals(mExpandedBubble.getKey()); && BubbleOverflow.KEY.equals(mExpandedBubble.getKey());
int[] paddings = mPositioner.getExpandedViewPadding( int[] paddings = mPositioner.getExpandedViewContainerPadding(
mStackAnimationController.isStackOnLeftSide(), isOverflowExpanded); mStackAnimationController.isStackOnLeftSide(), isOverflowExpanded);
mExpandedViewContainer.setPadding(paddings[0], paddings[1], paddings[2], paddings[3]); mExpandedViewContainer.setPadding(paddings[0], paddings[1], paddings[2], paddings[3]);
if (mIsExpansionAnimating) { if (mIsExpansionAnimating) {
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
} }
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble, mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
mExpandedAnimationController.getBubbleXOrYForOrientation( mPositioner.showBubblesVertically() ? p.y : p.x));
getBubbleIndex(mExpandedBubble))));
mExpandedViewContainer.setTranslationX(0f); mExpandedViewContainer.setTranslationX(0f);
mExpandedBubble.getExpandedView().updateView( mExpandedBubble.getExpandedView().updateView(
mExpandedViewContainer.getLocationOnScreen()); mExpandedViewContainer.getLocationOnScreen());
@@ -2856,8 +2857,13 @@ public class BubbleStackView extends FrameLayout
if (index == -1) { if (index == -1) {
return; return;
} }
float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index); PointF bubblePosition = mPositioner.getExpandedBubbleXY(index,
mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition, mStackOnLeftOrWillBe); mBubbleContainer.getChildCount(),
mStackOnLeftOrWillBe);
mExpandedBubble.getExpandedView().setPointerPosition(mPositioner.showBubblesVertically()
? bubblePosition.y
: bubblePosition.x,
mStackOnLeftOrWillBe);
} }
/** /**

View File

@@ -76,8 +76,6 @@ public class ExpandedAnimationController
/** Horizontal offset between bubbles, which we need to know to re-stack them. */ /** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx; private float mStackOffsetPx;
/** Space between status bar and bubbles in the expanded state. */
private float mBubblePaddingTop;
/** Size of each bubble. */ /** Size of each bubble. */
private float mBubbleSizePx; private float mBubbleSizePx;
/** Whether the expand / collapse animation is running. */ /** Whether the expand / collapse animation is running. */
@@ -118,8 +116,6 @@ public class ExpandedAnimationController
/** The bubble currently being dragged out of the row (to potentially be dismissed). */ /** The bubble currently being dragged out of the row (to potentially be dismissed). */
private MagnetizedObject<View> mMagnetizedBubbleDraggingOut; private MagnetizedObject<View> mMagnetizedBubbleDraggingOut;
private int mExpandedViewPadding;
/** /**
* Callback to run whenever any bubble is animated out. The BubbleStackView will check if the * Callback to run whenever any bubble is animated out. The BubbleStackView will check if the
* end of this animation means we have no bubbles left, and notify the BubbleController. * end of this animation means we have no bubbles left, and notify the BubbleController.
@@ -128,11 +124,10 @@ public class ExpandedAnimationController
private BubblePositioner mPositioner; private BubblePositioner mPositioner;
public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding, public ExpandedAnimationController(BubblePositioner positioner,
Runnable onBubbleAnimatedOutAction) { Runnable onBubbleAnimatedOutAction) {
mPositioner = positioner; mPositioner = positioner;
updateResources(); updateResources();
mExpandedViewPadding = expandedViewPadding;
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction; mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
mCollapsePoint = mPositioner.getDefaultStartPosition(); mCollapsePoint = mPositioner.getDefaultStartPosition();
} }
@@ -199,7 +194,6 @@ public class ExpandedAnimationController
return; return;
} }
Resources res = mLayout.getContext().getResources(); Resources res = mLayout.getContext().getResources();
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
mBubbleSizePx = mPositioner.getBubbleSize(); mBubbleSizePx = mPositioner.getBubbleSize();
} }
@@ -245,31 +239,22 @@ public class ExpandedAnimationController
final Path path = new Path(); final Path path = new Path();
path.moveTo(bubble.getTranslationX(), bubble.getTranslationY()); path.moveTo(bubble.getTranslationX(), bubble.getTranslationY());
final float expandedY = mPositioner.showBubblesVertically() boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
? getBubbleXOrYForOrientation(index) final PointF p = mPositioner.getExpandedBubbleXY(index,
: getExpandedY(); mLayout.getChildCount(),
onLeft);
if (expanding) { if (expanding) {
// If we're expanding, first draw a line from the bubble's current position to the // If we're expanding, first draw a line from the bubble's current position to where
// top of the screen. // it'll end up
path.lineTo(bubble.getTranslationX(), expandedY); path.lineTo(bubble.getTranslationX(), p.y);
// Then, draw a line across the screen to the bubble's resting position. // Then, draw a line across the screen to the bubble's resting position.
if (mPositioner.showBubblesVertically()) { path.lineTo(p.x, p.y);
Rect availableRect = mPositioner.getAvailableRect();
boolean onLeft = mCollapsePoint != null
&& mCollapsePoint.x < (availableRect.width() / 2f);
float translationX = onLeft
? availableRect.left
: availableRect.right - mBubbleSizePx;
path.lineTo(translationX, getBubbleXOrYForOrientation(index));
} else {
path.lineTo(getBubbleXOrYForOrientation(index), expandedY);
}
} else { } else {
final float stackedX = mCollapsePoint.x; final float stackedX = mCollapsePoint.x;
// If we're collapsing, draw a line from the bubble's current position to the side // If we're collapsing, draw a line from the bubble's current position to the side
// of the screen where the bubble will be stacked. // of the screen where the bubble will be stacked.
path.lineTo(stackedX, expandedY); path.lineTo(stackedX, p.y);
// Then, draw a line down to the stack position. // Then, draw a line down to the stack position.
path.lineTo(stackedX, mCollapsePoint.y path.lineTo(stackedX, mCollapsePoint.y
@@ -379,8 +364,9 @@ public class ExpandedAnimationController
bubbleView.setTranslationY(y); bubbleView.setTranslationY(y);
} }
final float expandedY = mPositioner.getExpandedBubblesY();
final boolean draggedOutEnough = final boolean draggedOutEnough =
y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx; y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
if (draggedOutEnough != mBubbleDraggedOutEnough) { if (draggedOutEnough != mBubbleDraggedOutEnough) {
updateBubblePositions(); updateBubblePositions();
mBubbleDraggedOutEnough = draggedOutEnough; mBubbleDraggedOutEnough = draggedOutEnough;
@@ -424,9 +410,10 @@ public class ExpandedAnimationController
return; return;
} }
final int index = mLayout.indexOfChild(bubbleView); final int index = mLayout.indexOfChild(bubbleView);
final PointF p = mPositioner.getExpandedBubbleXY(index, mLayout.getChildCount(),
mPositioner.isStackOnLeft(mCollapsePoint));
animationForChildAtIndex(index) animationForChildAtIndex(index)
.position(getBubbleXOrYForOrientation(index), getExpandedY()) .position(p.x, p.y)
.withPositionStartVelocities(velX, velY) .withPositionStartVelocities(velX, velY)
.start(() -> bubbleView.setTranslationZ(0f) /* after */); .start(() -> bubbleView.setTranslationZ(0f) /* after */);
@@ -443,17 +430,13 @@ public class ExpandedAnimationController
} }
/** /**
* Animates the bubbles to {@link #getExpandedY()} position. Used in response to IME showing. * Animates the bubbles to the y position. Used in response to IME showing.
*/ */
public void updateYPosition(Runnable after) { public void updateYPosition(Runnable after) {
if (mLayout == null) return; if (mLayout == null) return;
animationsForChildrenFromIndex( animationsForChildrenFromIndex(
0, (i, anim) -> anim.translationY(getExpandedY())).startAll(after); 0, (i, anim) -> anim.translationY(mPositioner.getExpandedBubblesY()))
} .startAll(after);
/** The Y value of the row of expanded bubbles. */
public float getExpandedY() {
return mPositioner.getAvailableRect().top + mBubblePaddingTop;
} }
/** Description of current animation controller state. */ /** Description of current animation controller state. */
@@ -511,35 +494,36 @@ public class ExpandedAnimationController
startOrUpdatePathAnimation(true /* expanding */); startOrUpdatePathAnimation(true /* expanding */);
} else if (mAnimatingCollapse) { } else if (mAnimatingCollapse) {
startOrUpdatePathAnimation(false /* expanding */); startOrUpdatePathAnimation(false /* expanding */);
} else if (mPositioner.showBubblesVertically()) { } else {
child.setTranslationY(getBubbleXOrYForOrientation(index)); boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
final PointF p = mPositioner.getExpandedBubbleXY(index,
mLayout.getChildCount(),
onLeft);
if (mPositioner.showBubblesVertically()) {
child.setTranslationY(p.y);
} else {
child.setTranslationX(p.x);
}
if (!mPreparingToCollapse) { if (!mPreparingToCollapse) {
// Only animate if we're not collapsing as that animation will handle placing the // Only animate if we're not collapsing as that animation will handle placing the
// new bubble in the stacked position. // new bubble in the stacked position.
if (mPositioner.showBubblesVertically()) {
Rect availableRect = mPositioner.getAvailableRect(); Rect availableRect = mPositioner.getAvailableRect();
boolean onLeft = mCollapsePoint != null
&& mCollapsePoint.x < (availableRect.width() / 2f);
float fromX = onLeft float fromX = onLeft
? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
: availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
float toX = onLeft
? availableRect.left + mExpandedViewPadding
: availableRect.right - mBubbleSizePx - mExpandedViewPadding;
animationForChild(child) animationForChild(child)
.translationX(fromX, toX) .translationX(fromX, p.y)
.start(); .start();
updateBubblePositions();
}
} else { } else {
child.setTranslationX(getBubbleXOrYForOrientation(index)); // Only animate if we're not collapsing as that animation will handle placing
if (!mPreparingToCollapse) { // the new bubble in the stacked position.
// Only animate if we're not collapsing as that animation will handle placing the float fromY = mPositioner.getExpandedBubblesY() - mBubbleSizePx
// new bubble in the stacked position. * ANIMATE_TRANSLATION_FACTOR;
float toY = getExpandedY();
float fromY = getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
animationForChild(child) animationForChild(child)
.translationY(fromY, toY) .translationY(fromY, p.y)
.start(); .start();
}
updateBubblePositions(); updateBubblePositions();
} }
} }
@@ -588,7 +572,7 @@ public class ExpandedAnimationController
if (mAnimatingExpand || mAnimatingCollapse) { if (mAnimatingExpand || mAnimatingCollapse) {
return; return;
} }
boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
for (int i = 0; i < mLayout.getChildCount(); i++) { for (int i = 0; i < mLayout.getChildCount(); i++) {
final View bubble = mLayout.getChildAt(i); final View bubble = mLayout.getChildAt(i);
@@ -598,39 +582,11 @@ public class ExpandedAnimationController
return; return;
} }
if (mPositioner.showBubblesVertically()) { final PointF p = mPositioner.getExpandedBubbleXY(i, mLayout.getChildCount(), onLeft);
Rect availableRect = mPositioner.getAvailableRect();
boolean onLeft = mCollapsePoint != null
&& mCollapsePoint.x < (availableRect.width() / 2f);
animationForChild(bubble) animationForChild(bubble)
.translationX(onLeft .translationX(p.x)
? availableRect.left .translationY(p.y)
: availableRect.right - mBubbleSizePx)
.translationY(getBubbleXOrYForOrientation(i))
.start();
} else {
animationForChild(bubble)
.translationX(getBubbleXOrYForOrientation(i))
.translationY(getExpandedY())
.start(); .start();
} }
} }
}
/**
* When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
* row. When in landscape or on a large screen, they show at the left or right side in a
* vertical row. This method accounts for screen orientation and will return an x or y value
* for the position of the bubble in the row.
*
* @param index bubble index in the row.
* @return the y position of the bubble if showing vertically and the x position if showing
* horizontally.
*/
public float getBubbleXOrYForOrientation(int index) {
if (mLayout == null) {
return 0;
}
return mPositioner.getBubbleXOrYForOrientation(index, mLayout.getChildCount());
}
} }

View File

@@ -305,10 +305,7 @@ public class StackAnimationController extends
if (mLayout == null || !isStackPositionSet()) { if (mLayout == null || !isStackPositionSet()) {
return true; // Default to left, which is where it starts by default. return true; // Default to left, which is where it starts by default.
} }
return mPositioner.isStackOnLeft(mStackPosition);
float stackCenter = mStackPosition.x + mBubbleSize / 2;
float screenCenter = mLayout.getWidth() / 2;
return stackCenter < screenCenter;
} }
/** /**

View File

@@ -19,6 +19,7 @@ package com.android.wm.shell.bubbles.animation;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -41,7 +42,6 @@ import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Spy;
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@@ -49,26 +49,26 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
private int mDisplayWidth = 500; private int mDisplayWidth = 500;
private int mDisplayHeight = 1000; private int mDisplayHeight = 1000;
private int mExpandedViewPadding = 10;
private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class); private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class);
@Spy
ExpandedAnimationController mExpandedController; ExpandedAnimationController mExpandedController;
private int mStackOffset; private int mStackOffset;
private PointF mExpansionPoint; private PointF mExpansionPoint;
private BubblePositioner mPositioner;
@SuppressLint("VisibleForTests") @SuppressLint("VisibleForTests")
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class)); mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
positioner.updateInternal(Configuration.ORIENTATION_PORTRAIT, mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0), Insets.of(0, 0, 0, 0),
new Rect(0, 0, mDisplayWidth, mDisplayHeight)); new Rect(0, 0, mDisplayWidth, mDisplayHeight));
mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding, mExpandedController = new ExpandedAnimationController(mPositioner,
mOnBubbleAnimatedOutAction); mOnBubbleAnimatedOutAction);
spyOn(mExpandedController);
addOneMoreThanBubbleLimitBubbles(); addOneMoreThanBubbleLimitBubbles();
mLayout.setActiveController(mExpandedController); mLayout.setActiveController(mExpandedController);
@@ -141,13 +141,16 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
/** Check that children are in the correct positions for being expanded. */ /** Check that children are in the correct positions for being expanded. */
private void testBubblesInCorrectExpandedPositions() { private void testBubblesInCorrectExpandedPositions() {
boolean onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
// Check all the visible bubbles to see if they're in the right place. // Check all the visible bubbles to see if they're in the right place.
for (int i = 0; i < mLayout.getChildCount(); i++) { for (int i = 0; i < mLayout.getChildCount(); i++) {
float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i); PointF expectedPosition = mPositioner.getExpandedBubbleXY(i,
assertEquals(expectedPosition, mLayout.getChildCount(),
onLeft);
assertEquals(expectedPosition.x,
mLayout.getChildAt(i).getTranslationX(), mLayout.getChildAt(i).getTranslationX(),
2f); 2f);
assertEquals(expectedPosition, assertEquals(expectedPosition.y,
mLayout.getChildAt(i).getTranslationY(), 2f); mLayout.getChildAt(i).getTranslationY(), 2f);
} }
} }