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

Commit 189d4af7 authored by Beverly's avatar Beverly
Browse files

Add dismissUserStats to NEM#performRemoveNotif

Now both notification pipelines require DismissedByUserStats when
removing a notification.

Also, refactored the DismissRunnable that gets called whenever a
notification is manually swiped away and dismissed from the
NotificationShade. Now, ExpandableNotificationRowController injects a
OnDismissCallback with a #onDismiss method that will get called whenever
a notification is manually swiped by the user OR is clicked with the
AUTO_CANCEL flag. Since it's injected it's easier to switch between the
new and old pipeline's OnDismissCallback (one which interacts with the
old pipeline's NotificationEntryManager, and another with the new
NotifCollection). Now this dismiss runnable doesn't need to be passed
around starting from inflation.

Test: atest SystemUITests
Change-Id: Iec985ce2c502462ee35cf86c1e0332168e578823
parent 3987fbd5
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -73,6 +73,11 @@ public final class NotificationStats implements Parcelable {
     * Notification has been dismissed from the notification shade.
     */
    public static final int DISMISSAL_SHADE = 3;
    /**
     * Notification has been dismissed as a bubble.
     * @hide
     */
    public static final int DISMISSAL_BUBBLE = 3;

    /** @hide */
    @IntDef(prefix = { "DISMISS_SENTIMENT_" }, value = {
+52 −9
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import static android.service.notification.NotificationListenerService.REASON_CA
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.INVISIBLE;
@@ -100,8 +102,11 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -277,7 +282,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
         * This can happen when an app cancels a bubbled notification or when the user dismisses a
         * bubble.
         */
        void removeNotification(@NonNull NotificationEntry entry, int reason);
        void removeNotification(
                @NonNull NotificationEntry entry,
                @NonNull DismissedByUserStats stats,
                int reason);

        /**
         * Called when a bubbled notification has changed whether it should be
@@ -543,6 +551,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                    }
                });

        // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
        mNotificationEntryManager.addNotificationRemoveInterceptor(
                new NotificationRemoveInterceptor() {
                    @Override
@@ -551,7 +560,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                            NotificationEntry entry,
                            int dismissReason) {
                        final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
                        final boolean isUserDimiss = dismissReason == REASON_CANCEL
                        final boolean isUserDismiss = dismissReason == REASON_CANCEL
                                || dismissReason == REASON_CLICK;
                        final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
                                || dismissReason == REASON_APP_CANCEL_ALL;
@@ -562,7 +571,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                        // previously been dismissed & entry.isRowDismissed would still be true
                        boolean userRemovedNotif =
                                (entry != null && entry.isRowDismissed() && !isAppCancel)
                                || isClearAll || isUserDimiss || isSummaryCancel;
                                || isClearAll || isUserDismiss || isSummaryCancel;

                        if (userRemovedNotif) {
                            return handleDismissalInterception(entry);
@@ -591,8 +600,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi

        addNotifCallback(new NotifCallback() {
            @Override
            public void removeNotification(NotificationEntry entry, int reason) {
                mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason);
            public void removeNotification(
                    NotificationEntry entry,
                    DismissedByUserStats dismissedByUserStats,
                    int reason
            ) {
                mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
                        dismissedByUserStats, reason);
            }

            @Override
@@ -612,7 +626,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                            mNotificationEntryManager.getActiveNotificationUnfiltered(
                                    mBubbleData.getSummaryKey(groupKey));
                    if (summary != null) {
                        mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
                        mNotificationEntryManager.performRemoveNotification(
                                summary.getSbn(),
                                getDismissedByUserStats(summary, false),
                                UNDEFINED_DISMISS_REASON);
                    }
                }
@@ -634,7 +650,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                    boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
                    if (!isSummaryThisNotif && (summaryChildren == null
                            || summaryChildren.isEmpty())) {
                        mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
                        mNotificationEntryManager.performRemoveNotification(
                                summary.getSbn(),
                                getDismissedByUserStats(summary, false),
                                UNDEFINED_DISMISS_REASON);
                    }
                }
@@ -1331,7 +1349,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                        // time to actually remove it
                        for (NotifCallback cb : mCallbacks) {
                            if (entry != null) {
                                cb.removeNotification(entry, REASON_CANCEL);
                                cb.removeNotification(
                                        entry,
                                        getDismissedByUserStats(entry, true),
                                        REASON_CANCEL);
                            }
                        }
                    } else {
@@ -1487,7 +1508,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                } else {
                    // non-bubbled children can be removed
                    for (NotifCallback cb : mCallbacks) {
                        cb.removeNotification(child, REASON_GROUP_SUMMARY_CANCELED);
                        cb.removeNotification(
                                child,
                                getDismissedByUserStats(child, true),
                                REASON_GROUP_SUMMARY_CANCELED);
                    }
                }
            }
@@ -1501,6 +1525,25 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                summary.getKey());
    }

    /**
     * Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
     * Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
     * Instead, this is taken care of by {@link BubbleCoordinator}.
     */
    private DismissedByUserStats getDismissedByUserStats(
            NotificationEntry entry,
            boolean isVisible) {
        return new DismissedByUserStats(
                DISMISSAL_BUBBLE,
                DISMISS_SENTIMENT_NEUTRAL,
                NotificationVisibility.obtain(
                        entry.getKey(),
                        entry.getRanking().getRank(),
                        mNotificationEntryManager.getActiveNotificationsCount(),
                        isVisible,
                        NotificationLogger.getNotificationLocation(entry)));
    }

    /**
     * Updates the visibility of the bubbles based on current state.
     * Does not un-bubble, just hides or un-hides.
+47 −59
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
 */
package com.android.systemui.statusbar.notification;

import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_ERROR;

import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
@@ -24,14 +23,11 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -41,7 +37,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
@@ -54,11 +49,11 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;

@@ -147,8 +142,6 @@ public class NotificationEntryManager implements
    private final NotificationRankingManager mRankingManager;
    private final FeatureFlags mFeatureFlags;
    private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
    private final HeadsUpManager mHeadsUpManager;
    private final StatusBarStateController mStatusBarStateController;

    private NotificationPresenter mPresenter;
    private RankingMap mLatestRankingMap;
@@ -213,8 +206,7 @@ public class NotificationEntryManager implements
            Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
            LeakDetector leakDetector,
            ForegroundServiceDismissalFeatureController fgsFeatureController,
            HeadsUpManager headsUpManager,
            StatusBarStateController statusBarStateController
            IStatusBarService statusBarService
    ) {
        mLogger = logger;
        mGroupManager = groupManager;
@@ -225,11 +217,7 @@ public class NotificationEntryManager implements
        mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
        mLeakDetector = leakDetector;
        mFgsFeatureController = fgsFeatureController;
        mHeadsUpManager = headsUpManager;
        mStatusBarStateController = statusBarStateController;

        mStatusBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
        mStatusBarService = statusBarService;
    }

    /** Once called, the NEM will start processing notification events from system server. */
@@ -284,16 +272,23 @@ public class NotificationEntryManager implements
    }

    /**
     * Requests a notification to be removed.
     * User requests a notification to be removed.
     *
     * @param n the notification to remove.
     * @param reason why it is being removed e.g. {@link NotificationListenerService#REASON_CANCEL},
     *               or 0 if unknown.
     */
    public void performRemoveNotification(StatusBarNotification n, int reason) {
        final NotificationVisibility nv = obtainVisibility(n.getKey());
    public void performRemoveNotification(
            StatusBarNotification n,
            @NonNull DismissedByUserStats stats,
            int reason
    ) {
        removeNotificationInternal(
                n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
                n.getKey(),
                null,
                stats.notificationVisibility,
                false /* forceRemove */,
                stats,
                reason);
    }

@@ -337,7 +332,11 @@ public class NotificationEntryManager implements
     */
    private void handleInflationException(StatusBarNotification n, Exception e) {
        removeNotificationInternal(
                n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
                n.getKey(),
                null,
                null,
                true /* forceRemove */,
                null /* dismissedByUserStats */,
                REASON_ERROR);
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onInflationError(n, e);
@@ -435,19 +434,28 @@ public class NotificationEntryManager implements
        reapplyFilterAndSort("addVisibleNotification");
    }


    public void removeNotification(String key, RankingMap ranking,
            int reason) {
        removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
                false /* removedByUser */, reason);
    @VisibleForTesting
    protected void removeNotification(String key, RankingMap ranking, int reason) {
        removeNotificationInternal(
                key,
                ranking,
                obtainVisibility(key),
                false /* forceRemove */,
                null /* dismissedByUserStats */,
                reason);
    }

    /**
     * Internally remove a notification because system server has reported the notification
     * should be removed OR the user has manually dismissed the notification
     * @param dismissedByUserStats non-null if the user manually dismissed the notification
     */
    private void removeNotificationInternal(
            String key,
            @Nullable RankingMap ranking,
            @Nullable NotificationVisibility visibility,
            boolean forceRemove,
            boolean removedByUser,
            DismissedByUserStats dismissedByUserStats,
            int reason) {

        final NotificationEntry entry = getActiveNotificationUnfiltered(key);
@@ -512,11 +520,11 @@ public class NotificationEntryManager implements
                handleGroupSummaryRemoved(key);
                removeVisibleNotification(key);
                updateNotifications("removeNotificationInternal");
                removedByUser |= entryDismissed;
                final boolean removedByUser = dismissedByUserStats != null;

                mLogger.logNotifRemoved(entry.getKey(), removedByUser);
                if (removedByUser && visibility != null) {
                    sendNotificationRemovalToServer(entry.getKey(), entry.getSbn(), visibility);
                    sendNotificationRemovalToServer(entry.getSbn(), dismissedByUserStats);
                }
                for (NotificationEntryListener listener : mNotificationEntryListeners) {
                    listener.onEntryRemoved(entry, visibility, removedByUser, reason);
@@ -534,30 +542,18 @@ public class NotificationEntryManager implements
    }

    private void sendNotificationRemovalToServer(
            String key,
            StatusBarNotification notification,
            NotificationVisibility nv) {
        final String pkg = notification.getPackageName();
        final String tag = notification.getTag();
        final int id = notification.getId();
        final int userId = notification.getUser().getIdentifier();
            DismissedByUserStats dismissedByUserStats) {
        try {
            int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
            if (mHeadsUpManager.isAlerting(key)) {
                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
            } else if (mStatusBarStateController.isDozing()) {
                dismissalSurface = NotificationStats.DISMISSAL_AOD;
            }
            int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
            mStatusBarService.onNotificationClear(
                    pkg,
                    tag,
                    id,
                    userId,
                    notification.getPackageName(),
                    notification.getTag(),
                    notification.getId(),
                    notification.getUser().getIdentifier(),
                    notification.getKey(),
                    dismissalSurface,
                    dismissalSentiment,
                    nv);
                    dismissedByUserStats.dismissalSurface,
                    dismissedByUserStats.dismissalSentiment,
                    dismissedByUserStats.notificationVisibility);
        } catch (RemoteException ex) {
            // system process is dead if we're here.
        }
@@ -641,11 +637,7 @@ public class NotificationEntryManager implements

        // Construct the expanded view.
        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
            mNotificationRowBinderLazy.get()
                    .inflateViews(
                            entry,
                            () -> performRemoveNotification(notification, REASON_CANCEL),
                            mInflationCallback);
            mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
        }

        mPendingNotifications.put(key, entry);
@@ -675,7 +667,7 @@ public class NotificationEntryManager implements

        final String key = notification.getKey();
        abortExistingInflation(key, "updateNotification");
        NotificationEntry entry = getActiveNotificationUnfiltered(key);
        final NotificationEntry entry = getActiveNotificationUnfiltered(key);
        if (entry == null) {
            return;
        }
@@ -701,11 +693,7 @@ public class NotificationEntryManager implements
        }

        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
            mNotificationRowBinderLazy.get()
                    .inflateViews(
                            entry,
                            () -> performRemoveNotification(notification, REASON_CANCEL),
                            mInflationCallback);
            mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
        }

        updateNotifications("updateNotificationInternal");
