Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +8 −4 Original line number Diff line number Diff line Loading @@ -1230,10 +1230,14 @@ public class BubbleController implements ConfigurationChangeListener, * A bubble was dragged and is released in dismiss target in Launcher. * * @param bubbleKey key of the bubble being dragged to dismiss target * @param timestamp the timestamp of the removal */ public void dragBubbleToDismiss(String bubbleKey) { public void dragBubbleToDismiss(String bubbleKey, long timestamp) { String selectedBubbleKey = mBubbleData.getSelectedBubbleKey(); removeBubble(bubbleKey, Bubbles.DISMISS_USER_GESTURE); if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) { mBubbleData.dismissBubbleWithKey( bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp); } if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) { // We did not remove the selected bubble. Expand it again mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true); Loading Loading @@ -2458,8 +2462,8 @@ public class BubbleController implements ConfigurationChangeListener, } @Override public void dragBubbleToDismiss(String key) { mMainExecutor.execute(() -> mController.dragBubbleToDismiss(key)); public void dragBubbleToDismiss(String key, long timestamp) { mMainExecutor.execute(() -> mController.dragBubbleToDismiss(key, timestamp)); } @Override Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +30 −5 Original line number Diff line number Diff line Loading @@ -150,9 +150,12 @@ public class BubbleData { : null; for (int i = 0; i < removedBubbles.size(); i++) { Pair<Bubble, Integer> pair = removedBubbles.get(i); // if the removal happened in launcher, don't send it back if (pair.second != Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER) { bubbleBarUpdate.removedBubbles.add( new RemovedBubble(pair.first.getKey(), pair.second)); } } if (orderChanged) { // Include the new order for (int i = 0; i < bubbles.size(); i++) { Loading Loading @@ -502,13 +505,35 @@ public class BubbleData { dispatchPendingChanges(); } /** Dismisses the bubble with the matching key, if it exists. */ public void dismissBubbleWithKey(String key, @DismissReason int reason) { dismissBubbleWithKey(key, reason, mTimeSource.currentTimeMillis()); } /** * Dismisses the bubble with the matching key, if it exists. * * <p>This is used when the bubble was dismissed in launcher, where the {@code removalTimestamp} * represents when the removal happened and can be used to check whether or not the bubble has * been updated after the removal. If no updates, it's safe to remove the bubble, otherwise the * removal is ignored. */ public void dismissBubbleWithKey(String key, @DismissReason int reason) { public void dismissBubbleWithKey(String key, @DismissReason int reason, long removalTimestamp) { boolean shouldRemove = true; // if the bubble was removed from launcher, verify that the removal happened after the last // time it was updated if (reason == Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER) { // if the bubble was removed from launcher it must be active. Bubble bubble = getBubbleInStackWithKey(key); if (bubble != null && bubble.getLastActivity() > removalTimestamp) { shouldRemove = false; } } if (shouldRemove) { doRemove(key, reason); dispatchPendingChanges(); } } /** * Adds a group key indicating that the summary for this group should be suppressed. Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +2 −1 Original line number Diff line number Diff line Loading @@ -62,7 +62,7 @@ public interface Bubbles { DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_ACCOUNT_REMOVED, DISMISS_SWITCH_TO_STACK}) DISMISS_SWITCH_TO_STACK, DISMISS_USER_GESTURE_FROM_LAUNCHER}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason { } Loading @@ -84,6 +84,7 @@ public interface Bubbles { int DISMISS_RELOAD_FROM_DISK = 15; int DISMISS_USER_ACCOUNT_REMOVED = 16; int DISMISS_SWITCH_TO_STACK = 17; int DISMISS_USER_GESTURE_FROM_LAUNCHER = 18; /** Returns a binder that can be passed to an external process to manipulate Bubbles. */ default IBubbles createExternalInterface() { Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ interface IBubbles { oneway void showBubble(in String key, in int topOnScreen) = 3; oneway void dragBubbleToDismiss(in String key) = 4; oneway void dragBubbleToDismiss(in String key, in long timestamp) = 4; oneway void removeAllBubbles() = 5; Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +42 −4 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ 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.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; Loading Loading @@ -254,6 +255,45 @@ public class BubbleDataTest extends ShellTestCase { assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_USER_GESTURE); } @Test public void testRemoveBubbleInLauncher_beforeBubbleUpdate_processedAfter_shouldNotBeRemoved() { sendUpdatedEntryAtTime(mEntryA1, 1000); sendUpdatedEntryAtTime(mEntryA2, 2000); mBubbleData.setListener(mListener); sendUpdatedEntryAtTime(mEntryA2, 3000); verifyUpdateReceived(); assertThat(mBubbleData.hasBubbleInStackWithKey(mEntryA2.getKey())).isTrue(); assertThat(mBubbleData.getBubbleInStackWithKey(mEntryA2.getKey()).getLastActivity()) .isEqualTo(3000); // dismiss the bubble with a timestamp in the past mBubbleData.dismissBubbleWithKey( mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, 2500); verifyNoMoreInteractions(mListener); assertThat(mBubbleData.hasBubbleInStackWithKey(mEntryA2.getKey())).isTrue(); } @Test public void testRemoveBubbleInLauncher_isNotSentBackToLauncher() { sendUpdatedEntryAtTime(mEntryA1, 1000); sendUpdatedEntryAtTime(mEntryA2, 2000); mBubbleData.setListener(mListener); mBubbleData.dismissBubbleWithKey( mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, 4000); verifyUpdateReceived(); BubbleData.Update update = mUpdateCaptor.getValue(); assertThat(update.removedBubbles).hasSize(1); assertThat(update.removedBubbles.getFirst().first.getKey()).isEqualTo(mBubbleA2.getKey()); BubbleBarUpdate bubbleBarUpdate = update.toBubbleBarUpdate(); assertThat(bubbleBarUpdate.removedBubbles).isEmpty(); } @Test public void ifSuppress_hideFlyout() { // Setup Loading Loading @@ -1415,15 +1455,13 @@ public class BubbleDataTest extends ShellTestCase { sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */); } private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, boolean textChanged) { private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, boolean textChanged) { setPostTime(entry, postTime); // BubbleController calls this: Bubble b = mBubbleData.getOrCreateBubble(entry, null /* persistedBubble */); b.setTextChangedForTest(textChanged); // And then this mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/, true /* showInShade */); mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/, true /* showInShade */); } private void changeExpandedStateAtTime(boolean shouldBeExpanded, long time) { Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +8 −4 Original line number Diff line number Diff line Loading @@ -1230,10 +1230,14 @@ public class BubbleController implements ConfigurationChangeListener, * A bubble was dragged and is released in dismiss target in Launcher. * * @param bubbleKey key of the bubble being dragged to dismiss target * @param timestamp the timestamp of the removal */ public void dragBubbleToDismiss(String bubbleKey) { public void dragBubbleToDismiss(String bubbleKey, long timestamp) { String selectedBubbleKey = mBubbleData.getSelectedBubbleKey(); removeBubble(bubbleKey, Bubbles.DISMISS_USER_GESTURE); if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) { mBubbleData.dismissBubbleWithKey( bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp); } if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) { // We did not remove the selected bubble. Expand it again mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true); Loading Loading @@ -2458,8 +2462,8 @@ public class BubbleController implements ConfigurationChangeListener, } @Override public void dragBubbleToDismiss(String key) { mMainExecutor.execute(() -> mController.dragBubbleToDismiss(key)); public void dragBubbleToDismiss(String key, long timestamp) { mMainExecutor.execute(() -> mController.dragBubbleToDismiss(key, timestamp)); } @Override Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +30 −5 Original line number Diff line number Diff line Loading @@ -150,9 +150,12 @@ public class BubbleData { : null; for (int i = 0; i < removedBubbles.size(); i++) { Pair<Bubble, Integer> pair = removedBubbles.get(i); // if the removal happened in launcher, don't send it back if (pair.second != Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER) { bubbleBarUpdate.removedBubbles.add( new RemovedBubble(pair.first.getKey(), pair.second)); } } if (orderChanged) { // Include the new order for (int i = 0; i < bubbles.size(); i++) { Loading Loading @@ -502,13 +505,35 @@ public class BubbleData { dispatchPendingChanges(); } /** Dismisses the bubble with the matching key, if it exists. */ public void dismissBubbleWithKey(String key, @DismissReason int reason) { dismissBubbleWithKey(key, reason, mTimeSource.currentTimeMillis()); } /** * Dismisses the bubble with the matching key, if it exists. * * <p>This is used when the bubble was dismissed in launcher, where the {@code removalTimestamp} * represents when the removal happened and can be used to check whether or not the bubble has * been updated after the removal. If no updates, it's safe to remove the bubble, otherwise the * removal is ignored. */ public void dismissBubbleWithKey(String key, @DismissReason int reason) { public void dismissBubbleWithKey(String key, @DismissReason int reason, long removalTimestamp) { boolean shouldRemove = true; // if the bubble was removed from launcher, verify that the removal happened after the last // time it was updated if (reason == Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER) { // if the bubble was removed from launcher it must be active. Bubble bubble = getBubbleInStackWithKey(key); if (bubble != null && bubble.getLastActivity() > removalTimestamp) { shouldRemove = false; } } if (shouldRemove) { doRemove(key, reason); dispatchPendingChanges(); } } /** * Adds a group key indicating that the summary for this group should be suppressed. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +2 −1 Original line number Diff line number Diff line Loading @@ -62,7 +62,7 @@ public interface Bubbles { DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_ACCOUNT_REMOVED, DISMISS_SWITCH_TO_STACK}) DISMISS_SWITCH_TO_STACK, DISMISS_USER_GESTURE_FROM_LAUNCHER}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason { } Loading @@ -84,6 +84,7 @@ public interface Bubbles { int DISMISS_RELOAD_FROM_DISK = 15; int DISMISS_USER_ACCOUNT_REMOVED = 16; int DISMISS_SWITCH_TO_STACK = 17; int DISMISS_USER_GESTURE_FROM_LAUNCHER = 18; /** Returns a binder that can be passed to an external process to manipulate Bubbles. */ default IBubbles createExternalInterface() { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ interface IBubbles { oneway void showBubble(in String key, in int topOnScreen) = 3; oneway void dragBubbleToDismiss(in String key) = 4; oneway void dragBubbleToDismiss(in String key, in long timestamp) = 4; oneway void removeAllBubbles() = 5; Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +42 −4 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ 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.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; Loading Loading @@ -254,6 +255,45 @@ public class BubbleDataTest extends ShellTestCase { assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_USER_GESTURE); } @Test public void testRemoveBubbleInLauncher_beforeBubbleUpdate_processedAfter_shouldNotBeRemoved() { sendUpdatedEntryAtTime(mEntryA1, 1000); sendUpdatedEntryAtTime(mEntryA2, 2000); mBubbleData.setListener(mListener); sendUpdatedEntryAtTime(mEntryA2, 3000); verifyUpdateReceived(); assertThat(mBubbleData.hasBubbleInStackWithKey(mEntryA2.getKey())).isTrue(); assertThat(mBubbleData.getBubbleInStackWithKey(mEntryA2.getKey()).getLastActivity()) .isEqualTo(3000); // dismiss the bubble with a timestamp in the past mBubbleData.dismissBubbleWithKey( mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, 2500); verifyNoMoreInteractions(mListener); assertThat(mBubbleData.hasBubbleInStackWithKey(mEntryA2.getKey())).isTrue(); } @Test public void testRemoveBubbleInLauncher_isNotSentBackToLauncher() { sendUpdatedEntryAtTime(mEntryA1, 1000); sendUpdatedEntryAtTime(mEntryA2, 2000); mBubbleData.setListener(mListener); mBubbleData.dismissBubbleWithKey( mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, 4000); verifyUpdateReceived(); BubbleData.Update update = mUpdateCaptor.getValue(); assertThat(update.removedBubbles).hasSize(1); assertThat(update.removedBubbles.getFirst().first.getKey()).isEqualTo(mBubbleA2.getKey()); BubbleBarUpdate bubbleBarUpdate = update.toBubbleBarUpdate(); assertThat(bubbleBarUpdate.removedBubbles).isEmpty(); } @Test public void ifSuppress_hideFlyout() { // Setup Loading Loading @@ -1415,15 +1455,13 @@ public class BubbleDataTest extends ShellTestCase { sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */); } private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, boolean textChanged) { private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, boolean textChanged) { setPostTime(entry, postTime); // BubbleController calls this: Bubble b = mBubbleData.getOrCreateBubble(entry, null /* persistedBubble */); b.setTextChangedForTest(textChanged); // And then this mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/, true /* showInShade */); mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/, true /* showInShade */); } private void changeExpandedStateAtTime(boolean shouldBeExpanded, long time) { Loading