summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2019-03-09 01:08:07 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-03-09 01:08:07 +0000
commit783f1815063b4a89a8defa7cc51e30da4030d24b (patch)
tree95a96df49e837e5607f9116e9e292ec294f19fae
parent0043d5c2fc820f06b6a61d7b04fe3ee0a84a8f9d (diff)
parent08bc42a06ecb207f10dd5b2af00572e491ada447 (diff)
Merge "Send deleteIntent when a bubble is dismissed"
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java28
6 files changed, 135 insertions, 36 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 51b2098ed9c6..eb85589f3781 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -23,6 +23,8 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.NotificationAlertingManager.alertAgain;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -42,6 +44,7 @@ import android.view.IPinnedStackListener;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.IntDef;
import androidx.annotation.MainThread;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,6 +62,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import java.lang.annotation.Retention;
+
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -70,10 +75,22 @@ import javax.inject.Singleton;
*/
@Singleton
public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener {
- private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
private static final String TAG = "BubbleController";
+ private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
+
+ @Retention(SOURCE)
+ @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
+ DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION})
+ @interface DismissReason {}
+ static final int DISMISS_USER_GESTURE = 1;
+ static final int DISMISS_AGED = 2;
+ static final int DISMISS_TASK_FINISHED = 3;
+ static final int DISMISS_BLOCKED = 4;
+ static final int DISMISS_NOTIF_CANCEL = 5;
+ static final int DISMISS_ACCESSIBILITY_ACTION = 6;
+
// Enables some subset of notifs to automatically become bubbles
private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
@@ -248,11 +265,11 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
/**
* Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
*/
- void dismissStack() {
+ void dismissStack(@DismissReason int reason) {
if (mStackView == null) {
return;
}
- mStackView.stackDismissed();
+ mStackView.stackDismissed(reason);
updateVisibility();
mNotificationEntryManager.updateNotifications();
@@ -304,9 +321,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
* Must be called from the main thread.
*/
@MainThread
- void removeBubble(String key) {
+ void removeBubble(String key, int reason) {
if (mStackView != null) {
- mStackView.removeBubble(key);
+ mStackView.removeBubble(key, reason);
}
mNotificationEntryManager.updateNotifications();
updateVisibility();
@@ -320,7 +337,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
boolean samePackage = entry.notification.getPackageName().equals(
e.notification.getPackageName());
if (samePackage) {
- removeBubble(entry.key);
+ removeBubble(entry.key, DISMISS_BLOCKED);
}
}
}
@@ -377,7 +394,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
}
if (!removedByUser) {
// This was a cancel so we should remove the bubble
- removeBubble(entry.key);
+ removeBubble(entry.key, DISMISS_NOTIF_CANCEL);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 25ee87a13b74..856b9d6c27e4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -135,7 +135,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
public void onTaskRemovalStarted(int taskId) {
if (mEntry != null) {
// Must post because this is called from a binder thread.
- post(() -> mBubbleController.removeBubble(mEntry.key));
+ post(() -> mBubbleController.removeBubble(mEntry.key,
+ BubbleController.DISMISS_TASK_FINISHED));
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 84efb9acbcf2..888e3fe282a8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,6 +19,8 @@ package com.android.systemui.bubbles;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
@@ -49,6 +51,7 @@ import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
@@ -62,6 +65,7 @@ import java.math.RoundingMode;
*/
public class BubbleStackView extends FrameLayout {
private static final String TAG = "BubbleStackView";
+ private static final boolean DEBUG = false;
private Point mDisplaySize;
@@ -232,7 +236,7 @@ public class BubbleStackView extends FrameLayout {
}
switch (action) {
case AccessibilityNodeInfo.ACTION_DISMISS:
- stackDismissed();
+ stackDismissed(BubbleController.DISMISS_ACCESSIBILITY_ACTION);
return true;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
collapseStack();
@@ -356,18 +360,12 @@ public class BubbleStackView extends FrameLayout {
/**
* Remove a bubble from the stack.
*/
- public void removeBubble(String key) {
+ public void removeBubble(String key, int reason) {
Bubble b = mBubbleData.removeBubble(key);
if (b == null) {
return;
}
- b.entry.setBubbleDismissed(true);
-
- // Remove it from the views
- int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
- b.expandedView.cleanUpExpandedState();
- mBubbleContainer.removeView(b.iconView);
-
+ int removedIndex = dismissBubble(b, reason);
int bubbleCount = mBubbleContainer.getChildCount();
if (bubbleCount == 0) {
// If no bubbles remain, collapse the entire stack.
@@ -385,26 +383,63 @@ public class BubbleStackView extends FrameLayout {
mExpandedBubble = null;
}
}
+ // TODO: consider logging reason code
logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
}
/**
* Dismiss the stack of bubbles.
*/
- public void stackDismissed() {
+ public void stackDismissed(int reason) {
for (Bubble bubble : mBubbleData.getBubbles()) {
- bubble.entry.setBubbleDismissed(true);
- bubble.expandedView.cleanUpExpandedState();
+ dismissBubble(bubble, reason);
}
mBubbleData.clear();
collapseStack();
mBubbleContainer.removeAllViews();
mExpandedViewContainer.removeAllViews();
+ // TODO: consider logging reason code
logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
}
/**
+ * Marks the notification entry as dismissed, cleans up Bubble icon and expanded view UI
+ * elements and calls deleteIntent if necessary.
+ *
+ * <p>Note: This does not remove the Bubble from BubbleData.
+ *
+ * @param bubble the Bubble being dismissed
+ * @param reason code for the reason the dismiss was triggered
+ * @see BubbleController.DismissReason
+ */
+ private int dismissBubble(Bubble bubble, @DismissReason int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason);
+ }
+ bubble.entry.setBubbleDismissed(true);
+ bubble.expandedView.cleanUpExpandedState();
+
+ // Remove it from the views
+ int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView);
+ mBubbleContainer.removeViewAt(removedIndex);
+
+ if (reason == BubbleController.DISMISS_USER_GESTURE) {
+ Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata();
+ PendingIntent deleteIntent = bubbleMetadata.getDeleteIntent();
+ if (deleteIntent != null) {
+ try {
+ deleteIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Failed to send delete intent for bubble with key: "
+ + (bubble.entry != null ? bubble.entry.key : " null entry"));
+ }
+ }
+ }
+ return removedIndex;
+ }
+
+ /**
* Updates a bubble in the stack.
*
* @param entry the entry to update in the stack.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index c8eebac4da3e..a7170d0256e3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -140,7 +140,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
case MotionEvent.ACTION_UP:
trackMovement(event);
if (mInDismissTarget && isStack) {
- mController.dismissStack();
+ mController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
} else if (mMovedEnough) {
mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
final float velX = mVelocityTracker.getXVelocity();
@@ -152,7 +152,8 @@ class BubbleTouchHandler implements View.OnTouchListener {
mStack.onBubbleDragFinish(
mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed);
if (dismissed) {
- mController.removeBubble(((BubbleView) mTouchedView).getKey());
+ mController.removeBubble(((BubbleView) mTouchedView).getKey(),
+ BubbleController.DISMISS_USER_GESTURE);
}
}
} else if (mTouchedView == mStack.getExpandedBubbleView()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 0fd783423464..42c221a91422 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -20,10 +20,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.IActivityManager;
+import android.app.PendingIntent;
import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -83,6 +86,9 @@ public class BubbleControllerTest extends SysuiTestCase {
@Mock
private BubbleController.BubbleExpandListener mBubbleExpandListener;
+ @Mock
+ private PendingIntent mDeleteIntent;
+
private BubbleData mBubbleData;
@Before
@@ -98,9 +104,9 @@ public class BubbleControllerTest extends SysuiTestCase {
// Need notifications for bubbles
mNotificationTestHelper = new NotificationTestHelper(mContext);
- mRow = mNotificationTestHelper.createBubble();
- mRow2 = mNotificationTestHelper.createBubble();
- mNoChannelRow = mNotificationTestHelper.createBubble();
+ mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
+ mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
+ mNoChannelRow = mNotificationTestHelper.createBubble(mDeleteIntent);
// Return non-null notification data from the NEM
when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
@@ -141,11 +147,10 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
- mBubbleController.removeBubble(mRow.getEntry().key);
+ mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
assertFalse(mStatusBarWindowController.getBubblesShowing());
assertTrue(mRow.getEntry().isBubbleDismissed());
verify(mNotificationEntryManager).updateNotifications();
-
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
}
@@ -155,7 +160,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
assertTrue(mBubbleController.hasBubbles());
- mBubbleController.dismissStack();
+ mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mStatusBarWindowController.getBubblesShowing());
verify(mNotificationEntryManager).updateNotifications();
assertTrue(mRow.getEntry().isBubbleDismissed());
@@ -271,7 +276,8 @@ public class BubbleControllerTest extends SysuiTestCase {
assertFalse(mRow2.getEntry().showInShadeWhenBubble());
// Dismiss currently expanded
- mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
+ mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
+ BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
// Make sure next bubble is selected
@@ -279,7 +285,8 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
// Dismiss that one
- mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
+ mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
+ BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
@@ -299,6 +306,28 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mRow.getEntry().showInShadeWhenBubble());
}
+ @Test
+ public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
+ mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_AGED);
+ verify(mDeleteIntent, never()).send();
+ }
+
+ @Test
+ public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
+ mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+ verify(mDeleteIntent, times(1)).send();
+ }
+
+ @Test
+ public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
+ mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
+ mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ verify(mDeleteIntent, times(2)).send();
+ }
+
static class TestableBubbleController extends BubbleController {
TestableBubbleController(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index cef78db1b16b..de155055b76b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.Notification;
+import android.app.Notification.BubbleMetadata;
import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
@@ -152,8 +153,18 @@ public class NotificationTestHelper {
* Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
*/
public ExpandableNotificationRow createBubble() throws Exception {
+ return createBubble(null);
+ }
+
+ /**
+ * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+ *
+ * @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent}
+ */
+ public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent)
+ throws Exception {
Notification n = createNotification(false /* isGroupSummary */,
- null /* groupKey */, true /* isBubble */);
+ null /* groupKey */, true /* isBubble */, deleteIntent);
return generateRow(n, PKG, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
}
@@ -196,7 +207,8 @@ public class NotificationTestHelper {
* @return a notification that is in the group specified or standalone if unspecified
*/
private Notification createNotification(boolean isGroupSummary, @Nullable String groupKey) {
- return createNotification(isGroupSummary, groupKey, false /* isBubble */);
+ return createNotification(isGroupSummary, groupKey, false /* isBubble */,
+ null /* bubbleDeleteIntent */);
}
/**
@@ -208,7 +220,8 @@ public class NotificationTestHelper {
* @return a notification that is in the group specified or standalone if unspecified
*/
private Notification createNotification(boolean isGroupSummary,
- @Nullable String groupKey, boolean isBubble) {
+ @Nullable String groupKey, boolean isBubble,
+ @Nullable PendingIntent bubbleDeleteIntent) {
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -227,7 +240,8 @@ public class NotificationTestHelper {
notificationBuilder.setGroup(groupKey);
}
if (isBubble) {
- notificationBuilder.setBubbleMetadata(makeBubbleMetadata());
+ BubbleMetadata metadata = makeBubbleMetadata(bubbleDeleteIntent);
+ notificationBuilder.setBubbleMetadata(metadata);
}
return notificationBuilder.build();
}
@@ -291,11 +305,13 @@ public class NotificationTestHelper {
return row;
}
- private Notification.BubbleMetadata makeBubbleMetadata() {
+ private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent) {
Intent target = new Intent(mContext, BubblesTestActivity.class);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0);
- return new Notification.BubbleMetadata.Builder()
+
+ return new BubbleMetadata.Builder()
.setIntent(bubbleIntent)
+ .setDeleteIntent(deleteIntent)
.setTitle("bubble title")
.setIcon(Icon.createWithResource(mContext, 1))
.setDesiredHeight(314)