blob: 765f85aab3192abe54583e0c5c7413cb7f3e76b4 [file] [log] [blame]
Eliot Courtney2b4c3a02017-11-27 13:27:46 +09001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar;
18
19import android.content.Context;
20import android.content.res.Resources;
Ned Burnsb3b4bd32019-06-27 19:36:58 -040021import android.os.Handler;
Lucas Dupin16013822018-05-17 18:00:16 -070022import android.os.Trace;
Selim Cinek6f0a62a2019-04-09 18:40:12 -070023import android.os.UserHandle;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090024import android.util.Log;
25import android.view.View;
26import android.view.ViewGroup;
27
28import com.android.systemui.R;
Mady Mellorce23c462019-06-17 17:30:07 -070029import com.android.systemui.bubbles.BubbleController;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050030import com.android.systemui.dagger.qualifiers.Main;
Beverly80110912019-02-13 12:20:57 -050031import com.android.systemui.plugins.statusbar.StatusBarStateController;
Sergey Nikolaienkov3d84cb42020-02-10 16:54:31 +010032import com.android.systemui.statusbar.dagger.StatusBarModule;
Kevin Han3cb379c2019-12-03 13:31:44 -080033import com.android.systemui.statusbar.notification.DynamicChildBindController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -070034import com.android.systemui.statusbar.notification.DynamicPrivacyController;
Rohan Shah20790b82018-07-02 17:21:04 -070035import com.android.systemui.statusbar.notification.NotificationEntryManager;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090036import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050037import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Kevin Hanf69b71e2020-04-13 15:46:14 -070038import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
Rohan Shah20790b82018-07-02 17:21:04 -070039import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Evan Laird04373662020-01-24 17:37:39 -050040import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
Rohan Shah20790b82018-07-02 17:21:04 -070041import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Selim Cinekb0fada62019-06-17 19:03:59 -070042import com.android.systemui.statusbar.phone.KeyguardBypassController;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090043import com.android.systemui.statusbar.phone.NotificationGroupManager;
Ned Burnsd4a69f72019-06-19 19:49:19 -040044import com.android.systemui.util.Assert;
Matt Pietal5a19cb62019-10-30 12:31:07 -040045import com.android.systemui.util.Utils;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090046
47import java.util.ArrayList;
48import java.util.HashMap;
49import java.util.List;
50import java.util.Stack;
51
52/**
53 * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
54 * on their group structure. For example, if a notification becomes bundled with another,
55 * NotificationViewHierarchyManager will update the view hierarchy to reflect that. It also will
56 * tell NotificationListContainer which notifications to display, and inform it of changes to those
57 * notifications that might affect their display.
58 */
Selim Cinek6f0a62a2019-04-09 18:40:12 -070059public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090060 private static final String TAG = "NotificationViewHierarchyManager";
61
Ned Burnsb3b4bd32019-06-27 19:36:58 -040062 private final Handler mHandler;
63
Kevin Han2b4bdbb02020-04-21 17:26:51 -070064 /** Re-usable map of top-level notifications to their sorted children if any.*/
Kevin Han3cb379c2019-12-03 13:31:44 -080065 private final HashMap<NotificationEntry, List<NotificationEntry>> mTmpChildOrderMap =
66 new HashMap<>();
Eliot Courtney6c313d32017-12-14 19:57:51 +090067
68 // Dependencies:
Kevin Han3cb379c2019-12-03 13:31:44 -080069 private final DynamicChildBindController mDynamicChildBindController;
Jason Monk09f4d372018-12-20 15:30:54 -050070 protected final NotificationLockscreenUserManager mLockscreenUserManager;
71 protected final NotificationGroupManager mGroupManager;
72 protected final VisualStabilityManager mVisualStabilityManager;
Beverly80110912019-02-13 12:20:57 -050073 private final SysuiStatusBarStateController mStatusBarStateController;
Jason Monk09f4d372018-12-20 15:30:54 -050074 private final NotificationEntryManager mEntryManager;
Kevin Hanf69b71e2020-04-13 15:46:14 -070075 private final LowPriorityInflationHelper mLowPriorityInflationHelper;
Jason Monk297c04e2018-08-23 17:16:59 -040076
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090077 /**
78 * {@code true} if notifications not part of a group should by default be rendered in their
79 * expanded state. If {@code false}, then only the first notification will be expanded if
80 * possible.
81 */
82 private final boolean mAlwaysExpandNonGroupedNotification;
Mady Mellorce23c462019-06-17 17:30:07 -070083 private final BubbleController mBubbleController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -070084 private final DynamicPrivacyController mDynamicPrivacyController;
Selim Cinekb0fada62019-06-17 19:03:59 -070085 private final KeyguardBypassController mBypassController;
Evan Laird04373662020-01-24 17:37:39 -050086 private final ForegroundServiceSectionController mFgsSectionController;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040087 private final Context mContext;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090088
89 private NotificationPresenter mPresenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090090 private NotificationListContainer mListContainer;
91
Ned Burnsd4a69f72019-06-19 19:49:19 -040092 // Used to help track down re-entrant calls to our update methods, which will cause bugs.
93 private boolean mPerformingUpdate;
Ned Burnsb3b4bd32019-06-27 19:36:58 -040094 // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down
95 // the problem.
96 private boolean mIsHandleDynamicPrivacyChangeScheduled;
Ned Burnsd4a69f72019-06-19 19:49:19 -040097
Sergey Nikolaienkov3d84cb42020-02-10 16:54:31 +010098 /**
99 * Injected constructor. See {@link StatusBarModule}.
100 */
101 public NotificationViewHierarchyManager(
102 Context context,
103 @Main Handler mainHandler,
Jason Monk09f4d372018-12-20 15:30:54 -0500104 NotificationLockscreenUserManager notificationLockscreenUserManager,
105 NotificationGroupManager groupManager,
106 VisualStabilityManager visualStabilityManager,
Beverly80110912019-02-13 12:20:57 -0500107 StatusBarStateController statusBarStateController,
Jason Monk09f4d372018-12-20 15:30:54 -0500108 NotificationEntryManager notificationEntryManager,
Selim Cinekb0fada62019-06-17 19:03:59 -0700109 KeyguardBypassController bypassController,
Mady Mellorce23c462019-06-17 17:30:07 -0700110 BubbleController bubbleController,
Evan Laird04373662020-01-24 17:37:39 -0500111 DynamicPrivacyController privacyController,
Kevin Han3cb379c2019-12-03 13:31:44 -0800112 ForegroundServiceSectionController fgsSectionController,
Kevin Hanf69b71e2020-04-13 15:46:14 -0700113 DynamicChildBindController dynamicChildBindController,
114 LowPriorityInflationHelper lowPriorityInflationHelper) {
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400115 mContext = context;
Ned Burnsb3b4bd32019-06-27 19:36:58 -0400116 mHandler = mainHandler;
Jason Monk09f4d372018-12-20 15:30:54 -0500117 mLockscreenUserManager = notificationLockscreenUserManager;
Selim Cinekb0fada62019-06-17 19:03:59 -0700118 mBypassController = bypassController;
Jason Monk09f4d372018-12-20 15:30:54 -0500119 mGroupManager = groupManager;
120 mVisualStabilityManager = visualStabilityManager;
Beverly80110912019-02-13 12:20:57 -0500121 mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
Jason Monk09f4d372018-12-20 15:30:54 -0500122 mEntryManager = notificationEntryManager;
Evan Laird04373662020-01-24 17:37:39 -0500123 mFgsSectionController = fgsSectionController;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900124 Resources res = context.getResources();
125 mAlwaysExpandNonGroupedNotification =
126 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
Mady Mellorce23c462019-06-17 17:30:07 -0700127 mBubbleController = bubbleController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700128 mDynamicPrivacyController = privacyController;
Kevin Han3cb379c2019-12-03 13:31:44 -0800129 mDynamicChildBindController = dynamicChildBindController;
Kevin Hanf69b71e2020-04-13 15:46:14 -0700130 mLowPriorityInflationHelper = lowPriorityInflationHelper;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900131 }
132
133 public void setUpWithPresenter(NotificationPresenter presenter,
Jason Monk297c04e2018-08-23 17:16:59 -0400134 NotificationListContainer listContainer) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900135 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900136 mListContainer = listContainer;
Heemin Seog0ce3c472020-04-18 10:13:29 -0700137 mDynamicPrivacyController.addListener(this);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900138 }
139
140 /**
141 * Updates the visual representation of the notifications.
142 */
Evan Laird94492852018-10-25 13:43:01 -0400143 //TODO: Rewrite this to focus on Entries, or some other data object instead of views
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900144 public void updateNotificationViews() {
Ned Burnsd4a69f72019-06-19 19:49:19 -0400145 Assert.isMainThread();
146 beginUpdate();
147
Evan Laird181de622019-10-24 09:53:02 -0400148 List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900149 ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
150 final int N = activeNotifications.size();
151 for (int i = 0; i < N; i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500152 NotificationEntry ent = activeNotifications.get(i);
Matt Pietal5a19cb62019-10-30 12:31:07 -0400153 boolean hideMedia = Utils.useQsMediaPlayer(mContext);
Selim Cinekfdf80332019-03-07 17:29:55 -0800154 if (ent.isRowDismissed() || ent.isRowRemoved()
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400155 || (ent.isMediaNotification() && hideMedia)
Evan Laird04373662020-01-24 17:37:39 -0500156 || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)
157 || mFgsSectionController.hasEntry(ent)) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900158 // we don't want to update removed notifications because they could
159 // temporarily become children if they were isolated before.
160 continue;
161 }
Mady Mellor5549dd22018-11-06 18:07:34 -0800162
Ned Burns00b4b2d2019-10-17 22:09:27 -0400163 int userId = ent.getSbn().getUserId();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900164
165 // Display public version of the notification if we need to redact.
166 // TODO: This area uses a lot of calls into NotificationLockscreenUserManager.
167 // We can probably move some of this code there.
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700168 int currentUserId = mLockscreenUserManager.getCurrentUserId();
169 boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(currentUserId);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900170 boolean userPublic = devicePublic
171 || mLockscreenUserManager.isLockscreenPublicMode(userId);
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700172 if (userPublic && mDynamicPrivacyController.isDynamicallyUnlocked()
173 && (userId == currentUserId || userId == UserHandle.USER_ALL
174 || !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) {
175 userPublic = false;
176 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900177 boolean needsRedaction = mLockscreenUserManager.needsRedaction(ent);
178 boolean sensitive = userPublic && needsRedaction;
179 boolean deviceSensitive = devicePublic
180 && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700181 currentUserId);
Selim Cinekb2c5dc52019-06-24 15:46:52 -0700182 ent.setSensitive(sensitive, deviceSensitive);
Evan Laird94492852018-10-25 13:43:01 -0400183 ent.getRow().setNeedsRedaction(needsRedaction);
Kevin Hanf69b71e2020-04-13 15:46:14 -0700184 mLowPriorityInflationHelper.recheckLowPriorityViewAndInflate(ent, ent.getRow());
Selim Cinekba069ae2020-04-01 19:45:16 -0700185 boolean isChildInGroup = mGroupManager.isChildInGroupWithSummary(ent.getSbn());
186
Beverly08742052020-05-06 15:50:42 -0400187 boolean groupChangesAllowed =
188 mVisualStabilityManager.areGroupChangesAllowed() // user isn't looking at notifs
189 || !ent.hasFinishedInitialization() // notif recently added
190 || !mListContainer.containsView(ent.getRow()); // notif recently unfiltered
191
Selim Cinekba069ae2020-04-01 19:45:16 -0700192 NotificationEntry parent = mGroupManager.getGroupSummary(ent.getSbn());
193 if (!groupChangesAllowed) {
194 // We don't to change groups while the user is looking at them
195 boolean wasChildInGroup = ent.isChildInGroup();
196 if (isChildInGroup && !wasChildInGroup) {
197 isChildInGroup = wasChildInGroup;
Selim Cinek0260c752020-05-11 16:03:52 -0700198 mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager,
199 false /* persistent */);
Selim Cinekba069ae2020-04-01 19:45:16 -0700200 } else if (!isChildInGroup && wasChildInGroup) {
201 // We allow grouping changes if the group was collapsed
202 if (mGroupManager.isLogicalGroupExpanded(ent.getSbn())) {
203 isChildInGroup = wasChildInGroup;
204 parent = ent.getRow().getNotificationParent().getEntry();
Selim Cinek0260c752020-05-11 16:03:52 -0700205 mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager,
206 false /* persistent */);
Selim Cinekba069ae2020-04-01 19:45:16 -0700207 }
208 }
209 }
210
211 if (isChildInGroup) {
212 List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900213 if (orderedChildren == null) {
214 orderedChildren = new ArrayList<>();
Selim Cinekba069ae2020-04-01 19:45:16 -0700215 mTmpChildOrderMap.put(parent, orderedChildren);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900216 }
Kevin Han3cb379c2019-12-03 13:31:44 -0800217 orderedChildren.add(ent);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900218 } else {
Kevin Han2b4bdbb02020-04-21 17:26:51 -0700219 // Top-level notif
220 mTmpChildOrderMap.put(ent, null);
Evan Laird94492852018-10-25 13:43:01 -0400221 toShow.add(ent.getRow());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900222 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900223 }
224
Rohan Shah524cf7b2018-03-15 14:40:02 -0700225 ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900226 for (int i=0; i< mListContainer.getContainerChildCount(); i++) {
227 View child = mListContainer.getContainerChildAt(i);
228 if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700229 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
230
231 // Blocking helper is effectively a detached view. Don't bother removing it from the
232 // layout.
233 if (!row.isBlockingHelperShowing()) {
234 viewsToRemove.add((ExpandableNotificationRow) child);
235 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900236 }
237 }
238
Rohan Shah524cf7b2018-03-15 14:40:02 -0700239 for (ExpandableNotificationRow viewToRemove : viewsToRemove) {
Selim Cinekba069ae2020-04-01 19:45:16 -0700240 if (mEntryManager.getPendingOrActiveNotif(viewToRemove.getEntry().getKey()) != null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900241 // we are only transferring this notification to its parent, don't generate an
242 // animation
243 mListContainer.setChildTransferInProgress(true);
244 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700245 if (viewToRemove.isSummaryWithChildren()) {
246 viewToRemove.removeAllChildren();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900247 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700248 mListContainer.removeContainerView(viewToRemove);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900249 mListContainer.setChildTransferInProgress(false);
250 }
251
252 removeNotificationChildren();
253
254 for (int i = 0; i < toShow.size(); i++) {
255 View v = toShow.get(i);
256 if (v.getParent() == null) {
257 mVisualStabilityManager.notifyViewAddition(v);
258 mListContainer.addContainerView(v);
Selim Cinekfdf80332019-03-07 17:29:55 -0800259 } else if (!mListContainer.containsView(v)) {
260 // the view is added somewhere else. Let's make sure
261 // the ordering works properly below, by excluding these
262 toShow.remove(v);
263 i--;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900264 }
265 }
266
267 addNotificationChildrenAndSort();
268
269 // So after all this work notifications still aren't sorted correctly.
270 // Let's do that now by advancing through toShow and mListContainer in
271 // lock-step, making sure mListContainer matches what we see in toShow.
272 int j = 0;
273 for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
274 View child = mListContainer.getContainerChildAt(i);
275 if (!(child instanceof ExpandableNotificationRow)) {
276 // We don't care about non-notification views.
277 continue;
278 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700279 if (((ExpandableNotificationRow) child).isBlockingHelperShowing()) {
280 // Don't count/reorder notifications that are showing the blocking helper!
281 continue;
282 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900283
284 ExpandableNotificationRow targetChild = toShow.get(j);
285 if (child != targetChild) {
286 // Oops, wrong notification at this position. Put the right one
287 // here and advance both lists.
288 if (mVisualStabilityManager.canReorderNotification(targetChild)) {
289 mListContainer.changeViewPosition(targetChild, i);
290 } else {
Selim Cinek0260c752020-05-11 16:03:52 -0700291 mVisualStabilityManager.addReorderingAllowedCallback(mEntryManager,
292 false /* persistent */);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900293 }
294 }
295 j++;
296
297 }
298
Kevin Han2b4bdbb02020-04-21 17:26:51 -0700299 mDynamicChildBindController.updateContentViews(mTmpChildOrderMap);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900300 mVisualStabilityManager.onReorderingFinished();
301 // clear the map again for the next usage
302 mTmpChildOrderMap.clear();
303
Ned Burnsd4a69f72019-06-19 19:49:19 -0400304 updateRowStatesInternal();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900305
306 mListContainer.onNotificationViewUpdateFinished();
Ned Burnsd4a69f72019-06-19 19:49:19 -0400307
308 endUpdate();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900309 }
310
311 private void addNotificationChildrenAndSort() {
312 // Let's now add all notification children which are missing
313 boolean orderChanged = false;
Kevin Han3cb379c2019-12-03 13:31:44 -0800314 ArrayList<ExpandableNotificationRow> orderedRows = new ArrayList<>();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900315 for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
316 View view = mListContainer.getContainerChildAt(i);
317 if (!(view instanceof ExpandableNotificationRow)) {
318 // We don't care about non-notification views.
319 continue;
320 }
321
322 ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
Kevin Han43077f92020-02-28 12:51:53 -0800323 List<ExpandableNotificationRow> children = parent.getAttachedChildren();
Kevin Han3cb379c2019-12-03 13:31:44 -0800324 List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
Kevin Han43077f92020-02-28 12:51:53 -0800325 if (orderedChildren == null) {
326 // Not a group
327 continue;
328 }
329 parent.setUntruncatedChildCount(orderedChildren.size());
330 for (int childIndex = 0; childIndex < orderedChildren.size(); childIndex++) {
Kevin Han3cb379c2019-12-03 13:31:44 -0800331 ExpandableNotificationRow childView = orderedChildren.get(childIndex).getRow();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900332 if (children == null || !children.contains(childView)) {
333 if (childView.getParent() != null) {
Kevin Han43077f92020-02-28 12:51:53 -0800334 Log.wtf(TAG, "trying to add a notification child that already has "
335 + "a parent. class:" + childView.getParent().getClass()
336 + "\n child: " + childView);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900337 // This shouldn't happen. We can recover by removing it though.
338 ((ViewGroup) childView.getParent()).removeView(childView);
339 }
340 mVisualStabilityManager.notifyViewAddition(childView);
341 parent.addChildNotification(childView, childIndex);
342 mListContainer.notifyGroupChildAdded(childView);
343 }
Kevin Han3cb379c2019-12-03 13:31:44 -0800344 orderedRows.add(childView);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900345 }
346
347 // Finally after removing and adding has been performed we can apply the order.
Kevin Han3cb379c2019-12-03 13:31:44 -0800348 orderChanged |= parent.applyChildOrder(orderedRows, mVisualStabilityManager,
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900349 mEntryManager);
Kevin Han3cb379c2019-12-03 13:31:44 -0800350 orderedRows.clear();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900351 }
352 if (orderChanged) {
353 mListContainer.generateChildOrderChangedEvent();
354 }
355 }
356
357 private void removeNotificationChildren() {
358 // First let's remove all children which don't belong in the parents
359 ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
360 for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
361 View view = mListContainer.getContainerChildAt(i);
362 if (!(view instanceof ExpandableNotificationRow)) {
363 // We don't care about non-notification views.
364 continue;
365 }
366
367 ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
Kevin Han43077f92020-02-28 12:51:53 -0800368 List<ExpandableNotificationRow> children = parent.getAttachedChildren();
Kevin Han3cb379c2019-12-03 13:31:44 -0800369 List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900370
371 if (children != null) {
372 toRemove.clear();
373 for (ExpandableNotificationRow childRow : children) {
374 if ((orderedChildren == null
Kevin Han3cb379c2019-12-03 13:31:44 -0800375 || !orderedChildren.contains(childRow.getEntry()))
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900376 && !childRow.keepInParent()) {
377 toRemove.add(childRow);
378 }
379 }
380 for (ExpandableNotificationRow remove : toRemove) {
381 parent.removeChildNotification(remove);
Evan Laird181de622019-10-24 09:53:02 -0400382 if (mEntryManager.getActiveNotificationUnfiltered(
Ned Burns1c2b85a42019-11-14 15:37:03 -0500383 remove.getEntry().getSbn().getKey()) == null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900384 // We only want to add an animation if the view is completely removed
385 // otherwise it's just a transfer
386 mListContainer.notifyGroupChildRemoved(remove,
387 parent.getChildrenContainer());
388 }
389 }
390 }
391 }
392 }
393
394 /**
395 * Updates expanded, dimmed and locked states of notification rows.
396 */
397 public void updateRowStates() {
Ned Burnsd4a69f72019-06-19 19:49:19 -0400398 Assert.isMainThread();
399 beginUpdate();
400 updateRowStatesInternal();
401 endUpdate();
402 }
403
404 private void updateRowStatesInternal() {
Lucas Dupin16013822018-05-17 18:00:16 -0700405 Trace.beginSection("NotificationViewHierarchyManager#updateRowStates");
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900406 final int N = mListContainer.getContainerChildCount();
407
408 int visibleNotifications = 0;
Jason Monk297c04e2018-08-23 17:16:59 -0400409 boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900410 int maxNotifications = -1;
Selim Cinekb0fada62019-06-17 19:03:59 -0700411 if (onKeyguard && !mBypassController.getBypassEnabled()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900412 maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */);
413 }
414 mListContainer.setMaxDisplayedNotifications(maxNotifications);
415 Stack<ExpandableNotificationRow> stack = new Stack<>();
416 for (int i = N - 1; i >= 0; i--) {
417 View child = mListContainer.getContainerChildAt(i);
418 if (!(child instanceof ExpandableNotificationRow)) {
419 continue;
420 }
421 stack.push((ExpandableNotificationRow) child);
422 }
423 while(!stack.isEmpty()) {
424 ExpandableNotificationRow row = stack.pop();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500425 NotificationEntry entry = row.getEntry();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900426 boolean isChildNotification =
Ned Burns00b4b2d2019-10-17 22:09:27 -0400427 mGroupManager.isChildInGroupWithSummary(entry.getSbn());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900428
Jason Monk297c04e2018-08-23 17:16:59 -0400429 row.setOnKeyguard(onKeyguard);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900430
Jason Monk297c04e2018-08-23 17:16:59 -0400431 if (!onKeyguard) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900432 // If mAlwaysExpandNonGroupedNotification is false, then only expand the
433 // very first notification and if it's not a child of grouped notifications.
434 row.setSystemExpanded(mAlwaysExpandNonGroupedNotification
435 || (visibleNotifications == 0 && !isChildNotification
436 && !row.isLowPriority()));
437 }
438
Ned Burns00b4b2d2019-10-17 22:09:27 -0400439 int userId = entry.getSbn().getUserId();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900440 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
Ned Burns00b4b2d2019-10-17 22:09:27 -0400441 entry.getSbn()) && !entry.isRowRemoved();
Ned Burns8c1b7632019-07-19 14:26:15 -0400442 boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry);
Selim Cinek3bf2d202018-10-16 17:30:05 -0700443 if (!showOnKeyguard) {
444 // min priority notifications should show if their summary is showing
Ned Burns00b4b2d2019-10-17 22:09:27 -0400445 if (mGroupManager.isChildInGroupWithSummary(entry.getSbn())) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500446 NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
Ned Burns00b4b2d2019-10-17 22:09:27 -0400447 entry.getSbn());
Ned Burns8c1b7632019-07-19 14:26:15 -0400448 if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) {
Selim Cinek3bf2d202018-10-16 17:30:05 -0700449 showOnKeyguard = true;
450 }
451 }
452 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900453 if (suppressedSummary
Pavel Grafov65152632018-03-26 15:14:45 +0100454 || mLockscreenUserManager.shouldHideNotifications(userId)
Jason Monk297c04e2018-08-23 17:16:59 -0400455 || (onKeyguard && !showOnKeyguard)) {
Evan Laird94492852018-10-25 13:43:01 -0400456 entry.getRow().setVisibility(View.GONE);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900457 } else {
Evan Laird94492852018-10-25 13:43:01 -0400458 boolean wasGone = entry.getRow().getVisibility() == View.GONE;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900459 if (wasGone) {
Evan Laird94492852018-10-25 13:43:01 -0400460 entry.getRow().setVisibility(View.VISIBLE);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900461 }
Evan Laird94492852018-10-25 13:43:01 -0400462 if (!isChildNotification && !entry.getRow().isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900463 if (wasGone) {
464 // notify the scroller of a child addition
Evan Laird94492852018-10-25 13:43:01 -0400465 mListContainer.generateAddAnimation(entry.getRow(),
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900466 !showOnKeyguard /* fromMoreCard */);
467 }
468 visibleNotifications++;
469 }
470 }
471 if (row.isSummaryWithChildren()) {
472 List<ExpandableNotificationRow> notificationChildren =
Kevin Han43077f92020-02-28 12:51:53 -0800473 row.getAttachedChildren();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900474 int size = notificationChildren.size();
475 for (int i = size - 1; i >= 0; i--) {
476 stack.push(notificationChildren.get(i));
477 }
478 }
Dan Sandler83b70a02018-01-24 23:20:18 -0500479
Julia Reynoldsfc640012018-02-21 12:25:27 -0500480 row.showAppOpsIcons(entry.mActiveAppOps);
Ned Burns60e94592019-09-06 14:47:25 -0400481 row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900482 }
483
Lucas Dupin16013822018-05-17 18:00:16 -0700484 Trace.beginSection("NotificationPresenter#onUpdateRowStates");
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900485 mPresenter.onUpdateRowStates();
Lucas Dupin16013822018-05-17 18:00:16 -0700486 Trace.endSection();
487 Trace.endSection();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900488 }
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700489
490 @Override
491 public void onDynamicPrivacyChanged() {
Ned Burnsb3b4bd32019-06-27 19:36:58 -0400492 if (mPerformingUpdate) {
493 Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
494 }
495 // This listener can be called from updateNotificationViews() via a convoluted listener
496 // chain, so we post here to prevent a re-entrant call. See b/136186188
497 // TODO: Refactor away the need for this
498 if (!mIsHandleDynamicPrivacyChangeScheduled) {
499 mIsHandleDynamicPrivacyChangeScheduled = true;
500 mHandler.post(this::onHandleDynamicPrivacyChanged);
501 }
502 }
503
504 private void onHandleDynamicPrivacyChanged() {
505 mIsHandleDynamicPrivacyChangeScheduled = false;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700506 updateNotificationViews();
507 }
Ned Burnsd4a69f72019-06-19 19:49:19 -0400508
509 private void beginUpdate() {
510 if (mPerformingUpdate) {
Ned Burnsbddd3f12019-06-28 12:54:25 -0400511 Log.wtf(TAG, "Re-entrant code during update", new Exception());
Ned Burnsd4a69f72019-06-19 19:49:19 -0400512 }
513 mPerformingUpdate = true;
514 }
515
516 private void endUpdate() {
517 if (!mPerformingUpdate) {
Ned Burnsbddd3f12019-06-28 12:54:25 -0400518 Log.wtf(TAG, "Manager state has become desynced", new Exception());
Ned Burnsd4a69f72019-06-19 19:49:19 -0400519 }
520 mPerformingUpdate = false;
521 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900522}