Merge "Ignore notification update for not active user" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7fb9bfccb6
@@ -70,12 +70,10 @@ import android.os.UserHandle;
|
|||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.service.notification.NotificationListenerService.RankingMap;
|
import android.service.notification.NotificationListenerService.RankingMap;
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.SparseSetArray;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
@@ -109,8 +107,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
@@ -177,8 +177,8 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
private int mCurrentUserId;
|
private int mCurrentUserId;
|
||||||
// Current profiles of the user (e.g. user with a workprofile)
|
// Current profiles of the user (e.g. user with a workprofile)
|
||||||
private SparseArray<UserInfo> mCurrentProfiles;
|
private SparseArray<UserInfo> mCurrentProfiles;
|
||||||
// Saves notification keys of active bubbles when users are switched.
|
// Saves data about active bubbles when users are switched.
|
||||||
private final SparseSetArray<String> mSavedBubbleKeysPerUser;
|
private final SparseArray<UserBubbleData> mSavedUserBubbleData;
|
||||||
|
|
||||||
// Used when ranking updates occur and we check if things should bubble / unbubble
|
// Used when ranking updates occur and we check if things should bubble / unbubble
|
||||||
private NotificationListenerService.Ranking mTmpRanking;
|
private NotificationListenerService.Ranking mTmpRanking;
|
||||||
@@ -271,7 +271,7 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
mCurrentUserId = ActivityManager.getCurrentUser();
|
mCurrentUserId = ActivityManager.getCurrentUser();
|
||||||
mBubblePositioner = positioner;
|
mBubblePositioner = positioner;
|
||||||
mBubbleData = data;
|
mBubbleData = data;
|
||||||
mSavedBubbleKeysPerUser = new SparseSetArray<>();
|
mSavedUserBubbleData = new SparseArray<>();
|
||||||
mBubbleIconFactory = new BubbleIconFactory(context);
|
mBubbleIconFactory = new BubbleIconFactory(context);
|
||||||
mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(context);
|
mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(context);
|
||||||
mDisplayController = displayController;
|
mDisplayController = displayController;
|
||||||
@@ -420,6 +420,13 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
List<UserInfo> users = mUserManager.getAliveUsers();
|
List<UserInfo> users = mUserManager.getAliveUsers();
|
||||||
mDataRepository.sanitizeBubbles(users);
|
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);
|
mShellController.addConfigurationChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,11 +781,13 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
*/
|
*/
|
||||||
private void saveBubbles(@UserIdInt int userId) {
|
private void saveBubbles(@UserIdInt int userId) {
|
||||||
// First clear any existing keys that might be stored.
|
// 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.
|
// Add in all active bubbles for the current user.
|
||||||
for (Bubble bubble : mBubbleData.getBubbles()) {
|
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
|
* @param userId the id of the user
|
||||||
*/
|
*/
|
||||||
private void restoreBubbles(@UserIdInt int userId) {
|
private void restoreBubbles(@UserIdInt int userId) {
|
||||||
ArraySet<String> savedBubbleKeys = mSavedBubbleKeysPerUser.get(userId);
|
UserBubbleData savedBubbleData = mSavedUserBubbleData.get(userId);
|
||||||
if (savedBubbleKeys == null) {
|
if (savedBubbleData == null) {
|
||||||
// There were no bubbles saved for this used.
|
// There were no bubbles saved for this used.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys, (entries) -> {
|
mSysuiProxy.getShouldRestoredEntries(savedBubbleData.getKeys(), (entries) -> {
|
||||||
mMainExecutor.execute(() -> {
|
mMainExecutor.execute(() -> {
|
||||||
for (BubbleEntry e : entries) {
|
for (BubbleEntry e : entries) {
|
||||||
if (canLaunchInTaskView(mContext, e)) {
|
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.
|
// Finally, remove the entries for this user now that bubbles are restored.
|
||||||
mSavedBubbleKeysPerUser.remove(userId);
|
mSavedUserBubbleData.remove(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -993,7 +1003,19 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void updateBubble(BubbleEntry notif) {
|
public void updateBubble(BubbleEntry notif) {
|
||||||
|
int bubbleUserId = notif.getStatusBarNotification().getUserId();
|
||||||
|
if (isCurrentProfile(bubbleUserId)) {
|
||||||
updateBubble(notif, false /* suppressFlyout */, true /* showInShade */);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
|
|||||||
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.service.notification.NotificationListenerService.RankingMap;
|
import android.service.notification.NotificationListenerService.RankingMap;
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
@@ -42,6 +40,7 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
@@ -284,7 +283,7 @@ public interface Bubbles {
|
|||||||
|
|
||||||
void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback);
|
void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback);
|
||||||
|
|
||||||
void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
|
void getShouldRestoredEntries(Set<String> savedBubbleKeys,
|
||||||
Consumer<List<BubbleEntry>> callback);
|
Consumer<List<BubbleEntry>> callback);
|
||||||
|
|
||||||
void setNotificationInterruption(String key);
|
void setNotificationInterruption(String key);
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import android.os.UserHandle;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.service.notification.NotificationListenerService.RankingMap;
|
import android.service.notification.NotificationListenerService.RankingMap;
|
||||||
import android.service.notification.ZenModeConfig;
|
import android.service.notification.ZenModeConfig;
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@@ -83,6 +82,7 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
@@ -262,7 +262,7 @@ public class BubblesManager implements Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
|
public void getShouldRestoredEntries(Set<String> savedBubbleKeys,
|
||||||
Consumer<List<BubbleEntry>> callback) {
|
Consumer<List<BubbleEntry>> callback) {
|
||||||
sysuiMainExecutor.execute(() -> {
|
sysuiMainExecutor.execute(() -> {
|
||||||
List<BubbleEntry> result = new ArrayList<>();
|
List<BubbleEntry> result = new ArrayList<>();
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.app.IActivityManager;
|
import android.app.IActivityManager;
|
||||||
import android.app.INotificationManager;
|
import android.app.INotificationManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
@@ -143,7 +144,10 @@ import org.mockito.ArgumentCaptor;
|
|||||||
import org.mockito.Captor;
|
import org.mockito.Captor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -252,6 +256,8 @@ public class BubblesTest extends SysuiTestCase {
|
|||||||
private TaskViewTransitions mTaskViewTransitions;
|
private TaskViewTransitions mTaskViewTransitions;
|
||||||
@Mock
|
@Mock
|
||||||
private Optional<OneHandedController> mOneHandedOptional;
|
private Optional<OneHandedController> mOneHandedOptional;
|
||||||
|
@Mock
|
||||||
|
private UserManager mUserManager;
|
||||||
|
|
||||||
private TestableBubblePositioner mPositioner;
|
private TestableBubblePositioner mPositioner;
|
||||||
|
|
||||||
@@ -314,6 +320,9 @@ public class BubblesTest extends SysuiTestCase {
|
|||||||
mPositioner.setMaxBubbles(5);
|
mPositioner.setMaxBubbles(5);
|
||||||
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
|
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
|
||||||
|
|
||||||
|
when(mUserManager.getProfiles(ActivityManager.getCurrentUser())).thenReturn(
|
||||||
|
Collections.singletonList(mock(UserInfo.class)));
|
||||||
|
|
||||||
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
|
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
|
||||||
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
|
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
|
||||||
mock(PowerManager.class),
|
mock(PowerManager.class),
|
||||||
@@ -339,7 +348,7 @@ public class BubblesTest extends SysuiTestCase {
|
|||||||
mStatusBarService,
|
mStatusBarService,
|
||||||
mWindowManager,
|
mWindowManager,
|
||||||
mWindowManagerShellWrapper,
|
mWindowManagerShellWrapper,
|
||||||
mock(UserManager.class),
|
mUserManager,
|
||||||
mLauncherApps,
|
mLauncherApps,
|
||||||
mBubbleLogger,
|
mBubbleLogger,
|
||||||
mTaskStackListener,
|
mTaskStackListener,
|
||||||
@@ -1025,7 +1034,7 @@ public class BubblesTest extends SysuiTestCase {
|
|||||||
assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull();
|
assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull();
|
||||||
|
|
||||||
// Switch users
|
// Switch users
|
||||||
mBubbleController.onUserChanged(secondUserId);
|
switchUser(secondUserId);
|
||||||
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
|
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
|
||||||
|
|
||||||
// Give this user some bubbles
|
// Give this user some bubbles
|
||||||
@@ -1042,6 +1051,41 @@ public class BubblesTest extends SysuiTestCase {
|
|||||||
verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
|
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.
|
* Verifies we only load the overflow data once.
|
||||||
*/
|
*/
|
||||||
@@ -1443,6 +1487,14 @@ public class BubblesTest extends SysuiTestCase {
|
|||||||
.build();
|
.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.
|
* Asserts that the bubble stack is expanded and also validates the cached state is updated.
|
||||||
|
|||||||
Reference in New Issue
Block a user