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

Commit 3acc32fb authored by Mady Mellor's avatar Mady Mellor
Browse files

Include bubble changes in ranking & move flagging to BubbleExtractor

Previously, only changes to the "allowBubbles" on the channel or
package would trigger a ranking change. This bit only indicates that
the notification is allowed to bubble -- it doesn't indicate that
the notification *is* a bubble. To allow active notifications to
become bubbles if the user changes the setting, we need to
flag them in response to ranking changes.

This CL moves the bubble flagging code into BubbleExtractor that
way the flag is always updated during ranking changes.

BubbleController listens to ranking changes and adds / removes bubbles
based on the ranking. The ranking needs to have an isBubble bit on
it because ranking changes won't pipe flag updates through. SysUI
uses this bit to flag the entry on SysUI's side.

Moves the shortcut getting / validating code into a helper class.

Also removes the inline reply requirement.

Test: NotificationManagerTest NotificationManagerServiceTest BubbleExtractorTest ShortcutHelperTest BubbleCheckerTest
Bug: 149736441
Change-Id: Ib5b62923c123187ae5f7073ec7ca50d7e20c04b1
parent 0054c04f
Loading
Loading
Loading
Loading
+17 −4
Original line number Original line Diff line number Diff line
@@ -55,7 +55,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Log;
import android.util.Slog;
import android.widget.RemoteViews;
import android.widget.RemoteViews;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
@@ -1594,6 +1593,7 @@ public abstract class NotificationListenerService extends Service {
        private boolean mIsConversation;
        private boolean mIsConversation;
        private ShortcutInfo mShortcutInfo;
        private ShortcutInfo mShortcutInfo;
        private @RankingAdjustment int mRankingAdjustment;
        private @RankingAdjustment int mRankingAdjustment;
        private boolean mIsBubble;


        private static final int PARCEL_VERSION = 2;
        private static final int PARCEL_VERSION = 2;


@@ -1629,6 +1629,7 @@ public abstract class NotificationListenerService extends Service {
            out.writeBoolean(mIsConversation);
            out.writeBoolean(mIsConversation);
            out.writeParcelable(mShortcutInfo, flags);
            out.writeParcelable(mShortcutInfo, flags);
            out.writeInt(mRankingAdjustment);
            out.writeInt(mRankingAdjustment);
            out.writeBoolean(mIsBubble);
        }
        }


        /** @hide */
        /** @hide */
@@ -1665,6 +1666,7 @@ public abstract class NotificationListenerService extends Service {
            mIsConversation = in.readBoolean();
            mIsConversation = in.readBoolean();
            mShortcutInfo = in.readParcelable(cl);
            mShortcutInfo = in.readParcelable(cl);
            mRankingAdjustment = in.readInt();
            mRankingAdjustment = in.readInt();
            mIsBubble = in.readBoolean();
        }
        }




@@ -1869,6 +1871,14 @@ public abstract class NotificationListenerService extends Service {
            return mIsConversation;
            return mIsConversation;
        }
        }


        /**
         * Returns whether this notification is actively a bubble.
         * @hide
         */
        public boolean isBubble() {
            return mIsBubble;
        }

        /**
        /**
         * @hide
         * @hide
         */
         */
@@ -1897,7 +1907,7 @@ public abstract class NotificationListenerService extends Service {
                boolean noisy, ArrayList<Notification.Action> smartActions,
                boolean noisy, ArrayList<Notification.Action> smartActions,
                ArrayList<CharSequence> smartReplies, boolean canBubble,
                ArrayList<CharSequence> smartReplies, boolean canBubble,
                boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo,
                boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo,
                int rankingAdjustment) {
                int rankingAdjustment, boolean isBubble) {
            mKey = key;
            mKey = key;
            mRank = rank;
            mRank = rank;
            mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
            mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1922,6 +1932,7 @@ public abstract class NotificationListenerService extends Service {
            mIsConversation = isConversation;
            mIsConversation = isConversation;
            mShortcutInfo = shortcutInfo;
            mShortcutInfo = shortcutInfo;
            mRankingAdjustment = rankingAdjustment;
            mRankingAdjustment = rankingAdjustment;
            mIsBubble = isBubble;
        }
        }


        /**
        /**
@@ -1950,7 +1961,8 @@ public abstract class NotificationListenerService extends Service {
                    other.mVisuallyInterruptive,
                    other.mVisuallyInterruptive,
                    other.mIsConversation,
                    other.mIsConversation,
                    other.mShortcutInfo,
                    other.mShortcutInfo,
                    other.mRankingAdjustment);
                    other.mRankingAdjustment,
                    other.mIsBubble);
        }
        }


        /**
        /**
@@ -2008,7 +2020,8 @@ public abstract class NotificationListenerService extends Service {
                    // Shortcutinfo doesn't have equals either; use id
                    // Shortcutinfo doesn't have equals either; use id
                    &&  Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()),
                    &&  Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()),
                    (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()))
                    (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()))
                    && Objects.equals(mRankingAdjustment, other.mRankingAdjustment);
                    && Objects.equals(mRankingAdjustment, other.mRankingAdjustment)
                    && Objects.equals(mIsBubble, other.mIsBubble);
        }
        }
    }
    }


+26 −4
Original line number Original line Diff line number Diff line
@@ -52,6 +52,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
import android.util.ArraySet;
@@ -146,13 +147,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    private BubbleData mBubbleData;
    private BubbleData mBubbleData;
    @Nullable private BubbleStackView mStackView;
    @Nullable private BubbleStackView mStackView;
    private BubbleIconFactory mBubbleIconFactory;
    private BubbleIconFactory mBubbleIconFactory;
    private int mMaxBubbles;


    // Tracks the id of the current (foreground) user.
    // Tracks the id of the current (foreground) user.
    private int mCurrentUserId;
    private int mCurrentUserId;
    // Saves notification keys of active bubbles when users are switched.
    // Saves notification keys of active bubbles when users are switched.
    private final SparseSetArray<String> mSavedBubbleKeysPerUser;
    private final SparseSetArray<String> mSavedBubbleKeysPerUser;


    // Used when ranking updates occur and we check if things should bubble / unbubble
    private NotificationListenerService.Ranking mTmpRanking;

    // Saves notification keys of user created "fake" bubbles so that we can allow notifications
    // Saves notification keys of user created "fake" bubbles so that we can allow notifications
    // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
    // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
    private final HashSet<String> mUserCreatedBubbles;
    private final HashSet<String> mUserCreatedBubbles;
@@ -338,7 +341,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi


        configurationController.addCallback(this /* configurationListener */);
        configurationController.addCallback(this /* configurationListener */);


        mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
        mBubbleData = data;
        mBubbleData = data;
        mBubbleData.setListener(mBubbleDataListener);
        mBubbleData.setListener(mBubbleDataListener);
        mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
        mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
