Merge "Ignore notification update for not active user" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-08-01 18:23:53 +00:00
committed by Android (Google) Code Review
4 changed files with 122 additions and 20 deletions

View File

@@ -70,12 +70,10 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseSetArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -109,8 +107,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -177,8 +177,8 @@ public class BubbleController implements ConfigurationChangeListener {
private int mCurrentUserId;
// Current profiles of the user (e.g. user with a workprofile)
private SparseArray<UserInfo> mCurrentProfiles;
// Saves notification keys of active bubbles when users are switched.
private final SparseSetArray<String> mSavedBubbleKeysPerUser;
// Saves data about active bubbles when users are switched.
private final SparseArray<UserBubbleData> mSavedUserBubbleData;
// Used when ranking updates occur and we check if things should bubble / unbubble
private NotificationListenerService.Ranking mTmpRanking;
@@ -271,7 +271,7 @@ public class BubbleController implements ConfigurationChangeListener {
mCurrentUserId = ActivityManager.getCurrentUser();
mBubblePositioner = positioner;
mBubbleData = data;
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mSavedUserBubbleData = new SparseArray<>();
mBubbleIconFactory = new BubbleIconFactory(context);
mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(context);
mDisplayController = displayController;
@@ -420,6 +420,13 @@ public class BubbleController implements ConfigurationChangeListener {
List<UserInfo> users = mUserManager.getAliveUsers();
mDataRepository.sanitizeBubbles(users);
// Init profiles
SparseArray<UserInfo> userProfiles = new SparseArray<>();
for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
userProfiles.put(user.id, user);
}
mCurrentProfiles = userProfiles;
mShellController.addConfigurationChangeListener(this);
}
@@ -774,11 +781,13 @@ public class BubbleController implements ConfigurationChangeListener {
*/
private void saveBubbles(@UserIdInt int userId) {
// First clear any existing keys that might be stored.
mSavedBubbleKeysPerUser.remove(userId);
mSavedUserBubbleData.remove(userId);
UserBubbleData userBubbleData = new UserBubbleData();
// Add in all active bubbles for the current user.
for (Bubble bubble : mBubbleData.getBubbles()) {
mSavedBubbleKeysPerUser.add(userId, bubble.getKey());
userBubbleData.add(bubble.getKey(), bubble.showInShade());
}
mSavedUserBubbleData.put(userId, userBubbleData);
}
/**
@@ -787,22 +796,23 @@ public class BubbleController implements ConfigurationChangeListener {
* @param userId the id of the user
*/
private void restoreBubbles(@UserIdInt int userId) {
ArraySet<String> savedBubbleKeys = mSavedBubbleKeysPerUser.get(userId);
if (savedBubbleKeys == null) {
UserBubbleData savedBubbleData = mSavedUserBubbleData.get(userId);
if (savedBubbleData == null) {
// There were no bubbles saved for this used.
return;
}
mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys, (entries) -> {
mSysuiProxy.getShouldRestoredEntries(savedBubbleData.getKeys(), (entries) -> {
mMainExecutor.execute(() -> {
for (BubbleEntry e : entries) {
if (canLaunchInTaskView(mContext, e)) {
updateBubble(e, true /* suppressFlyout */, false /* showInShade */);
boolean showInShade = savedBubbleData.isShownInShade(e.getKey());
updateBubble(e, true /* suppressFlyout */, showInShade);
}
}
});
});
// Finally, remove the entries for this user now that bubbles are restored.
mSavedBubbleKeysPerUser.remove(userId);
mSavedUserBubbleData.remove(userId);
}
@Override
@@ -993,7 +1003,19 @@ public class BubbleController implements ConfigurationChangeListener {
*/
@VisibleForTesting
public void updateBubble(BubbleEntry notif) {
updateBubble(notif, false /* suppressFlyout */, true /* showInShade */);
int bubbleUserId = notif.getStatusBarNotification().getUserId();
if (isCurrentProfile(bubbleUserId)) {
updateBubble(notif, false /* suppressFlyout */, true /* showInShade */);
} else {
// Skip update, but store it in user bubbles so it gets restored after user switch
mSavedUserBubbleData.get(bubbleUserId, new UserBubbleData()).add(notif.getKey(),
true /* shownInShade */);
if (DEBUG_BUBBLE_CONTROLLER) {
Log.d(TAG,
"Ignore update to bubble for not active user. Bubble userId=" + bubbleUserId
+ " current userId=" + mCurrentUserId);
}
}
}
/**
@@ -1842,4 +1864,33 @@ public class BubbleController implements ConfigurationChangeListener {
}
}
}
/**
* Bubble data that is stored per user.
* Used to store and restore active bubbles during user switching.
*/
private static class UserBubbleData {
private final Map<String, Boolean> mKeyToShownInShadeMap = new HashMap<>();
/**
* Add bubble key and whether it should be shown in notification shade
*/
void add(String key, boolean shownInShade) {
mKeyToShownInShadeMap.put(key, shownInShade);
}
/**
* Get all bubble keys stored for this user
*/
Set<String> getKeys() {
return mKeyToShownInShadeMap.keySet();
}
/**
* Check if this bubble with the given key should be shown in the notification shade
*/
boolean isShownInShade(String key) {
return mKeyToShownInShadeMap.get(key);
}
}
}

View File

@@ -23,12 +23,10 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.app.NotificationChannel;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
@@ -42,6 +40,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -284,7 +283,7 @@ public interface Bubbles {
void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback);
void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
void getShouldRestoredEntries(Set<String> savedBubbleKeys,
Consumer<List<BubbleEntry>> callback);
void setNotificationInterruption(String key);

View File

@@ -40,7 +40,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -83,6 +82,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -262,7 +262,7 @@ public class BubblesManager implements Dumpable {
}
@Override
public void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
public void getShouldRestoredEntries(Set<String> savedBubbleKeys,
Consumer<List<BubbleEntry>> callback) {
sysuiMainExecutor.execute(() -> {
List<BubbleEntry> result = new ArrayList<>();

View File

@@ -43,6 +43,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
@@ -143,7 +144,10 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@@ -252,6 +256,8 @@ public class BubblesTest extends SysuiTestCase {
private TaskViewTransitions mTaskViewTransitions;
@Mock
private Optional<OneHandedController> mOneHandedOptional;
@Mock
private UserManager mUserManager;
private TestableBubblePositioner mPositioner;
@@ -314,6 +320,9 @@ public class BubblesTest extends SysuiTestCase {
mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
when(mUserManager.getProfiles(ActivityManager.getCurrentUser())).thenReturn(
Collections.singletonList(mock(UserInfo.class)));
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
mock(PowerManager.class),
@@ -339,7 +348,7 @@ public class BubblesTest extends SysuiTestCase {
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
mock(UserManager.class),
mUserManager,
mLauncherApps,
mBubbleLogger,
mTaskStackListener,
@@ -1025,7 +1034,7 @@ public class BubblesTest extends SysuiTestCase {
assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull();
// Switch users
mBubbleController.onUserChanged(secondUserId);
switchUser(secondUserId);
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
// Give this user some bubbles
@@ -1042,6 +1051,41 @@ public class BubblesTest extends SysuiTestCase {
verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
}
@Test
public void testOnUserChanged_bubblesRestored() {
int firstUserId = mBubbleEntry.getStatusBarNotification().getUser().getIdentifier();
int secondUserId = mBubbleEntryUser11.getStatusBarNotification().getUser().getIdentifier();
// Mock current profile
when(mLockscreenUserManager.isCurrentProfile(firstUserId)).thenReturn(true);
when(mLockscreenUserManager.isCurrentProfile(secondUserId)).thenReturn(false);
mBubbleController.updateBubble(mBubbleEntry);
assertThat(mBubbleController.hasBubbles()).isTrue();
// We start with 1 bubble
assertThat(mBubbleData.getBubbles()).hasSize(1);
// Switch to second user
switchUser(secondUserId);
// Second user has no bubbles
assertThat(mBubbleController.hasBubbles()).isFalse();
// Send bubble update for first user, ensure it does not show up
mBubbleController.updateBubble(mBubbleEntry2);
assertThat(mBubbleController.hasBubbles()).isFalse();
// Start returning notif for first user again
when(mCommonNotifCollection.getAllNotifs()).thenReturn(Arrays.asList(mRow, mRow2));
// Switch back to first user
switchUser(firstUserId);
// Check we now have two bubbles, one previous and one new that came in
assertThat(mBubbleController.hasBubbles()).isTrue();
// Now there are 2 bubbles
assertThat(mBubbleData.getBubbles()).hasSize(2);
}
/**
* Verifies we only load the overflow data once.
*/
@@ -1443,6 +1487,14 @@ public class BubblesTest extends SysuiTestCase {
.build();
}
private void switchUser(int userId) {
when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenAnswer(
(Answer<Boolean>) invocation -> invocation.<Integer>getArgument(0) == userId);
SparseArray<UserInfo> userInfos = new SparseArray<>(1);
userInfos.put(userId, mock(UserInfo.class));
mBubbleController.onCurrentProfilesChanged(userInfos);
mBubbleController.onUserChanged(userId);
}
/**
* Asserts that the bubble stack is expanded and also validates the cached state is updated.