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

Commit 8496f4ee authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge "Ensure the notification is removed when bubble is removed & fix cancel all" into qt-dev

parents 0ff00cf3 c2ff011d
Loading
Loading
Loading
Loading
+52 −23
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.systemui.bubbles;

import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.INVISIBLE;
@@ -53,13 +57,13 @@ import androidx.annotation.MainThread;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
@@ -210,6 +214,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi

        mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
        mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
        mNotificationEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);

        mStatusBarWindowController = statusBarWindowController;
        mStatusBarStateListener = new StatusBarStateListener();
@@ -388,6 +393,46 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        }
    }

    @SuppressWarnings("FieldCanBeLocal")
    private final NotificationRemoveInterceptor mRemoveInterceptor =
            new NotificationRemoveInterceptor() {
            @Override
            public boolean onNotificationRemoveRequested(String key, int reason) {
                if (!mBubbleData.hasBubbleWithKey(key)) {
                    return false;
                }
                NotificationEntry entry = mBubbleData.getBubbleWithKey(key).entry;

                final boolean isClearAll = reason == REASON_CANCEL_ALL;
                final boolean isUserDimiss = reason == REASON_CANCEL;
                final boolean isAppCancel = reason == REASON_APP_CANCEL
                        || reason == REASON_APP_CANCEL_ALL;

                // Need to check for !appCancel here because the notification may have
                // previously been dismissed & entry.isRowDismissed would still be true
                boolean userRemovedNotif = (entry.isRowDismissed() && !isAppCancel)
                        || isClearAll || isUserDimiss;

                // The bubble notification sticks around in the data as long as the bubble is
                // not dismissed and the app hasn't cancelled the notification.
                boolean bubbleExtended = entry.isBubble() && !entry.isBubbleDismissed()
                        && userRemovedNotif;
                if (bubbleExtended) {
                    entry.setShowInShadeWhenBubble(false);
                    if (mStackView != null) {
                        mStackView.updateDotVisibility(entry.key);
                    }
                    mNotificationEntryManager.updateNotifications();
                    return true;
                } else if (!userRemovedNotif && !entry.isBubbleDismissed()) {
                    // This wasn't a user removal so we should remove the bubble as well
                    mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
                    return false;
                }
                return false;
            }
        };

    @SuppressWarnings("FieldCanBeLocal")
    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
        @Override
@@ -396,7 +441,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                return;
            }
            if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
                // TODO: handle group summaries?
                updateShowInShadeForSuppressNotification(entry);
            }
        }
@@ -426,23 +470,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                updateBubble(entry);
            }
        }

        @Override
        public void onEntryRemoved(NotificationEntry entry,
                @Nullable NotificationVisibility visibility,
                boolean removedByUser) {
            if (!areBubblesEnabled(mContext)) {
                return;
            }
            entry.setShowInShadeWhenBubble(false);
            if (mStackView != null) {
                mStackView.updateDotVisibility(entry.key);
            }
            if (!removedByUser) {
                // This was a cancel so we should remove the bubble
                removeBubble(entry.key, DISMISS_NOTIF_CANCEL);
            }
        }
    };

    @SuppressWarnings("FieldCanBeLocal")