+0 −32
Original line number Diff line number Diff line
@@ -16,17 +16,10 @@

package com.android.systemui.statusbar.notification.collection;

import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;

import android.service.notification.NotificationStats;

import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;

@@ -81,7 +74,6 @@ public class NotifInflaterImpl implements NotifInflater {
        try {
            requireBinder().inflateViews(
                    entry,
                    getDismissCallback(entry),
                    wrapInflationCallback(callback));
        } catch (InflationException e) {
            mNotifErrorManager.setInflationError(entry, e);
@@ -93,30 +85,6 @@ public class NotifInflaterImpl implements NotifInflater {
        entry.abortTask();
    }

    private Runnable getDismissCallback(NotificationEntry entry) {
        return new Runnable() {
            @Override
            public void run() {
                int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
                /*
                 * TODO: determine dismissal surface (ie: shade / headsup / aod)
                 * see {@link NotificationLogger#logNotificationClear}
                 */
                mNotifCollection.dismissNotification(
                        entry,
                        new DismissedByUserStats(
                                dismissalSurface,
                                DISMISS_SENTIMENT_NEUTRAL,
                                NotificationVisibility.obtain(entry.getKey(),
                                        entry.getRanking().getRank(),
                                        mNotifPipeline.getShadeListCount(),
                                        true,
                                        NotificationLogger.getNotificationLocation(entry))
                        ));
            }
        };
    }

    private NotificationContentInflater.InflationCallback wrapInflationCallback(
            InflationCallback callback) {
        return new NotificationContentInflater.InflationCallback() {
+7 −21
Original line number Diff line number Diff line
@@ -16,10 +16,6 @@

package com.android.systemui.statusbar.notification.collection.coordinator;

import static android.service.notification.NotificationStats.DISMISSAL_OTHER;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;

import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -27,7 +23,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;

import java.util.HashSet;
import java.util.Set;
@@ -101,7 +96,6 @@ public class BubbleCoordinator implements Coordinator {

        @Override
        public boolean shouldInterceptDismissal(NotificationEntry entry) {
            // TODO: b/149041810 add support for intercepting app-cancelled bubble notifications
            // for experimental bubbles
            if (mBubbleController.handleDismissalInterception(entry)) {
                mInterceptedDismissalEntries.add(entry.getKey());
@@ -121,17 +115,21 @@ public class BubbleCoordinator implements Coordinator {
    private final BubbleController.NotifCallback mNotifCallback =
            new BubbleController.NotifCallback() {
        @Override
        public void removeNotification(NotificationEntry entry, int reason) {
        public void removeNotification(
                NotificationEntry entry,
                DismissedByUserStats dismissedByUserStats,
                int reason
        ) {
            if (isInterceptingDismissal(entry)) {
                mInterceptedDismissalEntries.remove(entry.getKey());
                mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
                        createDismissedByUserStats(entry));
                        dismissedByUserStats);
            } else if (mNotifPipeline.getAllNotifs().contains(entry)) {
                // Bubbles are hiding the notifications from the shade, but the bubble was
                // deleted; therefore, the notification should be cancelled as if it were a user
                // dismissal (this won't re-enter handleInterceptDimissal because Bubbles
                // will have already marked it as no longer a bubble)
                mNotifCollection.dismissNotification(entry, createDismissedByUserStats(entry));
                mNotifCollection.dismissNotification(entry, dismissedByUserStats);
            }
        }

@@ -149,16 +147,4 @@ public class BubbleCoordinator implements Coordinator {
    private boolean isInterceptingDismissal(NotificationEntry entry) {
        return mInterceptedDismissalEntries.contains(entry.getKey());
    }

    private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) {
        return new DismissedByUserStats(
                DISMISSAL_OTHER,
                DISMISS_SENTIMENT_NEUTRAL,
                NotificationVisibility.obtain(entry.getKey(),
                        entry.getRanking().getRank(),
                        mNotifPipeline.getShadeListCount(),
                        true, // was visible as a bubble
                        NotificationLogger.getNotificationLocation(entry))
        );
    }
}
Loading