| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.car.notification; |
| |
| import android.annotation.NonNull; |
| import android.app.Notification; |
| import android.car.drivingstate.CarUxRestrictions; |
| import android.content.Context; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import androidx.annotation.Nullable; |
| import androidx.recyclerview.widget.DiffUtil; |
| import androidx.recyclerview.widget.LinearLayoutManager; |
| import androidx.recyclerview.widget.RecyclerView; |
| |
| import com.android.car.notification.template.CarNotificationBaseViewHolder; |
| import com.android.car.notification.template.CarNotificationFooterViewHolder; |
| import com.android.car.notification.template.CarNotificationHeaderViewHolder; |
| import com.android.car.notification.template.CarNotificationOlderViewHolder; |
| import com.android.car.notification.template.CarNotificationRecentsViewHolder; |
| import com.android.car.notification.template.GroupNotificationViewHolder; |
| import com.android.car.notification.template.GroupSummaryNotificationViewHolder; |
| import com.android.car.notification.template.MessageNotificationViewHolder; |
| import com.android.car.ui.recyclerview.ContentLimitingAdapter; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Notification data adapter that binds a notification to the corresponding view. |
| */ |
| public class CarNotificationViewAdapter extends ContentLimitingAdapter<RecyclerView.ViewHolder> |
| implements PreprocessingManager.CallStateListener { |
| private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG; |
| private static final String TAG = "CarNotificationAdapter"; |
| private static final int ID_HEADER = 0; |
| private static final int ID_RECENT_HEADER = 1; |
| private static final int ID_OLDER_HEADER = 2; |
| private static final int ID_FOOTER = 3; |
| |
| private final Context mContext; |
| private final LayoutInflater mInflater; |
| private final int mMaxNumberGroupChildrenShown; |
| private final boolean mIsGroupNotificationAdapter; |
| private final boolean mShowRecentsAndOlderHeaders; |
| |
| // book keeping expanded notification groups |
| private final List<ExpandedNotification> mExpandedNotifications = new ArrayList<>(); |
| private final CarNotificationItemController mNotificationItemController; |
| |
| private List<NotificationGroup> mNotifications = new ArrayList<>(); |
| private Map<String, Integer> mGroupKeyToCountMap = new HashMap<>(); |
| private LinearLayoutManager mLayoutManager; |
| private RecyclerView.RecycledViewPool mViewPool; |
| private CarUxRestrictions mCarUxRestrictions; |
| private NotificationClickHandlerFactory mClickHandlerFactory; |
| private NotificationDataManager mNotificationDataManager; |
| private boolean mIsInCall; |
| private boolean mHasHeaderAndFooter; |
| private boolean mHasUnseenNotifications; |
| private boolean mHasSeenNotifications; |
| private int mMaxItems = ContentLimitingAdapter.UNLIMITED; |
| |
| /** |
| * Constructor for a notification adapter. |
| * Can be used both by the root notification list view, or a grouped notification view. |
| * |
| * @param context the context for resources and inflating views |
| * @param isGroupNotificationAdapter true if this adapter is used by a grouped notification view |
| * @param notificationItemController shared logic to control notification items. |
| */ |
| public CarNotificationViewAdapter(Context context, boolean isGroupNotificationAdapter, |
| @Nullable CarNotificationItemController notificationItemController) { |
| mContext = context; |
| mInflater = LayoutInflater.from(context); |
| mMaxNumberGroupChildrenShown = |
| mContext.getResources().getInteger(R.integer.max_group_children_number); |
| mShowRecentsAndOlderHeaders = |
| mContext.getResources().getBoolean(R.bool.config_showRecentAndOldHeaders); |
| mIsGroupNotificationAdapter = isGroupNotificationAdapter; |
| mNotificationItemController = notificationItemController; |
| mNotificationDataManager = NotificationDataManager.getInstance(); |
| setHasStableIds(true); |
| if (!mIsGroupNotificationAdapter) { |
| mViewPool = new RecyclerView.RecycledViewPool(); |
| } |
| |
| PreprocessingManager.getInstance(context).addCallStateListener(this::onCallStateChanged); |
| } |
| |
| @Override |
| public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { |
| super.onAttachedToRecyclerView(recyclerView); |
| mLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); |
| } |
| |
| @Override |
| public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { |
| super.onDetachedFromRecyclerView(recyclerView); |
| mLayoutManager = null; |
| } |
| |
| @Override |
| public RecyclerView.ViewHolder onCreateViewHolderImpl(@NonNull ViewGroup parent, int viewType) { |
| RecyclerView.ViewHolder viewHolder; |
| View view; |
| switch (viewType) { |
| case NotificationViewType.HEADER: |
| view = mInflater.inflate(R.layout.notification_header_template, parent, false); |
| viewHolder = new CarNotificationHeaderViewHolder(mContext, view, |
| mNotificationItemController, mClickHandlerFactory); |
| break; |
| case NotificationViewType.FOOTER: |
| view = mInflater.inflate(R.layout.notification_footer_template, parent, false); |
| viewHolder = new CarNotificationFooterViewHolder(mContext, view, |
| mNotificationItemController, mClickHandlerFactory); |
| break; |
| case NotificationViewType.RECENTS: |
| view = mInflater.inflate(R.layout.notification_recents_template, parent, false); |
| viewHolder = new CarNotificationRecentsViewHolder(mContext, view, |
| mNotificationItemController); |
| break; |
| case NotificationViewType.OLDER: |
| view = mInflater.inflate(R.layout.notification_older_template, parent, false); |
| viewHolder = new CarNotificationOlderViewHolder(mContext, view, |
| mNotificationItemController); |
| break; |
| default: |
| CarNotificationTypeItem carNotificationTypeItem = CarNotificationTypeItem.of( |
| viewType); |
| view = mInflater.inflate( |
| carNotificationTypeItem.getNotificationCenterTemplate(), parent, false); |
| viewHolder = carNotificationTypeItem.getViewHolder(view, mClickHandlerFactory); |
| } |
| |
| return viewHolder; |
| } |
| |
| @Override |
| public void onBindViewHolderImpl(RecyclerView.ViewHolder holder, int position) { |
| NotificationGroup notificationGroup = mNotifications.get(position); |
| |
| int viewType = holder.getItemViewType(); |
| switch (viewType) { |
| case NotificationViewType.HEADER: |
| ((CarNotificationHeaderViewHolder) holder).bind(hasNotifications()); |
| return; |
| case NotificationViewType.FOOTER: |
| ((CarNotificationFooterViewHolder) holder).bind(hasNotifications()); |
| return; |
| case NotificationViewType.RECENTS: |
| ((CarNotificationRecentsViewHolder) holder).bind(mHasUnseenNotifications); |
| return; |
| case NotificationViewType.OLDER: |
| ((CarNotificationOlderViewHolder) holder) |
| .bind(mHasSeenNotifications, !mHasUnseenNotifications); |
| return; |
| case NotificationViewType.GROUP: |
| ((GroupNotificationViewHolder) holder) |
| .bind(notificationGroup, this, /* isExpanded= */ |
| isExpanded(notificationGroup.getGroupKey(), |
| notificationGroup.isSeen())); |
| return; |
| case NotificationViewType.GROUP_SUMMARY: |
| ((CarNotificationBaseViewHolder) holder).setHideDismissButton(true); |
| ((GroupSummaryNotificationViewHolder) holder).bind(notificationGroup); |
| return; |
| } |
| |
| CarNotificationTypeItem carNotificationTypeItem = CarNotificationTypeItem.of(viewType); |
| AlertEntry alertEntry = notificationGroup.getSingleNotification(); |
| |
| if (shouldRestrictMessagePreview() && (viewType == NotificationViewType.MESSAGE |
| || viewType == NotificationViewType.MESSAGE_IN_GROUP)) { |
| ((MessageNotificationViewHolder) holder) |
| .bindRestricted(alertEntry, /* isInGroup= */ false, /* isHeadsUp= */false); |
| } else { |
| carNotificationTypeItem.bind(alertEntry, false, (CarNotificationBaseViewHolder) holder); |
| } |
| } |
| |
| @Override |
| public int getItemViewTypeImpl(int position) { |
| NotificationGroup notificationGroup = mNotifications.get(position); |
| if (notificationGroup.isHeader()) { |
| return NotificationViewType.HEADER; |
| } |
| |
| if (notificationGroup.isFooter()) { |
| return NotificationViewType.FOOTER; |
| } |
| |
| if (notificationGroup.isRecentsHeader()) { |
| return NotificationViewType.RECENTS; |
| } |
| |
| if (notificationGroup.isOlderHeader()) { |
| return NotificationViewType.OLDER; |
| } |
| |
| ExpandedNotification expandedNotification = |
| new ExpandedNotification(notificationGroup.getGroupKey(), |
| notificationGroup.isSeen()); |
| if (notificationGroup.isGroup()) { |
| return NotificationViewType.GROUP; |
| } else if (mExpandedNotifications.contains(expandedNotification)) { |
| // when there are 2 notifications left in the expanded notification and one of them is |
| // removed at that time the item type changes from group to normal and hence the |
| // notification should be removed from expanded notifications. |
| setExpanded(expandedNotification.getKey(), expandedNotification.isExpanded(), |
| /* isExpanded= */ false); |
| } |
| |
| Notification notification = |
| notificationGroup.getSingleNotification().getNotification(); |
| Bundle extras = notification.extras; |
| |
| String category = notification.category; |
| if (category != null) { |
| switch (category) { |
| case Notification.CATEGORY_CALL: |
| return NotificationViewType.CALL; |
| case Notification.CATEGORY_CAR_EMERGENCY: |
| return NotificationViewType.CAR_EMERGENCY; |
| case Notification.CATEGORY_CAR_WARNING: |
| return NotificationViewType.CAR_WARNING; |
| case Notification.CATEGORY_CAR_INFORMATION: |
| return mIsGroupNotificationAdapter |
| ? NotificationViewType.CAR_INFORMATION_IN_GROUP |
| : NotificationViewType.CAR_INFORMATION; |
| case Notification.CATEGORY_MESSAGE: |
| return mIsGroupNotificationAdapter |
| ? NotificationViewType.MESSAGE_IN_GROUP : NotificationViewType.MESSAGE; |
| default: |
| break; |
| } |
| } |
| |
| // progress |
| int progressMax = extras.getInt(Notification.EXTRA_PROGRESS_MAX); |
| boolean isIndeterminate = extras.getBoolean( |
| Notification.EXTRA_PROGRESS_INDETERMINATE); |
| boolean hasValidProgress = isIndeterminate || progressMax != 0; |
| boolean isProgress = extras.containsKey(Notification.EXTRA_PROGRESS) |
| && extras.containsKey(Notification.EXTRA_PROGRESS_MAX) |
| && hasValidProgress |
| && !notification.hasCompletedProgress(); |
| if (isProgress) { |
| return mIsGroupNotificationAdapter |
| ? NotificationViewType.PROGRESS_IN_GROUP : NotificationViewType.PROGRESS; |
| } |
| |
| // inbox |
| boolean isInbox = extras.containsKey(Notification.EXTRA_TITLE_BIG) |
| && extras.containsKey(Notification.EXTRA_SUMMARY_TEXT); |
| if (isInbox) { |
| return mIsGroupNotificationAdapter |
| ? NotificationViewType.INBOX_IN_GROUP : NotificationViewType.INBOX; |
| } |
| |
| // group summary |
| boolean isGroupSummary = notificationGroup.getChildTitles() != null; |
| if (isGroupSummary) { |
| return NotificationViewType.GROUP_SUMMARY; |
| } |
| |
| // the big text and big picture styles are fallen back to basic template in car |
| // i.e. setting the big text and big picture does not have an effect |
| boolean isBigText = extras.containsKey(Notification.EXTRA_BIG_TEXT); |
| if (isBigText) { |
| Log.i(TAG, "Big text style is not supported as a car notification"); |
| } |
| boolean isBigPicture = extras.containsKey(Notification.EXTRA_PICTURE); |
| if (isBigPicture) { |
| Log.i(TAG, "Big picture style is not supported as a car notification"); |
| } |
| |
| // basic, big text, big picture |
| return mIsGroupNotificationAdapter |
| ? NotificationViewType.BASIC_IN_GROUP : NotificationViewType.BASIC; |
| } |
| |
| @Override |
| public int getUnrestrictedItemCount() { |
| return mNotifications.size(); |
| } |
| |
| @Override |
| public void setMaxItems(int maxItems) { |
| if (maxItems == ContentLimitingAdapter.UNLIMITED |
| || (!mHasHeaderAndFooter && !mHasUnseenNotifications && !mHasSeenNotifications)) { |
| mMaxItems = maxItems; |
| } else { |
| // Adding to max limit of notifications for each header so that they do not count |
| // towards limit. |
| // Footer is not accounted for since it as the end of the list and it doesn't affect the |
| // limit of notifications above it. |
| mMaxItems = maxItems; |
| if (mHasHeaderAndFooter) { |
| mMaxItems++; |
| } |
| if (mHasSeenNotifications) { |
| mMaxItems++; |
| } |
| if (mHasUnseenNotifications) { |
| mMaxItems++; |
| } |
| } |
| super.setMaxItems(mMaxItems); |
| } |
| |
| @Override |
| protected int getScrollToPositionWhenRestricted() { |
| if (mLayoutManager == null) { |
| return -1; |
| } |
| int firstItem = mLayoutManager.findFirstVisibleItemPosition(); |
| if (firstItem >= getItemCount() - 1) { |
| return getItemCount() - 1; |
| } |
| return -1; |
| } |
| |
| @Override |
| public long getItemId(int position) { |
| NotificationGroup notificationGroup = mNotifications.get(position); |
| if (notificationGroup.isHeader()) { |
| return ID_HEADER; |
| } |
| if (mShowRecentsAndOlderHeaders && !mIsGroupNotificationAdapter) { |
| if (notificationGroup.isRecentsHeader()) { |
| return ID_RECENT_HEADER; |
| } |
| if (notificationGroup.isOlderHeader()) { |
| return ID_OLDER_HEADER; |
| } |
| if (notificationGroup.isFooter()) { |
| return ID_FOOTER; |
| } |
| } |
| if (notificationGroup.isFooter()) { |
| // We can use recent header's ID when it isn't being used. |
| return ID_RECENT_HEADER; |
| } |
| |
| String key = notificationGroup.isGroup() |
| ? notificationGroup.getGroupKey() |
| : notificationGroup.getSingleNotification().getKey(); |
| |
| if (mShowRecentsAndOlderHeaders) { |
| key += notificationGroup.isSeen(); |
| } |
| |
| return key.hashCode(); |
| } |
| |
| /** |
| * Set the expansion state of a group notification given its group key. |
| * |
| * @param groupKey the unique identifier of a {@link NotificationGroup} |
| * @param isSeen whether the {@link NotificationGroup} has been seen by the user |
| * @param isExpanded whether the group notification should be expanded. |
| */ |
| public void setExpanded(String groupKey, boolean isSeen, boolean isExpanded) { |
| if (isExpanded(groupKey, isSeen) == isExpanded) { |
| return; |
| } |
| |
| ExpandedNotification expandedNotification = new ExpandedNotification(groupKey, isSeen); |
| if (isExpanded) { |
| mExpandedNotifications.add(expandedNotification); |
| } else { |
| mExpandedNotifications.remove(expandedNotification); |
| } |
| if (DEBUG) { |
| Log.d(TAG, "Expanded notification statuses: " + mExpandedNotifications); |
| } |
| } |
| |
| /** |
| * Collapses all expanded groups. |
| */ |
| public void collapseAllGroups() { |
| if (!mExpandedNotifications.isEmpty()) { |
| mExpandedNotifications.clear(); |
| } |
| } |
| |
| /** |
| * Returns whether the notification is expanded given its group key and it's seen status. |
| * |
| * @param groupKey the unique identifier of a {@link NotificationGroup} |
| * @param isSeen whether the {@link NotificationGroup} has been seen by the user |
| */ |
| boolean isExpanded(String groupKey, boolean isSeen) { |
| ExpandedNotification expandedNotification = new ExpandedNotification(groupKey, isSeen); |
| return mExpandedNotifications.contains(expandedNotification); |
| } |
| |
| /** |
| * Gets the current {@link CarUxRestrictions}. |
| */ |
| public CarUxRestrictions getCarUxRestrictions() { |
| return mCarUxRestrictions; |
| } |
| |
| /** |
| * Updates notifications and update views. |
| * |
| * @param setRecyclerViewListHeadersAndFooters sets the header and footer on the entire list of |
| * items within the recycler view. This is NOT the header/footer for the grouped notifications. |
| */ |
| public void setNotifications(List<NotificationGroup> notifications, |
| boolean setRecyclerViewListHeadersAndFooters) { |
| mGroupKeyToCountMap.clear(); |
| notifications.forEach(notificationGroup -> { |
| if ((mGroupKeyToCountMap.computeIfPresent(notificationGroup.getGroupKey(), |
| (key, currentValue) -> currentValue + 1)) == null) { |
| mGroupKeyToCountMap.put(notificationGroup.getGroupKey(), 1); |
| } |
| }); |
| |
| if (mShowRecentsAndOlderHeaders && !mIsGroupNotificationAdapter) { |
| List<NotificationGroup> seenNotifications = new ArrayList<>(); |
| List<NotificationGroup> unseenNotifications = new ArrayList<>(); |
| notifications.forEach(notificationGroup -> { |
| if (notificationGroup.isSeen()) { |
| seenNotifications.add(new NotificationGroup(notificationGroup)); |
| } else { |
| unseenNotifications.add(new NotificationGroup(notificationGroup)); |
| } |
| }); |
| setSeenAndUnseenNotifications(unseenNotifications, seenNotifications, |
| setRecyclerViewListHeadersAndFooters); |
| return; |
| } |
| |
| List<NotificationGroup> notificationGroupList = notifications.stream() |
| .map(notificationGroup -> new NotificationGroup(notificationGroup)) |
| .collect(Collectors.toList()); |
| |
| if (setRecyclerViewListHeadersAndFooters) { |
| // add header as the first item of the list. |
| notificationGroupList.add(0, createNotificationHeader()); |
| // add footer as the last item of the list. |
| notificationGroupList.add(createNotificationFooter()); |
| mHasHeaderAndFooter = true; |
| } else { |
| mHasHeaderAndFooter = false; |
| } |
| |
| CarNotificationDiff notificationDiff = |
| new CarNotificationDiff(mContext, mNotifications, notificationGroupList, mMaxItems); |
| notificationDiff.setShowRecentsAndOlderHeaders(false); |
| DiffUtil.DiffResult diffResult = |
| DiffUtil.calculateDiff(notificationDiff, /* detectMoves= */ false); |
| mNotifications = notificationGroupList; |
| if (DEBUG) { |
| Log.d(TAG, "Updated adapter view holders: " + mNotifications); |
| } |
| updateUnderlyingDataChanged(getUnrestrictedItemCount(), /* newAnchorIndex= */ 0); |
| diffResult.dispatchUpdatesTo(this); |
| } |
| |
| private void setSeenAndUnseenNotifications(List<NotificationGroup> unseenNotifications, |
| List<NotificationGroup> seenNotifications, |
| boolean setRecyclerViewListHeadersAndFooters) { |
| if (DEBUG) { |
| Log.d(TAG, "Seen notifications: " + seenNotifications); |
| Log.d(TAG, "Unseen notifications: " + unseenNotifications); |
| } |
| |
| List<NotificationGroup> notificationGroupList; |
| if (unseenNotifications.isEmpty()) { |
| mHasUnseenNotifications = false; |
| |
| notificationGroupList = new ArrayList<>(); |
| } else { |
| mHasUnseenNotifications = true; |
| |
| notificationGroupList = new ArrayList<>(unseenNotifications); |
| if (setRecyclerViewListHeadersAndFooters) { |
| // Add recents header as the first item of the list. |
| notificationGroupList.add(/* index= */ 0, createRecentsHeader()); |
| } |
| } |
| |
| if (seenNotifications.isEmpty()) { |
| mHasSeenNotifications = false; |
| } else { |
| mHasSeenNotifications = true; |
| |
| if (setRecyclerViewListHeadersAndFooters) { |
| // Append older header to the list. |
| notificationGroupList.add(createOlderHeader()); |
| } |
| notificationGroupList.addAll(seenNotifications); |
| } |
| |
| if (setRecyclerViewListHeadersAndFooters) { |
| // Add header as the first item of the list. |
| notificationGroupList.add(0, createNotificationHeader()); |
| // Add footer as the last item of the list. |
| notificationGroupList.add(createNotificationFooter()); |
| mHasHeaderAndFooter = true; |
| } else { |
| mHasHeaderAndFooter = false; |
| } |
| |
| CarNotificationDiff notificationDiff = |
| new CarNotificationDiff(mContext, mNotifications, notificationGroupList, mMaxItems); |
| notificationDiff.setShowRecentsAndOlderHeaders(true); |
| DiffUtil.DiffResult diffResult = |
| DiffUtil.calculateDiff(notificationDiff, /* detectMoves= */ false); |
| mNotifications = notificationGroupList; |
| if (DEBUG) { |
| Log.d(TAG, "Updated adapter view holders: " + mNotifications); |
| } |
| updateUnderlyingDataChanged(getUnrestrictedItemCount(), /* newAnchorIndex= */ 0); |
| diffResult.dispatchUpdatesTo(this); |
| } |
| |
| /** |
| * Returns {@code true} if notifications are present in adapter. |
| * |
| * Group notification list doesn't have any headers, hence, if there are any notifications |
| * present the size will be more than zero. |
| * |
| * Non-group notification list has header and footer by default. Therefore the min number of |
| * items in the adapter will always be two. If there are any notifications present the size will |
| * be more than two. |
| * |
| * When recent and older headers are enabled, each header will be accounted for when checking |
| * for the presence of notifications. |
| */ |
| public boolean hasNotifications() { |
| int numberOfHeaders; |
| if (mIsGroupNotificationAdapter) { |
| numberOfHeaders = 0; |
| } else { |
| numberOfHeaders = 2; |
| |
| if (mHasSeenNotifications) { |
| numberOfHeaders++; |
| } |
| |
| if (mHasUnseenNotifications) { |
| numberOfHeaders++; |
| } |
| } |
| |
| return getItemCount() > numberOfHeaders; |
| } |
| |
| private NotificationGroup createNotificationHeader() { |
| NotificationGroup notificationGroupWithHeader = new NotificationGroup(); |
| notificationGroupWithHeader.setHeader(true); |
| notificationGroupWithHeader.setGroupKey("notification_header"); |
| return notificationGroupWithHeader; |
| } |
| |
| private NotificationGroup createNotificationFooter() { |
| NotificationGroup notificationGroupWithFooter = new NotificationGroup(); |
| notificationGroupWithFooter.setFooter(true); |
| notificationGroupWithFooter.setGroupKey("notification_footer"); |
| return notificationGroupWithFooter; |
| } |
| |
| private NotificationGroup createRecentsHeader() { |
| NotificationGroup notificationGroupWithRecents = new NotificationGroup(); |
| notificationGroupWithRecents.setRecentsHeader(true); |
| notificationGroupWithRecents.setGroupKey("notification_recents"); |
| notificationGroupWithRecents.setSeen(false); |
| return notificationGroupWithRecents; |
| } |
| |
| private NotificationGroup createOlderHeader() { |
| NotificationGroup notificationGroupWithOlder = new NotificationGroup(); |
| notificationGroupWithOlder.setOlderHeader(true); |
| notificationGroupWithOlder.setGroupKey("notification_older"); |
| notificationGroupWithOlder.setSeen(true); |
| return notificationGroupWithOlder; |
| } |
| |
| /** Implementation of {@link PreprocessingManager.CallStateListener} **/ |
| @Override |
| public void onCallStateChanged(boolean isInCall) { |
| if (isInCall != mIsInCall) { |
| mIsInCall = isInCall; |
| notifyDataSetChanged(); |
| } |
| } |
| |
| /** |
| * Sets the current {@link CarUxRestrictions}. |
| */ |
| public void setCarUxRestrictions(CarUxRestrictions carUxRestrictions) { |
| Log.d(TAG, "setCarUxRestrictions"); |
| mCarUxRestrictions = carUxRestrictions; |
| notifyDataSetChanged(); |
| } |
| |
| /** |
| * Helper method that determines whether a notification is a messaging notification and |
| * should have restricted content (no message preview). |
| */ |
| private boolean shouldRestrictMessagePreview() { |
| return mCarUxRestrictions != null && (mCarUxRestrictions.getActiveRestrictions() |
| & CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE) != 0; |
| } |
| |
| /** |
| * Get root recycler view's view pool so that the child recycler view can share the same |
| * view pool with the parent. |
| */ |
| public RecyclerView.RecycledViewPool getViewPool() { |
| if (mIsGroupNotificationAdapter) { |
| // currently only support one level of expansion. |
| throw new IllegalStateException("CarNotificationViewAdapter is a child adapter; " |
| + "its view pool should not be reused."); |
| } |
| return mViewPool; |
| } |
| |
| /** |
| * Returns {@code true} if there are multiple groups with the same {@code groupKey}. |
| */ |
| public boolean shouldRemoveGroupSummary(String groupKey) { |
| return mGroupKeyToCountMap.getOrDefault(groupKey, /* defaultValue= */ 0) <= 1; |
| } |
| |
| /** |
| * Sets the NotificationClickHandlerFactory that allows for a hook to run a block off code |
| * when the notification is clicked. This is useful to dismiss a screen after |
| * a notification list clicked. |
| */ |
| public void setClickHandlerFactory(NotificationClickHandlerFactory clickHandlerFactory) { |
| mClickHandlerFactory = clickHandlerFactory; |
| } |
| |
| /** |
| * Set notification groups as seen. |
| * |
| * @param start Initial adapter position of the notification groups. |
| * @param end Final adapter position of the notification groups. |
| */ |
| void setVisibleNotificationsAsSeen(int start, int end) { |
| if (mNotificationDataManager == null || mIsGroupNotificationAdapter) { |
| return; |
| } |
| |
| start = Math.max(start, 0); |
| end = Math.min(end, mNotifications.size() - 1); |
| |
| List<AlertEntry> notifications = new ArrayList(); |
| for (int i = start; i <= end; i++) { |
| NotificationGroup group = mNotifications.get(i); |
| AlertEntry groupSummary = group.getGroupSummaryNotification(); |
| if (groupSummary != null) { |
| notifications.add(groupSummary); |
| } |
| |
| notifications.addAll(group.getChildNotifications()); |
| } |
| |
| mNotificationDataManager.setVisibleNotificationsAsSeen(notifications); |
| } |
| |
| @Override |
| public int getConfigurationId() { |
| return R.id.notification_list_uxr_config; |
| } |
| |
| private static class ExpandedNotification { |
| private String mKey; |
| private boolean mIsExpanded; |
| |
| ExpandedNotification(String key, boolean isExpanded) { |
| mKey = key; |
| mIsExpanded = isExpanded; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (!(obj instanceof ExpandedNotification)) { |
| return false; |
| } |
| ExpandedNotification other = (ExpandedNotification) obj; |
| |
| return mKey.equals(other.getKey()) && mIsExpanded == other.isExpanded(); |
| } |
| |
| public String getKey() { |
| return mKey; |
| } |
| |
| public boolean isExpanded() { |
| return mIsExpanded; |
| } |
| } |
| } |