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

Commit 33477397 authored by Joshua Tsuji's avatar Joshua Tsuji
Browse files

Remove bubbles if their shortcuts are removed or if the app is uninstalled.

Bug: 157755828
Test: modded bubble test app to remove shortcuts
Change-Id: I19acce432013a86695eb3b28c8fe59ab23a4fa88
parent 7ec25249
Loading
Loading
Loading
Loading
+51 −4
Original line number Diff line number Diff line
@@ -52,13 +52,16 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
@@ -130,7 +133,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
            DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
            DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
            DISMISS_OVERFLOW_MAX_REACHED})
            DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED})
    @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
    @interface DismissReason {}

@@ -145,6 +148,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    static final int DISMISS_GROUP_CANCELLED = 9;
    static final int DISMISS_INVALID_INTENT = 10;
    static final int DISMISS_OVERFLOW_MAX_REACHED = 11;
    static final int DISMISS_SHORTCUT_REMOVED = 12;
    static final int DISMISS_PACKAGE_REMOVED = 13;

    private final Context mContext;
    private final NotificationEntryManager mNotificationEntryManager;
@@ -334,7 +339,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            SysUiState sysUiState,
            INotificationManager notificationManager,
            @Nullable IStatusBarService statusBarService,
            WindowManager windowManager) {
            WindowManager windowManager,
            LauncherApps launcherApps) {
        dumpManager.registerDumpable(TAG, this);
        mContext = context;
        mShadeController = shadeController;
@@ -426,6 +432,47 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                });

        mBubbleIconFactory = new BubbleIconFactory(context);

        launcherApps.registerCallback(new LauncherApps.Callback() {
            @Override
            public void onPackageAdded(String s, UserHandle userHandle) {}

            @Override
            public void onPackageChanged(String s, UserHandle userHandle) {}

            @Override
            public void onPackageRemoved(String s, UserHandle userHandle) {
                // Remove bubbles with this package name, since it has been uninstalled and attempts
                // to open a bubble from an uninstalled app can cause issues.
                mBubbleData.removeBubblesWithPackageName(s, DISMISS_PACKAGE_REMOVED);
            }

            @Override
            public void onPackagesAvailable(String[] strings, UserHandle userHandle,
                    boolean b) {

            }

            @Override
            public void onPackagesUnavailable(String[] packages, UserHandle userHandle,
                    boolean b) {
                for (String packageName : packages) {
                    // Remove bubbles from unavailable apps. This can occur when the app is on
                    // external storage that has been removed.
                    mBubbleData.removeBubblesWithPackageName(packageName, DISMISS_PACKAGE_REMOVED);
                }
            }

            @Override
            public void onShortcutsChanged(String packageName, List<ShortcutInfo> validShortcuts,
                    UserHandle user) {
                super.onShortcutsChanged(packageName, validShortcuts, user);

                // Remove bubbles whose shortcuts aren't in the latest list of valid shortcuts.
                mBubbleData.removeBubblesWithInvalidShortcuts(
                        packageName, validShortcuts, DISMISS_SHORTCUT_REMOVED);
            }
        });
    }

    /**
@@ -1102,7 +1149,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    @MainThread
    void removeBubble(String key, int reason) {
        if (mBubbleData.hasAnyBubbleWithKey(key)) {
            mBubbleData.notificationEntryRemoved(key, reason);
            mBubbleData.dismissBubbleWithKey(key, reason);
        }
    }

@@ -1160,7 +1207,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            rankingMap.getRanking(key, mTmpRanking);
            boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
            if (isActiveBubble && !mTmpRanking.canBubble()) {
                mBubbleData.notificationEntryRemoved(entry.getKey(),
                mBubbleData.dismissBubbleWithKey(entry.getKey(),
                        BubbleController.DISMISS_BLOCKED);
            } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
                entry.setFlagBubble(true);
+65 −5
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.util.Log;
import android.util.Pair;
import android.view.View;
@@ -42,8 +43,12 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;

import javax.inject.Inject;
import javax.inject.Singleton;
@@ -286,9 +291,9 @@ public class BubbleData {
    }

    /**
     * Called when a notification associated with a bubble is removed.
     * Dismisses the bubble with the matching key, if it exists.
     */
    public void notificationEntryRemoved(String key, @DismissReason int reason) {
    public void dismissBubbleWithKey(String key, @DismissReason int reason) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "notificationEntryRemoved: key=" + key + " reason=" + reason);
        }
