Merge "Combine discrete listener methods into single state update" into qt-dev
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user