Merge "Combine discrete listener methods into single state update" into qt-dev

This commit is contained in:
Mark Renouf
2019-05-24 18:32:51 +00:00
committed by Android (Google) Code Review
3 changed files with 279 additions and 251 deletions

View File

@@ -56,6 +56,7 @@ import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
@@ -514,62 +515,66 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
@Override
public void onBubbleAdded(Bubble bubble) {
ensureStackViewCreated();
mStackView.addBubble(bubble);
}
@Override
public void onBubbleRemoved(Bubble bubble, @DismissReason int reason) {
if (mStackView != null) {
mStackView.removeBubble(bubble);
public void applyUpdate(BubbleData.Update update) {
if (mStackView == null && update.addedBubble != null) {
// Lazy init stack view when the first bubble is added.
ensureStackViewCreated();
}
if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
&& !bubble.entry.showInShadeWhenBubble()) {
// The bubble is gone & the notification is gone, time to actually remove it
mNotificationEntryManager.performRemoveNotification(bubble.entry.notification,
UNDEFINED_DISMISS_REASON);
} else {
// The notification is still in the shade but we've removed the bubble so
// lets make sure NoMan knows it's not a bubble anymore
try {
mBarService.onNotificationBubbleChanged(bubble.getKey(), false /* isBubble */);
} catch (RemoteException e) {
// Bad things have happened
// If not yet initialized, ignore all other changes.
if (mStackView == null) {
return;
}
if (update.addedBubble != null) {
mStackView.addBubble(update.addedBubble);
}
// Collapsing? Do this first before remaining steps.
if (update.expandedChanged && !update.expanded) {
mStackView.setExpanded(false);
}
// Do removals, if any.
for (Pair<Bubble, Integer> removed : update.removedBubbles) {
final Bubble bubble = removed.first;
@DismissReason final int reason = removed.second;
mStackView.removeBubble(bubble);
if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
&& !bubble.entry.showInShadeWhenBubble()) {
// The bubble is gone & the notification is gone, time to actually remove it
mNotificationEntryManager.performRemoveNotification(bubble.entry.notification,
UNDEFINED_DISMISS_REASON);
} else {
// The notification is still in the shade but we've removed the bubble so
// lets make sure NoMan knows it's not a bubble anymore
try {
mBarService.onNotificationBubbleChanged(bubble.getKey(),
false /* isBubble */);
} catch (RemoteException e) {
// Bad things have happened
}
}
}
}
public void onBubbleUpdated(Bubble bubble) {
if (mStackView != null) {
mStackView.updateBubble(bubble);
if (update.updatedBubble != null) {
mStackView.updateBubble(update.updatedBubble);
}
}
@Override
public void onOrderChanged(List<Bubble> bubbles) {
if (mStackView != null) {
mStackView.updateBubbleOrder(bubbles);
if (update.orderChanged) {
mStackView.updateBubbleOrder(update.bubbles);
}
}
@Override
public void onSelectionChanged(@Nullable Bubble selectedBubble) {
if (mStackView != null) {
mStackView.setSelectedBubble(selectedBubble);
if (update.selectionChanged) {
mStackView.setSelectedBubble(update.selectedBubble);
}
}
@Override
public void onExpandedChanged(boolean expanded) {
if (mStackView != null) {
mStackView.setExpanded(expanded);
// Expanding? Apply this last.
if (update.expandedChanged && update.expanded) {
mStackView.setExpanded(true);
}
}
// Runs on state change.
@Override
public void apply() {
mNotificationEntryManager.updateNotifications();
updateStack();

View File

@@ -60,54 +60,46 @@ public class BubbleData {
private static final Comparator<Map.Entry<String, Long>> GROUPS_BY_MAX_SORT_KEY_DESCENDING =
Comparator.<Map.Entry<String, Long>, Long>comparing(Map.Entry::getValue).reversed();
/** Contains information about changes that have been made to the state of bubbles. */
static final class Update {
boolean expandedChanged;
boolean selectionChanged;
boolean orderChanged;
boolean expanded;
@Nullable Bubble selectedBubble;
@Nullable Bubble addedBubble;
@Nullable Bubble updatedBubble;
// Pair with Bubble and @DismissReason Integer
final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>();
// A read-only view of the bubbles list, changes there will be reflected here.
final List<Bubble> bubbles;
private Update(List<Bubble> bubbleOrder) {
bubbles = Collections.unmodifiableList(bubbleOrder);
}
boolean anythingChanged() {
return expandedChanged
|| selectionChanged
|| addedBubble != null
|| updatedBubble != null
|| !removedBubbles.isEmpty()
|| orderChanged;
}
void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
removedBubbles.add(new Pair<>(bubbleToRemove, reason));
}
}
/**
* This interface reports changes to the state and appearance of bubbles which should be applied
* as necessary to the UI.
* <p>
* Each operation is a report of a pending operation. Each should be considered in
* combination, when {@link #apply()} is called. For example, both: onExpansionChanged,
* and onOrderChanged
*/
interface Listener {
/**
* A new Bubble has been added. A call to {@link #onOrderChanged(List)} will
* follow, including the new Bubble in position
*/
void onBubbleAdded(Bubble bubble);
/**
* A Bubble has been removed. A call to {@link #onOrderChanged(List)} will
* follow.
*/
void onBubbleRemoved(Bubble bubble, @DismissReason int reason);
/**
* An existing bubble has been updated.
*
* @param bubble the bubble which was updated
*/
void onBubbleUpdated(Bubble bubble);
/**
* Indicates that one or more bubbles should change position. This may be result of insert,
* or removal of a Bubble, in addition to re-sorting existing Bubbles.
*
* @param bubbles an immutable list of the bubbles in the new order
*/
void onOrderChanged(List<Bubble> bubbles);
/** Indicates the selected bubble changed. */
void onSelectionChanged(@Nullable Bubble selectedBubble);
/**
* The UI should transition to the given state, incorporating any pending changes during
* the animation.
*/
void onExpandedChanged(boolean expanded);
/** Commit any pending operations (since last call of apply()) */
void apply();
/** Reports changes have have occurred as a result of the most recent operation. */
void applyUpdate(Update update);
}
interface TimeSource {
@@ -115,17 +107,12 @@ public class BubbleData {
}
private final Context mContext;
private List<Bubble> mBubbles;
private final List<Bubble> mBubbles;
private Bubble mSelectedBubble;
private boolean mExpanded;
// State tracked during an operation -- keeps track of what listener events to dispatch.
private boolean mExpandedChanged;
private boolean mOrderChanged;
private boolean mSelectionChanged;
private Bubble mUpdatedBubble;
private Bubble mAddedBubble;
private final List<Pair<Bubble, Integer>> mRemovedBubbles = new ArrayList<>();
private Update mStateChange;
private TimeSource mTimeSource = System::currentTimeMillis;
@@ -136,6 +123,7 @@ public class BubbleData {
public BubbleData(Context context) {
mContext = context;
mBubbles = new ArrayList<>();
mStateChange = new Update(mBubbles);
}
public boolean hasBubbles() {
@@ -185,7 +173,6 @@ public class BubbleData {
// Updates an existing bubble
bubble.setEntry(entry);
doUpdate(bubble);
mUpdatedBubble = bubble;
}
if (shouldAutoExpand(entry)) {
setSelectedBubbleInternal(bubble);
@@ -217,11 +204,11 @@ public class BubbleData {
minInsertPoint = newGroup ? 0 : findFirstIndexForGroup(bubble.getGroupId());
}
if (insertBubble(minInsertPoint, bubble) < mBubbles.size() - 1) {
mOrderChanged = true;
mStateChange.orderChanged = true;
}
mAddedBubble = bubble;
mStateChange.addedBubble = bubble;
if (!isExpanded()) {
mOrderChanged |= packGroup(findFirstIndexForGroup(bubble.getGroupId()));
mStateChange.orderChanged |= packGroup(findFirstIndexForGroup(bubble.getGroupId()));
// Top bubble becomes selected.
setSelectedBubbleInternal(mBubbles.get(0));
}
@@ -243,6 +230,7 @@ public class BubbleData {
if (DEBUG) {
Log.d(TAG, "doUpdate: " + bubble);
}
mStateChange.updatedBubble = bubble;
if (!isExpanded()) {
// while collapsed, update causes re-pack
int prevPos = mBubbles.indexOf(bubble);
@@ -250,7 +238,7 @@ public class BubbleData {
int newPos = insertBubble(0, bubble);
if (prevPos != newPos) {
packGroup(newPos);
mOrderChanged = true;
mStateChange.orderChanged = true;
}
setSelectedBubbleInternal(mBubbles.get(0));
}
@@ -269,12 +257,12 @@ public class BubbleData {
}
if (indexToRemove < mBubbles.size() - 1) {
// Removing anything but the last bubble means positions will change.
mOrderChanged = true;
mStateChange.orderChanged = true;
}
mBubbles.remove(indexToRemove);
mRemovedBubbles.add(Pair.create(bubbleToRemove, reason));
mStateChange.bubbleRemoved(bubbleToRemove, reason);
if (!isExpanded()) {
mOrderChanged |= repackAll();
mStateChange.orderChanged |= repackAll();
}
// Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
@@ -301,77 +289,20 @@ public class BubbleData {
Bubble bubble = mBubbles.remove(0);
bubble.setDismissed();
maybeSendDeleteIntent(reason, bubble.entry);
mRemovedBubbles.add(Pair.create(bubble, reason));
mStateChange.bubbleRemoved(bubble, reason);
}
dispatchPendingChanges();
}
private void dispatchPendingChanges() {
if (mListener == null) {
mExpandedChanged = false;
mAddedBubble = null;
mSelectionChanged = false;
mRemovedBubbles.clear();
mUpdatedBubble = null;
mOrderChanged = false;
return;
}
boolean anythingChanged = false;
if (mAddedBubble != null) {
mListener.onBubbleAdded(mAddedBubble);
mAddedBubble = null;
anythingChanged = true;
}
// Compat workaround: Always collapse first.
if (mExpandedChanged && !mExpanded) {
mListener.onExpandedChanged(mExpanded);
mExpandedChanged = false;
anythingChanged = true;
}
if (mSelectionChanged) {
mListener.onSelectionChanged(mSelectedBubble);
mSelectionChanged = false;
anythingChanged = true;
}
if (!mRemovedBubbles.isEmpty()) {
for (Pair<Bubble, Integer> removed : mRemovedBubbles) {
mListener.onBubbleRemoved(removed.first, removed.second);
}
mRemovedBubbles.clear();
anythingChanged = true;
}
if (mUpdatedBubble != null) {
mListener.onBubbleUpdated(mUpdatedBubble);
mUpdatedBubble = null;
anythingChanged = true;
}
if (mOrderChanged) {
mListener.onOrderChanged(mBubbles);
mOrderChanged = false;
anythingChanged = true;
}
if (mExpandedChanged) {
mListener.onExpandedChanged(mExpanded);
mExpandedChanged = false;
anythingChanged = true;
}
if (anythingChanged) {
mListener.apply();
if (mListener != null && mStateChange.anythingChanged()) {
mListener.applyUpdate(mStateChange);
}
mStateChange = new Update(mBubbles);
}
/**
* Requests a change to the selected bubble. Calls {@link Listener#onSelectionChanged} if
* the value changes.
* Requests a change to the selected bubble.
*
* @param bubble the new selected bubble
*/
@@ -391,13 +322,12 @@ public class BubbleData {
bubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
}
mSelectedBubble = bubble;
mSelectionChanged = true;
return;
mStateChange.selectedBubble = bubble;
mStateChange.selectionChanged = true;
}
/**
* Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if
* the value changes.
* Requests a change to the expanded state.
*
* @param shouldExpand the new requested state
*/
@@ -418,11 +348,11 @@ public class BubbleData {
return;
}
mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
mOrderChanged |= repackAll();
mStateChange.orderChanged |= repackAll();
} else if (!mBubbles.isEmpty()) {
// Apply ordering and grouping rules from expanded -> collapsed, then save
// the result.
mOrderChanged |= repackAll();
mStateChange.orderChanged |= repackAll();
// Save the state which should be returned to when expanded (with no other changes)
if (mBubbles.indexOf(mSelectedBubble) > 0) {
@@ -442,7 +372,8 @@ public class BubbleData {
}
}
mExpanded = shouldExpand;
mExpandedChanged = true;
mStateChange.expanded = shouldExpand;
mStateChange.expandedChanged = true;
}
private static long sortKey(Bubble bubble) {
@@ -569,7 +500,8 @@ public class BubbleData {
if (repacked.equals(mBubbles)) {
return false;
}
mBubbles = repacked;
mBubbles.clear();
mBubbles.addAll(repacked);
return true;
}
@@ -595,7 +527,7 @@ public class BubbleData {
for (Iterator<Bubble> i = mBubbles.iterator(); i.hasNext(); ) {
Bubble bubble = i.next();
if (bubble.getGroupId().equals(blockedGroupId)) {
mRemovedBubbles.add(Pair.create(bubble, BubbleController.DISMISS_BLOCKED));
mStateChange.bubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
i.remove();
}
}

View File

@@ -16,19 +16,12 @@
package com.android.systemui.bubbles;
import static com.android.systemui.bubbles.BubbleController.DISMISS_AGED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -38,6 +31,7 @@ import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
import androidx.test.filters.SmallTest;
@@ -51,11 +45,20 @@ import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
/**
* Tests operations and the resulting state managed by BubbleData.
* <p>
* After each operation to verify, {@link #verifyUpdateReceived()} ensures the listener was called
* and captures the Update object received there.
* <p>
* Other methods beginning with 'assert' access the captured update object and assert on specific
* aspects of it.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -90,6 +93,9 @@ public class BubbleDataTest extends SysuiTestCase {
private NotificationTestHelper mNotificationTestHelper;
@Captor
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(mContext);
@@ -132,9 +138,9 @@ public class BubbleDataTest extends SysuiTestCase {
sendUpdatedEntryAtTime(mEntryA1, 1000);
// Verify
verify(mListener).onBubbleAdded(eq(mBubbleA1));
verify(mListener).onSelectionChanged(eq(mBubbleA1));
verify(mListener).apply();
verifyUpdateReceived();
assertBubbleAdded(mBubbleA1);
assertSelectionChangedTo(mBubbleA1);
}
@Test
@@ -149,8 +155,8 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
// Verify
verify(mListener).onBubbleRemoved(eq(mBubbleA1), eq(BubbleController.DISMISS_USER_GESTURE));
verify(mListener).apply();
verifyUpdateReceived();
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_USER_GESTURE);
}
// COLLAPSED / ADD
@@ -171,7 +177,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryC1, 6000);
verify(mListener).onBubbleRemoved(eq(mBubbleA1), eq(DISMISS_AGED));
verifyUpdateReceived();
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED);
}
/**
@@ -190,19 +197,20 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryA1, 1000);
verify(mListener, never()).onOrderChanged(anyList());
verifyUpdateReceived();
assertOrderNotChanged();
reset(mListener);
sendUpdatedEntryAtTime(mEntryB1, 2000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB1, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB1, mBubbleA1);
reset(mListener);
sendUpdatedEntryAtTime(mEntryB2, 3000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB2, mBubbleB1, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
reset(mListener);
sendUpdatedEntryAtTime(mEntryA2, 4000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1);
}
/**
@@ -223,19 +231,20 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
setOngoing(mEntryA1, true);
sendUpdatedEntryAtTime(mEntryA1, 1000);
verify(mListener, never()).onOrderChanged(anyList());
verifyUpdateReceived();
assertOrderNotChanged();
reset(mListener);
sendUpdatedEntryAtTime(mEntryB1, 2000);
verify(mListener, never()).onOrderChanged(eq(listOf(mBubbleA1, mBubbleB1)));
verifyUpdateReceived();
assertOrderNotChanged();
reset(mListener);
sendUpdatedEntryAtTime(mEntryB2, 3000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleA1, mBubbleB2, mBubbleB1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA1, mBubbleB2, mBubbleB1);
reset(mListener);
sendUpdatedEntryAtTime(mEntryA2, 4000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleA1, mBubbleA2, mBubbleB2, mBubbleB1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA1, mBubbleA2, mBubbleB2, mBubbleB1);
}
/**
@@ -252,20 +261,22 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryA1, 1000);
verify(mListener).onSelectionChanged(eq(mBubbleA1));
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA1);
reset(mListener);
sendUpdatedEntryAtTime(mEntryB1, 2000);
verify(mListener).onSelectionChanged(eq(mBubbleB1));
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB1);
reset(mListener);
sendUpdatedEntryAtTime(mEntryB2, 3000);
verify(mListener).onSelectionChanged(eq(mBubbleB2));
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB2);
reset(mListener);
sendUpdatedEntryAtTime(mEntryA2, 4000);
verify(mListener).onSelectionChanged(eq(mBubbleA2));
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA2);
}
/**
* Verifies that while collapsed, the selection will not change if the selected bubble is
* ongoing. It remains the top bubble and as such remains selected.
@@ -282,9 +293,17 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryB1, 2000);
verifyUpdateReceived();
assertSelectionNotChanged();
sendUpdatedEntryAtTime(mEntryB2, 3000);
verifyUpdateReceived();
assertSelectionNotChanged();
sendUpdatedEntryAtTime(mEntryA2, 4000);
verify(mListener, never()).onSelectionChanged(any(Bubble.class));
verifyUpdateReceived();
assertSelectionNotChanged();
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); // selection unchanged
}
@@ -305,7 +324,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB2, mBubbleB1, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
}
@@ -324,7 +344,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
verify(mListener, never()).onOrderChanged(anyList());
verifyUpdateReceived();
assertOrderNotChanged();
}
/**
@@ -343,7 +364,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_NOTIF_CANCEL);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB2, mBubbleB1, mBubbleA2)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA2);
}
/**
@@ -361,7 +383,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_NOTIF_CANCEL);
verify(mListener).onSelectionChanged(eq(mBubbleB2));
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB2);
}
// COLLAPSED / UPDATE
@@ -381,11 +404,12 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryB1, 5000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB1, mBubbleB2, mBubbleA2, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB1, mBubbleB2, mBubbleA2, mBubbleA1);
reset(mListener);
sendUpdatedEntryAtTime(mEntryA1, 6000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleA1, mBubbleA2, mBubbleB1, mBubbleB2)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA1, mBubbleA2, mBubbleB1, mBubbleB2);
}
/**
@@ -402,11 +426,12 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryB1, 5000);
verify(mListener).onSelectionChanged(eq(mBubbleB1));
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB1);
reset(mListener);
sendUpdatedEntryAtTime(mEntryA1, 6000);
verify(mListener).onSelectionChanged(eq(mBubbleA1));
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA1);
}
/**
@@ -425,7 +450,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryB2, 5000); // [A1*, A2, B2, B1]
verify(mListener, never()).onSelectionChanged(any(Bubble.class));
verifyUpdateReceived();
assertSelectionNotChanged();
}
/**
@@ -434,10 +460,10 @@ public class BubbleDataTest extends SysuiTestCase {
@Test
public void test_collapsed_expansion_whenEmpty_doesNothing() {
assertThat(mBubbleData.hasBubbles()).isFalse();
changeExpandedStateAtTime(true, 2000L);
mBubbleData.setListener(mListener);
verify(mListener, never()).onExpandedChanged(anyBoolean());
verify(mListener, never()).apply();
changeExpandedStateAtTime(true, 2000L);
verifyZeroInteractions(mListener);
}
@Test
@@ -450,7 +476,8 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
// Verify the selection was cleared.
verify(mListener).onSelectionChanged(isNull());
verifyUpdateReceived();
assertSelectionCleared();
}
// EXPANDED / ADD
@@ -476,7 +503,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryC1, 4000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleC1, mBubbleB1, mBubbleA2, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleC1, mBubbleB1, mBubbleA2, mBubbleA1);
}
/**
@@ -498,7 +526,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryC1, 4000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleA1, mBubbleA2, mBubbleC1, mBubbleB1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA1, mBubbleA2, mBubbleC1, mBubbleB1);
}
/**
@@ -519,7 +548,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryA3, 4000);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB1, mBubbleA3, mBubbleA2, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB1, mBubbleA3, mBubbleA2, mBubbleA1);
}
// EXPANDED / UPDATE
@@ -543,7 +573,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryA1, 4000);
verify(mListener, never()).onOrderChanged(anyList());
verifyUpdateReceived();
assertOrderNotChanged();
}
/**
@@ -564,9 +595,16 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
sendUpdatedEntryAtTime(mEntryA1, 6000);
verifyUpdateReceived();
assertOrderNotChanged();
sendUpdatedEntryAtTime(mEntryA2, 7000);
verifyUpdateReceived();
assertOrderNotChanged();
sendUpdatedEntryAtTime(mEntryB1, 8000);
verify(mListener, never()).onSelectionChanged(any(Bubble.class));
verifyUpdateReceived();
assertOrderNotChanged();
}
// EXPANDED / REMOVE
@@ -590,7 +628,8 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB1, mBubbleA2, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB1, mBubbleA2, mBubbleA1);
}
/**
@@ -614,11 +653,12 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
verify(mListener).onSelectionChanged(mBubbleA1);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA1);
reset(mListener);
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
verify(mListener).onSelectionChanged(mBubbleB1);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB1);
}
@Test
@@ -629,11 +669,12 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
changeExpandedStateAtTime(true, 3000L);
verify(mListener).onExpandedChanged(eq(true));
verifyUpdateReceived();
assertExpandedChangedTo(true);
reset(mListener);
changeExpandedStateAtTime(false, 4000L);
verify(mListener).onExpandedChanged(eq(false));
verifyUpdateReceived();
assertExpandedChangedTo(false);
}
/**
@@ -663,7 +704,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setSelectedBubble(mBubbleA2);
mBubbleData.setListener(mListener);
assertThat(mBubbleData.getBubbles()).isEqualTo(
listOf(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
// Test
@@ -678,12 +719,13 @@ public class BubbleDataTest extends SysuiTestCase {
//
// collapse -> selected bubble (A2) moves first.
changeExpandedStateAtTime(false, 8000L);
verify(mListener).onOrderChanged(eq(listOf(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2);
// expand -> "original" order/grouping restored
reset(mListener);
changeExpandedStateAtTime(true, 10000L);
verify(mListener).onOrderChanged(eq(listOf(mBubbleB1, mBubbleB2, mBubbleA2, mBubbleA1)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB1, mBubbleB2, mBubbleA2, mBubbleA1);
}
/**
@@ -717,15 +759,17 @@ public class BubbleDataTest extends SysuiTestCase {
//
// collapse -> selected bubble (A2) moves first.
changeExpandedStateAtTime(false, 8000L);
verify(mListener).onOrderChanged(eq(listOf(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2)));
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2);
// An update occurs, which causes sorting, and this invalidates the previously saved order.
sendUpdatedEntryAtTime(mEntryA2, 9000);
verifyUpdateReceived();
// No order changes when expanding because the new sorted order remains.
reset(mListener);
changeExpandedStateAtTime(true, 10000L);
verify(mListener, never()).onOrderChanged(anyList());
verifyUpdateReceived();
assertOrderNotChanged();
}
@Test
@@ -737,9 +781,61 @@ public class BubbleDataTest extends SysuiTestCase {
// Test
mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
verify(mListener).onExpandedChanged(eq(false));
verifyUpdateReceived();
assertExpandedChangedTo(false);
}
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
}
private void assertBubbleAdded(Bubble expected) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.addedBubble).named("addedBubble").isEqualTo(expected);
}
private void assertBubbleRemoved(Bubble expected, @BubbleController.DismissReason int reason) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.removedBubbles).named("removedBubbles")
.isEqualTo(ImmutableList.of(Pair.create(expected, reason)));
}
private void assertOrderNotChanged() {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.orderChanged).named("orderChanged").isFalse();
}
private void assertOrderChangedTo(Bubble... order) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.orderChanged).named("orderChanged").isTrue();
assertThat(update.bubbles).named("bubble order").isEqualTo(ImmutableList.copyOf(order));
}
private void assertSelectionNotChanged() {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.selectionChanged).named("selectionChanged").isFalse();
}
private void assertSelectionChangedTo(Bubble bubble) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.selectionChanged).named("selectionChanged").isTrue();
assertThat(update.selectedBubble).named("selectedBubble").isEqualTo(bubble);
}
private void assertSelectionCleared() {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.selectionChanged).named("selectionChanged").isTrue();
assertThat(update.selectedBubble).named("selectedBubble").isNull();
}
private void assertExpandedChangedTo(boolean expected) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.expandedChanged).named("expandedChanged").isTrue();
assertThat(update.expanded).named("expanded").isEqualTo(expected);
}
private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
return createBubbleEntry(userId, notifKey, packageName, 1000);
}
@@ -798,9 +894,4 @@ public class BubbleDataTest extends SysuiTestCase {
setCurrentTime(time);
mBubbleData.setExpanded(shouldBeExpanded);
}
/** Syntactic sugar to keep assertions more readable */
private static <T> List<T> listOf(T... a) {
return ImmutableList.copyOf(a);
}
}