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