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 com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE; 20 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED; 21 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED; 22 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC; 23 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE; 24 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE; 25 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER; 26 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER; 27 28 import static java.util.Objects.requireNonNull; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.content.Context; 33 import android.os.Build; 34 import android.view.ViewGroup; 35 36 import com.android.internal.util.NotificationMessagingUtil; 37 import com.android.systemui.dagger.SysUISingleton; 38 import com.android.systemui.flags.FeatureFlags; 39 import com.android.systemui.flags.Flags; 40 import com.android.systemui.shade.ShadeDisplayAware; 41 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 42 import com.android.systemui.statusbar.NotificationPresenter; 43 import com.android.systemui.statusbar.NotificationRemoteInputManager; 44 import com.android.systemui.statusbar.notification.InflationException; 45 import com.android.systemui.statusbar.notification.NotificationClicker; 46 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 47 import com.android.systemui.statusbar.notification.icon.IconManager; 48 import com.android.systemui.statusbar.notification.row.BigPictureIconManager; 49 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 50 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; 51 import com.android.systemui.statusbar.notification.row.NotifBindPipeline; 52 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; 53 import com.android.systemui.statusbar.notification.row.RowContentBindParams; 54 import com.android.systemui.statusbar.notification.row.RowContentBindStage; 55 import com.android.systemui.statusbar.notification.row.RowInflaterTask; 56 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; 57 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; 58 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; 59 import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; 60 import com.android.systemui.statusbar.notification.stack.NotificationListContainer; 61 62 import javax.inject.Inject; 63 import javax.inject.Provider; 64 65 /** Handles inflating and updating views for notifications. */ 66 @SysUISingleton 67 public class NotificationRowBinderImpl implements NotificationRowBinder { 68 69 private static final String TAG = "NotificationViewManager"; 70 71 private final Context mContext; 72 private final NotificationMessagingUtil mMessagingUtil; 73 private final NotificationRemoteInputManager mNotificationRemoteInputManager; 74 private final NotificationLockscreenUserManager mNotificationLockscreenUserManager; 75 private final NotifBindPipeline mNotifBindPipeline; 76 private final RowContentBindStage mRowContentBindStage; 77 private final Provider<RowInflaterTask> mRowInflaterTaskProvider; 78 private final ExpandableNotificationRowComponent.Builder 79 mExpandableNotificationRowComponentBuilder; 80 private final IconManager mIconManager; 81 private final NotificationRowBinderLogger mLogger; 82 83 private NotificationPresenter mPresenter; 84 private NotificationListContainer mListContainer; 85 private NotificationClicker mNotificationClicker; 86 private FeatureFlags mFeatureFlags; 87 88 @Inject NotificationRowBinderImpl( @hadeDisplayAware Context context, NotificationMessagingUtil notificationMessagingUtil, NotificationRemoteInputManager notificationRemoteInputManager, NotificationLockscreenUserManager notificationLockscreenUserManager, NotifBindPipeline notifBindPipeline, RowContentBindStage rowContentBindStage, Provider<RowInflaterTask> rowInflaterTaskProvider, ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder, IconManager iconManager, NotificationRowBinderLogger logger, FeatureFlags featureFlags)89 public NotificationRowBinderImpl( 90 @ShadeDisplayAware Context context, 91 NotificationMessagingUtil notificationMessagingUtil, 92 NotificationRemoteInputManager notificationRemoteInputManager, 93 NotificationLockscreenUserManager notificationLockscreenUserManager, 94 NotifBindPipeline notifBindPipeline, 95 RowContentBindStage rowContentBindStage, 96 Provider<RowInflaterTask> rowInflaterTaskProvider, 97 ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder, 98 IconManager iconManager, 99 NotificationRowBinderLogger logger, 100 FeatureFlags featureFlags) { 101 mContext = context; 102 mNotifBindPipeline = notifBindPipeline; 103 mRowContentBindStage = rowContentBindStage; 104 mMessagingUtil = notificationMessagingUtil; 105 mNotificationRemoteInputManager = notificationRemoteInputManager; 106 mNotificationLockscreenUserManager = notificationLockscreenUserManager; 107 mRowInflaterTaskProvider = rowInflaterTaskProvider; 108 mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder; 109 mIconManager = iconManager; 110 mLogger = logger; 111 mFeatureFlags = featureFlags; 112 } 113 114 /** 115 * Sets up late-bound dependencies for this component. 116 */ setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer)117 public void setUpWithPresenter(NotificationPresenter presenter, 118 NotificationListContainer listContainer) { 119 mPresenter = presenter; 120 mListContainer = listContainer; 121 122 mIconManager.attach(); 123 } 124 setNotificationClicker(NotificationClicker clicker)125 public void setNotificationClicker(NotificationClicker clicker) { 126 mNotificationClicker = clicker; 127 } 128 129 /** 130 * Inflates the views for the given entry (possibly asynchronously). 131 */ 132 @Override inflateViews( NotificationEntry entry, @NonNull NotifInflater.Params params, NotificationRowContentBinder.InflationCallback callback)133 public void inflateViews( 134 NotificationEntry entry, 135 @NonNull NotifInflater.Params params, 136 NotificationRowContentBinder.InflationCallback callback) 137 throws InflationException { 138 ViewGroup parent = mListContainer.getViewParentForNotification(); 139 140 if (entry.rowExists()) { 141 mLogger.logUpdatingRow(entry, params); 142 mIconManager.updateIcons(entry, /* usingCache = */ false); 143 ExpandableNotificationRow row = entry.getRow(); 144 row.reset(); 145 updateRow(entry, row); 146 inflateContentViews(entry, params, row, callback); 147 } else { 148 mLogger.logCreatingRow(entry, params); 149 mIconManager.createIcons(entry); 150 mLogger.logInflatingRow(entry); 151 mRowInflaterTaskProvider.get().inflate(mContext, parent, entry, 152 row -> { 153 mLogger.logInflatedRow(entry); 154 // Setup the controller for the view. 155 ExpandableNotificationRowComponent component = 156 mExpandableNotificationRowComponentBuilder 157 .expandableNotificationRow(row) 158 .notificationEntry(entry) 159 .onExpandClickListener(mPresenter) 160 .build(); 161 ExpandableNotificationRowController rowController = 162 component.getExpandableNotificationRowController(); 163 rowController.init(entry); 164 entry.setRowController(rowController); 165 maybeSetBigPictureIconManager(row, component); 166 bindRow(entry, row); 167 updateRow(entry, row); 168 inflateContentViews(entry, params, row, callback); 169 }); 170 } 171 } 172 173 @Override releaseViews(NotificationEntry entry)174 public void releaseViews(NotificationEntry entry) { 175 if (!entry.rowExists()) { 176 mLogger.logNotReleasingViewsRowDoesntExist(entry); 177 return; 178 } 179 mLogger.logReleasingViews(entry); 180 cancelRunningJobs(entry.getRow()); 181 final RowContentBindParams params = mRowContentBindStage.getStageParams(entry); 182 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED); 183 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED); 184 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC); 185 if (AsyncHybridViewInflation.isEnabled()) { 186 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_SINGLE_LINE); 187 if (LockscreenOtpRedaction.isSingleLineViewEnabled()) { 188 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE); 189 } 190 } 191 mRowContentBindStage.requestRebind(entry, null); 192 } 193 maybeSetBigPictureIconManager(ExpandableNotificationRow row, ExpandableNotificationRowComponent component)194 private void maybeSetBigPictureIconManager(ExpandableNotificationRow row, 195 ExpandableNotificationRowComponent component) { 196 if (mFeatureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) { 197 row.setBigPictureIconManager(component.getBigPictureIconManager()); 198 } 199 } 200 cancelRunningJobs(ExpandableNotificationRow row)201 private void cancelRunningJobs(ExpandableNotificationRow row) { 202 if (row == null) { 203 return; 204 } 205 BigPictureIconManager iconManager = row.getBigPictureIconManager(); 206 if (iconManager != null) { 207 iconManager.cancelJobs(); 208 } 209 } 210 211 /** 212 * Bind row to various controllers and managers. This is only called when the row is first 213 * created. 214 * 215 * TODO: This method associates a row with an entry, but eventually needs to not do that 216 */ bindRow(NotificationEntry entry, ExpandableNotificationRow row)217 private void bindRow(NotificationEntry entry, ExpandableNotificationRow row) { 218 mListContainer.bindRow(row); 219 mNotificationRemoteInputManager.bindRow(row); 220 entry.setRow(row); 221 mNotifBindPipeline.manageRow(entry, row); 222 mPresenter.onBindRow(row); 223 } 224 225 /** 226 * Update row after the notification has updated. 227 * 228 * @param entry notification that has updated 229 */ updateRow( NotificationEntry entry, ExpandableNotificationRow row)230 private void updateRow( 231 NotificationEntry entry, 232 ExpandableNotificationRow row) { 233 row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD 234 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); 235 236 // bind the click event to the content area 237 requireNonNull(mNotificationClicker).register(row, entry.getSbn()); 238 } 239 240 /** 241 * Inflate the row's basic content views. 242 */ inflateContentViews( NotificationEntry entry, @NonNull NotifInflater.Params inflaterParams, ExpandableNotificationRow row, @Nullable NotificationRowContentBinder.InflationCallback inflationCallback)243 private void inflateContentViews( 244 NotificationEntry entry, 245 @NonNull NotifInflater.Params inflaterParams, 246 ExpandableNotificationRow row, 247 @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) { 248 final boolean isMinimized = inflaterParams.isMinimized(); 249 250 // Set show snooze action 251 row.setShowSnooze(inflaterParams.getShowSnooze()); 252 253 RowContentBindParams params = mRowContentBindStage.getStageParams(entry); 254 params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED); 255 params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED); 256 params.setUseMinimized(isMinimized); 257 int redactionType = inflaterParams.getRedactionType(); 258 259 params.setRedactionType(redactionType); 260 if (redactionType != REDACTION_TYPE_NONE) { 261 params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC); 262 } else { 263 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC); 264 } 265 266 if (AsyncHybridViewInflation.isEnabled()) { 267 if (inflaterParams.isChildInGroup()) { 268 params.requireContentViews(FLAG_CONTENT_VIEW_SINGLE_LINE); 269 } else { 270 // TODO(b/217799515): here we decide whether to free the single-line view 271 // when the group status changes 272 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_SINGLE_LINE); 273 } 274 } 275 276 if (LockscreenOtpRedaction.isSingleLineViewEnabled()) { 277 if (inflaterParams.isChildInGroup() 278 && redactionType != REDACTION_TYPE_NONE) { 279 params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE); 280 } else { 281 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE); 282 } 283 } 284 285 if (AsyncGroupHeaderViewInflation.isEnabled()) { 286 if (inflaterParams.isGroupSummary()) { 287 params.requireContentViews(FLAG_GROUP_SUMMARY_HEADER); 288 if (isMinimized) { 289 params.requireContentViews(FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER); 290 } 291 } else { 292 params.markContentViewsFreeable(FLAG_GROUP_SUMMARY_HEADER); 293 params.markContentViewsFreeable(FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER); 294 } 295 } 296 params.rebindAllContentViews(); 297 mLogger.logRequestingRebind(entry, inflaterParams); 298 mRowContentBindStage.requestRebind(entry, en -> { 299 mLogger.logRebindComplete(entry); 300 row.setIsMinimized(isMinimized); 301 row.setRedactionType(redactionType); 302 if (inflationCallback != null) { 303 inflationCallback.onAsyncInflationFinished(en); 304 } 305 }); 306 } 307 } 308