1 /* 2 * Copyright (C) 2019 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 17 package com.android.systemui.statusbar.notification; 18 19 import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED; 20 import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED; 21 import static com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED; 22 23 import com.android.systemui.statusbar.notification.collection.NotifPipeline; 24 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 25 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 26 import com.android.systemui.statusbar.notification.row.RowContentBindParams; 27 import com.android.systemui.statusbar.notification.row.RowContentBindStage; 28 29 import java.util.List; 30 import java.util.Map; 31 32 import javax.inject.Inject; 33 34 /** 35 * Controller that binds/unbinds views content views on notification group children. 36 * 37 * We currently only show a limited number of notification children even if more exist, so we 38 * can save memory by freeing content views when they're not visible and binding them again when 39 * they get close to being visible. 40 * 41 * Eventually, when {@link NotifPipeline} takes over as the new notification pipeline, we'll have 42 * more control over which notifications even make it to inflation in the first place and be able 43 * to enforce this at an earlier stage at the level of the {@link ExpandableNotificationRow}, but 44 * for now, we're just doing it at the level of content views. 45 */ 46 public class DynamicChildBindController { 47 private final RowContentBindStage mStage; 48 private final int mChildBindCutoff; 49 50 @Inject DynamicChildBindController(RowContentBindStage stage)51 public DynamicChildBindController(RowContentBindStage stage) { 52 this(stage, CHILD_BIND_CUTOFF); 53 } 54 55 /** 56 * @param childBindCutoff the cutoff where we no longer bother having content views bound 57 */ DynamicChildBindController( RowContentBindStage stage, int childBindCutoff)58 DynamicChildBindController( 59 RowContentBindStage stage, 60 int childBindCutoff) { 61 mStage = stage; 62 mChildBindCutoff = childBindCutoff; 63 } 64 65 /** 66 * Update the content views, unbinding content views on children that won't be visible 67 * and binding content views on children that will be visible eventually and previously unbound 68 * children that are no longer children. 69 * 70 * @param groupNotifs map of top-level notifs to their children, if any 71 */ updateContentViews( Map<NotificationEntry, List<NotificationEntry>> groupNotifs)72 public void updateContentViews( 73 Map<NotificationEntry, List<NotificationEntry>> groupNotifs) { 74 for (NotificationEntry entry : groupNotifs.keySet()) { 75 List<NotificationEntry> children = groupNotifs.get(entry); 76 if (children == null) { 77 if (!hasContent(entry)) { 78 // Case where child is updated to be top level 79 bindContent(entry); 80 } 81 continue; 82 } 83 for (int j = 0; j < children.size(); j++) { 84 NotificationEntry childEntry = children.get(j); 85 if (j >= mChildBindCutoff) { 86 if (hasContent(childEntry)) { 87 freeContent(childEntry); 88 } 89 } else { 90 if (!hasContent(childEntry)) { 91 bindContent(childEntry); 92 } 93 } 94 } 95 } 96 } 97 hasContent(NotificationEntry entry)98 private boolean hasContent(NotificationEntry entry) { 99 ExpandableNotificationRow row = entry.getRow(); 100 return row.getPrivateLayout().getContractedChild() != null 101 || row.getPrivateLayout().getExpandedChild() != null; 102 } 103 freeContent(NotificationEntry entry)104 private void freeContent(NotificationEntry entry) { 105 RowContentBindParams params = mStage.getStageParams(entry); 106 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED); 107 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED); 108 mStage.requestRebind(entry, null); 109 } 110 bindContent(NotificationEntry entry)111 private void bindContent(NotificationEntry entry) { 112 RowContentBindParams params = mStage.getStageParams(entry); 113 params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED); 114 params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED); 115 mStage.requestRebind(entry, null); 116 } 117 118 /** 119 * How big the buffer of extra views we keep around to be ready to show when we do need to 120 * dynamically inflate. 121 */ 122 private static final int EXTRA_VIEW_BUFFER_COUNT = 1; 123 124 private static final int CHILD_BIND_CUTOFF = 125 NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED + EXTRA_VIEW_BUFFER_COUNT; 126 } 127