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

Commit c9871c4d authored by Mady Mellor's avatar Mady Mellor
Browse files

SysUI changes for setSuppressBubble

1) We need a way to listen for locusId, this is done via
   a new listener on ShellTaskOrganizer that is informed
   for all task info changes.
2) When locusId for focused activity changes, check for
   a bubble in data that has a matching locusId & has
   API set
3) Add suppress/unsuppress in the state change flow and
   update the views

TODO in later CL:
- animate the view being hidden / shown; handle flyout

Test: atest ShellTaskOrganizerTests BubbleDataTest BubbleXmlHelperTest (also CTS CL)
Bug: 170267239
Change-Id: I5c9bd15fa8b5b326e146efdc40916454ca1c73f6
parent 2dded21b
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.app.PendingIntent;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
@@ -62,6 +63,9 @@ public class Bubble implements BubbleViewProvider {
    private final String mKey;
    @Nullable
    private final String mGroupKey;
    @Nullable
    private final LocusId mLocusId;

    private final Executor mMainExecutor;

    private long mLastUpdated;
@@ -163,13 +167,14 @@ public class Bubble implements BubbleViewProvider {
     */
    Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
            final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
            int taskId, Executor mainExecutor) {
            int taskId, @Nullable final String locus, Executor mainExecutor) {
        Objects.requireNonNull(key);
        Objects.requireNonNull(shortcutInfo);
        mMetadataShortcutId = shortcutInfo.getId();
        mShortcutInfo = shortcutInfo;
        mKey = key;
        mGroupKey = null;
        mLocusId = locus != null ? new LocusId(locus) : null;
        mFlags = 0;
        mUser = shortcutInfo.getUserHandle();
        mPackageName = shortcutInfo.getPackage();
@@ -189,6 +194,7 @@ public class Bubble implements BubbleViewProvider {
            Executor mainExecutor) {
        mKey = entry.getKey();
        mGroupKey = entry.getGroupKey();
        mLocusId = entry.getLocusId();
        mSuppressionListener = listener;
        mIntentCancelListener = intent -> {
            if (mIntent != null) {
@@ -216,6 +222,10 @@ public class Bubble implements BubbleViewProvider {
        return mGroupKey;
    }

    public LocusId getLocusId() {
        return mLocusId;
    }

    public UserHandle getUser() {
        return mUser;
    }
@@ -556,6 +566,14 @@ public class Bubble implements BubbleViewProvider {
        return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_BUBBLE) != 0;
    }

    /**
     * Whether this bubble is able to be suppressed (i.e. has the developer opted into the API to
     * hide the bubble when in the same content).
     */
    boolean isSuppressable() {
        return (mFlags & Notification.BubbleMetadata.FLAG_SHOULD_SUPPRESS_BUBBLE) != 0;
    }

    /**
     * Whether this notification conversation is important.
     */
@@ -580,6 +598,26 @@ public class Bubble implements BubbleViewProvider {
        }
    }

    /**
     * Sets whether this bubble should be suppressed from the stack.
     */
    public void setSuppressBubble(boolean suppressBubble) {
        if (!isSuppressable()) {
            Log.e(TAG, "calling setSuppressBubble on "
                    + getKey() + " when bubble not suppressable");
            return;
        }
        boolean prevSuppressed = isSuppressed();
        if (suppressBubble) {
            mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_BUBBLE;
        } else {
            mFlags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_BUBBLE;
        }
        if (prevSuppressed != suppressBubble && mSuppressionListener != null) {
            mSuppressionListener.onBubbleNotificationSuppressionChange(this);
        }
    }

    /**
     * Sets whether the bubble for this notification should show a dot indicating updated content.
     */
+10 −0
Original line number Diff line number Diff line
@@ -279,6 +279,8 @@ public class BubbleController {

        mBubbleIconFactory = new BubbleIconFactory(context);
        mTaskOrganizer = organizer;
        mTaskOrganizer.addLocusIdListener((taskId, locus, visible) ->
                mBubbleData.onLocusVisibilityChanged(taskId, locus, visible));

        launcherApps.registerCallback(new LauncherApps.Callback() {
            @Override
@@ -1037,6 +1039,14 @@ 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) {
+76 −3
Original line number Diff line number Diff line
@@ -24,7 +24,10 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ShortcutInfo;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.View;
@@ -75,6 +78,8 @@ public class BubbleData {
        @Nullable Bubble updatedBubble;
        @Nullable Bubble addedOverflowBubble;
        @Nullable Bubble removedOverflowBubble;
        @Nullable Bubble suppressedBubble;
        @Nullable Bubble unsuppressedBubble;
        // Pair with Bubble and @DismissReason Integer
        final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>();

@@ -95,7 +100,9 @@ public class BubbleData {
                    || !removedBubbles.isEmpty()
                    || addedOverflowBubble != null
                    || removedOverflowBubble != null
                    || orderChanged;
                    || orderChanged
                    || suppressedBubble != null
                    || unsuppressedBubble != null;
        }

        void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
@@ -125,6 +132,11 @@ public class BubbleData {
    private final List<Bubble> mOverflowBubbles;
    /** Bubbles that are being loaded but haven't been added to the stack just yet. */
    private final HashMap<String, Bubble> mPendingBubbles;
    /** Bubbles that are suppressed due to locusId. */
    private final ArrayMap<LocusId, Bubble> mSuppressedBubbles = new ArrayMap<>();
    /** Visible locusIds. */
    private final ArraySet<LocusId> mVisibleLocusIds = new ArraySet<>();

    private BubbleViewProvider mSelectedBubble;
    private final BubbleOverflow mOverflow;
    private boolean mShowingOverflow;
@@ -141,7 +153,7 @@ public class BubbleData {
    private Listener mListener;

    @Nullable
    private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
    private Bubbles.SuppressionChangedListener mSuppressionListener;
    private Bubbles.PendingIntentCanceledListener mCancelledListener;

    /**
@@ -173,7 +185,7 @@ public class BubbleData {
    }

    public void setSuppressionChangedListener(
            Bubbles.NotificationSuppressionChangedListener listener) {
            Bubbles.SuppressionChangedListener listener) {
        mSuppressionListener = listener;
    }

@@ -321,6 +333,18 @@ public class BubbleData {
        bubble.setSuppressNotification(suppress);
        bubble.setShowDot(!isBubbleExpandedAndSelected /* show */);

        LocusId locusId = bubble.getLocusId();
        if (locusId != null) {
            boolean isSuppressed = mSuppressedBubbles.containsKey(locusId);
            if (isSuppressed && (!bubble.isSuppressed() || !bubble.isSuppressable())) {
                mSuppressedBubbles.remove(locusId);
                mStateChange.unsuppressedBubble = bubble;
            } else if (!isSuppressed && (bubble.isSuppressed()
                    || bubble.isSuppressable() && mVisibleLocusIds.contains(locusId))) {
                mSuppressedBubbles.put(locusId, bubble);
                mStateChange.suppressedBubble = bubble;
            }
        }
        dispatchPendingChanges();
    }

@@ -581,6 +605,43 @@ public class BubbleData {
        dispatchPendingChanges();
    }

    /**
     * Called in response to the visibility of a locusId changing. A locusId is set on a task
     * and if there's a matching bubble for that locusId then the bubble may be hidden or shown
     * depending on the visibility of the locusId.
     *
     * @param taskId the taskId associated with the locusId visibility change.
     * @param locusId the locusId whose visibility has changed.
     * @param visible whether the task with the locusId is visible or not.
     */
    public void onLocusVisibilityChanged(int taskId, LocusId locusId, boolean 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)) {
            mVisibleLocusIds.add(locusId);
        } else {
            mVisibleLocusIds.remove(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;
            dispatchPendingChanges();
        } else if (!visible) {
            Bubble unsuppressedBubble = mSuppressedBubbles.remove(locusId);
            if (unsuppressedBubble != null) {
                unsuppressedBubble.setSuppressBubble(false);
                mStateChange.unsuppressedBubble = unsuppressedBubble;
            }
            dispatchPendingChanges();
        }
    }

    private void dispatchPendingChanges() {
        if (mListener != null && mStateChange.anythingChanged()) {
            mListener.applyUpdate(mStateChange);
@@ -791,6 +852,18 @@ public class BubbleData {
        return null;
    }

    @Nullable
    private Bubble getBubbleInStackWithLocusId(LocusId locusId) {
        if (locusId == null) return null;
        for (int i = 0; i < mBubbles.size(); i++) {
            Bubble bubble = mBubbles.get(i);
            if (locusId.equals(bubble.getLocusId())) {
                return bubble;
            }
        }
        return null;
    }

    @Nullable
    Bubble getBubbleWithView(View view) {
        for (int i = 0; i < mBubbles.size(); i++) {
+3 −1
Original line number Diff line number Diff line
@@ -81,7 +81,8 @@ internal class BubbleDataRepository(
                    b.rawDesiredHeight,
                    b.rawDesiredHeightResId,
                    b.title,
                    b.taskId
                    b.taskId,
                    b.locusId?.id
            )
        }
    }
@@ -172,6 +173,7 @@ internal class BubbleDataRepository(
                            entity.desiredHeightResId,
                            entity.title,
                            entity.taskId,
                            entity.locus,
                            mainExecutor
                    ) }
        }
+6 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.app.Notification.FLAG_BUBBLE;
import android.app.Notification;
import android.app.Notification.BubbleMetadata;
import android.app.NotificationManager.Policy;
import android.content.LocusId;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;

@@ -75,6 +76,11 @@ public class BubbleEntry {
        return mSbn.getGroupKey();
    }

    /** @return the {@link LocusId} for this notification, if it exists. */
    public LocusId getLocusId() {
        return mSbn.getNotification().getLocusId();
    }

    /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */
    @Nullable
    public BubbleMetadata getBubbleMetadata() {
Loading