diff options
| author | 2018-05-08 14:43:21 -0700 | |
|---|---|---|
| committer | 2018-05-14 17:13:17 -0700 | |
| commit | f93bf3e2cf4f67b6eec2c66278a72f69c923a6da (patch) | |
| tree | de3f297f6a75c24d288a706eed47ef58f081f737 | |
| parent | 150bafd2176d149edf5bc830b3fcb193bfd536f5 (diff) | |
Fixed an issue where the wrong notification would be heads upped
When setting the heads up on the summary, the headsup could be transfered
to the children, which is correct whenever we only have one child but not
if contains more then one. We're now looking at the pending notifications
as well in order to make the heads up transfer and revert back in case
we just made the wrong decision.
Test: install test app from bug: first post group and first child and then group with second child, observe normal heads up behavior
Change-Id: I52803f6846d602b6eb542aa18185bde7d60573e0
Fixes: 71818291
5 files changed, 166 insertions, 0 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 3efeb6ed2d69..908ddba3faf0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -2690,6 +2690,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mNotificationViewState; } + public NotificationViewState getViewState() { + return mNotificationViewState; + } + @Override public boolean isAboveShelf() { return !isOnKeyguard() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java index 7681530503a9..f14ca71e61f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java @@ -229,6 +229,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mMessagingUtil = new NotificationMessagingUtil(context); mSystemServicesProxy = SystemServicesProxy.getInstance(mContext); + mGroupManager.setPendingEntries(mPendingNotifications); } public void setUpWithPresenter(NotificationPresenter presenter, @@ -739,6 +740,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mNotificationData.getImportance(key)); mPendingNotifications.put(key, shadeEntry); + mGroupManager.onPendingEntryAdded(shadeEntry); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index ca65965cc9e6..55ffb3e62e53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import android.app.Notification; +import android.os.SystemClock; import android.service.notification.StatusBarNotification; import android.support.annotation.Nullable; import android.util.Log; @@ -29,9 +31,11 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Objects; /** * A class to handle notifications and their corresponding groups. @@ -39,12 +43,14 @@ import java.util.Map; public class NotificationGroupManager implements OnHeadsUpChangedListener { private static final String TAG = "NotificationGroupManager"; + private static final long HEADS_UP_TRANSFER_TIMEOUT = 300; private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>(); private OnGroupChangeListener mListener; private int mBarState = -1; private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>(); private HeadsUpManager mHeadsUpManager; private boolean mIsUpdatingUnchangedGroup; + private HashMap<String, NotificationData.Entry> mPendingNotifications; public void setOnGroupChangeListener(OnGroupChangeListener listener) { mListener = listener; @@ -147,6 +153,103 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { mListener.onGroupCreatedFromChildren(group); } } + cleanUpHeadsUpStatesOnAdd(group, false /* addIsPending */); + } + + public void onPendingEntryAdded(NotificationData.Entry shadeEntry) { + String groupKey = getGroupKey(shadeEntry.notification); + NotificationGroup group = mGroupMap.get(groupKey); + if (group != null) { + cleanUpHeadsUpStatesOnAdd(group, true /* addIsPending */); + } + } + + /** + * Clean up the heads up states when a new child was added. + * @param group The group where a view was added or will be added. + * @param addIsPending True if is the addition still pending or false has it already been added. + */ + private void cleanUpHeadsUpStatesOnAdd(NotificationGroup group, boolean addIsPending) { + if (!addIsPending && group.hunSummaryOnNextAddition) { + if (!mHeadsUpManager.isHeadsUp(group.summary.key)) { + mHeadsUpManager.showNotification(group.summary); + } + group.hunSummaryOnNextAddition = false; + } + // Because notification groups are not delivered as a whole unit, it may happen that a + // group child gets added quite a bit after the summary got posted. Our guidance is, that + // apps should always post the group summary as well and we'll hide it for them if the child + // is the only child in a group. Because of this, we also have to transfer heads up to the + // child, otherwise the invisible summary would be heads-upped. + // This transfer to the child is not always correct in case the app has just posted another + // child in addition to the existing one, but it hasn't arrived in systemUI yet. In such + // a scenario we would transfer the heads up to the old child and the wrong notification + // would be heads-upped. In oder to avoid this, we'll recover from this issue and hun the + // summary again instead of the old child if it's within a certain timeout. + if (SystemClock.elapsedRealtime() - group.lastHeadsUpTransfer < HEADS_UP_TRANSFER_TIMEOUT) { + if (!onlySummaryAlerts(group.summary)) { + return; + } + int numChildren = group.children.size(); + NotificationData.Entry isolatedChild = getIsolatedChild(getGroupKey( + group.summary.notification)); + int numPendingChildren = getPendingChildrenNotAlerting(group); + numChildren += numPendingChildren; + if (isolatedChild != null) { + numChildren++; + } + if (numChildren <= 1) { + return; + } + boolean releasedChild = false; + ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values()); + int size = children.size(); + for (int i = 0; i < size; i++) { + NotificationData.Entry entry = children.get(i); + if (onlySummaryAlerts(entry) && entry.row.isHeadsUp()) { + releasedChild = true; + mHeadsUpManager.releaseImmediately(entry.key); + } + } + if (isolatedChild != null && onlySummaryAlerts(isolatedChild) + && isolatedChild.row.isHeadsUp()) { + releasedChild = true; + mHeadsUpManager.releaseImmediately(isolatedChild.key); + } + if (releasedChild && !mHeadsUpManager.isHeadsUp(group.summary.key)) { + boolean notifyImmediately = (numChildren - numPendingChildren) > 1; + if (notifyImmediately) { + mHeadsUpManager.showNotification(group.summary); + } else { + group.hunSummaryOnNextAddition = true; + } + group.lastHeadsUpTransfer = 0; + } + } + } + + private int getPendingChildrenNotAlerting(NotificationGroup group) { + if (mPendingNotifications == null) { + return 0; + } + int number = 0; + String groupKey = getGroupKey(group.summary.notification); + Collection<NotificationData.Entry> values = mPendingNotifications.values(); + for (NotificationData.Entry entry : values) { + if (!isGroupChild(entry.notification)) { + continue; + } + if (!Objects.equals(getGroupKey(entry.notification), groupKey)) { + continue; + } + if (group.children.containsKey(entry.key)) { + continue; + } + if (onlySummaryAlerts(entry)) { + number++; + } + } + return number; } private void onEntryBecomingChild(NotificationData.Entry entry) { @@ -421,8 +524,16 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { || !entry.row.isHeadsUp()) { return; } + // The parent of a suppressed group got huned, lets hun the child! NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); + + if (pendingInflationsWillAddChildren(notificationGroup)) { + // New children will actually be added to this group, let's not transfer the heads + // up + return; + } + if (notificationGroup != null) { Iterator<NotificationData.Entry> iterator = notificationGroup.children.values().iterator(); @@ -438,6 +549,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { if (mHeadsUpManager.isHeadsUp(child.key)) { mHeadsUpManager.updateNotification(child, true); } else { + if (onlySummaryAlerts(entry)) { + notificationGroup.lastHeadsUpTransfer = SystemClock.elapsedRealtime(); + } mHeadsUpManager.showNotification(child); } } @@ -445,6 +559,35 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { mHeadsUpManager.releaseImmediately(entry.key); } + private boolean onlySummaryAlerts(NotificationData.Entry entry) { + return entry.notification.getNotification().getGroupAlertBehavior() + == Notification.GROUP_ALERT_SUMMARY; + } + + /** + * Check if the pending inflations will add children to this group. + * @param group The group to check. + */ + private boolean pendingInflationsWillAddChildren(NotificationGroup group) { + if (mPendingNotifications == null) { + return false; + } + Collection<NotificationData.Entry> values = mPendingNotifications.values(); + String groupKey = getGroupKey(group.summary.notification); + for (NotificationData.Entry entry : values) { + if (!isGroupChild(entry.notification)) { + continue; + } + if (!Objects.equals(getGroupKey(entry.notification), groupKey)) { + continue; + } + if (!group.children.containsKey(entry.key)) { + return true; + } + } + return false; + } + private boolean shouldIsolate(StatusBarNotification sbn) { NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); return (sbn.isGroup() && !sbn.getNotification().isGroupSummary()) @@ -477,6 +620,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { } } + public void setPendingEntries(HashMap<String, NotificationData.Entry> pendingNotifications) { + mPendingNotifications = pendingNotifications; + } + public static class NotificationGroup { public final HashMap<String, NotificationData.Entry> children = new HashMap<>(); public NotificationData.Entry summary; @@ -485,6 +632,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { * Is this notification group suppressed, i.e its summary is hidden */ public boolean suppressed; + /** + * The time when the last heads transfer from group to child happened, while the summary + * has the flags to heads up on its own. + */ + public long lastHeadsUpTransfer; + public boolean hunSummaryOnNextAddition; @Override public String toString() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index e5ab712e9bdd..a93fb89bbc11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -268,6 +268,12 @@ public class NotificationChildrenContainer extends ViewGroup { updateGroupOverflow(); row.setContentTransformationAmount(0, false /* isLastChild */); + // It doesn't make sense to keep old animations around, lets cancel them! + ExpandableNotificationRow.NotificationViewState viewState = row.getViewState(); + if (viewState != null) { + viewState.cancelAnimations(row); + row.cancelAppearDrawing(); + } } public void removeNotification(ExpandableNotificationRow row) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index eeaa6cb58681..0ff6bfc564c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -3201,6 +3201,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (row.isChildInGroup()) { // We can otherwise get stuck in there if it was just isolated row.setHeadsUpAnimatingAway(false); + continue; } } else { ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row); |