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