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