• 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.settings.notification.history;
18 
19 import static android.app.Notification.COLOR_DEFAULT;
20 import static android.content.pm.PackageManager.MATCH_ANY_USER;
21 import static android.content.pm.PackageManager.NameNotFoundException;
22 import static android.os.UserHandle.USER_ALL;
23 import static android.provider.Settings.EXTRA_APP_PACKAGE;
24 import static android.provider.Settings.EXTRA_CHANNEL_ID;
25 import static android.provider.Settings.EXTRA_CONVERSATION_ID;
26 
27 import static com.android.settings.notification.history.NotificationHistoryActivity.ROUND_CORNER_BOTTOM;
28 import static com.android.settings.notification.history.NotificationHistoryActivity.ROUND_CORNER_CENTER;
29 import static com.android.settings.notification.history.NotificationHistoryActivity.ROUND_CORNER_TOP;
30 
31 import android.annotation.ColorInt;
32 import android.annotation.UserIdInt;
33 import android.app.ActivityManager;
34 import android.app.Notification;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageManager;
39 import android.content.res.Configuration;
40 import android.graphics.PorterDuff;
41 import android.graphics.PorterDuffColorFilter;
42 import android.graphics.drawable.Drawable;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.provider.Settings;
46 import android.service.notification.StatusBarNotification;
47 import android.text.TextUtils;
48 import android.util.Log;
49 import android.util.Slog;
50 import android.view.LayoutInflater;
51 import android.view.View;
52 import android.view.ViewGroup;
53 
54 import androidx.annotation.NonNull;
55 import androidx.recyclerview.widget.RecyclerView;
56 
57 import com.android.internal.logging.UiEventLogger;
58 import com.android.internal.util.ContrastColorUtil;
59 import com.android.settings.R;
60 import com.android.settingslib.Utils;
61 
62 import java.util.ArrayList;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Map;
66 
67 public class NotificationSbnAdapter extends
68         RecyclerView.Adapter<NotificationSbnViewHolder> {
69 
70     private static final String TAG = "SbnAdapter";
71     private List<StatusBarNotification> mValues;
72     private Map<Integer, Drawable> mUserBadgeCache;
73     private final Context mContext;
74     private PackageManager mPm;
75     private @ColorInt int mBackgroundColor;
76     private boolean mInNightMode;
77     private @UserIdInt int mCurrentUser;
78     private List<Integer> mEnabledProfiles = new ArrayList<>();
79     private boolean mIsSnoozed;
80     private UiEventLogger mUiEventLogger;
81 
NotificationSbnAdapter(Context context, PackageManager pm, UserManager um, boolean isSnoozed, UiEventLogger uiEventLogger)82     public NotificationSbnAdapter(Context context, PackageManager pm, UserManager um,
83             boolean isSnoozed, UiEventLogger uiEventLogger) {
84         mContext = context;
85         mPm = pm;
86         mUserBadgeCache = new HashMap<>();
87         mValues = new ArrayList<>();
88         mBackgroundColor = Utils.getColorAttrDefaultColor(context,
89                 android.R.attr.colorBackground);
90         Configuration currentConfig = mContext.getResources().getConfiguration();
91         mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
92                 == Configuration.UI_MODE_NIGHT_YES;
93         mCurrentUser = ActivityManager.getCurrentUser();
94         int[] enabledUsers = um.getEnabledProfileIds(mCurrentUser);
95         for (int id : enabledUsers) {
96             if (!um.isQuietModeEnabled(UserHandle.of(id))) {
97                 mEnabledProfiles.add(id);
98             }
99         }
100         setHasStableIds(true);
101         // If true, this is the panel for snoozed notifs, otherwise the one for dismissed notifs.
102         mIsSnoozed = isSnoozed;
103         mUiEventLogger = uiEventLogger;
104     }
105 
106     @Override
onCreateViewHolder(ViewGroup parent, int viewType)107     public NotificationSbnViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
108         View view = LayoutInflater.from(parent.getContext())
109                 .inflate(R.layout.notification_sbn_log_row, parent, false);
110         return new NotificationSbnViewHolder(view);
111     }
112 
113     @Override
onBindViewHolder(final @NonNull NotificationSbnViewHolder holder, int position)114     public void onBindViewHolder(final @NonNull NotificationSbnViewHolder holder,
115             int position) {
116         final StatusBarNotification sbn = mValues.get(position);
117         if (sbn != null) {
118             int cornerType = ROUND_CORNER_CENTER;
119             if (position == (getItemCount() - 1)) {
120                 cornerType |= ROUND_CORNER_BOTTOM;
121             }
122             if (position == 0) {
123                 cornerType |= ROUND_CORNER_TOP;
124             }
125             int backgroundRes = NotificationHistoryActivity.getRoundCornerDrawableRes(cornerType);
126             holder.itemView.setBackgroundResource(backgroundRes);
127 
128             holder.setIconBackground(loadBackground(sbn));
129             holder.setIcon(loadIcon(sbn));
130             holder.setPackageLabel(loadPackageLabel(sbn.getPackageName()).toString());
131             holder.setTitle(getTitleString(sbn.getNotification()));
132             holder.setSummary(getTextString(mContext, sbn.getNotification()));
133             holder.setPostedTime(sbn.getPostTime());
134             int userId = normalizeUserId(sbn);
135             if (!mUserBadgeCache.containsKey(userId)) {
136                 Drawable profile = mContext.getPackageManager().getUserBadgeForDensityNoBackground(
137                         UserHandle.of(userId), 0);
138                 mUserBadgeCache.put(userId, profile);
139             }
140             holder.setProfileBadge(mUserBadgeCache.get(userId));
141             holder.addOnClick(position, sbn.getPackageName(), sbn.getUid(), sbn.getUserId(),
142                     sbn.getNotification().contentIntent, sbn.getInstanceId(), mIsSnoozed,
143                     mUiEventLogger);
144             holder.itemView.setOnLongClickListener(v -> {
145                 Intent intent =  new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
146                         .setPackage(mContext.getPackageName())
147                         .putExtra(EXTRA_APP_PACKAGE, sbn.getPackageName())
148                         .putExtra(EXTRA_CHANNEL_ID, sbn.getNotification().getChannelId())
149                         .putExtra(EXTRA_CONVERSATION_ID, sbn.getNotification().getShortcutId());
150                 holder.itemView.getContext().startActivityAsUser(intent, UserHandle.of(userId));
151                 return true;
152             });
153         } else {
154             Slog.w(TAG, "null entry in list at position " + position);
155         }
156     }
157 
loadBackground(StatusBarNotification sbn)158     private Drawable loadBackground(StatusBarNotification sbn) {
159         Drawable bg = mContext.getDrawable(R.drawable.circle);
160         int color = sbn.getNotification().color;
161         if (color == COLOR_DEFAULT) {
162             color = Utils.getColorAttrDefaultColor(
163                     mContext, com.android.internal.R.attr.colorAccent);
164         }
165         color = ContrastColorUtil.resolveContrastColor(
166                 mContext, color, mBackgroundColor, mInNightMode);
167         bg.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
168         return bg;
169     }
170 
171     @Override
getItemCount()172     public int getItemCount() {
173         return mValues.size();
174     }
175 
onRebuildComplete(List<StatusBarNotification> notifications)176     public void onRebuildComplete(List<StatusBarNotification> notifications) {
177         for (int i = notifications.size() - 1; i >= 0; i--) {
178             StatusBarNotification sbn = notifications.get(i);
179             if (!shouldShowSbn(sbn)) {
180                 notifications.remove(i);
181             }
182         }
183         mValues = notifications;
184         notifyDataSetChanged();
185     }
186 
addSbn(StatusBarNotification sbn)187     public void addSbn(StatusBarNotification sbn) {
188         if (!shouldShowSbn(sbn)) {
189             return;
190         }
191         mValues.add(0, sbn);
192         notifyDataSetChanged();
193     }
194 
shouldShowSbn(StatusBarNotification sbn)195     private boolean shouldShowSbn(StatusBarNotification sbn) {
196         // summaries are low content; don't bother showing them
197         if (sbn.isGroup() && sbn.getNotification().isGroupSummary()) {
198             return false;
199         }
200         // also don't show profile notifications if the profile is currently disabled
201         if (!mEnabledProfiles.contains(normalizeUserId(sbn))) {
202             return false;
203         }
204         return true;
205     }
206 
loadPackageLabel(String pkg)207     private @NonNull CharSequence loadPackageLabel(String pkg) {
208         try {
209             ApplicationInfo info = mPm.getApplicationInfo(pkg,
210                     MATCH_ANY_USER);
211             if (info != null) return mPm.getApplicationLabel(info);
212         } catch (NameNotFoundException e) {
213             Log.e(TAG, "Cannot load package name", e);
214         }
215         return pkg;
216     }
217 
getTitleString(Notification n)218     private static String getTitleString(Notification n) {
219         CharSequence title = null;
220         if (n.extras != null) {
221             title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
222         }
223         return title == null? null : String.valueOf(title);
224     }
225 
226     /**
227      * Returns the appropriate substring for this notification based on the style of notification.
228      */
getTextString(Context appContext, Notification n)229     private static String getTextString(Context appContext, Notification n) {
230         CharSequence text = null;
231         if (n.extras != null) {
232             text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
233 
234             Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
235 
236             if (nb.getStyle() instanceof Notification.BigTextStyle) {
237                 text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
238             } else if (nb.getStyle() instanceof Notification.MessagingStyle) {
239                 Notification.MessagingStyle ms = (Notification.MessagingStyle) nb.getStyle();
240                 final List<Notification.MessagingStyle.Message> messages = ms.getMessages();
241                 if (messages != null && messages.size() > 0) {
242                     text = messages.get(messages.size() - 1).getText();
243                 }
244             }
245 
246             if (TextUtils.isEmpty(text)) {
247                 text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
248             }
249         }
250         return text == null ? null : String.valueOf(text);
251     }
252 
loadIcon(StatusBarNotification sbn)253     private Drawable loadIcon(StatusBarNotification sbn) {
254         Drawable draw = sbn.getNotification().getSmallIcon().loadDrawableAsUser(
255                 sbn.getPackageContext(mContext), normalizeUserId(sbn));
256         if (draw == null) {
257             return null;
258         }
259         draw.mutate();
260         draw.setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
261         return draw;
262     }
263 
normalizeUserId(StatusBarNotification sbn)264     private int normalizeUserId(StatusBarNotification sbn) {
265         int userId = sbn.getUserId();
266         if (userId == USER_ALL) {
267             userId = mCurrentUser;
268         }
269         return userId;
270     }
271 }
272