Add shadows back to bubble avatar

* Add shadows back -- icon lib is no longer adding shadows but
  bubbles should still have them for background protection.
* The shadow is active on all bubbles when:
  - the stack is being dragged around & bubbles trail behind it
  - the stack is expanded (the bubbles can be dragged to dismiss &
    can show on top of the expanded view or overflow)
* When the stack is resting, only the top two bubbles show a shadow
  & the bubbles below that animate their Z translation so the change
  is smooth.
* Also put the # of bubbles we show in the stack in a static int &
  updated usages

Test: visual - add 5 of the 'test bubbles' from the test app
               and notice the white backgrounds don't blend together
               due to the shadow & that they are distinct when
               placed on a white background
             - drag / fling the stack around & notice shadows show
               for all the bubbles while moving but once they come
               to rest you don't see a stack of shadows
Bug: 183657577
Change-Id: I0c273f4d0261db9eac1b49ef584fd62fbb67cc9a
This commit is contained in:
Mady Mellor
2021-05-26 15:05:44 -07:00
parent bb53cce0db
commit 5f00d56451
6 changed files with 106 additions and 25 deletions

View File

@@ -23,15 +23,19 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.PathParser;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.animation.Interpolators;
import java.util.EnumSet;
@@ -104,6 +108,19 @@ public class BadgedImageView extends ImageView {
setFocusable(true);
setClickable(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
BadgedImageView.this.getOutline(outline);
}
});
}
private void getOutline(Outline outline) {
final int bubbleSize = mPositioner.getBubbleSize();
final int normalizedSize = IconNormalizer.getNormalizedCircleSize(bubbleSize);
final int inset = (bubbleSize - normalizedSize) / 2;
outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize);
}
public void initialize(BubblePositioner positioner) {

View File

@@ -56,11 +56,8 @@ public class BubblePositioner {
public static final int TASKBAR_POSITION_LEFT = 1;
public static final int TASKBAR_POSITION_BOTTOM = 2;
/**
* The bitmap in the bubble is slightly smaller than the overall size of the bubble.
* This is the percentage to scale the image down based on the overall bubble size.
*/
private static final float BUBBLE_BITMAP_SIZE_PERCENT = 0.86f;
/** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/
public static final int NUM_VISIBLE_WHEN_RESTING = 2;
private Context mContext;
private WindowManager mWindowManager;

View File

@@ -22,6 +22,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -632,6 +633,7 @@ public class BubbleStackView extends FrameLayout
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
if (!passEventToMagnetizedObject(ev)) {
updateBubbleShadows(true /* showForAllBubbles */);
if (mBubbleData.isExpanded() || mPositioner.showingInTaskbar()) {
mExpandedAnimationController.dragBubbleOut(
v, viewInitialX + dx, viewInitialY + dy);
@@ -671,7 +673,7 @@ public class BubbleStackView extends FrameLayout
mStackAnimationController.flingStackThenSpringToEdge(
viewInitialX + dx, velX, velY) <= 0;
final boolean updateForCollapsedStack = oldOnLeft != mStackOnLeftOrWillBe;
updateBadgesAndZOrder(updateForCollapsedStack);
updateBadges(updateForCollapsedStack);
logBubbleEvent(null /* no bubble associated with bubble stack move */,
FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
@@ -783,9 +785,9 @@ public class BubbleStackView extends FrameLayout
mBubbleController.onAllBubblesAnimatedOut();
}
};
mStackAnimationController = new StackAnimationController(
floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, mPositioner);
floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut,
this::animateShadows /* onStackAnimationFinished */, mPositioner);
mExpandedAnimationController = new ExpandedAnimationController(
mPositioner, mExpandedViewPadding, onBubbleAnimatedOut);
@@ -1505,13 +1507,12 @@ public class BubbleStackView extends FrameLayout
// Set the dot position to the opposite of the side the stack is resting on, since the stack
// resting slightly off-screen would result in the dot also being off-screen.
bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
bubble.getIconView().setOnClickListener(mBubbleClickListener);
bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
mPositioner.getBubbleSize()));
updateBubbleShadows(false /* showForAllBubbles */);
animateInFlyoutForBubble(bubble);
requestUpdate();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
@@ -1566,7 +1567,8 @@ public class BubbleStackView extends FrameLayout
};
if (mIsExpanded || isExpansionAnimating()) {
reorder.run();
updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
updateBadges(false /* setBadgeForCollapsedStack */);
updateZOrder();
} else if (!isExpansionAnimating()) {
List<View> bubbleViews = bubbles.stream()
.map(b -> b.getIconView()).collect(Collectors.toList());
@@ -1803,7 +1805,8 @@ public class BubbleStackView extends FrameLayout
}
beforeExpandedViewAnimation();
updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
updateZOrder();
updateBadges(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
updateOverflowVisibility();
updatePointerPosition();
@@ -2014,7 +2017,8 @@ public class BubbleStackView extends FrameLayout
mExpandedBubble));
}
updateOverflowVisibility();
updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
updateZOrder();
updateBadges(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
if (previouslySelected != null) {
previouslySelected.setTaskViewVisibility(false);
@@ -2714,14 +2718,56 @@ public class BubbleStackView extends FrameLayout
}
/**
* Sets the appropriate Z-order, badge, and dot position for each bubble in the stack.
* Animate dot and badge changes.
* Updates whether each of the bubbles should show shadows. When collapsed & resting, only the
* visible bubbles (top 2) will show a shadow. When the stack is being dragged, everything
* shows a shadow. When an individual bubble is dragged out, it should show a shadow.
*/
private void updateBadgesAndZOrder(boolean setBadgeForCollapsedStack) {
private void updateBubbleShadows(boolean showForAllBubbles) {
int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
final float z = (mMaxBubbles * mBubbleElevation) - i;
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
boolean isDraggedOut = mMagnetizedObject != null
&& mMagnetizedObject.getUnderlyingObject().equals(bv);
if (showForAllBubbles || isDraggedOut) {
bv.setZ(z);
} else {
final float tz = i < NUM_VISIBLE_WHEN_RESTING ? z : 0f;
bv.setZ(tz);
}
}
}
/**
* When the bubbles are flung and then rest, the shadows stack up for the bubbles hidden
* beneath the top two bubbles, to avoid this we animate the Z translations once the stack
* is resting so that they fade away nicely.
*/
private void animateShadows() {
int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
boolean fullShadow = i < NUM_VISIBLE_WHEN_RESTING;
if (!fullShadow) {
bv.animate().translationZ(0).start();
}
}
}
private void updateZOrder() {
int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
bv.setZ(i < NUM_VISIBLE_WHEN_RESTING
? (mMaxBubbles * mBubbleElevation) - i
: 0f);
}
}
private void updateBadges(boolean setBadgeForCollapsedStack) {
int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
bv.setZ((mMaxBubbles * mBubbleElevation) - i);
if (mIsExpanded) {
// If we're not displaying vertically, we always show the badge on the left.
boolean onLeft = mPositioner.showBubblesVertically() && !mStackOnLeftOrWillBe;

View File

@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles.animation;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.PointF;
@@ -289,7 +291,8 @@ public class ExpandedAnimationController
path.lineTo(stackedX, expandedY);
// Then, draw a line down to the stack position.
path.lineTo(stackedX, mCollapsePoint.y + Math.min(index, 1) * mStackOffsetPx);
path.lineTo(stackedX, mCollapsePoint.y
+ Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx);
}
// The lead bubble should be the bubble with the longest distance to travel when we're

View File

@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles.animation;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import android.content.ContentResolver;
import android.content.res.Resources;
import android.graphics.PointF;
@@ -255,14 +257,21 @@ public class StackAnimationController extends
*/
private Runnable mOnBubbleAnimatedOutAction;
/**
* Callback to run whenever the stack is finished being flung somewhere.
*/
private Runnable mOnStackAnimationFinished;
public StackAnimationController(
FloatingContentCoordinator floatingContentCoordinator,
IntSupplier bubbleCountSupplier,
Runnable onBubbleAnimatedOutAction,
Runnable onStackAnimationFinished,
BubblePositioner positioner) {
mFloatingContentCoordinator = floatingContentCoordinator;
mBubbleCountSupplier = bubbleCountSupplier;
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
mOnStackAnimationFinished = onStackAnimationFinished;
mPositioner = positioner;
}
@@ -672,6 +681,10 @@ public class StackAnimationController extends
mPositioner.setRestingPosition(mStackPosition);
}
if (mOnStackAnimationFinished != null) {
mOnStackAnimationFinished.run();
}
if (after != null) {
for (Runnable callback : after) {
callback.run();
@@ -716,7 +729,7 @@ public class StackAnimationController extends
} else {
// We only show the first two bubbles in the stack & the rest hide behind them
// so they don't need an offset.
return index > 1 ? 0f : mStackOffset;
return index > (NUM_VISIBLE_WHEN_RESTING - 1) ? 0f : mStackOffset;
}
} else {
return 0f;
@@ -825,7 +838,8 @@ public class StackAnimationController extends
private void moveToFinalIndex(View view, int newIndex,
Runnable finishReorder) {
final ViewPropertyAnimator animator = view.animate()
.translationY(getStackPosition().y + Math.min(newIndex, 1) * mStackOffset)
.translationY(getStackPosition().y
+ Math.min(newIndex, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffset)
.setDuration(BUBBLE_SWAP_DURATION)
.withEndAction(() -> {
view.setTag(R.id.reorder_animator_tag, null);
@@ -834,8 +848,9 @@ public class StackAnimationController extends
view.setTag(R.id.reorder_animator_tag, animator);
}
// TODO: do we need this & BubbleStackView#updateBadgesAndZOrder?
private void updateBadgesAndZOrder(View v, int index) {
v.setZ((mMaxBubbles * mElevation) - index);
v.setZ(index < NUM_VISIBLE_WHEN_RESTING ? (mMaxBubbles * mElevation) - index : 0f);
BadgedImageView bv = (BadgedImageView) v;
if (index == 0) {
bv.showDotAndBadge(!isStackOnLeftSide());
@@ -939,8 +954,9 @@ public class StackAnimationController extends
final float yOffset = getOffsetForChainedPropertyAnimation(
DynamicAnimation.TRANSLATION_Y, 0);
for (int i = 0; i < mLayout.getChildCount(); i++) {
mLayout.getChildAt(i).setTranslationX(pos.x + (Math.min(i, 1) * xOffset));
mLayout.getChildAt(i).setTranslationY(pos.y + (Math.min(i, 1) * yOffset));
float index = Math.min(i, NUM_VISIBLE_WHEN_RESTING - 1);
mLayout.getChildAt(i).setTranslationX(pos.x + (index * xOffset));
mLayout.getChildAt(i).setTranslationY(pos.y + (index * yOffset));
}
}
}

View File

@@ -69,7 +69,7 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
public int getAsInt() {
return mLayout.getChildCount();
}
}, mock(Runnable.class)));
}, mock(Runnable.class), mock(Runnable.class)));
mLayout.setActiveController(mStackController);
addOneMoreThanBubbleLimitBubbles();
mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -307,10 +307,12 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
TestableStackController(
FloatingContentCoordinator floatingContentCoordinator,
IntSupplier bubbleCountSupplier,
Runnable onBubbleAnimatedOutAction) {
Runnable onBubbleAnimatedOutAction,
Runnable onStackAnimationFinished) {
super(floatingContentCoordinator,
bubbleCountSupplier,
onBubbleAnimatedOutAction,
onStackAnimationFinished,
new TestableBubblePositioner(mContext, mock(WindowManager.class)));
}