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

Commit 5f32b96c authored by Ats Jenk's avatar Ats Jenk Committed by Android (Google) Code Review
Browse files

Merge "Hide suppressed bubble"

parents 4a300e64 bb4306e6
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -367,4 +367,9 @@ public class BadgedImageView extends ConstraintLayout {
    void hideBadge() {
        mAppIcon.setVisibility(GONE);
    }

    @Override
    public String toString() {
        return "BadgedImageView{" + mBubble + "}";
    }
}
+36 −17
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
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.TASKBAR_POSITION_BOTTOM;
@@ -997,6 +998,12 @@ public class BubbleController {
            // Update the bubble but don't promote it out of overflow
            Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
            b.setEntry(notif);
        } else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) {
            // Update the bubble but don't promote it out of overflow
            Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey());
            if (b != null) {
                b.setEntry(notif);
            }
        } else {
            Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
            inflateAndAdd(bubble, suppressFlyout, showInShade);
@@ -1170,6 +1177,18 @@ public class BubbleController {

        @Override
        public void applyUpdate(BubbleData.Update update) {
            if (DEBUG_BUBBLE_CONTROLLER) {
                Log.d(TAG, "applyUpdate:" + " bubbleAdded=" + (update.addedBubble != null)
                        + " bubbleRemoved="
                        + (update.removedBubbles != null && update.removedBubbles.size() > 0)
                        + " bubbleUpdated=" + (update.updatedBubble != null)
                        + " orderChanged=" + update.orderChanged
                        + " expandedChanged=" + update.expandedChanged
                        + " selectionChanged=" + update.selectionChanged
                        + " suppressed=" + (update.suppressedBubble != null)
                        + " unsuppressed=" + (update.unsuppressedBubble != null));
            }

            ensureStackViewCreated();

            // Lazy load overflow bubbles from disk
@@ -1249,6 +1268,14 @@ public class BubbleController {
                mStackView.updateBubble(update.updatedBubble);
            }

            if (update.suppressedBubble != null && mStackView != null) {
                mStackView.setBubbleSuppressed(update.suppressedBubble, true);
            }

            if (update.unsuppressedBubble != null && mStackView != null) {
                mStackView.setBubbleSuppressed(update.unsuppressedBubble, false);
            }

            // At this point, the correct bubbles are inflated in the stack.
            // Make sure the order in bubble data is reflected in bubble row.
            if (update.orderChanged && mStackView != null) {
@@ -1263,14 +1290,6 @@ public class BubbleController {
                }
            }

            if (update.suppressedBubble != null && mStackView != null) {
                mStackView.setBubbleVisibility(update.suppressedBubble, false);
            }

            if (update.unsuppressedBubble != null && mStackView != null) {
                mStackView.setBubbleVisibility(update.unsuppressedBubble, true);
            }

            // Expanding? Apply this last.
            if (update.expandedChanged && update.expanded) {
                if (mStackView != null) {
+141 −28
Original line number Diff line number Diff line
@@ -224,7 +224,8 @@ public class BubbleData {
    }

    public boolean hasAnyBubbleWithKey(String key) {
        return hasBubbleInStackWithKey(key) || hasOverflowBubbleWithKey(key);
        return hasBubbleInStackWithKey(key) || hasOverflowBubbleWithKey(key)
                || hasSuppressedBubbleWithKey(key);
    }

    public boolean hasBubbleInStackWithKey(String key) {
@@ -235,6 +236,20 @@ public class BubbleData {
        return getOverflowBubbleWithKey(key) != null;
    }

    /**
     * Check if there are any bubbles suppressed with the given notification <code>key</code>
     */
    public boolean hasSuppressedBubbleWithKey(String key) {
        return mSuppressedBubbles.values().stream().anyMatch(b -> b.getKey().equals(key));
    }

    /**
     * Check if there are any bubbles suppressed with the given <code>LocusId</code>
     */
    public boolean isSuppressedWithLocusId(LocusId locusId) {
        return mSuppressedBubbles.get(locusId) != null;
    }

    @Nullable
    public BubbleViewProvider getSelectedBubble() {
        return mSelectedBubble;
@@ -356,11 +371,11 @@ public class BubbleData {
            boolean isSuppressed = mSuppressedBubbles.containsKey(locusId);
            if (isSuppressed && (!bubble.isSuppressed() || !bubble.isSuppressable())) {
                mSuppressedBubbles.remove(locusId);
                mStateChange.unsuppressedBubble = bubble;
                doUnsuppress(bubble);
            } else if (!isSuppressed && (bubble.isSuppressed()
                    || bubble.isSuppressable() && mVisibleLocusIds.contains(locusId))) {
                mSuppressedBubbles.put(locusId, bubble);
                mStateChange.suppressedBubble = bubble;
                doSuppress(bubble);
            }
        }
        dispatchPendingChanges();
@@ -532,16 +547,19 @@ public class BubbleData {
        if (mPendingBubbles.containsKey(key)) {
            mPendingBubbles.remove(key);
        }
        int indexToRemove = indexForKey(key);
        if (indexToRemove == -1) {
            if (hasOverflowBubbleWithKey(key)
                    && (reason == Bubbles.DISMISS_NOTIF_CANCEL

        boolean shouldRemoveHiddenBubble = reason == Bubbles.DISMISS_NOTIF_CANCEL
                || reason == Bubbles.DISMISS_GROUP_CANCELLED
                || reason == Bubbles.DISMISS_NO_LONGER_BUBBLE
                || reason == Bubbles.DISMISS_BLOCKED
                || reason == Bubbles.DISMISS_SHORTCUT_REMOVED
                || reason == Bubbles.DISMISS_PACKAGE_REMOVED
                        || reason == Bubbles.DISMISS_USER_CHANGED)) {
                || reason == Bubbles.DISMISS_USER_CHANGED;

        int indexToRemove = indexForKey(key);
        if (indexToRemove == -1) {
            if (hasOverflowBubbleWithKey(key)
                    && shouldRemoveHiddenBubble) {

                Bubble b = getOverflowBubbleWithKey(key);
                if (DEBUG_BUBBLE_DATA) {
@@ -555,6 +573,17 @@ public class BubbleData {
                mStateChange.bubbleRemoved(b, reason);
                mStateChange.removedOverflowBubble = b;
            }
            if (hasSuppressedBubbleWithKey(key) && shouldRemoveHiddenBubble) {
                Bubble b = getSuppressedBubbleWithKey(key);
                if (DEBUG_BUBBLE_DATA) {
                    Log.d(TAG, "Cancel suppressed bubble: " + b);
                }
                if (b != null) {
                    mSuppressedBubbles.remove(b.getLocusId());
                    b.stopInflation();
                    mStateChange.bubbleRemoved(b, reason);
                }
            }
            return;
        }
        Bubble bubbleToRemove = mBubbles.get(indexToRemove);
@@ -579,12 +608,66 @@ public class BubbleData {

        // Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
        if (Objects.equals(mSelectedBubble, bubbleToRemove)) {
            setNewSelectedIndex(indexToRemove);
        }
        maybeSendDeleteIntent(reason, bubbleToRemove);
    }

    private void setNewSelectedIndex(int indexOfSelected) {
        if (mBubbles.isEmpty()) {
            Log.w(TAG, "Bubbles list empty when attempting to select index: " + indexOfSelected);
            return;
        }
        // Move selection to the new bubble at the same position.
            int newIndex = Math.min(indexToRemove, mBubbles.size() - 1);
        int newIndex = Math.min(indexOfSelected, mBubbles.size() - 1);
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "setNewSelectedIndex: " + indexOfSelected);
        }
        BubbleViewProvider newSelected = mBubbles.get(newIndex);
        setSelectedBubbleInternal(newSelected);
    }
        maybeSendDeleteIntent(reason, bubbleToRemove);

    private void doSuppress(Bubble bubble) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "doSuppressed: " + bubble);
        }
        mStateChange.suppressedBubble = bubble;
        bubble.setSuppressBubble(true);

        int indexToRemove = mBubbles.indexOf(bubble);
        // Order changes if we are not suppressing the last bubble
        mStateChange.orderChanged = !(mBubbles.size() - 1 == indexToRemove);
        mBubbles.remove(indexToRemove);

        // Update selection if we suppressed the selected bubble
        if (Objects.equals(mSelectedBubble, bubble)) {
            if (mBubbles.isEmpty()) {
                // Don't use setSelectedBubbleInternal because we don't want to trigger an
                // applyUpdate
                mSelectedBubble = null;
            } else {
                // Mark new first bubble as selected
                setNewSelectedIndex(0);
            }
        }
    }

    private void doUnsuppress(Bubble bubble) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "doUnsuppressed: " + bubble);
        }
        bubble.setSuppressBubble(false);
        mStateChange.unsuppressedBubble = bubble;
        mBubbles.add(bubble);
        if (mBubbles.size() > 1) {
            // See where the bubble actually lands
            repackAll();
            mStateChange.orderChanged = true;
        }
        if (mBubbles.get(0) == bubble) {
            // Unsuppressed bubble is sorted to first position. Mark it as the selected.
            setNewSelectedIndex(0);
        }
    }

    void overflowBubble(@DismissReason int reason, Bubble bubble) {
@@ -619,7 +702,7 @@ public class BubbleData {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "dismissAll: reason=" + reason);
        }
        if (mBubbles.isEmpty()) {
        if (mBubbles.isEmpty() && mSuppressedBubbles.isEmpty()) {
            return;
        }
        setExpandedInternal(false);
@@ -627,6 +710,10 @@ public class BubbleData {
        while (!mBubbles.isEmpty()) {
            doRemove(mBubbles.get(0).getKey(), reason);
        }
        while (!mSuppressedBubbles.isEmpty()) {
            Bubble bubble = mSuppressedBubbles.removeAt(0);
            doRemove(bubble.getKey(), reason);
        }
        dispatchPendingChanges();
    }

@@ -640,6 +727,10 @@ public class BubbleData {
     * @param visible whether the task with the locusId is visible or not.
     */
    public void onLocusVisibilityChanged(int taskId, LocusId locusId, boolean visible) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "onLocusVisibilityChanged: " + locusId + " visible=" + visible);
        }

        Bubble matchingBubble = getBubbleInStackWithLocusId(locusId);
        // Don't add the locus if it's from a bubble'd activity, we only suppress for non-bubbled.
        if (visible && (matchingBubble == null || matchingBubble.getTaskId() != taskId)) {
@@ -647,21 +738,23 @@ public class BubbleData {
        } else {
            mVisibleLocusIds.remove(locusId);
        }
        if (matchingBubble == null) {
            // Check if there is a suppressed bubble for this LocusId
            matchingBubble = mSuppressedBubbles.get(locusId);
            if (matchingBubble == null) {
                return;
            }
        }
        boolean isAlreadySuppressed = mSuppressedBubbles.get(locusId) != null;
        if (visible && !isAlreadySuppressed && matchingBubble.isSuppressable()
                && taskId != matchingBubble.getTaskId()) {
            mSuppressedBubbles.put(locusId, matchingBubble);
            matchingBubble.setSuppressBubble(true);
            mStateChange.suppressedBubble = matchingBubble;
            doSuppress(matchingBubble);
            dispatchPendingChanges();
        } else if (!visible) {
            Bubble unsuppressedBubble = mSuppressedBubbles.remove(locusId);
            if (unsuppressedBubble != null) {
                unsuppressedBubble.setSuppressBubble(false);
                mStateChange.unsuppressedBubble = unsuppressedBubble;
                doUnsuppress(unsuppressedBubble);
            }
            dispatchPendingChanges();
        }
@@ -869,6 +962,9 @@ public class BubbleData {
        if (b == null) {
            b = getOverflowBubbleWithKey(key);
        }
        if (b == null) {
            b = getSuppressedBubbleWithKey(key);
        }
        return b;
    }

@@ -946,6 +1042,23 @@ public class BubbleData {
        return null;
    }

    /**
     * Get a suppressed bubble with given notification <code>key</code>
     *
     * @param key notification key
     * @return bubble that matches or null
     */
    @Nullable
    @VisibleForTesting(visibility = PRIVATE)
    public Bubble getSuppressedBubbleWithKey(String key) {
        for (Bubble b : mSuppressedBubbles.values()) {
            if (b.getKey().equals(key)) {
                return b;
            }
        }
        return null;
    }

    @VisibleForTesting(visibility = PRIVATE)
    void setTimeSource(TimeSource timeSource) {
        mTimeSource = timeSource;
+55 −30
Original line number Diff line number Diff line
@@ -170,11 +170,11 @@ public class BubbleStackView extends FrameLayout
            new SurfaceSynchronizer() {
                @Override
                public void syncSurfaceAndRun(Runnable callback) {
            Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
                // Just wait 2 frames. There is no guarantee, but this is usually enough time that
                // the requested change is reflected on the screen.
                // TODO: Once SurfaceFlinger provide APIs to sync the state of {@code View} and
                // surfaces, rewrite this logic with them.
                    Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {
                        // Just wait 2 frames. There is no guarantee, but this is usually enough
                        // time that the requested change is reflected on the screen.
                        // TODO: Once SurfaceFlinger provide APIs to sync the state of
                        //  {@code View} and surfaces, rewrite this logic with them.
                        private int mFrameWait = 2;

                        @Override
@@ -185,7 +185,8 @@ public class BubbleStackView extends FrameLayout
                                callback.run();
                            }
                        }
            });
                    };
                    Choreographer.getInstance().postFrameCallback(frameCallback);
                }
            };
    private final BubbleController mBubbleController;
@@ -1656,8 +1657,13 @@ public class BubbleStackView extends FrameLayout
                return;
            }
        }
        // If a bubble is suppressed, it is not attached to the container. Clean it up.
        if (bubble.isSuppressed()) {
            bubble.cleanupViews();
        } else {
            Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
        }
    }

    private void updateOverflowVisibility() {
        mBubbleOverflow.setVisible((mIsExpanded || mBubbleData.isShowingOverflow())
@@ -1842,11 +1848,30 @@ public class BubbleStackView extends FrameLayout
        }
    }

    void setBubbleVisibility(Bubble b, boolean visible) {
        if (b.getIconView() != null) {
            b.getIconView().setVisibility(visible ? VISIBLE : GONE);
    void setBubbleSuppressed(Bubble bubble, boolean suppressed) {
        if (DEBUG_BUBBLE_STACK_VIEW) {
            Log.d(TAG, "setBubbleSuppressed: suppressed=" + suppressed + " bubble=" + bubble);
        }
        if (suppressed) {
            int index = getBubbleIndex(bubble);
            mBubbleContainer.removeViewAt(index);
            updateExpandedView();
        } else {
            if (bubble.getIconView() == null) {
                return;
            }
            if (bubble.getIconView().getParent() != null) {
                Log.e(TAG, "Bubble is already added to parent. Can't unsuppress: " + bubble);
                return;
            }
            int index = mBubbleData.getBubbles().indexOf(bubble);
            // Add the view back to the correct position
            mBubbleContainer.addView(bubble.getIconView(), index,
                    new LayoutParams(mPositioner.getBubbleSize(),
                            mPositioner.getBubbleSize()));
            updateBubbleShadows(false /* showForAllBubbles */);
            requestUpdate();
        }
        // TODO(b/181166384): Animate in / out & handle adjusting how the bubbles overlap
    }

    /**
+5 −0
Original line number Diff line number Diff line
@@ -364,6 +364,11 @@ public class PhysicsAnimationLayout extends FrameLayout {
        final int oldIndex = indexOfChild(view);

        super.removeView(view);
        if (view.getParent() != null) {
            // View still has a parent. This could have been added as a transient view.
            // Remove it from transient views.
            super.removeTransientView(view);
        }
        addViewInternal(view, index, view.getLayoutParams(), true /* isReorder */);

        if (mController != null) {
Loading