BubbleData [6/n]: Splice BubbleData into code path
BubbleData internal representation becomes a list in prep
for managing order and grouping internally. Changes are
passed through bubbleData and dispatched for UI updates
via Listener.
Bug: 123542488
Test: atest BubbleControllerTest
Change-Id: I380ca718bc896307b31e73030b4202762a6cf353
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 665df77..0332477 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,47 +16,65 @@
package com.android.systemui.bubbles;
+import android.os.UserHandle;
import android.view.LayoutInflater;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import java.util.Objects;
+
/**
* Encapsulates the data and UI elements of a bubble.
*/
class Bubble {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "Bubble";
+
private final String mKey;
+ private final String mGroupId;
private final BubbleExpandedView.OnBubbleBlockedListener mListener;
private boolean mInflated;
-
- public BubbleView iconView;
- public BubbleExpandedView expandedView;
public NotificationEntry entry;
+ BubbleView iconView;
+ BubbleExpandedView expandedView;
+
+ private static String groupId(NotificationEntry entry) {
+ UserHandle user = entry.notification.getUser();
+ return user.getIdentifier() + '|' + entry.notification.getPackageName();
+ }
Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) {
entry = e;
mKey = e.key;
+ mGroupId = groupId(e);
mListener = listener;
}
- /** @deprecated use the other constructor to defer View creation. */
- @Deprecated
- Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView,
- BubbleExpandedView.OnBubbleBlockedListener listener) {
- this(e, listener);
- inflate(inflater, stackView);
- }
-
public String getKey() {
return mKey;
}
+ public String getGroupId() {
+ return mGroupId;
+ }
+
+ public String getPackageName() {
+ return entry.notification.getPackageName();
+ }
+
boolean isInflated() {
return mInflated;
}
+ public void updateDotVisibility() {
+ if (iconView != null) {
+ iconView.updateDotVisibility();
+ }
+ }
+
void inflate(LayoutInflater inflater, BubbleStackView stackView) {
if (mInflated) {
return;
@@ -73,10 +91,32 @@
mInflated = true;
}
+ void setDismissed() {
+ entry.setBubbleDismissed(true);
+ // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker
+ if (expandedView != null) {
+ expandedView.cleanUpExpandedState();
+ }
+ }
+
void setEntry(NotificationEntry entry) {
+ this.entry = entry;
if (mInflated) {
iconView.update(entry);
expandedView.update(entry);
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Bubble)) return false;
+ Bubble bubble = (Bubble) o;
+ return Objects.equals(mKey, bubble.mKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mKey);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 0fcc950..acdcfb2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -77,8 +77,7 @@
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
@Singleton
-public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener,
- ConfigurationController.ConfigurationListener {
+public class BubbleController implements ConfigurationController.ConfigurationListener {
private static final String TAG = "BubbleController";
@@ -174,6 +173,10 @@
@Override
public void onStateChanged(int newState) {
mState = newState;
+ boolean shouldCollapse = (mState != SHADE);
+ if (shouldCollapse) {
+ collapseStack();
+ }
updateVisibility();
}
}
@@ -236,7 +239,6 @@
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
}
- mStackView.setOnBlockedListener(this);
}
}
@@ -284,28 +286,38 @@
if (mStackView == null) {
return false;
}
- for (Bubble bubble : mBubbleData.getBubbles()) {
- if (!bubble.entry.isBubbleDismissed()) {
- return true;
- }
- }
- return false;
+ return mBubbleData.hasBubbles();
}
/**
* Whether the stack of bubbles is expanded or not.
*/
public boolean isStackExpanded() {
- return mStackView != null && mStackView.isExpanded();
+ return mBubbleData.isExpanded();
+ }
+
+ /**
+ * Tell the stack of bubbles to expand.
+ */
+ public void expandStack() {
+ mBubbleData.setExpanded(true);
}
/**
* Tell the stack of bubbles to collapse.
*/
public void collapseStack() {
- if (mStackView != null) {
- mStackView.collapseStack();
- }
+ mBubbleData.setExpanded(false /* expanded */);
+ }
+
+ void selectBubble(Bubble bubble) {
+ mBubbleData.setSelectedBubble(bubble);
+ }
+
+ @VisibleForTesting
+ void selectBubble(String key) {
+ Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ selectBubble(bubble);
}
/**
@@ -314,8 +326,10 @@
* @param notificationKey the notification key for the bubble to be selected
*/
public void expandStackAndSelectBubble(String notificationKey) {
- if (mStackView != null && mBubbleData.getBubble(notificationKey) != null) {
- mStackView.setExpandedBubble(notificationKey);
+ Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey);
+ if (bubble != null) {
+ mBubbleData.setSelectedBubble(bubble);
+ mBubbleData.setExpanded(true);
}
}
@@ -323,13 +337,7 @@
* Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
*/
void dismissStack(@DismissReason int reason) {
- if (mStackView == null) {
- return;
- }
- mStackView.stackDismissed(reason);
-
- updateVisibility();
- mNotificationEntryManager.updateNotifications();
+ mBubbleData.dismissAll(reason);
}
/**
@@ -348,20 +356,7 @@
* @param notif the notification associated with this bubble.
*/
void updateBubble(NotificationEntry notif) {
- if (mStackView != null && mBubbleData.getBubble(notif.key) != null) {
- // It's an update
- mStackView.updateBubble(notif);
- } else {
- // It's new
- ensureStackViewCreated();
- mStackView.addBubble(notif);
- }
- Bubble bubble = mBubbleData.getBubble(notif.key);
- if (shouldAutoExpand(notif)) {
- mStackView.setSelectedBubble(bubble);
- mStackView.setExpanded(true);
- }
- updateVisibility();
+ mBubbleData.notificationEntryUpdated(notif);
}
/**
@@ -371,23 +366,10 @@
*/
@MainThread
void removeBubble(String key, int reason) {
- if (mStackView != null) {
- mStackView.removeBubble(key, reason);
- }
- mNotificationEntryManager.updateNotifications();
- updateVisibility();
- }
-
- @Override
- public void onBubbleBlocked(NotificationEntry entry) {
- Object[] bubbles = mBubbleData.getBubbles().toArray();
- for (int i = 0; i < bubbles.length; i++) {
- NotificationEntry e = ((Bubble) bubbles[i]).entry;
- boolean samePackage = entry.notification.getPackageName().equals(
- e.notification.getPackageName());
- if (samePackage) {
- removeBubble(entry.key, DISMISS_BLOCKED);
- }
+ // TEMP: refactor to change this to pass entry
+ Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ if (bubble != null) {
+ mBubbleData.notificationEntryRemoved(bubble.entry, reason);
}
}
@@ -424,7 +406,6 @@
updateShowInShadeForSuppressNotification(entry);
entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
updateBubble(entry);
- mStackView.updateDotVisibility(entry.key);
}
}
@@ -446,44 +427,57 @@
}
};
+ @SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
+
@Override
public void onBubbleAdded(Bubble bubble) {
-
+ ensureStackViewCreated();
+ mStackView.addBubble(bubble);
}
@Override
public void onBubbleRemoved(Bubble bubble, int reason) {
-
+ if (mStackView != null) {
+ mStackView.removeBubble(bubble);
+ }
}
public void onBubbleUpdated(Bubble bubble) {
-
+ if (mStackView != null) {
+ mStackView.updateBubble(bubble);
+ }
}
@Override
public void onOrderChanged(List<Bubble> bubbles) {
-
}
@Override
public void onSelectionChanged(Bubble selectedBubble) {
-
+ if (mStackView != null) {
+ mStackView.setSelectedBubble(selectedBubble);
+ }
}
@Override
public void onExpandedChanged(boolean expanded) {
-
+ if (mStackView != null) {
+ mStackView.setExpanded(expanded);
+ }
}
@Override
public void showFlyoutText(Bubble bubble, String text) {
-
+ if (mStackView != null) {
+ mStackView.animateInFlyoutForBubble(bubble);
+ }
}
@Override
public void apply() {
-
+ mNotificationEntryManager.updateNotifications();
+ updateVisibility();
}
};
@@ -514,7 +508,6 @@
mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
} else if (mStackView != null) {
mStackView.setVisibility(INVISIBLE);
- collapseStack();
}
updateBubblesShowing();
}
@@ -621,14 +614,14 @@
@Override
public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
- mStackView.collapseStack();
+ mBubbleData.setExpanded(false);
}
}
@Override
public void onActivityLaunchOnSecondaryDisplayRerouted() {
if (mStackView != null) {
- mStackView.collapseStack();
+ mBubbleData.setExpanded(false);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index fe3f9d1..259665d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -15,14 +15,24 @@
*/
package com.android.systemui.bubbles;
-import androidx.annotation.Nullable;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -33,6 +43,8 @@
@Singleton
public class BubbleData {
+ private static final String TAG = "BubbleData";
+
/**
* This interface reports changes to the state and appearance of bubbles which should be applied
* as necessary to the UI.
@@ -53,7 +65,7 @@
* A Bubble has been removed. A call to {@link #onOrderChanged(List)} will
* follow.
*/
- void onBubbleRemoved(Bubble bubble, @BubbleController.DismissReason int reason);
+ void onBubbleRemoved(Bubble bubble, @DismissReason int reason);
/**
* An existing bubble has been updated.
@@ -86,46 +98,253 @@
void apply();
}
- private HashMap<String, Bubble> mBubbles = new HashMap<>();
+ private final Context mContext;
+ private final List<Bubble> mBubbles = new ArrayList<>();
+ private Bubble mSelectedBubble;
+ private boolean mExpanded;
private Listener mListener;
@VisibleForTesting
@Inject
- public BubbleData() {}
-
- /**
- * The set of bubbles.
- */
- public Collection<Bubble> getBubbles() {
- return mBubbles.values();
+ public BubbleData(Context context) {
+ mContext = context;
}
- @Nullable
- public Bubble getBubble(String key) {
- return mBubbles.get(key);
+ public boolean hasBubbles() {
+ return !mBubbles.isEmpty();
}
- public void addBubble(Bubble b) {
- mBubbles.put(b.getKey(), b);
+ public boolean isExpanded() {
+ return mExpanded;
}
- @Nullable
- public Bubble removeBubble(String key) {
- return mBubbles.remove(key);
+ public boolean hasBubbleWithKey(String key) {
+ return getBubbleWithKey(key) != null;
}
- public void updateBubble(String key, NotificationEntry newEntry) {
- Bubble oldBubble = mBubbles.get(key);
- if (oldBubble != null) {
- oldBubble.setEntry(newEntry);
+ public void setExpanded(boolean expanded) {
+ if (setExpandedInternal(expanded)) {
+ mListener.apply();
}
}
- public void clear() {
- mBubbles.clear();
+ public void setSelectedBubble(Bubble bubble) {
+ if (setSelectedBubbleInternal(bubble)) {
+ mListener.apply();
+ }
+ }
+
+ public void notificationEntryUpdated(NotificationEntry entry) {
+ Bubble bubble = getBubbleWithKey(entry.key);
+ if (bubble == null) {
+ // Create a new bubble
+ bubble = new Bubble(entry, this::onBubbleBlocked);
+ mBubbles.add(0, bubble); // TODO: reorder/group
+ mListener.onBubbleAdded(bubble);
+ } else {
+ // Updates an existing bubble
+ bubble.setEntry(entry);
+ mListener.onBubbleUpdated(bubble);
+ }
+ if (shouldAutoExpand(entry)) {
+ setSelectedBubbleInternal(bubble);
+ if (!mExpanded) {
+ setExpandedInternal(true);
+ }
+ } else if (mSelectedBubble == null) {
+ setSelectedBubbleInternal(bubble);
+ }
+ // TODO: reorder/group
+ mListener.apply();
+ }
+
+ public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
+ int indexToRemove = indexForKey(entry.key);
+ if (indexToRemove >= 0) {
+ Bubble removed = mBubbles.remove(indexToRemove);
+ removed.setDismissed();
+ mListener.onBubbleRemoved(removed, reason);
+ maybeSendDeleteIntent(reason, removed.entry);
+
+ if (mBubbles.isEmpty()) {
+ setExpandedInternal(false);
+ setSelectedBubbleInternal(null);
+ } else if (removed == mSelectedBubble) {
+ int newIndex = Math.min(indexToRemove, mBubbles.size() - 1);
+ Bubble newSelected = mBubbles.get(newIndex);
+ setSelectedBubbleInternal(newSelected);
+ }
+ // TODO: reorder/group
+ mListener.apply();
+ }
+ }
+
+ public void dismissAll(@DismissReason int reason) {
+ boolean changed = setExpandedInternal(false);
+ while (!mBubbles.isEmpty()) {
+ Bubble bubble = mBubbles.remove(0);
+ bubble.setDismissed();
+ maybeSendDeleteIntent(reason, bubble.entry);
+ mListener.onBubbleRemoved(bubble, reason);
+ changed = true;
+ }
+ if (setSelectedBubbleInternal(null)) {
+ changed = true;
+ }
+ if (changed) {
+ // TODO: reorder/group
+ mListener.apply();
+ }
+ }
+
+ /**
+ * Requests a change to the selected bubble. Calls {@link Listener#onSelectionChanged} if
+ * the value changes.
+ *
+ * @param bubble the new selected bubble
+ * @return true if the state changed as a result
+ */
+ private boolean setSelectedBubbleInternal(Bubble bubble) {
+ if (Objects.equals(bubble, mSelectedBubble)) {
+ return false;
+ }
+ if (bubble != null && !mBubbles.contains(bubble)) {
+ Log.e(TAG, "Cannot select bubble which doesn't exist!"
+ + " (" + bubble + ") bubbles=" + mBubbles);
+ return false;
+ }
+ if (mExpanded) {
+ // TODO: bubble.markAsActive() ?
+ bubble.entry.setShowInShadeWhenBubble(false);
+ }
+ mSelectedBubble = bubble;
+ mListener.onSelectionChanged(mSelectedBubble);
+ return true;
+ }
+
+
+ /**
+ * Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if
+ * the value changes.
+ *
+ * @param shouldExpand the new requested state
+ * @return true if the state changed as a result
+ */
+ private boolean setExpandedInternal(boolean shouldExpand) {
+ if (mExpanded == shouldExpand) {
+ return false;
+ }
+ if (shouldExpand) {
+ if (mBubbles.isEmpty()) {
+ Log.e(TAG, "Attempt to expand stack when empty!");
+ return false;
+ }
+ if (mSelectedBubble == null) {
+ Log.e(TAG, "Attempt to expand stack without selected bubble!");
+ return false;
+ }
+ // TODO: bubble.markAsActive() ?
+ mSelectedBubble.entry.setShowInShadeWhenBubble(false);
+ }
+ // TODO: reorder/regroup
+ mExpanded = shouldExpand;
+ mListener.onExpandedChanged(mExpanded);
+ return true;
+ }
+
+ private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
+ if (reason == BubbleController.DISMISS_USER_GESTURE) {
+ Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
+ PendingIntent deleteIntent = bubbleMetadata != null
+ ? bubbleMetadata.getDeleteIntent()
+ : null;
+ if (deleteIntent != null) {
+ try {
+ deleteIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Failed to send delete intent for bubble with key: " + entry.key);
+ }
+ }
+ }
+ }
+
+ private void onBubbleBlocked(NotificationEntry entry) {
+ boolean changed = false;
+ final String blockedPackage = entry.notification.getPackageName();
+ for (Iterator<Bubble> i = mBubbles.iterator(); i.hasNext(); ) {
+ Bubble bubble = i.next();
+ if (bubble.getPackageName().equals(blockedPackage)) {
+ i.remove();
+ mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
+ changed = true;
+ }
+ }
+ if (changed) {
+ // TODO: reorder/group
+ mListener.apply();
+ }
+ }
+
+ private int indexForKey(String key) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getKey().equals(key)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private Bubble removeBubbleWithKey(String key) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getKey().equals(key)) {
+ mBubbles.remove(i);
+ return bubble;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The set of bubbles.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public Collection<Bubble> getBubbles() {
+ return Collections.unmodifiableList(mBubbles);
+ }
+
+ @VisibleForTesting(visibility = PRIVATE)
+ Bubble getBubbleWithKey(String key) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getKey().equals(key)) {
+ return bubble;
+ }
+ }
+ return null;
}
public void setListener(Listener listener) {
mListener = listener;
}
-}
+
+ boolean shouldAutoExpand(NotificationEntry entry) {
+ Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
+ return metadata != null && metadata.getAutoExpandBubble()
+ && isForegroundApp(entry.notification.getPackageName());
+ }
+
+ /**
+ * Return true if the applications with the package name is running in foreground.
+ *
+ * @param pkgName application package name.
+ */
+ boolean isForegroundApp(String pkgName) {
+ ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */);
+ return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 424cd55..18b2e37 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -20,8 +20,6 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
@@ -54,9 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
-import com.android.systemui.Dependency;
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;
@@ -359,14 +355,13 @@
}
switch (action) {
case AccessibilityNodeInfo.ACTION_DISMISS:
- Dependency.get(BubbleController.class).dismissStack(
- BubbleController.DISMISS_ACCESSIBILITY_ACTION);
+ mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION);
return true;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
- collapseStack();
+ mBubbleData.setExpanded(false);
return true;
case AccessibilityNodeInfo.ACTION_EXPAND:
- expandStack();
+ mBubbleData.setExpanded(true);
return true;
}
return false;
@@ -393,9 +388,9 @@
* @param key the {@link NotificationEntry#key} associated with the bubble.
*/
public void updateDotVisibility(String key) {
- Bubble b = mBubbleData.getBubble(key);
+ Bubble b = mBubbleData.getBubbleWithKey(key);
if (b != null) {
- b.iconView.updateDotVisibility();
+ b.updateDotVisibility();
}
}
@@ -407,16 +402,6 @@
}
/**
- * Sets the listener to notify when a bubble is blocked.
- */
- public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) {
- mBlockedListener = listener;
- for (Bubble b : mBubbleData.getBubbles()) {
- b.expandedView.setOnBlockedListener(mBlockedListener);
- }
- }
-
- /**
* Whether the stack of bubbles is expanded or not.
*/
public boolean isExpanded() {
@@ -445,7 +430,7 @@
*/
@Deprecated
void setExpandedBubble(String key) {
- Bubble bubbleToExpand = mBubbleData.getBubble(key);
+ Bubble bubbleToExpand = mBubbleData.getBubbleWithKey(key);
if (bubbleToExpand != null) {
setSelectedBubble(bubbleToExpand);
bubbleToExpand.entry.setShowInShadeWhenBubble(false);
@@ -466,11 +451,36 @@
}
}
+ // via BubbleData.Listener
+ void addBubble(Bubble bubble) {
+ bubble.inflate(mInflater, this);
+ mBubbleContainer.addView(bubble.iconView, 0,
+ new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ ViewClippingUtil.setClippingDeactivated(bubble.iconView, true, mClippingParameters);
+ requestUpdate();
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+ }
+
+ // via BubbleData.Listener
+ void removeBubble(Bubble bubble) {
+ // Remove it from the views
+ int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView);
+ mBubbleContainer.removeViewAt(removedIndex);
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ }
+
+ // via BubbleData.Listener
+ void updateBubble(Bubble bubble) {
+ requestUpdate();
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+ }
+
/**
* Changes the currently selected bubble. If the stack is already expanded, the newly selected
* bubble will be shown immediately. This does not change the expanded state or change the
* position of any bubble.
*/
+ // via BubbleData.Listener
public void setSelectedBubble(Bubble bubbleToSelect) {
if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
return;
@@ -489,7 +499,8 @@
logBubbleEvent(previouslySelected, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
logBubbleEvent(bubbleToSelect, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
notifyExpansionChanged(previouslySelected.entry, false /* expanded */);
- notifyExpansionChanged(bubbleToSelect.entry, true /* expanded */);
+ notifyExpansionChanged(bubbleToSelect == null ? null : bubbleToSelect.entry,
+ true /* expanded */);
});
}
}
@@ -497,13 +508,15 @@
/**
* Changes the expanded state of the stack.
*
- * @param expanded whether the bubble stack should appear expanded
+ * @param shouldExpand whether the bubble stack should appear expanded
*/
- public void setExpanded(boolean expanded) {
- if (expanded == mIsExpanded) {
+ // via BubbleData.Listener
+ public void setExpanded(boolean shouldExpand) {
+ boolean wasExpanded = mIsExpanded;
+ if (shouldExpand == wasExpanded) {
return;
}
- if (mIsExpanded) {
+ if (wasExpanded) {
// Collapse the stack
animateExpansion(false /* expand */);
logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
@@ -518,131 +531,17 @@
}
/**
- * Adds a bubble to the top of the stack.
- *
- * @param entry the notification to add to the stack of bubbles.
- */
- void addBubble(NotificationEntry entry) {
- Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener);
- mBubbleData.addBubble(b);
-
- mBubbleContainer.addView(b.iconView, 0,
- new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters);
-
- requestUpdate();
- logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
-
- animateInFlyoutForBubble(b);
- }
-
- /**
- * Remove a bubble from the stack.
- */
- void removeBubble(String key, int reason) {
- Bubble b = mBubbleData.removeBubble(key);
- if (b == null) {
- return;
- }
- setBubbleDismissed(b, reason);
-
- // Remove it from the views
- int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
- mBubbleContainer.removeViewAt(removedIndex);
-
- int bubbleCount = mBubbleContainer.getChildCount();
- if (bubbleCount == 0) {
- // If no bubbles remain, collapse the entire stack.
- collapseStack();
- return;
- } else if (b.equals(mExpandedBubble)) {
- // Was the current bubble just removed?
- // If we have other bubbles and are expanded go to the next one or previous
- // if the bubble removed was last
- int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
- BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
- if (mIsExpanded) {
- setExpandedBubble(expandedBubble.getKey());
- } else {
- mExpandedBubble = null;
- }
- }
- // TODO: consider logging reason code
- logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
- }
-
- /**
* Dismiss the stack of bubbles.
+ * @deprecated
*/
+ @Deprecated
void stackDismissed(int reason) {
- for (Bubble bubble : mBubbleData.getBubbles()) {
- setBubbleDismissed(bubble, reason);
- }
- mBubbleData.clear();
- collapseStack();
- mBubbleContainer.removeAllViews();
- mExpandedViewContainer.removeAllViews();
- // TODO: consider logging reason code
+ mBubbleData.dismissAll(reason);
logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
}
/**
- * Marks the notification entry as dismissed & calls any delete intents for the bubble.
- *
- * <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 void setBubbleDismissed(Bubble bubble, @DismissReason int reason) {
- if (DEBUG) {
- Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason);
- }
- bubble.entry.setBubbleDismissed(true);
- bubble.expandedView.cleanUpExpandedState();
-
- if (reason == BubbleController.DISMISS_USER_GESTURE) {
- Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata();
- PendingIntent deleteIntent = bubbleMetadata != null
- ? bubbleMetadata.getDeleteIntent()
- : null;
- 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"));
- }
- }
- }
- }
-
- /**
- * Updates a bubble in the stack.
- * @param entry the entry to update in the stack.
- */
- public void updateBubble(NotificationEntry entry) {
- Bubble b = mBubbleData.getBubble(entry.key);
- mBubbleData.updateBubble(entry.key, entry);
-
- if (!mIsExpanded) {
- // If alerting it gets promoted to top of the stack.
- if (mBubbleContainer.indexOfChild(b.iconView) != 0) {
- mBubbleContainer.moveViewTo(b.iconView, 0);
- }
- requestUpdate();
- animateInFlyoutForBubble(b /* bubble */);
- }
- if (mIsExpanded && entry.equals(mExpandedBubble.entry)) {
- entry.setShowInShadeWhenBubble(false);
- requestUpdate();
- }
- logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
- }
-
- /**
* @return the view the touch event is on
*/
@Nullable
@@ -670,7 +569,7 @@
return this;
}
- public View getFlyoutView() {
+ View getFlyoutView() {
return mFlyout;
}
@@ -683,13 +582,8 @@
*/
@Deprecated
@MainThread
- public void collapseStack() {
- if (mIsExpanded) {
- // TODO: Save opened bubble & move it to top of stack
- animateExpansion(false /* shouldExpand */);
- notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded);
- logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
- }
+ void collapseStack() {
+ mBubbleData.setExpanded(false);
}
/**
@@ -712,12 +606,8 @@
*/
@Deprecated
@MainThread
- public void expandStack() {
- if (!mIsExpanded) {
- String expandedBubbleKey = getBubbleAt(0).getKey();
- setExpandedBubble(expandedBubbleKey);
- logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
- }
+ void expandStack() {
+ mBubbleData.setExpanded(true);
}
/**