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

Commit 5f00d564 authored by Mady Mellor's avatar Mady Mellor
Browse files

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
parent bb53cce0
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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) {
+2 −5
Original line number Diff line number Diff line
@@ -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;
+58 −12
Original line number Diff line number Diff line
@@ -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 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 updateBadgesAndZOrder(boolean setBadgeForCollapsedStack) {
    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;
+4 −1
Original line number Diff line number Diff line
@@ -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
+21 −5
Original line number Diff line number Diff line
@@ -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));
            }
        }
    }
Loading