diff options
author | 2020-04-04 00:30:10 +0000 | |
---|---|---|
committer | 2020-04-04 00:30:10 +0000 | |
commit | 822b72843d828f1ecd05418ed40677f47ac57eb1 (patch) | |
tree | 7b9662a754baea5966acef6256a6e1d750cd7b97 | |
parent | 2c3bc7ccf6d9b18c1e301e3f4fa647294f7d48a6 (diff) | |
parent | ba069aeea388c572fb787dd865b91545e7d5c545 (diff) |
Merge "Isolating important conversations" into rvc-dev
16 files changed, 290 insertions, 108 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index fe2f1f3eefc5..1297f996b743 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -177,12 +177,32 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle currentUserId); ent.setSensitive(sensitive, deviceSensitive); ent.getRow().setNeedsRedaction(needsRedaction); - if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) { - NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn()); - List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(summary); + boolean isChildInGroup = mGroupManager.isChildInGroupWithSummary(ent.getSbn()); + + boolean groupChangesAllowed = mVisualStabilityManager.areGroupChangesAllowed() + || !ent.hasFinishedInitialization(); + NotificationEntry parent = mGroupManager.getGroupSummary(ent.getSbn()); + if (!groupChangesAllowed) { + // We don't to change groups while the user is looking at them + boolean wasChildInGroup = ent.isChildInGroup(); + if (isChildInGroup && !wasChildInGroup) { + isChildInGroup = wasChildInGroup; + mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager); + } else if (!isChildInGroup && wasChildInGroup) { + // We allow grouping changes if the group was collapsed + if (mGroupManager.isLogicalGroupExpanded(ent.getSbn())) { + isChildInGroup = wasChildInGroup; + parent = ent.getRow().getNotificationParent().getEntry(); + mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager); + } + } + } + + if (isChildInGroup) { + List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent); if (orderedChildren == null) { orderedChildren = new ArrayList<>(); - mTmpChildOrderMap.put(summary, orderedChildren); + mTmpChildOrderMap.put(parent, orderedChildren); } orderedChildren.add(ent); } else { @@ -205,7 +225,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } for (ExpandableNotificationRow viewToRemove : viewsToRemove) { - if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getEntry().getSbn())) { + if (mEntryManager.getPendingOrActiveNotif(viewToRemove.getEntry().getKey()) != null) { // we are only transferring this notification to its parent, don't generate an // animation mListContainer.setChildTransferInProgress(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 7ef1d0eba3f1..1696f0715865 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -26,6 +26,7 @@ import com.android.internal.widget.ConversationLayout import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView +import com.android.systemui.statusbar.phone.NotificationGroupManager import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Singleton @@ -60,6 +61,7 @@ class ConversationNotificationProcessor @Inject constructor( @Singleton class ConversationNotificationManager @Inject constructor( private val notificationEntryManager: NotificationEntryManager, + private val notificationGroupManager: NotificationGroupManager, private val context: Context ) { // Need this state to be thread safe, since it's accessed from the ui thread @@ -81,10 +83,19 @@ class ConversationNotificationManager @Inject constructor( if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) { val important = ranking.channel.isImportantConversation + var changed = false entry.row?.layouts?.asSequence() ?.flatMap(::getLayouts) ?.mapNotNull { it as? ConversationLayout } - ?.forEach { it.setIsImportantConversation(important) } + ?.forEach { + if (important != it.isImportantConversation) { + it.setIsImportantConversation(important) + changed = true + } + } + if (changed) { + notificationGroupManager.updateIsolation(entry) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 77376e595819..295adae9c9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -252,7 +252,7 @@ public class NotificationEntryManager implements } @Override - public void onReorderingAllowed() { + public void onChangeAllowed() { updateNotifications("reordering is now allowed"); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java index b357ada7bcf1..7ac59954cb57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java @@ -42,12 +42,14 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000; - private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final ArrayList<Callback> mReorderingAllowedCallbacks = new ArrayList<>(); + private final ArrayList<Callback> mGroupChangesAllowedCallbacks = new ArrayList<>(); private final Handler mHandler; private boolean mPanelExpanded; private boolean mScreenOn; private boolean mReorderingAllowed; + private boolean mGroupChangedAllowed; private boolean mIsTemporaryReorderingAllowed; private long mTemporaryReorderingStart; private VisibilityLocationProvider mVisibilityLocationProvider; @@ -83,13 +85,22 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl /** * Add a callback to invoke when reordering is allowed again. - * @param callback */ public void addReorderingAllowedCallback(Callback callback) { - if (mCallbacks.contains(callback)) { + if (mReorderingAllowedCallbacks.contains(callback)) { return; } - mCallbacks.add(callback); + mReorderingAllowedCallbacks.add(callback); + } + + /** + * Add a callback to invoke when group changes are allowed again. + */ + public void addGroupChangesAllowedCallback(Callback callback) { + if (mGroupChangesAllowedCallbacks.contains(callback)) { + return; + } + mGroupChangesAllowedCallbacks.add(callback); } /** @@ -97,7 +108,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl */ public void setPanelExpanded(boolean expanded) { mPanelExpanded = expanded; - updateReorderingAllowed(); + updateAllowedStates(); } /** @@ -105,7 +116,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl */ public void setScreenOn(boolean screenOn) { mScreenOn = screenOn; - updateReorderingAllowed(); + updateAllowedStates(); } /** @@ -116,25 +127,30 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl return; } mPulsing = pulsing; - updateReorderingAllowed(); + updateAllowedStates(); } - private void updateReorderingAllowed() { + private void updateAllowedStates() { boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded || mIsTemporaryReorderingAllowed) && !mPulsing; boolean changedToTrue = reorderingAllowed && !mReorderingAllowed; mReorderingAllowed = reorderingAllowed; if (changedToTrue) { - notifyCallbacks(); + notifyChangeAllowed(mReorderingAllowedCallbacks); + } + boolean groupChangesAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing; + changedToTrue = groupChangesAllowed && !mGroupChangedAllowed; + mGroupChangedAllowed = groupChangesAllowed; + if (changedToTrue) { + notifyChangeAllowed(mGroupChangesAllowedCallbacks); } } - private void notifyCallbacks() { - for (int i = 0; i < mCallbacks.size(); i++) { - Callback callback = mCallbacks.get(i); - callback.onReorderingAllowed(); + private void notifyChangeAllowed(ArrayList<Callback> callbacks) { + for (int i = 0; i < callbacks.size(); i++) { + callbacks.get(i).onChangeAllowed(); } - mCallbacks.clear(); + callbacks.clear(); } /** @@ -145,6 +161,13 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl } /** + * @return whether changes in the grouping should be allowed right now. + */ + public boolean areGroupChangesAllowed() { + return mGroupChangedAllowed; + } + + /** * @return whether a specific notification is allowed to reorder. Certain notifications are * allowed to reorder even if {@link #isReorderingAllowed()} returns false, like newly added * notifications or heads-up notifications that are out of view. @@ -197,12 +220,12 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl mTemporaryReorderingStart = SystemClock.elapsedRealtime(); } mIsTemporaryReorderingAllowed = true; - updateReorderingAllowed(); + updateAllowedStates(); } private final Runnable mOnTemporaryReorderingExpired = () -> { mIsTemporaryReorderingAllowed = false; - updateReorderingAllowed(); + updateAllowedStates(); }; /** @@ -229,9 +252,9 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl public interface Callback { /** - * Called when reordering is allowed again. + * Called when changing is allowed again. */ - void onReorderingAllowed(); + void onChangeAllowed(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index d22564b2a811..dd7be2775209 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -136,7 +136,6 @@ public final class NotificationEntry extends ListEntry { */ public EditedSuggestionInfo editedSuggestionInfo; - private NotificationEntry parent; // our parent (if we're in a group) private ExpandableNotificationRow row; // the outer expanded view private ExpandableNotificationRowController mRowController; @@ -710,7 +709,7 @@ public final class NotificationEntry extends ListEntry { } public boolean isChildInGroup() { - return parent == null; + return row != null && row.isChildInGroup(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt new file mode 100644 index 000000000000..1bac938a9fca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter +import javax.inject.Inject +import javax.inject.Singleton + +/** + * A coordinator that elevates important conversation notifications + */ +@Singleton +class ConversationCoordinator @Inject constructor() : Coordinator { + + private val notificationPromoter = object : NotifPromoter(TAG) { + override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean { + return entry.channel?.isImportantConversation == true + } + } + + override fun attach(pipeline: NotifPipeline) { + pipeline.addPromoter(notificationPromoter) + } + + companion object { + private const val TAG = "ConversationCoordinator" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index 03c0ae6fde50..2b279bbd553a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -56,6 +56,7 @@ public class NotifCoordinators implements Dumpable { DeviceProvisionedCoordinator deviceProvisionedCoordinator, BubbleCoordinator bubbleCoordinator, HeadsUpCoordinator headsUpCoordinator, + ConversationCoordinator conversationCoordinator, PreparationCoordinator preparationCoordinator) { dumpManager.registerDumpable(TAG, this); mCoordinators.add(new HideLocallyDismissedNotifsCoordinator()); @@ -66,6 +67,7 @@ public class NotifCoordinators implements Dumpable { mCoordinators.add(deviceProvisionedCoordinator); mCoordinators.add(bubbleCoordinator); if (featureFlags.isNewNotifPipelineRenderingEnabled()) { + mCoordinators.add(conversationCoordinator); mCoordinators.add(headsUpCoordinator); mCoordinators.add(preparationCoordinator); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index be3873a5fd77..88cca43fd1a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -96,7 +96,7 @@ class PeopleNotificationIdentifierImpl @Inject constructor( return TYPE_NON_PERSON } - val childTypes = groupManager.getLogicalChildren(statusBarNotification) + val childTypes = groupManager.getChildren(statusBarNotification) ?.asSequence() ?.map { getPeopleNotificationType(it.sbn, it.ranking) } ?: return TYPE_NON_PERSON diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 8e2bfb84e2dd..6fc1264d69e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -20,7 +20,6 @@ import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; -import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS; import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; @@ -44,7 +43,6 @@ import android.os.Handler; import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.transition.ChangeBounds; @@ -56,15 +54,12 @@ import android.util.Log; import android.util.Slog; import android.view.View; import android.view.accessibility.AccessibilityEvent; -import android.widget.Button; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.notification.ConversationIconFactory; -import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.notification.VisualStabilityManager; @@ -517,7 +512,6 @@ public class NotificationConversationInfo extends LinearLayout implements bgHandler.post( new UpdateChannelRunnable(mINotificationManager, mPackageName, mAppUid, mSelectedAction, mNotificationChannel)); - mVisualStabilityManager.temporarilyAllowReordering(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 63fe7005e703..6b0df95f54dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -337,7 +337,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, // VisualStabilityManager.Callback overrides: @Override - public void onReorderingAllowed() { + public void onChangeAllowed() { mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) { if (isAlerting(entry.getKey())) { 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 ccf670708e44..84dd48b6eb6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import android.annotation.Nullable; +import android.app.NotificationChannel; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.Log; @@ -85,6 +86,17 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return group.expanded; } + /** + * @return if the group that this notification is associated with logically is expanded + */ + public boolean isLogicalGroupExpanded(StatusBarNotification sbn) { + NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); + if (group == null) { + return false; + } + return group.expanded; + } + public void setGroupExpanded(StatusBarNotification sbn, boolean expanded) { NotificationGroup group = mGroupMap.get(getGroupKey(sbn)); if (group == null) { @@ -147,7 +159,15 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } } + /** + * Notify the group manager that a new entry was added + */ public void onEntryAdded(final NotificationEntry added) { + updateIsolation(added); + onEntryAddedInternal(added); + } + + private void onEntryAddedInternal(final NotificationEntry added) { if (added.isRowRemoved()) { added.setDebugThrowable(new Throwable()); } @@ -193,9 +213,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } private void onEntryBecomingChild(NotificationEntry entry) { - if (shouldIsolate(entry)) { - isolateNotification(entry); - } + updateIsolation(entry); } private void updateSuppression(NotificationGroup group) { @@ -242,15 +260,6 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return count; } - private NotificationEntry getIsolatedChild(String groupKey) { - for (StatusBarNotification sbn : mIsolatedEntries.values()) { - if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) { - return mGroupMap.get(sbn.getKey()).summary; - } - } - return null; - } - /** * Update an entry's group information * @param entry notification entry to update @@ -278,7 +287,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) { onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary); } - onEntryAdded(entry); + onEntryAddedInternal(entry); mIsUpdatingUnchangedGroup = false; if (isIsolated(entry.getSbn().getKey())) { mIsolatedEntries.put(entry.getKey(), entry.getSbn()); @@ -413,14 +422,29 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return null; } ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values()); - NotificationEntry isolatedChild = getIsolatedChild(summary.getGroupKey()); - if (isolatedChild != null) { - children.add(isolatedChild); + for (StatusBarNotification sbn : mIsolatedEntries.values()) { + if (sbn.getGroupKey().equals(summary.getGroupKey())) { + children.add(mGroupMap.get(sbn.getKey()).summary); + } } return children; } /** + * Get the children that are in the summary's group, not including those isolated. + * + * @param summary summary of a group + * @return list of the children + */ + public @Nullable ArrayList<NotificationEntry> getChildren(StatusBarNotification summary) { + NotificationGroup group = mGroupMap.get(summary.getGroupKey()); + if (group == null) { + return null; + } + return new ArrayList<>(group.children.values()); + } + + /** * If there is a {@link NotificationGroup} associated with the provided entry, this method * will update the suppression of that group. */ @@ -495,17 +519,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - onAlertStateChanged(entry, isHeadsUp); - } - - private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting) { - if (isAlerting) { - if (shouldIsolate(entry)) { - isolateNotification(entry); - } - } else { - stopIsolatingNotification(entry); - } + updateIsolation(entry); } /** @@ -519,13 +533,17 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State private boolean shouldIsolate(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); - NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) { return false; } + NotificationChannel channel = entry.getChannel(); + if (channel != null && channel.isImportantConversation()) { + return true; + } if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) { return false; } + NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); return (sbn.getNotification().fullScreenIntent != null || notificationGroup == null || !notificationGroup.expanded @@ -545,7 +563,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State mIsolatedEntries.put(sbn.getKey(), sbn); - onEntryAdded(entry); + onEntryAddedInternal(entry); // We also need to update the suppression of the old group, because this call comes // even before the groupManager knows about the notification at all. // When the notification gets added afterwards it is already isolated and therefore @@ -557,17 +575,31 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } /** + * Update the isolation of an entry, splitting it from the group. + */ + public void updateIsolation(NotificationEntry entry) { + boolean isIsolated = isIsolated(entry.getSbn().getKey()); + if (shouldIsolate(entry)) { + if (!isIsolated) { + isolateNotification(entry); + } + } else if (isIsolated) { + stopIsolatingNotification(entry); + } + } + + /** * Stop isolating a notification and re-group it with its original logical group. * * @param entry the notification to un-isolate */ private void stopIsolatingNotification(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); - if (mIsolatedEntries.containsKey(sbn.getKey())) { + if (isIsolated(sbn.getKey())) { // not isolated anymore, we need to update the groups onEntryRemovedInternal(entry, entry.getSbn()); mIsolatedEntries.remove(sbn.getKey()); - onEntryAdded(entry); + onEntryAddedInternal(entry); for (OnGroupChangeListener listener : mListeners) { listener.onGroupsChanged(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index ea68516e639c..e55ea41d94e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -98,6 +98,8 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mLockscreenUserManager); mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager); mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); + when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true); + when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true); mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java index 9079223649ff..3d06c57cac37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java @@ -110,7 +110,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback); mVisualStabilityManager.setScreenOn(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -119,7 +119,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback); mVisualStabilityManager.setPanelExpanded(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -130,7 +130,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setScreenOn(false); mVisualStabilityManager.setScreenOn(true); mVisualStabilityManager.setScreenOn(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -190,7 +190,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setPulsing(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback); mVisualStabilityManager.setPulsing(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -204,7 +204,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.temporarilyAllowReordering(); // THEN callbacks are notified that reordering is allowed - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @@ -218,7 +218,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.temporarilyAllowReordering(); // THEN reordering is still not allowed - verify(mCallback, never()).onReorderingAllowed(); + verify(mCallback, never()).onChangeAllowed(); assertFalse(mVisualStabilityManager.isReorderingAllowed()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt new file mode 100644 index 000000000000..dfc627e14d8c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import android.app.NotificationChannel +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class ConversationCoordinatorTest : SysuiTestCase() { + + private var coordinator: ConversationCoordinator = ConversationCoordinator() + + // captured listeners and pluggables: + private var promoter: NotifPromoter? = null + + @Mock + private val pipeline: NotifPipeline? = null + @Mock + private val channel: NotificationChannel? = null + private var entry: NotificationEntry? = null + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(channel!!.isImportantConversation).thenReturn(true) + + coordinator.attach(pipeline!!) + + // capture arguments: + val notifPromoterCaptor = ArgumentCaptor.forClass(NotifPromoter::class.java) + verify(pipeline).addPromoter(notifPromoterCaptor.capture()) + promoter = notifPromoterCaptor.value + + entry = NotificationEntryBuilder().setChannel(channel).build() + } + + @Test + fun testPromotesCurrentHUN() { + + // only promote important conversations + assertTrue(promoter!!.shouldPromoteToTopLevel(entry)) + assertFalse(promoter!!.shouldPromoteToTopLevel(NotificationEntryBuilder().build())) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 6998edda3127..b6bd5e213dd4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -816,29 +816,4 @@ public class NotificationConversationInfoTest extends SysuiTestCase { verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); } - - @Test - public void testAdjustImportanceTemporarilyAllowsReordering() { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mConversationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mShortcutManager, - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, - null, - null, - mIconFactory, - true); - - mNotificationInfo.findViewById(R.id.silence).performClick(); - mNotificationInfo.findViewById(R.id.done).performClick(); - - mTestableLooper.processAllMessages(); - - verify(mVisualStabilityManager).temporarilyAllowReordering(); - } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e8d8ed7a462d..ed3b9f1fc265 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6227,7 +6227,7 @@ public class NotificationManagerService extends SystemService { cancelNotificationLocked( r, mSendDelete, mReason, mRank, mCount, wasPosted, listenerName); cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName, - mSendDelete, childrenFlagChecker); + mSendDelete, childrenFlagChecker, mReason); updateLightsLocked(); if (mShortcutHelper != null) { mShortcutHelper.maybeListenForShortcutChangesForBubbles(r, @@ -6687,7 +6687,7 @@ public class NotificationManagerService extends SystemService { // notification was a summary and its group key changed. if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) { cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */, - null); + null, REASON_APP_CANCEL); } } @@ -7892,7 +7892,7 @@ public class NotificationManagerService extends SystemService { final int M = canceledNotifications.size(); for (int i = 0; i < M; i++) { cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, - listenerName, false /* sendDelete */, flagChecker); + listenerName, false /* sendDelete */, flagChecker, reason); } updateLightsLocked(); } @@ -7963,7 +7963,7 @@ public class NotificationManagerService extends SystemService { // Warning: The caller is responsible for invoking updateLightsLocked(). @GuardedBy("mNotificationLock") private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, - String listenerName, boolean sendDelete, FlagChecker flagChecker) { + String listenerName, boolean sendDelete, FlagChecker flagChecker, int reason) { Notification n = r.getNotification(); if (!n.isGroupSummary()) { return; @@ -7977,30 +7977,33 @@ public class NotificationManagerService extends SystemService { } cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName, - sendDelete, true, flagChecker); + sendDelete, true, flagChecker, reason); cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid, - listenerName, sendDelete, false, flagChecker); + listenerName, sendDelete, false, flagChecker, reason); } @GuardedBy("mNotificationLock") private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList, NotificationRecord parentNotification, int callingUid, int callingPid, - String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) { + String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker, + int reason) { final String pkg = parentNotification.getSbn().getPackageName(); final int userId = parentNotification.getUserId(); - final int reason = REASON_GROUP_SUMMARY_CANCELED; + final int childReason = REASON_GROUP_SUMMARY_CANCELED; for (int i = notificationList.size() - 1; i >= 0; i--) { final NotificationRecord childR = notificationList.get(i); final StatusBarNotification childSbn = childR.getSbn(); if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) && childR.getGroupKey().equals(parentNotification.getGroupKey()) && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0 - && (flagChecker == null || flagChecker.apply(childR.getFlags()))) { + && (flagChecker == null || flagChecker.apply(childR.getFlags())) + && (!childR.getChannel().isImportantConversation() + || reason != REASON_CANCEL)) { EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), - childSbn.getTag(), userId, 0, 0, reason, listenerName); + childSbn.getTag(), userId, 0, 0, childReason, listenerName); notificationList.remove(i); mNotificationsByKey.remove(childR.getKey()); - cancelNotificationLocked(childR, sendDelete, reason, wasPosted, listenerName); + cancelNotificationLocked(childR, sendDelete, childReason, wasPosted, listenerName); } } } |