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

Commit 458a6263 authored by Mady Mellor's avatar Mady Mellor
Browse files

Fix some things with overflow, persistence, & visibility

1) Bubbles that are persisted no longer have an entry in
   some cases this wasn't being handled correctly (i.e.
   persisted bubbles would never autoexpand)
2) Removed the custom work to promote something from the
   overflow -- in most cases we should be able to update
   flagBubble & rely on the subsequent notification update
   which minimizes the # of code paths
3) mPendingBubbles makes more sense as a HashMap
4) The visibility fix: If the bubble switched from a
   shortcut to a pending intent, we would skip making the
   view visible

Fixes: 158480978
Fixes: 157755108
Test: atest BubbleControllerTest
Test: manual - create a shortcut bubble
             - reboot the device
             - promote that bubble from the overflow
             => ensure that it auto-expands
             - collapse the stack
             - receive an update for that bubble but
               now it's a pendingIntent
             - open the stack
             => ensure that the view is visible
Change-Id: I37dc780e9a66b9e2f2ae46b5386dcd291dc0e0ab
parent 94bedc9a
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -303,10 +303,10 @@ class Bubble implements BubbleViewProvider {
        mDotPath = info.dotPath;

        if (mExpandedView != null) {
            mExpandedView.update(/* bubble */ this);
            mExpandedView.update(this /* bubble */);
        }
        if (mIconView != null) {
            mIconView.setRenderedBubble(/* bubble */ this);
            mIconView.setRenderedBubble(this /* bubble */);
        }
    }

@@ -548,13 +548,13 @@ class Bubble implements BubbleViewProvider {
    }

    private boolean shouldSuppressNotification() {
        if (mEntry == null) return false;
        if (mEntry == null) return true;
        return mEntry.getBubbleMetadata() != null
                && mEntry.getBubbleMetadata().isNotificationSuppressed();
    }

    boolean shouldAutoExpand() {
        if (mEntry == null) return false;
        if (mEntry == null) return mShouldAutoExpand;
        Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata();
        return (metadata != null && metadata.getAutoExpandBubble()) ||  mShouldAutoExpand;
    }
+35 −20
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    private final ShadeController mShadeController;
    private final FloatingContentCoordinator mFloatingContentCoordinator;
    private final BubbleDataRepository mDataRepository;
    private BubbleLogger mLogger = new BubbleLoggerImpl();

    private BubbleData mBubbleData;
    private ScrimView mBubbleScrim;