@@ -939,9 +941,29 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        }
        }
    }
    }


    /**
     * Called when NotificationListener has received adjusted notification rank and reapplied
     * filtering and sorting. This is used to dismiss or create bubbles based on changes in
     * permissions on the notification channel or the global setting.
     *
     * @param rankingMap the updated ranking map from NotificationListenerService
     */
    private void onRankingUpdated(RankingMap rankingMap) {
    private void onRankingUpdated(RankingMap rankingMap) {
        // Forward to BubbleData to block any bubbles which should no longer be shown
        if (mTmpRanking == null) {
        mBubbleData.notificationRankingUpdated(rankingMap);
            mTmpRanking = new NotificationListenerService.Ranking();
        }
        String[] orderedKeys = rankingMap.getOrderedKeys();
        for (int i = 0; i < orderedKeys.length; i++) {
            String key = orderedKeys[i];
            NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
            rankingMap.getRanking(key, mTmpRanking);
            if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
                mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
            } else if (entry != null && mTmpRanking.isBubble()) {
                entry.setFlagBubble(true);
                onEntryUpdated(entry);
            }
        }
    }
    }


    @SuppressWarnings("FieldCanBeLocal")
    @SuppressWarnings("FieldCanBeLocal")
+0 −26
Original line number Original line Diff line number Diff line
@@ -26,7 +26,6 @@ import android.app.Notification;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Context;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.util.Pair;


@@ -288,31 +287,6 @@ public class BubbleData {
        dispatchPendingChanges();
        dispatchPendingChanges();
    }
    }


    /**
     * Called when NotificationListener has received adjusted notification rank and reapplied
     * filtering and sorting. This is used to dismiss any bubbles which should no longer be shown
     * due to changes in permissions on the notification channel or the global setting.
     *
     * @param rankingMap the updated ranking map from NotificationListenerService
     */
    public void notificationRankingUpdated(RankingMap rankingMap) {
        if (mTmpRanking == null) {
            mTmpRanking = new NotificationListenerService.Ranking();
        }

        String[] orderedKeys = rankingMap.getOrderedKeys();
        for (int i = 0; i < orderedKeys.length; i++) {
            String key = orderedKeys[i];
            if (hasBubbleWithKey(key)) {
                rankingMap.getRanking(key, mTmpRanking);
                if (!mTmpRanking.canBubble()) {
                    doRemove(key, BubbleController.DISMISS_BLOCKED);
                }
            }
        }
        dispatchPendingChanges();
    }

    /**
    /**
     * Adds a group key indicating that the summary for this group should be suppressed.
     * Adds a group key indicating that the summary for this group should be suppressed.
     *
     *
+2 −1
Original line number Original line Diff line number Diff line
@@ -207,7 +207,8 @@ public class NotificationListener extends NotificationListenerWithPlugins {
                    false,
                    false,
                    false,
                    false,
                    null,
                    null,
                    0
                    0,
                    false
            );
            );
        }
        }
        return ranking;
        return ranking;
+4 −1
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@ public class RankingBuilder {
    private boolean mIsConversation = false;
    private boolean mIsConversation = false;
    private ShortcutInfo mShortcutInfo = null;
    private ShortcutInfo mShortcutInfo = null;
    private int mRankingAdjustment = 0;
    private int mRankingAdjustment = 0;
    private boolean mIsBubble = false;


    public RankingBuilder() {
    public RankingBuilder() {
    }
    }
@@ -84,6 +85,7 @@ public class RankingBuilder {
        mIsConversation = ranking.isConversation();
        mIsConversation = ranking.isConversation();
        mShortcutInfo = ranking.getShortcutInfo();
        mShortcutInfo = ranking.getShortcutInfo();
        mRankingAdjustment = ranking.getRankingAdjustment();
        mRankingAdjustment = ranking.getRankingAdjustment();
        mIsBubble = ranking.isBubble();
    }
    }


    public Ranking build() {
    public Ranking build() {
@@ -111,7 +113,8 @@ public class RankingBuilder {
                mIsVisuallyInterruptive,
                mIsVisuallyInterruptive,
                mIsConversation,
                mIsConversation,
                mShortcutInfo,
                mShortcutInfo,
                mRankingAdjustment);
                mRankingAdjustment,
                mIsBubble);
        return ranking;
        return ranking;
    }
    }


Loading