• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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;
18 
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
21 import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
22 import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
23 
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.os.Build;
29 import android.service.notification.StatusBarNotification;
30 import android.util.Log;
31 import android.view.ViewGroup;
32 
33 import com.android.internal.util.NotificationMessagingUtil;
34 import com.android.systemui.Dependency;
35 import com.android.systemui.R;
36 import com.android.systemui.UiOffloadThread;
37 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
38 import com.android.systemui.statusbar.NotificationPresenter;
39 import com.android.systemui.statusbar.NotificationRemoteInputManager;
40 import com.android.systemui.statusbar.NotificationUiAdjustment;
41 import com.android.systemui.statusbar.notification.InflationException;
42 import com.android.systemui.statusbar.notification.NotificationClicker;
43 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
44 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
45 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
46 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
47 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
48 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
49 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
50 import com.android.systemui.statusbar.phone.NotificationGroupManager;
51 import com.android.systemui.statusbar.phone.StatusBar;
52 import com.android.systemui.statusbar.policy.HeadsUpManager;
53 
54 /** Handles inflating and updating views for notifications. */
55 public class NotificationRowBinderImpl implements NotificationRowBinder {
56 
57     private static final String TAG = "NotificationViewManager";
58 
59     private final NotificationGroupManager mGroupManager =
60             Dependency.get(NotificationGroupManager.class);
61     private final NotificationGutsManager mGutsManager =
62             Dependency.get(NotificationGutsManager.class);
63     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
64     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
65             Dependency.get(NotificationInterruptionStateProvider.class);
66 
67     private final Context mContext;
68     private final NotificationMessagingUtil mMessagingUtil;
69     private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
70             this::logNotificationExpansion;
71     private final boolean mAllowLongPress;
72 
73     private NotificationRemoteInputManager mRemoteInputManager;
74     private NotificationPresenter mPresenter;
75     private NotificationListContainer mListContainer;
76     private HeadsUpManager mHeadsUpManager;
77     private NotificationContentInflater.InflationCallback mInflationCallback;
78     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
79     private BindRowCallback mBindRowCallback;
80     private NotificationClicker mNotificationClicker;
81     private final NotificationLogger mNotificationLogger = Dependency.get(NotificationLogger.class);
82 
NotificationRowBinderImpl(Context context, boolean allowLongPress)83     public NotificationRowBinderImpl(Context context, boolean allowLongPress) {
84         mContext = context;
85         mMessagingUtil = new NotificationMessagingUtil(context);
86         mAllowLongPress = allowLongPress;
87     }
88 
getRemoteInputManager()89     private NotificationRemoteInputManager getRemoteInputManager() {
90         if (mRemoteInputManager == null) {
91             mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
92         }
93         return mRemoteInputManager;
94     }
95 
96     /**
97      * Sets up late-bound dependencies for this component.
98      */
setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, HeadsUpManager headsUpManager, NotificationContentInflater.InflationCallback inflationCallback, BindRowCallback bindRowCallback)99     public void setUpWithPresenter(NotificationPresenter presenter,
100             NotificationListContainer listContainer,
101             HeadsUpManager headsUpManager,
102             NotificationContentInflater.InflationCallback inflationCallback,
103             BindRowCallback bindRowCallback) {
104         mPresenter = presenter;
105         mListContainer = listContainer;
106         mHeadsUpManager = headsUpManager;
107         mInflationCallback = inflationCallback;
108         mBindRowCallback = bindRowCallback;
109         mOnAppOpsClickListener = mGutsManager::openGuts;
110     }
111 
setNotificationClicker(NotificationClicker clicker)112     public void setNotificationClicker(NotificationClicker clicker) {
113         mNotificationClicker = clicker;
114     }
115 
116     /**
117      * Inflates the views for the given entry (possibly asynchronously).
118      */
119     @Override
inflateViews( NotificationEntry entry, Runnable onDismissRunnable)120     public void inflateViews(
121             NotificationEntry entry,
122             Runnable onDismissRunnable)
123             throws InflationException {
124         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
125         PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
126                 entry.notification.getUser().getIdentifier());
127 
128         final StatusBarNotification sbn = entry.notification;
129         if (entry.rowExists()) {
130             entry.updateIcons(mContext, sbn);
131             entry.reset();
132             updateNotification(entry, pmUser, sbn, entry.getRow());
133         } else {
134             entry.createIcons(mContext, sbn);
135             new RowInflaterTask().inflate(mContext, parent, entry,
136                     row -> {
137                         bindRow(entry, pmUser, sbn, row, onDismissRunnable);
138                         updateNotification(entry, pmUser, sbn, row);
139                     });
140         }
141     }
142 
bindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row, Runnable onDismissRunnable)143     private void bindRow(NotificationEntry entry, PackageManager pmUser,
144             StatusBarNotification sbn, ExpandableNotificationRow row,
145             Runnable onDismissRunnable) {
146         row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
147         row.setGroupManager(mGroupManager);
148         row.setHeadsUpManager(mHeadsUpManager);
149         row.setOnExpandClickListener(mPresenter);
150         row.setInflationCallback(mInflationCallback);
151         if (mAllowLongPress) {
152             row.setLongPressListener(mGutsManager::openGuts);
153         }
154         mListContainer.bindRow(row);
155         getRemoteInputManager().bindRow(row);
156 
157         // Get the app name.
158         // Note that Notification.Builder#bindHeaderAppName has similar logic
159         // but since this field is used in the guts, it must be accurate.
160         // Therefore we will only show the application label, or, failing that, the
161         // package name. No substitutions.
162         final String pkg = sbn.getPackageName();
163         String appname = pkg;
164         try {
165             final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
166                     PackageManager.MATCH_UNINSTALLED_PACKAGES
167                             | PackageManager.MATCH_DISABLED_COMPONENTS);
168             if (info != null) {
169                 appname = String.valueOf(pmUser.getApplicationLabel(info));
170             }
171         } catch (PackageManager.NameNotFoundException e) {
172             // Do nothing
173         }
174         row.setAppName(appname);
175         row.setOnDismissRunnable(onDismissRunnable);
176         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
177         if (ENABLE_REMOTE_INPUT) {
178             row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
179         }
180 
181         row.setAppOpsOnClickListener(mOnAppOpsClickListener);
182 
183         mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
184     }
185 
186     /**
187      * Updates the views bound to an entry when the entry's ranking changes, either in-place or by
188      * reinflating them.
189      */
190     @Override
onNotificationRankingUpdated( NotificationEntry entry, @Nullable Integer oldImportance, NotificationUiAdjustment oldAdjustment, NotificationUiAdjustment newAdjustment)191     public void onNotificationRankingUpdated(
192             NotificationEntry entry,
193             @Nullable Integer oldImportance,
194             NotificationUiAdjustment oldAdjustment,
195             NotificationUiAdjustment newAdjustment) {
196         if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
197             if (entry.rowExists()) {
198                 entry.reset();
199                 PackageManager pmUser = StatusBar.getPackageManagerForUser(
200                         mContext,
201                         entry.notification.getUser().getIdentifier());
202                 updateNotification(entry, pmUser, entry.notification, entry.getRow());
203             } else {
204                 // Once the RowInflaterTask is done, it will pick up the updated entry, so
205                 // no-op here.
206             }
207         } else {
208             if (oldImportance != null && entry.importance != oldImportance) {
209                 if (entry.rowExists()) {
210                     entry.getRow().onNotificationRankingUpdated();
211                 }
212             }
213         }
214     }
215 
216     //TODO: This method associates a row with an entry, but eventually needs to not do that
updateNotification( NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row)217     private void updateNotification(
218             NotificationEntry entry,
219             PackageManager pmUser,
220             StatusBarNotification sbn,
221             ExpandableNotificationRow row) {
222         row.setIsLowPriority(entry.ambient);
223 
224         // Extract target SDK version.
225         try {
226             ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
227             entry.targetSdk = info.targetSdkVersion;
228         } catch (PackageManager.NameNotFoundException ex) {
229             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
230         }
231         row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
232                 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
233 
234         // TODO: should updates to the entry be happening somewhere else?
235         entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
236         entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
237 
238         entry.setRow(row);
239         row.setOnActivatedListener(mPresenter);
240 
241         boolean useIncreasedCollapsedHeight =
242                 mMessagingUtil.isImportantMessaging(sbn, entry.importance);
243         boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
244                 && !mPresenter.isPresenterFullyCollapsed();
245         row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
246         row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
247         row.setEntry(entry);
248 
249         if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
250             row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
251         }
252         if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
253             row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
254         }
255         row.setNeedsRedaction(
256                 Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
257         row.inflateViews();
258 
259         // bind the click event to the content area
260         checkNotNull(mNotificationClicker).register(row, sbn);
261     }
262 
logNotificationExpansion(String key, boolean userAction, boolean expanded)263     private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
264         mNotificationLogger.onExpansionChanged(key, userAction, expanded);
265     }
266 
267     /** Callback for when a row is bound to an entry. */
268     public interface BindRowCallback {
269         /**
270          * Called when a new notification and row is created.
271          *
272          * @param entry  entry for the notification
273          * @param pmUser package manager for user
274          * @param sbn    notification
275          * @param row    row for the notification
276          */
onBindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row)277         void onBindRow(NotificationEntry entry, PackageManager pmUser,
278                 StatusBarNotification sbn, ExpandableNotificationRow row);
279     }
280 }
281