• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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