1 /* 2 * Copyright (C) 2020 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.collection.inflation; 18 19 import static java.util.Objects.requireNonNull; 20 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.os.Build; 24 import android.view.ViewGroup; 25 26 import com.android.internal.util.NotificationMessagingUtil; 27 import com.android.systemui.dagger.SysUISingleton; 28 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 29 import com.android.systemui.statusbar.NotificationPresenter; 30 import com.android.systemui.statusbar.NotificationRemoteInputManager; 31 import com.android.systemui.statusbar.NotificationUiAdjustment; 32 import com.android.systemui.statusbar.notification.InflationException; 33 import com.android.systemui.statusbar.notification.NotificationClicker; 34 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 35 import com.android.systemui.statusbar.notification.icon.IconManager; 36 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 37 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; 38 import com.android.systemui.statusbar.notification.row.NotifBindPipeline; 39 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; 40 import com.android.systemui.statusbar.notification.row.RowContentBindParams; 41 import com.android.systemui.statusbar.notification.row.RowContentBindStage; 42 import com.android.systemui.statusbar.notification.row.RowInflaterTask; 43 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; 44 import com.android.systemui.statusbar.notification.stack.NotificationListContainer; 45 46 import javax.inject.Inject; 47 import javax.inject.Provider; 48 49 /** Handles inflating and updating views for notifications. */ 50 @SysUISingleton 51 public class NotificationRowBinderImpl implements NotificationRowBinder { 52 53 private static final String TAG = "NotificationViewManager"; 54 55 private final Context mContext; 56 private final NotificationMessagingUtil mMessagingUtil; 57 private final NotificationRemoteInputManager mNotificationRemoteInputManager; 58 private final NotificationLockscreenUserManager mNotificationLockscreenUserManager; 59 private final NotifBindPipeline mNotifBindPipeline; 60 private final RowContentBindStage mRowContentBindStage; 61 private final Provider<RowInflaterTask> mRowInflaterTaskProvider; 62 private final ExpandableNotificationRowComponent.Builder 63 mExpandableNotificationRowComponentBuilder; 64 private final IconManager mIconManager; 65 private final LowPriorityInflationHelper mLowPriorityInflationHelper; 66 67 private NotificationPresenter mPresenter; 68 private NotificationListContainer mListContainer; 69 private BindRowCallback mBindRowCallback; 70 private NotificationClicker mNotificationClicker; 71 72 @Inject NotificationRowBinderImpl( Context context, NotificationMessagingUtil notificationMessagingUtil, NotificationRemoteInputManager notificationRemoteInputManager, NotificationLockscreenUserManager notificationLockscreenUserManager, NotifBindPipeline notifBindPipeline, RowContentBindStage rowContentBindStage, Provider<RowInflaterTask> rowInflaterTaskProvider, ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder, IconManager iconManager, LowPriorityInflationHelper lowPriorityInflationHelper)73 public NotificationRowBinderImpl( 74 Context context, 75 NotificationMessagingUtil notificationMessagingUtil, 76 NotificationRemoteInputManager notificationRemoteInputManager, 77 NotificationLockscreenUserManager notificationLockscreenUserManager, 78 NotifBindPipeline notifBindPipeline, 79 RowContentBindStage rowContentBindStage, 80 Provider<RowInflaterTask> rowInflaterTaskProvider, 81 ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder, 82 IconManager iconManager, 83 LowPriorityInflationHelper lowPriorityInflationHelper) { 84 mContext = context; 85 mNotifBindPipeline = notifBindPipeline; 86 mRowContentBindStage = rowContentBindStage; 87 mMessagingUtil = notificationMessagingUtil; 88 mNotificationRemoteInputManager = notificationRemoteInputManager; 89 mNotificationLockscreenUserManager = notificationLockscreenUserManager; 90 mRowInflaterTaskProvider = rowInflaterTaskProvider; 91 mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder; 92 mIconManager = iconManager; 93 mLowPriorityInflationHelper = lowPriorityInflationHelper; 94 } 95 96 /** 97 * Sets up late-bound dependencies for this component. 98 */ setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, BindRowCallback bindRowCallback)99 public void setUpWithPresenter(NotificationPresenter presenter, 100 NotificationListContainer listContainer, 101 BindRowCallback bindRowCallback) { 102 mPresenter = presenter; 103 mListContainer = listContainer; 104 mBindRowCallback = bindRowCallback; 105 106 mIconManager.attach(); 107 } 108 setNotificationClicker(NotificationClicker clicker)109 public void setNotificationClicker(NotificationClicker clicker) { 110 mNotificationClicker = clicker; 111 } 112 113 /** 114 * Inflates the views for the given entry (possibly asynchronously). 115 */ 116 @Override inflateViews( NotificationEntry entry, NotificationRowContentBinder.InflationCallback callback)117 public void inflateViews( 118 NotificationEntry entry, 119 NotificationRowContentBinder.InflationCallback callback) 120 throws InflationException { 121 ViewGroup parent = mListContainer.getViewParentForNotification(entry); 122 123 if (entry.rowExists()) { 124 mIconManager.updateIcons(entry); 125 ExpandableNotificationRow row = entry.getRow(); 126 row.reset(); 127 updateRow(entry, row); 128 inflateContentViews(entry, row, callback); 129 } else { 130 mIconManager.createIcons(entry); 131 mRowInflaterTaskProvider.get().inflate(mContext, parent, entry, 132 row -> { 133 // Setup the controller for the view. 134 ExpandableNotificationRowComponent component = 135 mExpandableNotificationRowComponentBuilder 136 .expandableNotificationRow(row) 137 .notificationEntry(entry) 138 .onExpandClickListener(mPresenter) 139 .listContainer(mListContainer) 140 .build(); 141 ExpandableNotificationRowController rowController = 142 component.getExpandableNotificationRowController(); 143 rowController.init(entry); 144 entry.setRowController(rowController); 145 bindRow(entry, row); 146 updateRow(entry, row); 147 inflateContentViews(entry, row, callback); 148 }); 149 } 150 } 151 152 /** 153 * Bind row to various controllers and managers. This is only called when the row is first 154 * created. 155 * 156 * TODO: This method associates a row with an entry, but eventually needs to not do that 157 */ bindRow(NotificationEntry entry, ExpandableNotificationRow row)158 private void bindRow(NotificationEntry entry, ExpandableNotificationRow row) { 159 mListContainer.bindRow(row); 160 mNotificationRemoteInputManager.bindRow(row); 161 row.setOnActivatedListener(mPresenter); 162 entry.setRow(row); 163 mNotifBindPipeline.manageRow(entry, row); 164 mBindRowCallback.onBindRow(row); 165 } 166 167 /** 168 * Updates the views bound to an entry when the entry's ranking changes, either in-place or by 169 * reinflating them. 170 * 171 * TODO: Should this method be in this class? 172 */ 173 @Override onNotificationRankingUpdated( NotificationEntry entry, @Nullable Integer oldImportance, NotificationUiAdjustment oldAdjustment, NotificationUiAdjustment newAdjustment, NotificationRowContentBinder.InflationCallback callback)174 public void onNotificationRankingUpdated( 175 NotificationEntry entry, 176 @Nullable Integer oldImportance, 177 NotificationUiAdjustment oldAdjustment, 178 NotificationUiAdjustment newAdjustment, 179 NotificationRowContentBinder.InflationCallback callback) { 180 if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) { 181 if (entry.rowExists()) { 182 ExpandableNotificationRow row = entry.getRow(); 183 row.reset(); 184 updateRow(entry, row); 185 inflateContentViews(entry, row, callback); 186 } else { 187 // Once the RowInflaterTask is done, it will pick up the updated entry, so 188 // no-op here. 189 } 190 } else { 191 if (oldImportance != null && entry.getImportance() != oldImportance) { 192 if (entry.rowExists()) { 193 entry.getRow().onNotificationRankingUpdated(); 194 } 195 } 196 } 197 } 198 199 /** 200 * Update row after the notification has updated. 201 * 202 * @param entry notification that has updated 203 */ updateRow( NotificationEntry entry, ExpandableNotificationRow row)204 private void updateRow( 205 NotificationEntry entry, 206 ExpandableNotificationRow row) { 207 row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD 208 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); 209 210 // bind the click event to the content area 211 requireNonNull(mNotificationClicker).register(row, entry.getSbn()); 212 } 213 214 /** 215 * Inflate the row's basic content views. 216 */ inflateContentViews( NotificationEntry entry, ExpandableNotificationRow row, @Nullable NotificationRowContentBinder.InflationCallback inflationCallback)217 private void inflateContentViews( 218 NotificationEntry entry, 219 ExpandableNotificationRow row, 220 @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) { 221 final boolean useIncreasedCollapsedHeight = 222 mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance()); 223 // If this is our first time inflating, we don't actually know the groupings for real 224 // yet, so we might actually inflate a low priority content view incorrectly here and have 225 // to correct it later in the pipeline. On subsequent inflations (i.e. updates), this 226 // should inflate the correct view. 227 final boolean isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry); 228 229 RowContentBindParams params = mRowContentBindStage.getStageParams(entry); 230 params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); 231 params.setUseLowPriority(isLowPriority); 232 233 // TODO: Replace this API with RowContentBindParams directly. Also move to a separate 234 // redaction controller. 235 row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry)); 236 237 params.rebindAllContentViews(); 238 mRowContentBindStage.requestRebind(entry, en -> { 239 row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight); 240 row.setIsLowPriority(isLowPriority); 241 if (inflationCallback != null) { 242 inflationCallback.onAsyncInflationFinished(en); 243 } 244 }); 245 } 246 247 /** Callback for when a row is bound to an entry. */ 248 public interface BindRowCallback { 249 /** 250 * Called when a new row is created and bound to a notification. 251 */ onBindRow(ExpandableNotificationRow row)252 void onBindRow(ExpandableNotificationRow row); 253 } 254 } 255