blob: d6dac2f36ba1fa6764640587c23099e8bcaa2510 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.bubbles;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleData.TimeSource;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubbleDataTest extends SysuiTestCase {
private NotificationEntry mEntryA1;
private NotificationEntry mEntryA2;
private NotificationEntry mEntryA3;
private NotificationEntry mEntryB1;
private NotificationEntry mEntryB2;
private NotificationEntry mEntryB3;
private NotificationEntry mEntryC1;
private Bubble mBubbleA1;
private Bubble mBubbleA2;
private Bubble mBubbleA3;
private Bubble mBubbleB1;
private Bubble mBubbleB2;
private Bubble mBubbleB3;
private Bubble mBubbleC1;
private BubbleData mBubbleData;
@Mock
private TimeSource mTimeSource;
@Mock
private BubbleData.Listener mListener;
@Mock
private PendingIntent mExpandIntent;
@Mock
private PendingIntent mDeleteIntent;
private NotificationTestHelper mNotificationTestHelper;
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(mContext);
MockitoAnnotations.initMocks(this);
mEntryA1 = createBubbleEntry(1, "a1", "package.a");
mEntryA2 = createBubbleEntry(1, "a2", "package.a");
mEntryA3 = createBubbleEntry(1, "a3", "package.a");
mEntryB1 = createBubbleEntry(1, "b1", "package.b");
mEntryB2 = createBubbleEntry(1, "b2", "package.b");
mEntryB3 = createBubbleEntry(1, "b3", "package.b");
mEntryC1 = createBubbleEntry(1, "c1", "package.c");
mBubbleA1 = new Bubble(mEntryA1);
mBubbleA2 = new Bubble(mEntryA2);
mBubbleA3 = new Bubble(mEntryA3);
mBubbleB1 = new Bubble(mEntryB1);
mBubbleB2 = new Bubble(mEntryB2);
mBubbleB3 = new Bubble(mEntryB3);
mBubbleC1 = new Bubble(mEntryC1);
mBubbleData = new BubbleData(getContext());
// Used by BubbleData to set lastAccessedTime
when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
mBubbleData.setTimeSource(mTimeSource);
}
private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
return createBubbleEntry(userId, notifKey, packageName, 1000);
}
private void setPostTime(NotificationEntry entry, long postTime) {
when(entry.notification.getPostTime()).thenReturn(postTime);
}
private void setOngoing(NotificationEntry entry, boolean ongoing) {
if (ongoing) {
entry.notification.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
} else {
entry.notification.getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
}
}
/**
* No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
* required for BubbleData functionality and verification. NotificationTestHelper is used only
* as a convenience to create a Notification w/BubbleMetadata.
*/
private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName,
long postTime) {
// BubbleMetadata
Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder()
.setIntent(mExpandIntent)
.setDeleteIntent(mDeleteIntent)
.setIcon(Icon.createWithResource("", 0))
.build();
// Notification -> BubbleMetadata
Notification notification = mNotificationTestHelper.createNotification(false,
null /* groupKey */, bubbleMetadata);
// StatusBarNotification
StatusBarNotification sbn = mock(StatusBarNotification.class);
when(sbn.getKey()).thenReturn(notifKey);
when(sbn.getUser()).thenReturn(new UserHandle(userId));
when(sbn.getPackageName()).thenReturn(packageName);
when(sbn.getPostTime()).thenReturn(postTime);
when(sbn.getNotification()).thenReturn(notification);
// NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
return new NotificationEntry(sbn);
}
private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
setPostTime(entry, postTime);
mBubbleData.notificationEntryUpdated(entry);
}
private void changeExpandedStateAtTime(boolean shouldBeExpanded, long time) {
when(mTimeSource.currentTimeMillis()).thenReturn(time);
mBubbleData.setExpanded(shouldBeExpanded);
}
@Test
public void testAddBubble() {
// Setup
mBubbleData.setListener(mListener);
// Test
setPostTime(mEntryA1, 1000);
mBubbleData.notificationEntryUpdated(mEntryA1);
// Verify
verify(mListener).onBubbleAdded(eq(mBubbleA1));
verify(mListener).onSelectionChanged(eq(mBubbleA1));
verify(mListener).apply();
}
@Test
public void testRemoveBubble() {
// Setup
mBubbleData.notificationEntryUpdated(mEntryA1);
mBubbleData.notificationEntryUpdated(mEntryA2);
mBubbleData.notificationEntryUpdated(mEntryA3);
mBubbleData.setListener(mListener);
// Test
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
// Verify
verify(mListener).onBubbleRemoved(eq(mBubbleA1), eq(BubbleController.DISMISS_USER_GESTURE));
verify(mListener).onSelectionChanged(eq(mBubbleA2));
verify(mListener).apply();
}
@Test
public void test_collapsed_addBubble_atMaxBubbles_expiresLeastActive() {
// Given
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryA2, 2000);
sendUpdatedEntryAtTime(mEntryA3, 3000);
sendUpdatedEntryAtTime(mEntryB1, 4000);
sendUpdatedEntryAtTime(mEntryB2, 5000);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
// When
sendUpdatedEntryAtTime(mEntryC1, 6000);
// Then
// A2 is removed. A1 is oldest but is the selected bubble.
assertThat(mBubbleData.getBubbles()).doesNotContain(mBubbleA2);
}
@Test
public void test_collapsed_expand_whenEmpty_doesNothing() {
assertThat(mBubbleData.hasBubbles()).isFalse();
changeExpandedStateAtTime(true, 2000L);
verify(mListener, never()).onExpandedChanged(anyBoolean());
verify(mListener, never()).apply();
}
// New bubble while stack is collapsed
@Test
public void test_collapsed_addBubble() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
// When
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryB1, 2000);
sendUpdatedEntryAtTime(mEntryB2, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
// Then
// New bubbles move to front when collapsed, bringing bubbles from the same app along
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
}
// New bubble while collapsed with ongoing bubble present
@Test
public void test_collapsed_addBubble_withOngoing() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
// When
setOngoing(mEntryA1, true);
setPostTime(mEntryA1, 1000);
mBubbleData.notificationEntryUpdated(mEntryA1);
setPostTime(mEntryB1, 2000);
mBubbleData.notificationEntryUpdated(mEntryB1);
setPostTime(mEntryB2, 3000);
mBubbleData.notificationEntryUpdated(mEntryB2);
setPostTime(mEntryA2, 4000);
mBubbleData.notificationEntryUpdated(mEntryA2);
// Then
// New bubbles move to front, but stay behind any ongoing bubbles.
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB2, mBubbleB1));
}
// Remove the selected bubble (middle bubble), while the stack is collapsed.
@Test
public void test_collapsed_removeBubble_selected() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
setPostTime(mEntryA1, 1000);
mBubbleData.notificationEntryUpdated(mEntryA1);
setPostTime(mEntryB1, 2000);
mBubbleData.notificationEntryUpdated(mEntryB1);
setPostTime(mEntryB2, 3000);
mBubbleData.notificationEntryUpdated(mEntryB2);
setPostTime(mEntryA2, 4000);
mBubbleData.notificationEntryUpdated(mEntryA2);
mBubbleData.setSelectedBubble(mBubbleB2);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
// When
mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
// Then
// (Selection remains in the same position)
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
}
// Remove the selected bubble (last bubble), while the stack is collapsed.
@Test
public void test_collapsed_removeSelectedBubble_inLastPosition() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryB1, 2000);
sendUpdatedEntryAtTime(mEntryB2, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
mBubbleData.setSelectedBubble(mBubbleB1);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
// When
mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
// Then
// (Selection is forced to move to previous)
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB2);
}
@Test
public void test_collapsed_addBubble_ongoing() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
// When
setPostTime(mEntryA1, 1000);
mBubbleData.notificationEntryUpdated(mEntryA1);
setPostTime(mEntryB1, 2000);
mBubbleData.notificationEntryUpdated(mEntryB1);
setPostTime(mEntryB2, 3000);
setOngoing(mEntryB2, true);
mBubbleData.notificationEntryUpdated(mEntryB2);
setPostTime(mEntryA2, 4000);
mBubbleData.notificationEntryUpdated(mEntryA2);
// Then
// New bubbles move to front, but stay behind any ongoing bubbles.
// Does not break grouping. (A2 is inserted after B1, even though it's newer).
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
}
@Test
public void test_collapsed_removeBubble() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryB1, 2000);
sendUpdatedEntryAtTime(mEntryB2, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
// When
mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
// Then
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1));
}
@Test
public void test_collapsed_updateBubble() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryB1, 2000);
sendUpdatedEntryAtTime(mEntryB2, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
// When
sendUpdatedEntryAtTime(mEntryB2, 5000);
// Then
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
}
@Test
public void test_collapsed_updateBubble_withOngoing() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
setPostTime(mEntryA1, 1000);
mBubbleData.notificationEntryUpdated(mEntryA1);
setPostTime(mEntryB1, 2000);
mBubbleData.notificationEntryUpdated(mEntryB1);
setPostTime(mEntryB2, 3000);
mBubbleData.notificationEntryUpdated(mEntryB2);
setOngoing(mEntryA2, true);
setPostTime(mEntryA2, 4000);
mBubbleData.notificationEntryUpdated(mEntryA2);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
// When
setPostTime(mEntryB1, 5000);
mBubbleData.notificationEntryUpdated(mEntryB1);
// Then
// A2 remains in first position, due to being ongoing. B1 moves before B2, Group A
// remains before group B.
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2));
}
@Test
public void test_collapse_afterUpdateWhileExpanded() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryB1, 2000);
sendUpdatedEntryAtTime(mEntryB2, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
changeExpandedStateAtTime(true, 5000L);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
sendUpdatedEntryAtTime(mEntryB1, 6000);
// (No reordering while expanded)
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
// When
changeExpandedStateAtTime(false, 7000L);
// Then
// A1 moves to front on collapse, since it is the selected bubble (and most recently
// accessed).
// A2 moves next to A1 to maintain grouping.
// B1 moves in front of B2, since it received an update while expanded
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1, mBubbleB2));
}
@Test
public void test_collapse_afterUpdateWhileExpanded_withOngoing() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryB1, 2000);
setOngoing(mEntryB2, true);
sendUpdatedEntryAtTime(mEntryB2, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
changeExpandedStateAtTime(true, 5000L);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
sendUpdatedEntryAtTime(mEntryA1, 6000);
// No reordering if expanded
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
// When
changeExpandedStateAtTime(false, 7000L);
// Then
// B2 remains in first position because it is ongoing.
// B1 remains grouped with B2
// A1 moves in front of A2, since it is more recently updated (and is selected).
// B1 moves in front of B2, since it has more recent activity.
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA1, mBubbleA2));
}
@Test
public void test_collapsed_removeLastBubble_clearsSelectedBubble() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryB1, 2000);
sendUpdatedEntryAtTime(mEntryB2, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
assertThat(mBubbleData.getSelectedBubble()).isNull();
}
@Test
public void test_expanded_addBubble_atMaxBubbles_expiresLeastActive() {
// Given
sendUpdatedEntryAtTime(mEntryA1, 1000);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
changeExpandedStateAtTime(true, 2000L);
assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(2000);
sendUpdatedEntryAtTime(mEntryA2, 3000);
sendUpdatedEntryAtTime(mEntryA3, 4000);
sendUpdatedEntryAtTime(mEntryB1, 5000);
sendUpdatedEntryAtTime(mEntryB2, 6000);
sendUpdatedEntryAtTime(mEntryB3, 7000);
// Then
// A1 would be removed, but it is selected and expanded, so it should not go away.
// Instead, fall through to removing A2 (the next oldest).
assertThat(mBubbleData.getBubbles()).doesNotContain(mEntryA2);
}
@Test
public void test_expanded_removeLastBubble_collapsesStack() {
// Given
setPostTime(mEntryA1, 1000);
mBubbleData.notificationEntryUpdated(mEntryA1);
setPostTime(mEntryB1, 2000);
mBubbleData.notificationEntryUpdated(mEntryB1);
setPostTime(mEntryB2, 3000);
mBubbleData.notificationEntryUpdated(mEntryC1);
mBubbleData.setExpanded(true);
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
mBubbleData.notificationEntryRemoved(mEntryC1, BubbleController.DISMISS_USER_GESTURE);
assertThat(mBubbleData.isExpanded()).isFalse();
assertThat(mBubbleData.getSelectedBubble()).isNull();
}
// Bubbles do not reorder while expanded
@Test
public void test_expanded_selection_collapseToTop() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryA2, 2000);
sendUpdatedEntryAtTime(mEntryB1, 3000);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1));
changeExpandedStateAtTime(true, 4000L);
// regrouping only happens when collapsed (after new or update) or expanded->collapsed
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1));
changeExpandedStateAtTime(false, 6000L);
// A1 is still selected and it's lastAccessed time has been updated
// on collapse, sorting is applied, keeping the selected bubble at the front
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1));
}
// New bubble from new app while stack is expanded
@Test
public void test_expanded_addBubble_newApp() {
// Given
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryA2, 2000);
sendUpdatedEntryAtTime(mEntryA3, 3000);
sendUpdatedEntryAtTime(mEntryB1, 4000);
sendUpdatedEntryAtTime(mEntryB2, 5000);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
changeExpandedStateAtTime(true, 6000L);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L);
// regrouping only happens when collapsed (after new or update) or expanded->collapsed
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2, mBubbleA1));
// When
sendUpdatedEntryAtTime(mEntryC1, 7000);
// Then
// A2 is expired. A1 was oldest, but lastActivityTime is reset when expanded, since A1 is
// selected.
// C1 is added at the end since bubbles are expanded.
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA1, mBubbleC1));
}
// New bubble from existing app while stack is expanded
@Test
public void test_expanded_addBubble_existingApp() {
// Given
sendUpdatedEntryAtTime(mEntryB1, 1000);
sendUpdatedEntryAtTime(mEntryB2, 2000);
sendUpdatedEntryAtTime(mEntryA1, 3000);
sendUpdatedEntryAtTime(mEntryA2, 4000);
sendUpdatedEntryAtTime(mEntryA3, 5000);
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
changeExpandedStateAtTime(true, 6000L);
// B1 is first (newest, since it's just been expanded and is selected)
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L);
// regrouping only happens when collapsed (after new or update) or while collapsing
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
// When
sendUpdatedEntryAtTime(mEntryB3, 7000);
// Then
// (B2 is expired, B1 was oldest, but it's lastActivityTime is updated at the point when
// the stack was expanded, since it is the selected bubble.
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB3, mBubbleB1));
}
// Updated bubble from existing app while stack is expanded
@Test
public void test_expanded_updateBubble_existingApp() {
sendUpdatedEntryAtTime(mEntryA1, 1000);
sendUpdatedEntryAtTime(mEntryA2, 2000);
sendUpdatedEntryAtTime(mEntryB1, 3000);
sendUpdatedEntryAtTime(mEntryB2, 4000);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
mBubbleData.setExpanded(true);
sendUpdatedEntryAtTime(mEntryA1, 5000);
// Does not reorder while expanded (for an update).
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
}
@Test
public void test_expanded_updateBubble() {
// Given
assertThat(mBubbleData.hasBubbles()).isFalse();
assertThat(mBubbleData.isExpanded()).isFalse();
setPostTime(mEntryA1, 1000);
mBubbleData.notificationEntryUpdated(mEntryA1);
setPostTime(mEntryB1, 2000);
mBubbleData.notificationEntryUpdated(mEntryB1);
setPostTime(mEntryB2, 3000);
mBubbleData.notificationEntryUpdated(mEntryB2);
setPostTime(mEntryA2, 4000);
mBubbleData.notificationEntryUpdated(mEntryA2);
mBubbleData.setExpanded(true);
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
// When
setPostTime(mEntryB1, 5000);
mBubbleData.notificationEntryUpdated(mEntryB1);
// Then
// B1 remains in the same place due to being expanded
assertThat(mBubbleData.getBubbles()).isEqualTo(
ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
}
}