@@ -317,6 +318,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            BubbleDataRepository dataRepository,
            SysUiState sysUiState,
            INotificationManager notificationManager,
            @Nullable IStatusBarService statusBarService,
            WindowManager windowManager) {
        dumpManager.registerDumpable(TAG, this);
        mContext = context;
@@ -387,8 +389,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        mSurfaceSynchronizer = synchronizer;

        mWindowManager = windowManager;
        mBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        mBarService = statusBarService == null
                ? IStatusBarService.Stub.asInterface(
                        ServiceManager.getService(Context.STATUS_BAR_SERVICE))
                : statusBarService;

        mBubbleScrim = new ScrimView(mContext);

@@ -894,9 +898,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    }

    void promoteBubbleFromOverflow(Bubble bubble) {
        mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
        bubble.setInflateSynchronously(mInflateSynchronously);
        setIsBubble(bubble, /* isBubble */ true);
        mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
        bubble.setShouldAutoExpand(true);
        bubble.markUpdatedAt(System.currentTimeMillis());
        setIsBubble(bubble, true /* isBubble */);
    }

    /**
@@ -910,20 +916,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        Bubble bubble = mBubbleData.getBubbleInStackWithKey(key);
        if (bubble != null) {
            mBubbleData.setSelectedBubble(bubble);
            mBubbleData.setExpanded(true);
        } else {
            bubble = mBubbleData.getOverflowBubbleWithKey(key);
            if (bubble != null) {
                bubble.setShouldAutoExpand(true);
                promoteBubbleFromOverflow(bubble);
            } else if (entry.canBubble()) {
                // It can bubble but it's not -- it got aged out of the overflow before it
                // was dismissed or opened, make it a bubble again.
                setIsBubble(entry, true);
                updateBubble(entry, true /* suppressFlyout */, false /* showInShade */);
                setIsBubble(entry, true /* isBubble */, true /* autoExpand */);
            }
        }

        mBubbleData.setExpanded(true);
    }

    /**
@@ -967,13 +970,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    }

    void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) {
        // Lazy init stack view when a bubble is created
        ensureStackViewCreated();
        // If this is an interruptive notif, mark that it's interrupted
        if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
            notif.setInterruption();
        }
        Bubble bubble = mBubbleData.getOrCreateBubble(notif);
        Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
        inflateAndAdd(bubble, suppressFlyout, showInShade);
    }

    void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
        // Lazy init stack view when a bubble is created
        ensureStackViewCreated();
        bubble.setInflateSynchronously(mInflateSynchronously);
        bubble.inflate(
                b -> {
@@ -1119,7 +1126,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        }
    }

    private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble) {
    private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble,
            final boolean autoExpand) {
        Objects.requireNonNull(entry);
        if (isBubble) {
            entry.getSbn().getNotification().flags |= FLAG_BUBBLE;
@@ -1127,7 +1135,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            entry.getSbn().getNotification().flags &= ~FLAG_BUBBLE;
        }
        try {
            mBarService.onNotificationBubbleChanged(entry.getKey(), isBubble, 0);
            int flags = 0;
            if (autoExpand) {
                flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
                flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
            }
            mBarService.onNotificationBubbleChanged(entry.getKey(), isBubble, flags);
        } catch (RemoteException e) {
            // Bad things have happened
        }
@@ -1141,13 +1154,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            b.disable(FLAG_BUBBLE);
        }
        if (b.getEntry() != null) {
            setIsBubble(b.getEntry(), isBubble);
            // Updating the entry to be a bubble will trigger our normal update flow
            setIsBubble(b.getEntry(), isBubble, b.shouldAutoExpand());
        } else {
            try {
                mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0);
            } catch (RemoteException e) {
                // Bad things have happened
            }
            // If we have no entry to update, it's a persisted bubble so
            // we need to add it to the stack ourselves
            Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
            inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
                    !bubble.shouldAutoExpand() /* showInShade */);

        }
    }

