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