@@ -349,6 +354,44 @@ public class BubbleData {
        return bubbleChildren;
    }

    /**
     * Removes bubbles from the given package whose shortcut are not in the provided list of valid
     * shortcuts.
     */
    public void removeBubblesWithInvalidShortcuts(
            String packageName, List<ShortcutInfo> validShortcuts, int reason) {

        final Set<String> validShortcutIds = new HashSet<String>();
        for (ShortcutInfo info : validShortcuts) {
            validShortcutIds.add(info.getId());
        }

        final Predicate<Bubble> invalidBubblesFromPackage = bubble ->
                packageName.equals(bubble.getPackageName())
                        && (bubble.getShortcutInfo() == null
                            || !bubble.getShortcutInfo().isEnabled()
                            || !validShortcutIds.contains(bubble.getShortcutInfo().getId()));

        final Consumer<Bubble> removeBubble = bubble ->
                dismissBubbleWithKey(bubble.getKey(), reason);

        performActionOnBubblesMatching(getBubbles(), invalidBubblesFromPackage, removeBubble);
        performActionOnBubblesMatching(
                getOverflowBubbles(), invalidBubblesFromPackage, removeBubble);
    }

    /** Dismisses all bubbles from the given package. */
    public void removeBubblesWithPackageName(String packageName, int reason) {
        final Predicate<Bubble> bubbleMatchesPackage = bubble ->
                bubble.getPackageName().equals(packageName);

        final Consumer<Bubble> removeBubble = bubble ->
                dismissBubbleWithKey(bubble.getKey(), reason);

        performActionOnBubblesMatching(getBubbles(), bubbleMatchesPackage, removeBubble);
        performActionOnBubblesMatching(getOverflowBubbles(), bubbleMatchesPackage, removeBubble);
    }

    private void doAdd(Bubble bubble) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "doAdd: " + bubble);
@@ -388,6 +431,21 @@ public class BubbleData {
        }
    }

    /** Runs the given action on Bubbles that match the given predicate. */
    private void performActionOnBubblesMatching(
            List<Bubble> bubbles, Predicate<Bubble> predicate, Consumer<Bubble> action) {
        final List<Bubble> matchingBubbles = new ArrayList<>();
        for (Bubble bubble : bubbles) {
            if (predicate.test(bubble)) {
                matchingBubbles.add(bubble);
            }
        }

        for (Bubble matchingBubble : matchingBubbles) {
            action.accept(matchingBubble);
        }
    }

    private void doRemove(String key, @DismissReason int reason) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "doRemove: " + key);
@@ -402,7 +460,9 @@ public class BubbleData {
                && (reason == BubbleController.DISMISS_NOTIF_CANCEL
                    || reason == BubbleController.DISMISS_GROUP_CANCELLED
                    || reason == BubbleController.DISMISS_NO_LONGER_BUBBLE
                || reason == BubbleController.DISMISS_BLOCKED)) {
                    || reason == BubbleController.DISMISS_BLOCKED
                    || reason == BubbleController.DISMISS_SHORTCUT_REMOVED
                    || reason == BubbleController.DISMISS_PACKAGE_REMOVED)) {

                Bubble b = getOverflowBubbleWithKey(key);
                if (DEBUG_BUBBLE_DATA) {
+1 −1
Original line number Diff line number Diff line
@@ -2229,7 +2229,7 @@ public class BubbleStackView extends FrameLayout

    private void dismissBubbleIfExists(@Nullable Bubble bubble) {
        if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
            mBubbleData.notificationEntryRemoved(
            mBubbleData.dismissBubbleWithKey(
                    bubble.getKey(), BubbleController.DISMISS_USER_GESTURE);
        }
    }
+5 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.bubbles.dagger;

import android.app.INotificationManager;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.view.WindowManager;

import com.android.internal.statusbar.IStatusBarService;
@@ -72,7 +73,8 @@ public interface BubbleModule {
            SysUiState sysUiState,
            INotificationManager notifManager,
            IStatusBarService statusBarService,
            WindowManager windowManager) {
            WindowManager windowManager,
            LauncherApps launcherApps) {
        return new BubbleController(
                context,
                notificationShadeWindowController,
@@ -94,6 +96,7 @@ public interface BubbleModule {
                sysUiState,
                notifManager,
                statusBarService,
                windowManager);
                windowManager,
                launcherApps);
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.pm.LauncherApps;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.face.FaceManager;
import android.os.Handler;
@@ -179,6 +180,8 @@ public class BubbleControllerTest extends SysuiTestCase {
    private NotificationShadeWindowView mNotificationShadeWindowView;
    @Mock
    private IStatusBarService mStatusBarService;
    @Mock
    private LauncherApps mLauncherApps;

    private BubbleData mBubbleData;

@@ -256,7 +259,8 @@ public class BubbleControllerTest extends SysuiTestCase {
                mSysUiState,
                mock(INotificationManager.class),
                mStatusBarService,
                mWindowManager);
                mWindowManager,
                mLauncherApps);
        mBubbleController.setExpandListener(mBubbleExpandListener);

        // Get a reference to the BubbleController's entry listener
Loading