Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +51 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 {} Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } }); } /** Loading Loading @@ -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); } } Loading Loading @@ -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); Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +65 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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) { Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +1 −1 Original line number Diff line number Diff line Loading @@ -2232,7 +2232,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); } } Loading packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +5 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -72,7 +73,8 @@ public interface BubbleModule { SysUiState sysUiState, INotificationManager notifManager, IStatusBarService statusBarService, WindowManager windowManager) { WindowManager windowManager, LauncherApps launcherApps) { return new BubbleController( context, notificationShadeWindowController, Loading @@ -94,6 +96,7 @@ public interface BubbleModule { sysUiState, notifManager, statusBarService, windowManager); windowManager, launcherApps); } } packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +5 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -179,6 +180,8 @@ public class BubbleControllerTest extends SysuiTestCase { private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private IStatusBarService mStatusBarService; @Mock private LauncherApps mLauncherApps; private BubbleData mBubbleData; Loading Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +51 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 {} Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } }); } /** Loading Loading @@ -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); } } Loading Loading @@ -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); Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +65 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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) { Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +1 −1 Original line number Diff line number Diff line Loading @@ -2232,7 +2232,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); } } Loading
packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +5 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -72,7 +73,8 @@ public interface BubbleModule { SysUiState sysUiState, INotificationManager notifManager, IStatusBarService statusBarService, WindowManager windowManager) { WindowManager windowManager, LauncherApps launcherApps) { return new BubbleController( context, notificationShadeWindowController, Loading @@ -94,6 +96,7 @@ public interface BubbleModule { sysUiState, notifManager, statusBarService, windowManager); windowManager, launcherApps); } }
packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +5 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -179,6 +180,8 @@ public class BubbleControllerTest extends SysuiTestCase { private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private IStatusBarService mStatusBarService; @Mock private LauncherApps mLauncherApps; private BubbleData mBubbleData; Loading Loading @@ -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