blob: 8d4a9efbcd7ae8e5bd22b0f7d0ee1a0be342e36a [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;
Selim Cinek6f0a62a2019-04-09 18:40:12 -070032import com.android.systemui.statusbar.notification.DynamicPrivacyController;
Rohan Shah20790b82018-07-02 17:21:04 -070033import com.android.systemui.statusbar.notification.NotificationEntryManager;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090034import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050035import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Rohan Shah20790b82018-07-02 17:21:04 -070036import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
37import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Selim Cinekb0fada62019-06-17 19:03:59 -070038import com.android.systemui.statusbar.phone.KeyguardBypassController;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090039import com.android.systemui.statusbar.phone.NotificationGroupManager;
Ned Burnsd4a69f72019-06-19 19:49:19 -040040import com.android.systemui.util.Assert;
Matt Pietal5a19cb62019-10-30 12:31:07 -040041import com.android.systemui.util.Utils;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090042
43import java.util.ArrayList;
44import java.util.HashMap;
45import java.util.List;
46import java.util.Stack;
47
Jason Monk27d01a622018-12-10 15:57:09 -050048import javax.inject.Inject;
49import javax.inject.Singleton;
50
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090051/**
52 * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
53 * on their group structure. For example, if a notification becomes bundled with another,
54 * NotificationViewHierarchyManager will update the view hierarchy to reflect that. It also will
55 * tell NotificationListContainer which notifications to display, and inform it of changes to those
56 * notifications that might affect their display.
57 */
Jason Monk27d01a622018-12-10 15:57:09 -050058@Singleton
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
Evan Laird94492852018-10-25 13:43:01 -040064 //TODO: change this top <Entry, List<Entry>>?
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090065 private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
66 mTmpChildOrderMap = new HashMap<>();
Eliot Courtney6c313d32017-12-14 19:57:51 +090067
68 // Dependencies:
Jason Monk09f4d372018-12-20 15:30:54 -050069 protected final NotificationLockscreenUserManager mLockscreenUserManager;
70 protected final NotificationGroupManager mGroupManager;
71 protected final VisualStabilityManager mVisualStabilityManager;
Beverly80110912019-02-13 12:20:57 -050072 private final SysuiStatusBarStateController mStatusBarStateController;
Jason Monk09f4d372018-12-20 15:30:54 -050073 private final NotificationEntryManager mEntryManager;
Jason Monk297c04e2018-08-23 17:16:59 -040074
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090075 /**
76 * {@code true} if notifications not part of a group should by default be rendered in their
77 * expanded state. If {@code false}, then only the first notification will be expanded if
78 * possible.
79 */
80 private final boolean mAlwaysExpandNonGroupedNotification;
Mady Mellorce23c462019-06-17 17:30:07 -070081 private final BubbleController mBubbleController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -070082 private final DynamicPrivacyController mDynamicPrivacyController;
Selim Cinekb0fada62019-06-17 19:03:59 -070083 private final KeyguardBypassController mBypassController;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040084 private final Context mContext;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090085
86 private NotificationPresenter mPresenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090087 private NotificationListContainer mListContainer;
88
Ned Burnsd4a69f72019-06-19 19:49:19 -040089 // Used to help track down re-entrant calls to our update methods, which will cause bugs.
90 private boolean mPerformingUpdate;
Ned Burnsb3b4bd32019-06-27 19:36:58 -040091 // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down
92 // the problem.
93 private boolean mIsHandleDynamicPrivacyChangeScheduled;
Ned Burnsd4a69f72019-06-19 19:49:19 -040094
Jason Monk27d01a622018-12-10 15:57:09 -050095 @Inject
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050096 public NotificationViewHierarchyManager(Context context, @Main Handler mainHandler,
Jason Monk09f4d372018-12-20 15:30:54 -050097 NotificationLockscreenUserManager notificationLockscreenUserManager,
98 NotificationGroupManager groupManager,
99 VisualStabilityManager visualStabilityManager,
Beverly80110912019-02-13 12:20:57 -0500100 StatusBarStateController statusBarStateController,
Jason Monk09f4d372018-12-20 15:30:54 -0500101 NotificationEntryManager notificationEntryManager,
Selim Cinekb0fada62019-06-17 19:03:59 -0700102 KeyguardBypassController bypassController,
Mady Mellorce23c462019-06-17 17:30:07 -0700103 BubbleController bubbleController,
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700104 DynamicPrivacyController privacyController) {
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400105 mContext = context;
Ned Burnsb3b4bd32019-06-27 19:36:58 -0400106 mHandler = mainHandler;
Jason Monk09f4d372018-12-20 15:30:54 -0500107 mLockscreenUserManager = notificationLockscreenUserManager;
Selim Cinekb0fada62019-06-17 19:03:59 -0700108 mBypassController = bypassController;
Jason Monk09f4d372018-12-20 15:30:54 -0500109 mGroupManager = groupManager;
110 mVisualStabilityManager = visualStabilityManager;
Beverly80110912019-02-13 12:20:57 -0500111 mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
Jason Monk09f4d372018-12-20 15:30:54 -0500112 mEntryManager = notificationEntryManager;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900113 Resources res = context.getResources();
114 mAlwaysExpandNonGroupedNotification =
115 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
Mady Mellorce23c462019-06-17 17:30:07 -0700116 mBubbleController = bubbleController;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700117 mDynamicPrivacyController = privacyController;
118 privacyController.addListener(this);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900119 }
120
121 public void setUpWithPresenter(NotificationPresenter presenter,
Jason Monk297c04e2018-08-23 17:16:59 -0400122 NotificationListContainer listContainer) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900123 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900124 mListContainer = listContainer;
125 }
126
127 /**
128 * Updates the visual representation of the notifications.
129 */
Evan Laird94492852018-10-25 13:43:01 -0400130 //TODO: Rewrite this to focus on Entries, or some other data object instead of views
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900131 public void updateNotificationViews() {
Ned Burnsd4a69f72019-06-19 19:49:19 -0400132 Assert.isMainThread();
133 beginUpdate();
134
Evan Laird181de622019-10-24 09:53:02 -0400135 List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900136 ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
137 final int N = activeNotifications.size();
138 for (int i = 0; i < N; i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500139 NotificationEntry ent = activeNotifications.get(i);
Matt Pietal5a19cb62019-10-30 12:31:07 -0400140 boolean hideMedia = Utils.useQsMediaPlayer(mContext);
Selim Cinekfdf80332019-03-07 17:29:55 -0800141 if (ent.isRowDismissed() || ent.isRowRemoved()
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400142 || (ent.isMediaNotification() && hideMedia)
Beverlyed8aea22020-01-22 16:52:47 -0500143 || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900144 // we don't want to update removed notifications because they could
145 // temporarily become children if they were isolated before.
146 continue;
147 }
Mady Mellor5549dd22018-11-06 18:07:34 -0800148
Ned Burns00b4b2d2019-10-17 22:09:27 -0400149 int userId = ent.getSbn().getUserId();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900150
151 // Display public version of the notification if we need to redact.
152 // TODO: This area uses a lot of calls into NotificationLockscreenUserManager.
153 // We can probably move some of this code there.
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700154 int currentUserId = mLockscreenUserManager.getCurrentUserId();
155 boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(currentUserId);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900156 boolean userPublic = devicePublic
157 || mLockscreenUserManager.isLockscreenPublicMode(userId);
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700158 if (userPublic && mDynamicPrivacyController.isDynamicallyUnlocked()
159 && (userId == currentUserId || userId == UserHandle.USER_ALL
160 || !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) {
161 userPublic = false;
162 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900163 boolean needsRedaction = mLockscreenUserManager.needsRedaction(ent);
164 boolean sensitive = userPublic && needsRedaction;
165 boolean deviceSensitive = devicePublic
166 && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700167 currentUserId);
Selim Cinekb2c5dc52019-06-24 15:46:52 -0700168 ent.setSensitive(sensitive, deviceSensitive);
Evan Laird94492852018-10-25 13:43:01 -0400169 ent.getRow().setNeedsRedaction(needsRedaction);
Ned Burns00b4b2d2019-10-17 22:09:27 -0400170 if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) {
171 NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900172 List<ExpandableNotificationRow> orderedChildren =
Evan Laird94492852018-10-25 13:43:01 -0400173 mTmpChildOrderMap.get(summary.getRow());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900174 if (orderedChildren == null) {
175 orderedChildren = new ArrayList<>();
Evan Laird94492852018-10-25 13:43:01 -0400176 mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900177 }
Evan Laird94492852018-10-25 13:43:01 -0400178 orderedChildren.add(ent.getRow());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900179 } else {
Evan Laird94492852018-10-25 13:43:01 -0400180 toShow.add(ent.getRow());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900181 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900182 }
183
Rohan Shah524cf7b2018-03-15 14:40:02 -0700184 ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900185 for (int i=0; i< mListContainer.getContainerChildCount(); i++) {
186 View child = mListContainer.getContainerChildAt(i);
187 if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700188 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
189
190 // Blocking helper is effectively a detached view. Don't bother removing it from the
191 // layout.
192 if (!row.isBlockingHelperShowing()) {
193 viewsToRemove.add((ExpandableNotificationRow) child);
194 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900195 }
196 }
197
Rohan Shah524cf7b2018-03-15 14:40:02 -0700198 for (ExpandableNotificationRow viewToRemove : viewsToRemove) {
Ned Burns1c2b85a42019-11-14 15:37:03 -0500199 if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getEntry().getSbn())) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900200 // we are only transferring this notification to its parent, don't generate an
201 // animation
202 mListContainer.setChildTransferInProgress(true);
203 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700204 if (viewToRemove.isSummaryWithChildren()) {
205 viewToRemove.removeAllChildren();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900206 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700207 mListContainer.removeContainerView(viewToRemove);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900208 mListContainer.setChildTransferInProgress(false);
209 }
210
211 removeNotificationChildren();
212
213 for (int i = 0; i < toShow.size(); i++) {
214 View v = toShow.get(i);
215 if (v.getParent() == null) {
216 mVisualStabilityManager.notifyViewAddition(v);
217 mListContainer.addContainerView(v);
Selim Cinekfdf80332019-03-07 17:29:55 -0800218 } else if (!mListContainer.containsView(v)) {
219 // the view is added somewhere else. Let's make sure
220 // the ordering works properly below, by excluding these
221 toShow.remove(v);
222 i--;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900223 }
224 }
225
226 addNotificationChildrenAndSort();
227
228 // So after all this work notifications still aren't sorted correctly.
229 // Let's do that now by advancing through toShow and mListContainer in
230 // lock-step, making sure mListContainer matches what we see in toShow.
231 int j = 0;
232 for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
233 View child = mListContainer.getContainerChildAt(i);
234 if (!(child instanceof ExpandableNotificationRow)) {
235 // We don't care about non-notification views.
236 continue;
237 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700238 if (((ExpandableNotificationRow) child).isBlockingHelperShowing()) {
239 // Don't count/reorder notifications that are showing the blocking helper!
240 continue;
241 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900242
243 ExpandableNotificationRow targetChild = toShow.get(j);
244 if (child != targetChild) {
245 // Oops, wrong notification at this position. Put the right one
246 // here and advance both lists.
247 if (mVisualStabilityManager.canReorderNotification(targetChild)) {
248 mListContainer.changeViewPosition(targetChild, i);
249 } else {
250 mVisualStabilityManager.addReorderingAllowedCallback(mEntryManager);
251 }
252 }
253 j++;
254
255 }
256
257 mVisualStabilityManager.onReorderingFinished();
258 // clear the map again for the next usage
259 mTmpChildOrderMap.clear();
260
Ned Burnsd4a69f72019-06-19 19:49:19 -0400261 updateRowStatesInternal();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900262
263 mListContainer.onNotificationViewUpdateFinished();
Ned Burnsd4a69f72019-06-19 19:49:19 -0400264
265 endUpdate();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900266 }
267
268 private void addNotificationChildrenAndSort() {
269 // Let's now add all notification children which are missing
270 boolean orderChanged = false;
271 for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
272 View view = mListContainer.getContainerChildAt(i);
273 if (!(view instanceof ExpandableNotificationRow)) {
274 // We don't care about non-notification views.
275 continue;
276 }
277
278 ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
279 List<ExpandableNotificationRow> children = parent.getNotificationChildren();
280 List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
281
282 for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
283 childIndex++) {
284 ExpandableNotificationRow childView = orderedChildren.get(childIndex);
285 if (children == null || !children.contains(childView)) {
286 if (childView.getParent() != null) {
287 Log.wtf(TAG, "trying to add a notification child that already has " +
288 "a parent. class:" + childView.getParent().getClass() +
289 "\n child: " + childView);
290 // This shouldn't happen. We can recover by removing it though.
291 ((ViewGroup) childView.getParent()).removeView(childView);
292 }
293 mVisualStabilityManager.notifyViewAddition(childView);
294 parent.addChildNotification(childView, childIndex);
295 mListContainer.notifyGroupChildAdded(childView);
296 }
297 }
298
299 // Finally after removing and adding has been performed we can apply the order.
300 orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager,
301 mEntryManager);
302 }
303 if (orderChanged) {
304 mListContainer.generateChildOrderChangedEvent();
305 }
306 }
307
308 private void removeNotificationChildren() {
309 // First let's remove all children which don't belong in the parents
310 ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
311 for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
312 View view = mListContainer.getContainerChildAt(i);
313 if (!(view instanceof ExpandableNotificationRow)) {
314 // We don't care about non-notification views.
315 continue;
316 }
317
318 ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
319 List<ExpandableNotificationRow> children = parent.getNotificationChildren();
320 List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
321
322 if (children != null) {
323 toRemove.clear();
324 for (ExpandableNotificationRow childRow : children) {
325 if ((orderedChildren == null
326 || !orderedChildren.contains(childRow))
327 && !childRow.keepInParent()) {
328 toRemove.add(childRow);
329 }
330 }
331 for (ExpandableNotificationRow remove : toRemove) {
332 parent.removeChildNotification(remove);
Evan Laird181de622019-10-24 09:53:02 -0400333 if (mEntryManager.getActiveNotificationUnfiltered(
Ned Burns1c2b85a42019-11-14 15:37:03 -0500334 remove.getEntry().getSbn().getKey()) == null) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900335 // We only want to add an animation if the view is completely removed
336 // otherwise it's just a transfer
337 mListContainer.notifyGroupChildRemoved(remove,
338 parent.getChildrenContainer());
339 }
340 }
341 }
342 }
343 }
344
345 /**
346 * Updates expanded, dimmed and locked states of notification rows.
347 */
348 public void updateRowStates() {
Ned Burnsd4a69f72019-06-19 19:49:19 -0400349 Assert.isMainThread();
350 beginUpdate();
351 updateRowStatesInternal();
352 endUpdate();
353 }
354
355 private void updateRowStatesInternal() {
Lucas Dupin16013822018-05-17 18:00:16 -0700356 Trace.beginSection("NotificationViewHierarchyManager#updateRowStates");
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900357 final int N = mListContainer.getContainerChildCount();
358
359 int visibleNotifications = 0;
Jason Monk297c04e2018-08-23 17:16:59 -0400360 boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900361 int maxNotifications = -1;
Selim Cinekb0fada62019-06-17 19:03:59 -0700362 if (onKeyguard && !mBypassController.getBypassEnabled()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900363 maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */);
364 }
365 mListContainer.setMaxDisplayedNotifications(maxNotifications);
366 Stack<ExpandableNotificationRow> stack = new Stack<>();
367 for (int i = N - 1; i >= 0; i--) {
368 View child = mListContainer.getContainerChildAt(i);
369 if (!(child instanceof ExpandableNotificationRow)) {
370 continue;
371 }
372 stack.push((ExpandableNotificationRow) child);
373 }
374 while(!stack.isEmpty()) {
375 ExpandableNotificationRow row = stack.pop();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500376 NotificationEntry entry = row.getEntry();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900377 boolean isChildNotification =
Ned Burns00b4b2d2019-10-17 22:09:27 -0400378 mGroupManager.isChildInGroupWithSummary(entry.getSbn());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900379
Jason Monk297c04e2018-08-23 17:16:59 -0400380 row.setOnKeyguard(onKeyguard);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900381
Jason Monk297c04e2018-08-23 17:16:59 -0400382 if (!onKeyguard) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900383 // If mAlwaysExpandNonGroupedNotification is false, then only expand the
384 // very first notification and if it's not a child of grouped notifications.
385 row.setSystemExpanded(mAlwaysExpandNonGroupedNotification
386 || (visibleNotifications == 0 && !isChildNotification
387 && !row.isLowPriority()));
388 }
389
Ned Burns00b4b2d2019-10-17 22:09:27 -0400390 int userId = entry.getSbn().getUserId();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900391 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
Ned Burns00b4b2d2019-10-17 22:09:27 -0400392 entry.getSbn()) && !entry.isRowRemoved();
Ned Burns8c1b7632019-07-19 14:26:15 -0400393 boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry);
Selim Cinek3bf2d202018-10-16 17:30:05 -0700394 if (!showOnKeyguard) {
395 // min priority notifications should show if their summary is showing
Ned Burns00b4b2d2019-10-17 22:09:27 -0400396 if (mGroupManager.isChildInGroupWithSummary(entry.getSbn())) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500397 NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
Ned Burns00b4b2d2019-10-17 22:09:27 -0400398 entry.getSbn());
Ned Burns8c1b7632019-07-19 14:26:15 -0400399 if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) {
Selim Cinek3bf2d202018-10-16 17:30:05 -0700400 showOnKeyguard = true;
401 }
402 }
403 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900404 if (suppressedSummary
Pavel Grafov65152632018-03-26 15:14:45 +0100405 || mLockscreenUserManager.shouldHideNotifications(userId)
Jason Monk297c04e2018-08-23 17:16:59 -0400406 || (onKeyguard && !showOnKeyguard)) {
Evan Laird94492852018-10-25 13:43:01 -0400407 entry.getRow().setVisibility(View.GONE);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900408 } else {
Evan Laird94492852018-10-25 13:43:01 -0400409 boolean wasGone = entry.getRow().getVisibility() == View.GONE;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900410 if (wasGone) {
Evan Laird94492852018-10-25 13:43:01 -0400411 entry.getRow().setVisibility(View.VISIBLE);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900412 }
Evan Laird94492852018-10-25 13:43:01 -0400413 if (!isChildNotification && !entry.getRow().isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900414 if (wasGone) {
415 // notify the scroller of a child addition
Evan Laird94492852018-10-25 13:43:01 -0400416 mListContainer.generateAddAnimation(entry.getRow(),
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900417 !showOnKeyguard /* fromMoreCard */);
418 }
419 visibleNotifications++;
420 }
421 }
422 if (row.isSummaryWithChildren()) {
423 List<ExpandableNotificationRow> notificationChildren =
424 row.getNotificationChildren();
425 int size = notificationChildren.size();
426 for (int i = size - 1; i >= 0; i--) {
427 stack.push(notificationChildren.get(i));
428 }
429 }
Dan Sandler83b70a02018-01-24 23:20:18 -0500430
Julia Reynoldsfc640012018-02-21 12:25:27 -0500431 row.showAppOpsIcons(entry.mActiveAppOps);
Ned Burns60e94592019-09-06 14:47:25 -0400432 row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900433 }
434
Lucas Dupin16013822018-05-17 18:00:16 -0700435 Trace.beginSection("NotificationPresenter#onUpdateRowStates");
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900436 mPresenter.onUpdateRowStates();
Lucas Dupin16013822018-05-17 18:00:16 -0700437 Trace.endSection();
438 Trace.endSection();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900439 }
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700440
441 @Override
442 public void onDynamicPrivacyChanged() {
Ned Burnsb3b4bd32019-06-27 19:36:58 -0400443 if (mPerformingUpdate) {
444 Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
445 }
446 // This listener can be called from updateNotificationViews() via a convoluted listener
447 // chain, so we post here to prevent a re-entrant call. See b/136186188
448 // TODO: Refactor away the need for this
449 if (!mIsHandleDynamicPrivacyChangeScheduled) {
450 mIsHandleDynamicPrivacyChangeScheduled = true;
451 mHandler.post(this::onHandleDynamicPrivacyChanged);
452 }
453 }
454
455 private void onHandleDynamicPrivacyChanged() {
456 mIsHandleDynamicPrivacyChangeScheduled = false;
Selim Cinek6f0a62a2019-04-09 18:40:12 -0700457 updateNotificationViews();
458 }
Ned Burnsd4a69f72019-06-19 19:49:19 -0400459
460 private void beginUpdate() {
461 if (mPerformingUpdate) {
Ned Burnsbddd3f12019-06-28 12:54:25 -0400462 Log.wtf(TAG, "Re-entrant code during update", new Exception());
Ned Burnsd4a69f72019-06-19 19:49:19 -0400463 }
464 mPerformingUpdate = true;
465 }
466
467 private void endUpdate() {
468 if (!mPerformingUpdate) {
Ned Burnsbddd3f12019-06-28 12:54:25 -0400469 Log.wtf(TAG, "Manager state has become desynced", new Exception());
Ned Burnsd4a69f72019-06-19 19:49:19 -0400470 }
471 mPerformingUpdate = false;
472 }
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900473}