+34 −52
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ public class BubbleData {
    /** Bubbles that aged out to overflow. */
    private final List<Bubble> mOverflowBubbles;
    /** Bubbles that are being loaded but haven't been added to the stack just yet. */
    private final List<Bubble> mPendingBubbles;
    private final HashMap<String, Bubble> mPendingBubbles;
    private Bubble mSelectedBubble;
    private boolean mShowingOverflow;
    private boolean mExpanded;
@@ -151,7 +151,7 @@ public class BubbleData {
        mContext = context;
        mBubbles = new ArrayList<>();
        mOverflowBubbles = new ArrayList<>();
        mPendingBubbles = new ArrayList<>();
        mPendingBubbles = new HashMap<>();
        mStateChange = new Update(mBubbles, mOverflowBubbles);
        mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
        mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
@@ -203,62 +203,46 @@ public class BubbleData {
        dispatchPendingChanges();
    }

    public void promoteBubbleFromOverflow(Bubble bubble, BubbleStackView stack,
            BubbleIconFactory factory) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "promoteBubbleFromOverflow: " + bubble);
        }
        mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
        moveOverflowBubbleToPending(bubble);
        // Preserve new order for next repack, which sorts by last updated time.
        bubble.inflate(
                b -> {
                    b.setShouldAutoExpand(true);
                    b.markUpdatedAt(mTimeSource.currentTimeMillis());
                    notificationEntryUpdated(bubble, false /* suppressFlyout */,
                            true /* showInShade */);
                },
                mContext, stack, factory, false /* skipInflation */);
    }

    void setShowingOverflow(boolean showingOverflow) {
        mShowingOverflow = showingOverflow;
    }

    private void moveOverflowBubbleToPending(Bubble b) {
        mOverflowBubbles.remove(b);
        mPendingBubbles.add(b);
    }

    /**
     * Constructs a new bubble or returns an existing one. Does not add new bubbles to
     * bubble data, must go through {@link #notificationEntryUpdated(Bubble, boolean, boolean)}
     * for that.
     *
     * @param entry The notification entry to use, only null if it's a bubble being promoted from
     *              the overflow that was persisted over reboot.
     * @param persistedBubble The bubble to use, only non-null if it's a bubble being promoted from
     *              the overflow that was persisted over reboot.
     */
    Bubble getOrCreateBubble(NotificationEntry entry) {
        String key = entry.getKey();
        Bubble bubble = getBubbleInStackWithKey(entry.getKey());
        if (bubble != null) {
            bubble.setEntry(entry);
    Bubble getOrCreateBubble(NotificationEntry entry, Bubble persistedBubble) {
        String key = entry != null ? entry.getKey() : persistedBubble.getKey();
        Bubble bubbleToReturn = getBubbleInStackWithKey(key);

        if (bubbleToReturn == null) {
            bubbleToReturn = getOverflowBubbleWithKey(key);
            if (bubbleToReturn != null) {
                // Promoting from overflow
                mOverflowBubbles.remove(bubbleToReturn);
            } else if (mPendingBubbles.containsKey(key)) {
                // Update while it was pending
                bubbleToReturn = mPendingBubbles.get(key);
            } else if (entry != null) {
                // New bubble
                bubbleToReturn = new Bubble(entry, mSuppressionListener);
            } else {
            bubble = getOverflowBubbleWithKey(key);
            if (bubble != null) {
                moveOverflowBubbleToPending(bubble);
                bubble.setEntry(entry);
                return bubble;
            }
            // Check for it in pending
            for (int i = 0; i < mPendingBubbles.size(); i++) {
                Bubble b = mPendingBubbles.get(i);
                if (b.getKey().equals(entry.getKey())) {
                    b.setEntry(entry);
                    return b;
                // Persisted bubble being promoted
                bubbleToReturn = persistedBubble;
            }
        }
            bubble = new Bubble(entry, mSuppressionListener);
            mPendingBubbles.add(bubble);

        if (entry != null) {
            bubbleToReturn.setEntry(entry);
        }
        return bubble;
        mPendingBubbles.put(key, bubbleToReturn);
        return bubbleToReturn;
    }

    /**
@@ -270,7 +254,7 @@ public class BubbleData {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "notificationEntryUpdated: " + bubble);
        }
        mPendingBubbles.remove(bubble); // No longer pending once we're here
        mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here
        Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
        suppressFlyout |= bubble.getEntry() == null
                || !bubble.getEntry().getRanking().visuallyInterruptive();
@@ -408,10 +392,8 @@ public class BubbleData {
            Log.d(TAG, "doRemove: " + key);
        }
        //  If it was pending remove it
        for (int i = 0; i < mPendingBubbles.size(); i++) {
            if (mPendingBubbles.get(i).getKey().equals(key)) {
                mPendingBubbles.remove(mPendingBubbles.get(i));
            }
        if (mPendingBubbles.containsKey(key)) {
            mPendingBubbles.remove(key);
        }
        int indexToRemove = indexForKey(key);
        if (indexToRemove == -1) {
+7 −1
Original line number Diff line number Diff line
@@ -490,7 +490,7 @@ public class BubbleExpandedView extends LinearLayout {
        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
            Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
        }
        boolean isNew = mBubble == null;
        boolean isNew = mBubble == null || didBackingContentChange(bubble);
        if (isNew || bubble != null && bubble.getKey().equals(mBubble.getKey())) {
            mBubble = bubble;
            mSettingsIcon.setContentDescription(getResources().getString(
@@ -523,6 +523,12 @@ public class BubbleExpandedView extends LinearLayout {
        }
    }

    private boolean didBackingContentChange(Bubble newBubble) {
        boolean prevWasIntentBased = mBubble != null && mPendingIntent != null;
        boolean newIsIntentBased = newBubble.getBubbleIntent() != null;
        return prevWasIntentBased != newIsIntentBased;
    }

    /**
     * Lets activity view know it should be shown / populated with activity content.
     */
+3 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.INotificationManager;
import android.content.Context;
import android.view.WindowManager;

import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubbleData;
import com.android.systemui.bubbles.BubbleDataRepository;
@@ -70,6 +71,7 @@ public interface BubbleModule {
            BubbleDataRepository bubbleDataRepository,
            SysUiState sysUiState,
            INotificationManager notifManager,
            IStatusBarService statusBarService,
            WindowManager windowManager) {
        return new BubbleController(
                context,
@@ -91,6 +93,7 @@ public interface BubbleModule {
                bubbleDataRepository,
                sysUiState,
                notifManager,
                statusBarService,
                windowManager);
    }
}
Loading