@@ -455,13 +482,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        }

        @Override
        public void onBubbleRemoved(Bubble bubble, int reason) {
        public void onBubbleRemoved(Bubble bubble, @DismissReason int reason) {
            if (mStackView != null) {
                mStackView.removeBubble(bubble);
            }
            if (!bubble.entry.showInShadeWhenBubble()) {
                // The notification is gone & bubble is gone, time to actually remove it
                mNotificationEntryManager.performRemoveNotification(bubble.entry.notification);
            if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
                    && !bubble.entry.showInShadeWhenBubble()) {
                // The bubble is gone & the notification is gone, time to actually remove it
                mNotificationEntryManager.performRemoveNotification(bubble.entry.notification,
                        0 /* reason */);
            } else {
                // The notification is still in the shade but we've removed the bubble so
                // lets make sure NoMan knows it's not a bubble anymore
+10 −5
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ public class NotificationListener extends NotificationListenerWithPlugins {

                    // Remove existing notification to avoid stale data.
                    if (isUpdate) {
                        mEntryManager.removeNotification(key, rankingMap);
                        mEntryManager.removeNotification(key, rankingMap, 0 /* reason */);
                    } else {
                        mEntryManager.getNotificationData()
                                .updateRanking(rankingMap);
@@ -121,17 +121,22 @@ public class NotificationListener extends NotificationListenerWithPlugins {
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
            int reason) {
        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn + " reason: " + reason);
        if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
            final String key = sbn.getKey();
            Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
                mEntryManager.removeNotification(key, rankingMap);
                mEntryManager.removeNotification(key, rankingMap, reason);
            });
        }
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
        onNotificationRemoved(sbn, rankingMap, 0 /* reason */);
    }

    @Override
    public void onNotificationRankingUpdate(final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onRankingUpdate");
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar;

import android.service.notification.NotificationListenerService;

/**
 * Interface for anything that may need to prevent notifications from being removed. This is
 * similar to a {@link NotificationLifetimeExtender} in the sense that it extends the life of
 * a notification by preventing the removal, however, unlike the extender, the remove interceptor
 * gets first pick at intercepting any type of removal -- the life time extender is unable to
 * extend the life of a user dismissed or force removed notification.
 */
public interface NotificationRemoveInterceptor {

    /**
     * Called when a notification has been removed.
     *
     * @param key the entry key of the notification being removed.
     * @param removeReason why the notification is being removed, e.g.
     * {@link NotificationListenerService#REASON_CANCEL} or 0 if unknown.
     *
     * @return true if the removal should be ignored, false otherwise.
     */
    boolean onNotificationRemoveRequested(String key, int removeReason);
}
+3 −1
Original line number Diff line number Diff line
@@ -37,8 +37,10 @@ public interface NotificationUpdateHandler {
     *
     * @param key Key identifying the notification to remove
     * @param ranking RankingMap to update with
     * @param reason why the notification is being removed, e.g.
     * {@link NotificationListenerService#REASON_CANCEL}.
     */
    void removeNotification(String key, NotificationListenerService.RankingMap ranking);
    void removeNotification(String key, NotificationListenerService.RankingMap ranking, int reason);

    /**
     * Update a given notification and the current notification ranking map.
+39 −9
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 */
package com.android.systemui.statusbar.notification;

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

import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
@@ -30,6 +33,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
import com.android.systemui.statusbar.notification.collection.NotificationData;
@@ -82,6 +86,7 @@ public class NotificationEntryManager implements
    final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
            = new ArrayList<>();
    private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
    private NotificationRemoveInterceptor mRemoveInterceptor;

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -115,6 +120,11 @@ public class NotificationEntryManager implements
        mNotificationEntryListeners.add(listener);
    }

    /** Sets the {@link NotificationRemoveInterceptor}. */
    public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
        mRemoveInterceptor = interceptor;
    }

    /**
     * Our dependencies can have cyclic references, so some need to be lazy
     */
@@ -146,7 +156,7 @@ public class NotificationEntryManager implements
    /** Adds a {@link NotificationLifetimeExtender}. */
    public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
        mNotificationLifetimeExtenders.add(extender);
        extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
        extender.setCallback(key -> removeNotification(key, mLatestRankingMap, 0));
    }

    public NotificationData getNotificationData() {
@@ -158,10 +168,18 @@ public class NotificationEntryManager implements
        updateNotifications();
    }

    public void performRemoveNotification(StatusBarNotification n) {
    /**
     * 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());
        removeNotificationInternal(
                n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
                n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
                reason);
    }

    private NotificationVisibility obtainVisibility(String key) {
@@ -193,7 +211,8 @@ public class NotificationEntryManager implements
    @Override
    public void handleInflationException(StatusBarNotification n, Exception e) {
        removeNotificationInternal(
                n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */);
                n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
                REASON_ERROR);
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onInflationError(n, e);
        }
@@ -228,9 +247,10 @@ public class NotificationEntryManager implements
    }

    @Override
    public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
    public void removeNotification(String key, NotificationListenerService.RankingMap ranking,
            int reason) {
        removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
                false /* removedByUser */);
                false /* removedByUser */, reason);
    }

    private void removeNotificationInternal(
@@ -238,7 +258,15 @@ public class NotificationEntryManager implements
            @Nullable NotificationListenerService.RankingMap ranking,
            @Nullable NotificationVisibility visibility,
            boolean forceRemove,
            boolean removedByUser) {
            boolean removedByUser,
            int reason) {

        if (mRemoveInterceptor != null
                && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) {
            // Remove intercepted; skip
            return;
        }

        final NotificationEntry entry = mNotificationData.get(key);

        abortExistingInflation(key);
@@ -342,7 +370,8 @@ public class NotificationEntryManager implements

        Dependency.get(LeakDetector.class).trackInstance(entry);
        // Construct the expanded view.
        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
                REASON_CANCEL));

        abortExistingInflation(key);

@@ -383,7 +412,8 @@ public class NotificationEntryManager implements
            listener.onPreEntryUpdated(entry);
        }

        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
                REASON_CANCEL));
        updateNotifications();

        if (DEBUG) {
Loading