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