diff options
Diffstat (limited to 'libs')
9 files changed, 321 insertions, 21 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index a45931953c0b..f427a2c4bc95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -66,6 +66,7 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.os.UserManager; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; import android.util.ArraySet; @@ -147,6 +148,7 @@ public class BubbleController { private final FloatingContentCoordinator mFloatingContentCoordinator; private final BubbleDataRepository mDataRepository; private final WindowManagerShellWrapper mWindowManagerShellWrapper; + private final UserManager mUserManager; private final LauncherApps mLauncherApps; private final IStatusBarService mBarService; private final WindowManager mWindowManager; @@ -231,6 +233,7 @@ public class BubbleController { @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, + UserManager userManager, LauncherApps launcherApps, TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger, @@ -248,8 +251,8 @@ public class BubbleController { BubbleData data = new BubbleData(context, logger, positioner, mainExecutor); return new BubbleController(context, data, synchronizer, floatingContentCoordinator, new BubbleDataRepository(context, launcherApps, mainExecutor), - statusBarService, windowManager, windowManagerShellWrapper, launcherApps, - logger, taskStackListener, organizer, positioner, displayController, + statusBarService, windowManager, windowManagerShellWrapper, userManager, + launcherApps, logger, taskStackListener, organizer, positioner, displayController, oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, taskViewTransitions, syncQueue); } @@ -266,6 +269,7 @@ public class BubbleController { @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, + UserManager userManager, LauncherApps launcherApps, BubbleLogger bubbleLogger, TaskStackListenerImpl taskStackListener, @@ -287,6 +291,7 @@ public class BubbleController { : statusBarService; mWindowManager = windowManager; mWindowManagerShellWrapper = windowManagerShellWrapper; + mUserManager = userManager; mFloatingContentCoordinator = floatingContentCoordinator; mDataRepository = dataRepository; mLogger = bubbleLogger; @@ -447,6 +452,10 @@ public class BubbleController { mOneHandedOptional.ifPresent(this::registerOneHandedState); mDragAndDropController.addListener(this::collapseStack); + + // Clear out any persisted bubbles on disk that no longer have a valid user. + List<UserInfo> users = mUserManager.getAliveUsers(); + mDataRepository.sanitizeBubbles(users); } @VisibleForTesting @@ -590,6 +599,17 @@ public class BubbleController { mCurrentProfiles = currentProfiles; } + /** Called when a user is removed from the device, including work profiles. */ + public void onUserRemoved(int removedUserId) { + UserInfo parent = mUserManager.getProfileParent(removedUserId); + int parentUserId = parent != null ? parent.getUserHandle().getIdentifier() : -1; + mBubbleData.removeBubblesForUser(removedUserId); + // Typically calls from BubbleData would remove bubbles from the DataRepository as well, + // however, this gets complicated when users are removed (mCurrentUserId won't necessarily + // be correct for this) so we update the repo directly. + mDataRepository.removeBubblesForUser(removedUserId, parentUserId); + } + /** Whether this userId belongs to the current user. */ private boolean isCurrentProfile(int userId) { return userId == UserHandle.USER_ALL @@ -1809,6 +1829,13 @@ public class BubbleController { } @Override + public void onUserRemoved(int removedUserId) { + mMainExecutor.execute(() -> { + BubbleController.this.onUserRemoved(removedUserId); + }); + } + + @Override public void onConfigChanged(Configuration newConfig) { mMainExecutor.execute(() -> { BubbleController.this.onConfigChanged(newConfig); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index e4a0fd03860c..fa86c8436647 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -465,7 +465,7 @@ public class BubbleData { getOverflowBubbles(), invalidBubblesFromPackage, removeBubble); } - /** Dismisses all bubbles from the given package. */ + /** Removes all bubbles from the given package. */ public void removeBubblesWithPackageName(String packageName, int reason) { final Predicate<Bubble> bubbleMatchesPackage = bubble -> bubble.getPackageName().equals(packageName); @@ -477,6 +477,18 @@ public class BubbleData { performActionOnBubblesMatching(getOverflowBubbles(), bubbleMatchesPackage, removeBubble); } + /** Removes all bubbles for the given user. */ + public void removeBubblesForUser(int userId) { + List<Bubble> removedBubbles = filterAllBubbles(bubble -> + userId == bubble.getUser().getIdentifier()); + for (Bubble b : removedBubbles) { + doRemove(b.getKey(), Bubbles.DISMISS_USER_REMOVED); + } + if (!removedBubbles.isEmpty()) { + dispatchPendingChanges(); + } + } + private void doAdd(Bubble bubble) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "doAdd: " + bubble); @@ -552,7 +564,8 @@ public class BubbleData { || reason == Bubbles.DISMISS_BLOCKED || reason == Bubbles.DISMISS_SHORTCUT_REMOVED || reason == Bubbles.DISMISS_PACKAGE_REMOVED - || reason == Bubbles.DISMISS_USER_CHANGED; + || reason == Bubbles.DISMISS_USER_CHANGED + || reason == Bubbles.DISMISS_USER_REMOVED; int indexToRemove = indexForKey(key); if (indexToRemove == -1) { @@ -1073,6 +1086,35 @@ public class BubbleData { return null; } + /** + * Returns a list of bubbles that match the provided predicate. This checks all types of + * bubbles (i.e. pending, suppressed, active, and overflowed). + */ + private List<Bubble> filterAllBubbles(Predicate<Bubble> predicate) { + ArrayList<Bubble> matchingBubbles = new ArrayList<>(); + for (Bubble b : mPendingBubbles.values()) { + if (predicate.test(b)) { + matchingBubbles.add(b); + } + } + for (Bubble b : mSuppressedBubbles.values()) { + if (predicate.test(b)) { + matchingBubbles.add(b); + } + } + for (Bubble b : mBubbles) { + if (predicate.test(b)) { + matchingBubbles.add(b); + } + } + for (Bubble b : mOverflowBubbles) { + if (predicate.test(b)) { + matchingBubbles.add(b); + } + } + return matchingBubbles; + } + @VisibleForTesting(visibility = PRIVATE) void setTimeSource(TimeSource timeSource) { mTimeSource = timeSource; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt index 9d9e442affd3..97560f44fb06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt @@ -22,6 +22,7 @@ import android.content.pm.LauncherApps import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER +import android.content.pm.UserInfo import android.os.UserHandle import android.util.Log import com.android.wm.shell.bubbles.storage.BubbleEntity @@ -73,6 +74,22 @@ internal class BubbleDataRepository( if (entities.isNotEmpty()) persistToDisk() } + /** + * Removes all the bubbles associated with the provided user from memory. Then persists the + * snapshot to disk asynchronously. + */ + fun removeBubblesForUser(@UserIdInt userId: Int, @UserIdInt parentId: Int) { + if (volatileRepository.removeBubblesForUser(userId, parentId)) persistToDisk() + } + + /** + * Remove any bubbles that don't have a user id from the provided list of users. + */ + fun sanitizeBubbles(users: List<UserInfo>) { + val userIds = users.map { u -> u.id } + if (volatileRepository.sanitizeBubbles(userIds)) persistToDisk() + } + private fun transform(bubbles: List<Bubble>): List<BubbleEntity> { return bubbles.mapNotNull { b -> BubbleEntity( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index c7db8d8d1646..8a0db0a12711 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -57,7 +57,7 @@ public interface Bubbles { DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, - DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK}) + DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason {} @@ -76,6 +76,7 @@ public interface Bubbles { int DISMISS_PACKAGE_REMOVED = 13; int DISMISS_NO_BUBBLE_UP = 14; int DISMISS_RELOAD_FROM_DISK = 15; + int DISMISS_USER_REMOVED = 16; /** * @return {@code true} if there is a bubble associated with the provided key and if its @@ -243,6 +244,13 @@ public interface Bubbles { void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles); /** + * Called when a user is removed. + * + * @param removedUserId the id of the removed user. + */ + void onUserRemoved(int removedUserId); + + /** * Called when config changed. * * @param newConfig the new config. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt index a5267d8be9fe..0d3ba9a78e35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.bubbles.storage +import android.annotation.UserIdInt import android.content.pm.LauncherApps import android.os.UserHandle import android.util.SparseArray @@ -95,10 +96,63 @@ class BubbleVolatileRepository(private val launcherApps: LauncherApps) { } @Synchronized - fun removeBubbles(userId: Int, bubbles: List<BubbleEntity>) = + fun removeBubbles(@UserIdInt userId: Int, bubbles: List<BubbleEntity>) = uncache(bubbles.filter { b: BubbleEntity -> getEntities(userId).removeIf { e: BubbleEntity -> b.key == e.key } }) + /** + * Removes all the bubbles associated with the provided userId. + * @return whether bubbles were removed or not. + */ + @Synchronized + fun removeBubblesForUser(@UserIdInt userId: Int, @UserIdInt parentUserId: Int): Boolean { + if (parentUserId != -1) { + return removeBubblesForUserWithParent(userId, parentUserId) + } else { + val entities = entitiesByUser.get(userId) + entitiesByUser.remove(userId) + return entities != null + } + } + + /** + * Removes all the bubbles associated with the provided userId when that userId is part of + * a profile (e.g. managed account). + * + * @return whether bubbles were removed or not. + */ + @Synchronized + private fun removeBubblesForUserWithParent( + @UserIdInt userId: Int, + @UserIdInt parentUserId: Int + ): Boolean { + return entitiesByUser.get(parentUserId).removeIf { b: BubbleEntity -> b.userId == userId } + } + + /** + * Goes through all the persisted bubbles and removes them if the user is not in the active + * list of users. + * + * @return whether the list of bubbles changed or not (i.e. was a removal made). + */ + @Synchronized + fun sanitizeBubbles(activeUsers: List<Int>): Boolean { + for (i in 0 until entitiesByUser.size()) { + // First check if the user is a parent / top-level user + val parentUserId = entitiesByUser.keyAt(i) + if (!activeUsers.contains(parentUserId)) { + return removeBubblesForUser(parentUserId, -1) + } else { + // Then check if each of the bubbles in the top-level user, still has a valid user + // as it could belong to a profile and have a different id from the parent. + return entitiesByUser.get(parentUserId).removeIf { b: BubbleEntity -> + !activeUsers.contains(b.userId) + } + } + } + return false + } + private fun cache(bubbles: List<BubbleEntity>) { bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) -> launcherApps.cacheShortcuts(key.pkg, bubbles.map { it.shortcutId }, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 98de60c419b3..b3799e2cf8d9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -20,6 +20,7 @@ import android.animation.AnimationHandler; import android.content.Context; import android.content.pm.LauncherApps; import android.os.Handler; +import android.os.UserManager; import android.view.WindowManager; import com.android.internal.jank.InteractionJankMonitor; @@ -106,6 +107,7 @@ public class WMShellModule { IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, + UserManager userManager, LauncherApps launcherApps, TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger, @@ -120,7 +122,7 @@ public class WMShellModule { SyncTransactionQueue syncQueue) { return BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator, statusBarService, windowManager, - windowManagerShellWrapper, launcherApps, taskStackListener, + windowManagerShellWrapper, userManager, launcherApps, taskStackListener, uiEventLogger, organizer, displayController, oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, taskViewTransitions, syncQueue); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index c3369e4bb90b..49b6968f9417 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -254,6 +254,7 @@ public class BackAnimationControllerTest { verify(mIOnBackInvokedCallback, never()).onBackInvoked(); } + @Test public void ignoresGesture_transitionInProgress() throws RemoteException { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index bde94d9d6c29..e6711aca19c1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -129,8 +129,8 @@ public class BubbleDataTest extends ShellTestCase { mEntryA3 = createBubbleEntry(1, "a3", "package.a", null); mEntryB1 = createBubbleEntry(1, "b1", "package.b", null); mEntryB2 = createBubbleEntry(1, "b2", "package.b", null); - mEntryB3 = createBubbleEntry(1, "b3", "package.b", null); - mEntryC1 = createBubbleEntry(1, "c1", "package.c", null); + mEntryB3 = createBubbleEntry(11, "b3", "package.b", null); + mEntryC1 = createBubbleEntry(11, "c1", "package.c", null); NotificationListenerService.Ranking ranking = mock(NotificationListenerService.Ranking.class); @@ -1058,6 +1058,37 @@ public class BubbleDataTest extends ShellTestCase { assertBubbleListContains(mBubbleA2, mBubbleA1, mBubbleLocusId); } + @Test + public void test_removeBubblesForUser() { + // A is user 1 + sendUpdatedEntryAtTime(mEntryA1, 2000); + sendUpdatedEntryAtTime(mEntryA2, 3000); + // B & C belong to user 11 + sendUpdatedEntryAtTime(mEntryB3, 4000); + sendUpdatedEntryAtTime(mEntryC1, 5000); + mBubbleData.setListener(mListener); + + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); + verifyUpdateReceived(); + assertOverflowChangedTo(ImmutableList.of(mBubbleA1)); + assertBubbleListContains(mBubbleC1, mBubbleB3, mBubbleA2); + + // Remove all the A bubbles + mBubbleData.removeBubblesForUser(1); + verifyUpdateReceived(); + + // Verify the update has the removals. + BubbleData.Update update = mUpdateCaptor.getValue(); + assertThat(update.removedBubbles.get(0)).isEqualTo( + Pair.create(mBubbleA2, Bubbles.DISMISS_USER_REMOVED)); + assertThat(update.removedBubbles.get(1)).isEqualTo( + Pair.create(mBubbleA1, Bubbles.DISMISS_USER_REMOVED)); + + // Verify no A bubbles in active or overflow. + assertBubbleListContains(mBubbleC1, mBubbleB3); + assertOverflowChangedTo(ImmutableList.of()); + } + private void verifyUpdateReceived() { verify(mListener).applyUpdate(mUpdateCaptor.capture()); reset(mListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt index bfdf5208bbf0..77c70557fb70 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt @@ -23,14 +23,19 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import org.junit.Test import com.android.wm.shell.ShellTestCase +import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertEquals import org.junit.Before import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito import org.mockito.Mockito.mock -import org.mockito.Mockito.verify +import org.mockito.Mockito.never import org.mockito.Mockito.reset +import org.mockito.Mockito.verify @SmallTest @RunWith(AndroidTestingRunner::class) @@ -41,17 +46,17 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { private val user11 = UserHandle.of(11) // user, package, shortcut, notification key, height, res-height, title, taskId, locusId - private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", - "0key-1", 120, 0, null, 1, null) - private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob", - "10key-2", 0, 16537428, "title", 2, null) - private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", - "0key-3", 120, 0, null, INVALID_TASK_ID, null) - - private val bubble11 = BubbleEntity(11, "com.example.messenger", - "shortcut-1", "01key-1", 120, 0, null, 3) - private val bubble12 = BubbleEntity(11, "com.example.chat", "alice and bob", - "11key-2", 0, 16537428, "title", INVALID_TASK_ID) + private val bubble1 = BubbleEntity(user0.identifier, + "com.example.messenger", "shortcut-1", "0key-1", 120, 0, null, 1, null) + private val bubble2 = BubbleEntity(user10_managed.identifier, + "com.example.chat", "alice and bob", "10key-2", 0, 16537428, "title", 2, null) + private val bubble3 = BubbleEntity(user0.identifier, + "com.example.messenger", "shortcut-2", "0key-3", 120, 0, null, INVALID_TASK_ID, null) + + private val bubble11 = BubbleEntity(user11.identifier, + "com.example.messenger", "shortcut-1", "01key-1", 120, 0, null, 3) + private val bubble12 = BubbleEntity(user11.identifier, + "com.example.chat", "alice and bob", "11key-2", 0, 16537428, "title", INVALID_TASK_ID) private val user0bubbles = listOf(bubble1, bubble2, bubble3) private val user11bubbles = listOf(bubble11, bubble12) @@ -151,6 +156,119 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { repository.addBubbles(user0.identifier, listOf(bubbleModified)) assertEquals(bubbleModified, repository.getEntities(user0.identifier).get(0)) } + + @Test + fun testRemoveBubblesForUser() { + repository.addBubbles(user0.identifier, user0bubbles) + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble2, bubble3)) + + val ret = repository.removeBubblesForUser(user0.identifier, -1) + assertThat(ret).isTrue() // bubbles were removed + + assertThat(repository.getEntities(user0.identifier).toList()).isEmpty() + verify(launcherApps, never()).uncacheShortcuts(anyString(), + any(), + any(UserHandle::class.java), anyInt()) + } + + @Test + fun testRemoveBubblesForUser_parentUserRemoved() { + repository.addBubbles(user0.identifier, user0bubbles) + // bubble2 is the work profile bubble + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble2, bubble3)) + + val ret = repository.removeBubblesForUser(user10_managed.identifier, user0.identifier) + assertThat(ret).isTrue() // bubbles were removed + + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble3)) + verify(launcherApps, never()).uncacheShortcuts(anyString(), + any(), + any(UserHandle::class.java), anyInt()) + } + + @Test + fun testRemoveBubblesForUser_withoutBubbles() { + repository.addBubbles(user0.identifier, user0bubbles) + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble2, bubble3)) + + val ret = repository.removeBubblesForUser(user11.identifier, -1) + assertThat(ret).isFalse() // bubbles were NOT removed + + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble2, bubble3)) + verify(launcherApps, never()).uncacheShortcuts(anyString(), + any(), + any(UserHandle::class.java), anyInt()) + } + + @Test + fun testSanitizeBubbles_noChanges() { + repository.addBubbles(user0.identifier, user0bubbles) + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble2, bubble3)) + repository.addBubbles(user11.identifier, user11bubbles) + assertThat(repository.getEntities(user11.identifier).toList()) + .isEqualTo(listOf(bubble11, bubble12)) + + val ret = repository.sanitizeBubbles(listOf(user0.identifier, + user10_managed.identifier, + user11.identifier)) + assertThat(ret).isFalse() // bubbles were NOT removed + + verify(launcherApps, never()).uncacheShortcuts(anyString(), + any(), + any(UserHandle::class.java), anyInt()) + } + + @Test + fun testSanitizeBubbles_userRemoved() { + repository.addBubbles(user0.identifier, user0bubbles) + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble2, bubble3)) + repository.addBubbles(user11.identifier, user11bubbles) + assertThat(repository.getEntities(user11.identifier).toList()) + .isEqualTo(listOf(bubble11, bubble12)) + + val ret = repository.sanitizeBubbles(listOf(user11.identifier)) + assertThat(ret).isTrue() // bubbles were removed + + assertThat(repository.getEntities(user0.identifier).toList()).isEmpty() + verify(launcherApps, never()).uncacheShortcuts(anyString(), + any(), + any(UserHandle::class.java), anyInt()) + + // User 11 bubbles should still be here + assertThat(repository.getEntities(user11.identifier).toList()) + .isEqualTo(listOf(bubble11, bubble12)) + } + + @Test + fun testSanitizeBubbles_userParentRemoved() { + repository.addBubbles(user0.identifier, user0bubbles) + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble2, bubble3)) + + repository.addBubbles(user11.identifier, user11bubbles) + assertThat(repository.getEntities(user11.identifier).toList()) + .isEqualTo(listOf(bubble11, bubble12)) + + val ret = repository.sanitizeBubbles(listOf(user0.identifier, user11.identifier)) + assertThat(ret).isTrue() // bubbles were removed + // bubble2 is the work profile bubble and should be removed + assertThat(repository.getEntities(user0.identifier).toList()) + .isEqualTo(listOf(bubble1, bubble3)) + verify(launcherApps, never()).uncacheShortcuts(anyString(), + any(), + any(UserHandle::class.java), anyInt()) + + // User 11 bubbles should still be here + assertThat(repository.getEntities(user11.identifier).toList()) + .isEqualTo(listOf(bubble11, bubble12)) + } } private const val PKG_MESSENGER = "com.example.messenger" |