• 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 package com.android.car.notification;
17 
18 import android.app.Notification;
19 import android.car.drivingstate.CarUxRestrictions;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.service.notification.StatusBarNotification;
24 import android.util.Log;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 
29 import androidx.recyclerview.widget.RecyclerView;
30 
31 import com.android.car.notification.template.BasicNotificationViewHolder;
32 import com.android.car.notification.template.CallNotificationViewHolder;
33 import com.android.car.notification.template.CarNotificationFooterViewHolder;
34 import com.android.car.notification.template.CarNotificationHeaderViewHolder;
35 import com.android.car.notification.template.EmergencyNotificationViewHolder;
36 import com.android.car.notification.template.GroupNotificationViewHolder;
37 import com.android.car.notification.template.GroupSummaryNotificationViewHolder;
38 import com.android.car.notification.template.InboxNotificationViewHolder;
39 import com.android.car.notification.template.MessageNotificationViewHolder;
40 import com.android.car.notification.template.ProgressNotificationViewHolder;
41 
42 import java.util.ArrayList;
43 import java.util.List;
44 
45 /**
46  * Notification data adapter that binds a notification to the corresponding view.
47  */
48 public class CarNotificationViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
49     private static final String TAG = "CarNotificationAdapter";
50 
51     // Delay in posting notifyDataSetChanged for the adapter in milliseconds.
52     private static final int NOTIFY_DATASET_CHANGED_DELAY = 100;
53 
54     private final Context mContext;
55     private final LayoutInflater mInflater;
56     private final int mMaxNumberGroupChildrenShown;
57     private final boolean mIsGroupNotificationAdapter;
58     private final Handler mHandler = new Handler();
59 
60     // book keeping expanded notification groups
61     private final List<String> mExpandedNotifications = new ArrayList<>();
62 
63     private List<NotificationGroup> mNotifications = new ArrayList<>();
64     private RecyclerView.RecycledViewPool mViewPool;
65     private CarUxRestrictions mCarUxRestrictions;
66     private NotificationClickHandlerFactory mClickHandlerFactory;
67     private NotificationDataManager mNotificationDataManager;
68 
69     private Runnable mNotifyDataSetChangedRunnable = this::notifyDataSetChanged;
70 
71     /**
72      * Constructor for a notification adapter.
73      * Can be used both by the root notification list view, or a grouped notification view.
74      *
75      * @param context the context for resources and inflating views
76      * @param isGroupNotificationAdapter true if this adapter is used by a grouped notification view
77      */
CarNotificationViewAdapter(Context context, boolean isGroupNotificationAdapter)78     public CarNotificationViewAdapter(Context context, boolean isGroupNotificationAdapter) {
79         mContext = context;
80         mInflater = LayoutInflater.from(context);
81         mMaxNumberGroupChildrenShown =
82                 mContext.getResources().getInteger(R.integer.max_group_children_number);
83         mIsGroupNotificationAdapter = isGroupNotificationAdapter;
84         setHasStableIds(true);
85         if (!mIsGroupNotificationAdapter) {
86             mViewPool = new RecyclerView.RecycledViewPool();
87         }
88     }
89 
90     @Override
onCreateViewHolder(ViewGroup parent, int viewType)91     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
92         RecyclerView.ViewHolder viewHolder;
93         View view;
94         switch (viewType) {
95             case NotificationViewType.HEADER:
96                 view = mInflater.inflate(R.layout.notification_header_template, parent, false);
97                 viewHolder = new CarNotificationHeaderViewHolder(view, mClickHandlerFactory);
98                 break;
99             case NotificationViewType.FOOTER:
100                 view = mInflater.inflate(R.layout.notification_footer_template, parent, false);
101                 viewHolder = new CarNotificationFooterViewHolder(view, mClickHandlerFactory);
102                 break;
103             case NotificationViewType.GROUP_EXPANDED:
104             case NotificationViewType.GROUP_COLLAPSED:
105                 view = mInflater.inflate(
106                         R.layout.group_notification_template, parent, false);
107                 viewHolder = new GroupNotificationViewHolder(view, mClickHandlerFactory);
108                 break;
109             case NotificationViewType.GROUP_SUMMARY:
110                 view = mInflater
111                         .inflate(R.layout.group_summary_notification_template, parent, false);
112                 viewHolder = new GroupSummaryNotificationViewHolder(view, mClickHandlerFactory);
113                 break;
114             case NotificationViewType.CALL:
115                 view = mInflater
116                         .inflate(R.layout.call_notification_template, parent, false);
117                 viewHolder = new CallNotificationViewHolder(view, mClickHandlerFactory);
118                 break;
119             case NotificationViewType.CAR_EMERGENCY:
120                 view = mInflater.inflate(
121                         R.layout.car_emergency_notification_template, parent, false);
122                 viewHolder = new EmergencyNotificationViewHolder(view, mClickHandlerFactory);
123                 break;
124             case NotificationViewType.CAR_WARNING:
125                 view = mInflater.inflate(
126                         R.layout.car_warning_notification_template, parent, false);
127                 // Using the basic view holder because they share the same view binding logic
128                 // OEMs should create view holders if needed
129                 viewHolder = new BasicNotificationViewHolder(view, mClickHandlerFactory);
130                 break;
131             case NotificationViewType.CAR_INFORMATION:
132                 view = mInflater.inflate(
133                         R.layout.car_information_notification_template, parent, false);
134                 // Using the basic view holder because they share the same view binding logic
135                 // OEMs should create view holders if needed
136                 viewHolder = new BasicNotificationViewHolder(view, mClickHandlerFactory);
137                 break;
138             case NotificationViewType.CAR_INFORMATION_IN_GROUP:
139                 view = mInflater.inflate(
140                         R.layout.car_information_notification_template_inner, parent, false);
141                 // Using the basic view holder because they share the same view binding logic
142                 // OEMs should create view holders if needed
143                 viewHolder = new BasicNotificationViewHolder(view, mClickHandlerFactory);
144                 break;
145             case NotificationViewType.MESSAGE_IN_GROUP:
146                 view = mInflater.inflate(
147                         R.layout.message_notification_template_inner, parent, false);
148                 viewHolder = new MessageNotificationViewHolder(view, mClickHandlerFactory);
149                 break;
150             case NotificationViewType.MESSAGE:
151                 view = mInflater.inflate(R.layout.message_notification_template, parent, false);
152                 viewHolder = new MessageNotificationViewHolder(view, mClickHandlerFactory);
153                 break;
154             case NotificationViewType.PROGRESS_IN_GROUP:
155                 view = mInflater.inflate(
156                         R.layout.progress_notification_template_inner, parent, false);
157                 viewHolder = new ProgressNotificationViewHolder(view, mClickHandlerFactory);
158                 break;
159             case NotificationViewType.PROGRESS:
160                 view = mInflater
161                         .inflate(R.layout.progress_notification_template, parent, false);
162                 viewHolder = new ProgressNotificationViewHolder(view, mClickHandlerFactory);
163                 break;
164             case NotificationViewType.INBOX_IN_GROUP:
165                 view = mInflater
166                         .inflate(R.layout.inbox_notification_template_inner, parent, false);
167                 viewHolder = new InboxNotificationViewHolder(view, mClickHandlerFactory);
168                 break;
169             case NotificationViewType.INBOX:
170                 view = mInflater
171                         .inflate(R.layout.inbox_notification_template, parent, false);
172                 viewHolder = new InboxNotificationViewHolder(view, mClickHandlerFactory);
173                 break;
174             case NotificationViewType.BASIC_IN_GROUP:
175                 view = mInflater
176                         .inflate(R.layout.basic_notification_template_inner, parent, false);
177                 viewHolder = new BasicNotificationViewHolder(view, mClickHandlerFactory);
178                 break;
179             case NotificationViewType.BASIC:
180             default:
181                 view = mInflater
182                         .inflate(R.layout.basic_notification_template, parent, false);
183                 viewHolder = new BasicNotificationViewHolder(view, mClickHandlerFactory);
184                 break;
185         }
186         return viewHolder;
187     }
188 
189     @Override
onBindViewHolder(RecyclerView.ViewHolder holder, int position)190     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
191         NotificationGroup notificationGroup = mNotifications.get(position);
192 
193         switch (holder.getItemViewType()) {
194             case NotificationViewType.HEADER:
195                 ((CarNotificationHeaderViewHolder) holder).bind(hasNotifications());
196                 break;
197             case NotificationViewType.FOOTER:
198                 ((CarNotificationFooterViewHolder) holder).bind(hasNotifications());
199                 break;
200             case NotificationViewType.GROUP_EXPANDED:
201                 ((GroupNotificationViewHolder) holder)
202                         .bind(notificationGroup, this, /* isExpanded= */ true);
203                 break;
204             case NotificationViewType.GROUP_COLLAPSED:
205                 ((GroupNotificationViewHolder) holder)
206                         .bind(notificationGroup, this, /* isExpanded= */ false);
207                 break;
208             case NotificationViewType.GROUP_SUMMARY:
209                 ((GroupSummaryNotificationViewHolder) holder).bind(notificationGroup);
210                 break;
211             case NotificationViewType.CALL: {
212                 StatusBarNotification notification = notificationGroup.getSingleNotification();
213                 ((CallNotificationViewHolder) holder)
214                         .bind(notification, /* isInGroup= */ false, /* isHeadsUp= */ false);
215                 break;
216             }
217             case NotificationViewType.CAR_EMERGENCY: {
218                 StatusBarNotification notification = notificationGroup.getSingleNotification();
219                 ((EmergencyNotificationViewHolder) holder)
220                         .bind(notification, /* isInGroup= */ false, /* isHeadsUp= */ false);
221                 break;
222             }
223             case NotificationViewType.MESSAGE: {
224                 StatusBarNotification notification = notificationGroup.getSingleNotification();
225                 if (shouldRestrictMessagePreview()) {
226                     ((MessageNotificationViewHolder) holder)
227                             .bindRestricted(notification, /* isInGroup= */ false, /* isHeadsUp= */
228                                     false);
229                 } else {
230                     ((MessageNotificationViewHolder) holder)
231                             .bind(notification, /* isInGroup= */ false, /* isHeadsUp= */ false);
232                 }
233                 break;
234             }
235             case NotificationViewType.MESSAGE_IN_GROUP: {
236                 StatusBarNotification notification = notificationGroup.getSingleNotification();
237                 if (shouldRestrictMessagePreview()) {
238                     ((MessageNotificationViewHolder) holder)
239                             .bindRestricted(notification, /* isInGroup= */ true, /* isHeadsUp= */
240                                     false);
241                 } else {
242                     ((MessageNotificationViewHolder) holder)
243                             .bind(notification, /* isInGroup= */ true, /* isHeadsUp= */ false);
244                 }
245                 break;
246             }
247             case NotificationViewType.PROGRESS: {
248                 StatusBarNotification notification = notificationGroup.getSingleNotification();
249                 ((ProgressNotificationViewHolder) holder)
250                         .bind(notification, /* isInGroup= */ false, false);
251                 break;
252             }
253             case NotificationViewType.PROGRESS_IN_GROUP: {
254                 StatusBarNotification notification = notificationGroup.getSingleNotification();
255                 ((ProgressNotificationViewHolder) holder).bind(notification, /* isInGroup= */
256                         true, false);
257                 break;
258             }
259             case NotificationViewType.INBOX: {
260                 StatusBarNotification notification = notificationGroup.getSingleNotification();
261                 ((InboxNotificationViewHolder) holder).bind(notification, /* isInGroup= */ false,
262                         /* isHeadsUp= */ false);
263                 break;
264             }
265             case NotificationViewType.INBOX_IN_GROUP: {
266                 StatusBarNotification notification = notificationGroup.getSingleNotification();
267                 ((InboxNotificationViewHolder) holder).bind(notification, /* isInGroup= */ true,
268                         /* isHeadsUp= */ false);
269                 break;
270             }
271             case NotificationViewType.CAR_INFORMATION_IN_GROUP:
272             case NotificationViewType.BASIC_IN_GROUP: {
273                 StatusBarNotification notification = notificationGroup.getSingleNotification();
274                 ((BasicNotificationViewHolder) holder).bind(notification, /* isInGroup= */ true,
275                         /* isHeadsUp= */ false);
276                 break;
277             }
278             case NotificationViewType.CAR_WARNING:
279             case NotificationViewType.CAR_INFORMATION:
280             case NotificationViewType.BASIC:
281             default: {
282                 StatusBarNotification notification = notificationGroup.getSingleNotification();
283                 ((BasicNotificationViewHolder) holder).bind(notification, /* isInGroup= */ false,
284                         /* isHeadsUp= */ false);
285                 break;
286             }
287         }
288     }
289 
290     @Override
getItemViewType(int position)291     public int getItemViewType(int position) {
292         NotificationGroup notificationGroup = mNotifications.get(position);
293 
294         if (notificationGroup.isHeader()) {
295             return NotificationViewType.HEADER;
296         }
297 
298         if (notificationGroup.isFooter()) {
299             return NotificationViewType.FOOTER;
300         }
301 
302         if (notificationGroup.isGroup()) {
303             if (mExpandedNotifications.contains(notificationGroup.getGroupKey())) {
304                 return NotificationViewType.GROUP_EXPANDED;
305             } else {
306                 return NotificationViewType.GROUP_COLLAPSED;
307             }
308         } else if (mExpandedNotifications.contains(notificationGroup.getGroupKey())) {
309             // when there are 2 notifications left in the expanded notification and one of them is
310             // removed at that time the item type changes from group to normal and hence the
311             // notification should be removed from expanded notifications.
312             setExpanded(notificationGroup.getGroupKey(), false);
313         }
314 
315         Notification notification =
316                 notificationGroup.getSingleNotification().getNotification();
317         Bundle extras = notification.extras;
318 
319         String category = notification.category;
320         if (category != null) {
321             switch (category) {
322                 case Notification.CATEGORY_CALL:
323                     return NotificationViewType.CALL;
324                 case Notification.CATEGORY_CAR_EMERGENCY:
325                     return NotificationViewType.CAR_EMERGENCY;
326                 case Notification.CATEGORY_CAR_WARNING:
327                     return NotificationViewType.CAR_WARNING;
328                 case Notification.CATEGORY_CAR_INFORMATION:
329                     return mIsGroupNotificationAdapter
330                             ? NotificationViewType.CAR_INFORMATION_IN_GROUP
331                             : NotificationViewType.CAR_INFORMATION;
332                 case Notification.CATEGORY_MESSAGE:
333                     return mIsGroupNotificationAdapter
334                             ? NotificationViewType.MESSAGE_IN_GROUP : NotificationViewType.MESSAGE;
335                 default:
336                     break;
337             }
338         }
339 
340         // progress
341         int progressMax = extras.getInt(Notification.EXTRA_PROGRESS_MAX);
342         boolean isIndeterminate = extras.getBoolean(
343                 Notification.EXTRA_PROGRESS_INDETERMINATE);
344         boolean hasValidProgress = isIndeterminate || progressMax != 0;
345         boolean isProgress = extras.containsKey(Notification.EXTRA_PROGRESS)
346                 && extras.containsKey(Notification.EXTRA_PROGRESS_MAX)
347                 && hasValidProgress
348                 && !notification.hasCompletedProgress();
349         if (isProgress) {
350             return mIsGroupNotificationAdapter
351                     ? NotificationViewType.PROGRESS_IN_GROUP : NotificationViewType.PROGRESS;
352         }
353 
354         // inbox
355         boolean isInbox = extras.containsKey(Notification.EXTRA_TITLE_BIG)
356                 && extras.containsKey(Notification.EXTRA_SUMMARY_TEXT);
357         if (isInbox) {
358             return mIsGroupNotificationAdapter
359                     ? NotificationViewType.INBOX_IN_GROUP : NotificationViewType.INBOX;
360         }
361 
362         // group summary
363         boolean isGroupSummary = notificationGroup.getChildTitles() != null;
364         if (isGroupSummary) {
365             return NotificationViewType.GROUP_SUMMARY;
366         }
367 
368         // the big text and big picture styles are fallen back to basic template in car
369         // i.e. setting the big text and big picture does not have an effect
370         boolean isBigText = extras.containsKey(Notification.EXTRA_BIG_TEXT);
371         if (isBigText) {
372             Log.i(TAG, "Big text style is not supported as a car notification");
373         }
374         boolean isBigPicture = extras.containsKey(Notification.EXTRA_PICTURE);
375         if (isBigPicture) {
376             Log.i(TAG, "Big picture style is not supported as a car notification");
377         }
378 
379         // basic, big text, big picture
380         return mIsGroupNotificationAdapter
381                 ? NotificationViewType.BASIC_IN_GROUP : NotificationViewType.BASIC;
382     }
383 
384     @Override
getItemCount()385     public int getItemCount() {
386         int itemCount = mNotifications.size();
387 
388         if (mIsGroupNotificationAdapter && itemCount > mMaxNumberGroupChildrenShown) {
389             return mMaxNumberGroupChildrenShown;
390         }
391 
392         if (!mIsGroupNotificationAdapter && mCarUxRestrictions != null
393                 && (mCarUxRestrictions.getActiveRestrictions()
394                 & CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT) != 0) {
395 
396             int maxItemCount = mCarUxRestrictions.getMaxCumulativeContentItems();
397 
398             return Math.min(itemCount, maxItemCount);
399         }
400         return itemCount;
401     }
402 
403     @Override
getItemId(int position)404     public long getItemId(int position) {
405         NotificationGroup notificationGroup = mNotifications.get(position);
406         if (notificationGroup.isHeader()) {
407             return 0;
408         }
409 
410         if (notificationGroup.isFooter()) {
411             return 1;
412         }
413 
414         return notificationGroup.isGroup()
415                 ? notificationGroup.getGroupKey().hashCode()
416                 : notificationGroup.getSingleNotification().getKey().hashCode();
417     }
418 
419     /**
420      * Set the expansion state of a group notification given its group key.
421      *
422      * @param groupKey the unique identifier of a {@link NotificationGroup}
423      * @param isExpanded whether the group notification should be expanded.
424      */
setExpanded(String groupKey, boolean isExpanded)425     public void setExpanded(String groupKey, boolean isExpanded) {
426         if (isExpanded(groupKey) == isExpanded) {
427             return;
428         }
429 
430         if (isExpanded) {
431             mExpandedNotifications.add(groupKey);
432         } else {
433             mExpandedNotifications.remove(groupKey);
434         }
435     }
436 
437     /**
438      * Collapses all expanded groups.
439      */
collapseAllGroups()440     public void collapseAllGroups() {
441         if (!mExpandedNotifications.isEmpty()) {
442             mExpandedNotifications.clear();
443         }
444     }
445 
446     /**
447      * Returns whether the notification is expanded given its group key.
448      */
isExpanded(String groupKey)449     boolean isExpanded(String groupKey) {
450         return mExpandedNotifications.contains(groupKey);
451     }
452 
453     /**
454      * Gets the current {@link CarUxRestrictions}.
455      */
getCarUxRestrictions()456     public CarUxRestrictions getCarUxRestrictions() {
457         return mCarUxRestrictions;
458     }
459 
460     /**
461      * Clear all notifications.
462      */
clearAllNotifications()463     public void clearAllNotifications() {
464         mClickHandlerFactory.clearAllNotifications();
465     }
466 
467     /**
468      * Updates notifications and update views.
469      *
470      * @param setRecyclerViewListHeaderAndFooter sets the header and footer on the entire list of
471      * items within the recycler view. This is NOT the header/footer for the grouped notifications.
472      */
setNotifications(List<NotificationGroup> notifications, boolean setRecyclerViewListHeaderAndFooter)473     public void setNotifications(List<NotificationGroup> notifications,
474             boolean setRecyclerViewListHeaderAndFooter) {
475 
476         List<NotificationGroup> notificationGroupList = new ArrayList<>(notifications);
477 
478         if (setRecyclerViewListHeaderAndFooter) {
479             // add header as the first item of the list.
480             notificationGroupList.add(0, createNotificationHeader());
481             // add footer as the last item of the list.
482             notificationGroupList.add(createNotificationFooter());
483         }
484 
485         mNotifications = notificationGroupList;
486 
487         mHandler.removeCallbacks(mNotifyDataSetChangedRunnable);
488         mHandler.postDelayed(mNotifyDataSetChangedRunnable, NOTIFY_DATASET_CHANGED_DELAY);
489     }
490 
491     /**
492      * Notification list has header and footer by default. Therefore the min number of items in the
493      * adapter will always be two. If there are any notifications present the size will be more than
494      * two.
495      */
hasNotifications()496     private boolean hasNotifications() {
497         return getItemCount() > 2;
498     }
499 
createNotificationHeader()500     private NotificationGroup createNotificationHeader() {
501         NotificationGroup notificationGroupWithHeader = new NotificationGroup();
502         notificationGroupWithHeader.setHeader(true);
503         notificationGroupWithHeader.setGroupKey("notification_header");
504         return notificationGroupWithHeader;
505     }
506 
createNotificationFooter()507     private NotificationGroup createNotificationFooter() {
508         NotificationGroup notificationGroupWithFooter = new NotificationGroup();
509         notificationGroupWithFooter.setFooter(true);
510         notificationGroupWithFooter.setGroupKey("notification_footer");
511         return notificationGroupWithFooter;
512     }
513 
514     /**
515      * Sets the current {@link CarUxRestrictions}.
516      */
setCarUxRestrictions(CarUxRestrictions carUxRestrictions)517     public void setCarUxRestrictions(CarUxRestrictions carUxRestrictions) {
518         Log.d(TAG, "setCarUxRestrictions");
519         mCarUxRestrictions = carUxRestrictions;
520         notifyDataSetChanged();
521     }
522 
523     /**
524      * Helper method that determines whether a notification is a messaging notification and
525      * should have restricted content (no message preview).
526      */
shouldRestrictMessagePreview()527     private boolean shouldRestrictMessagePreview() {
528         return mCarUxRestrictions != null && (mCarUxRestrictions.getActiveRestrictions()
529                 & CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE) != 0;
530     }
531 
532     /**
533      * Get root recycler view's view pool so that the child recycler view can share the same
534      * view pool with the parent.
535      */
getViewPool()536     public RecyclerView.RecycledViewPool getViewPool() {
537         if (mIsGroupNotificationAdapter) {
538             // currently only support one level of expansion.
539             throw new IllegalStateException("CarNotificationViewAdapter is a child adapter; "
540                     + "its view pool should not be reused.");
541         }
542         return mViewPool;
543     }
544 
545     /**
546      * Sets the NotificationClickHandlerFactory that allows for a hook to run a block off code
547      * when  the notification is clicked. This is useful to dismiss a screen after
548      * a notification list clicked.
549      */
setClickHandlerFactory(NotificationClickHandlerFactory clickHandlerFactory)550     public void setClickHandlerFactory(NotificationClickHandlerFactory clickHandlerFactory) {
551         mClickHandlerFactory = clickHandlerFactory;
552     }
553 
554     /**
555      * Sets NotificationDataManager that handles additional states for notifications such as "seen",
556      * and muting a messaging type notification.
557      *
558      * @param notificationDataManager An instance of NotificationDataManager.
559      */
setNotificationDataManager(NotificationDataManager notificationDataManager)560     public void setNotificationDataManager(NotificationDataManager notificationDataManager) {
561         mNotificationDataManager = notificationDataManager;
562     }
563 
564     /**
565      * Set the notification group as seen.
566      *
567      * @param position Adapter position of the notification group.
568      */
setNotificationAsSeen(int position)569     public void setNotificationAsSeen(int position) {
570         NotificationGroup notificationGroup = null;
571 
572         try {
573             notificationGroup = mNotifications.get(position);
574         } catch (IndexOutOfBoundsException e) {
575             Log.e(TAG, "trying to mark none existent notification as seen.");
576             return;
577         }
578 
579         if (mNotificationDataManager != null) {
580             for (StatusBarNotification notification : notificationGroup.getChildNotifications()) {
581                 mNotificationDataManager.setNotificationAsSeen(notification);
582             }
583         }
584     }
585 }
586