Introduced a group manager to manage group notifications
Bug: 15869874 Change-Id: I1bbcd9e5a2b8dae62bd8d93908dacc5d8fc08887
This commit is contained in:
@@ -97,6 +97,7 @@ import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.statusbar.NotificationData.Entry;
|
||||
import com.android.systemui.statusbar.phone.NavigationBarView;
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
|
||||
import com.android.systemui.statusbar.policy.PreviewInflater;
|
||||
@@ -156,6 +157,8 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
protected NotificationData mNotificationData;
|
||||
protected NotificationStackScrollLayout mStackScroller;
|
||||
|
||||
protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
|
||||
|
||||
// for heads up notifications
|
||||
protected HeadsUpNotificationView mHeadsUpNotificationView;
|
||||
protected int mHeadsUpNotificationDecay;
|
||||
@@ -438,8 +441,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
// Ignore children of notifications that have a summary, since we're not
|
||||
// going to show them anyway. This is true also when the summary is canceled,
|
||||
// because children are automatically canceled by NoMan in that case.
|
||||
if (n.isGroupChild() &&
|
||||
mNotificationData.isGroupWithSummary(sbn.getGroupKey())) {
|
||||
if (mGroupManager.isChildInGroupWithSummary(sbn)) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
|
||||
}
|
||||
@@ -707,6 +709,11 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationGroupManager getGroupManager() {
|
||||
return mGroupManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
|
||||
* @param action A dismiss action that is called if it's safe to start the activity.
|
||||
@@ -2016,6 +2023,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
&& publicUnchanged) {
|
||||
if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
|
||||
oldEntry.notification = notification;
|
||||
mGroupManager.onEntryUpdated(oldEntry, oldNotification);
|
||||
try {
|
||||
if (oldEntry.icon != null) {
|
||||
// Update the icon
|
||||
@@ -2074,6 +2082,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
if (!shouldInterrupt) {
|
||||
if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key);
|
||||
oldEntry.notification = notification;
|
||||
mGroupManager.onEntryUpdated(oldEntry, oldNotification);
|
||||
mHeadsUpNotificationView.release();
|
||||
return;
|
||||
}
|
||||
@@ -2087,6 +2096,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
|
||||
oldEntry.notification = notification;
|
||||
mGroupManager.onEntryUpdated(oldEntry, oldNotification);
|
||||
final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
|
||||
notification.getUser(),
|
||||
n.icon,
|
||||
|
||||
@@ -22,9 +22,10 @@ import android.service.notification.NotificationListenerService.Ranking;
|
||||
import android.service.notification.NotificationListenerService.RankingMap;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -91,10 +92,12 @@ public class NotificationData {
|
||||
|
||||
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
|
||||
private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
|
||||
private ArraySet<String> mGroupsWithSummaries = new ArraySet<>();
|
||||
|
||||
private NotificationGroupManager mGroupManager;
|
||||
|
||||
private RankingMap mRankingMap;
|
||||
private final Ranking mTmpRanking = new Ranking();
|
||||
|
||||
private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
|
||||
private final Ranking mRankingA = new Ranking();
|
||||
private final Ranking mRankingB = new Ranking();
|
||||
@@ -141,6 +144,7 @@ public class NotificationData {
|
||||
|
||||
public NotificationData(Environment environment) {
|
||||
mEnvironment = environment;
|
||||
mGroupManager = environment.getGroupManager();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,12 +167,14 @@ public class NotificationData {
|
||||
public void add(Entry entry, RankingMap ranking) {
|
||||
mEntries.put(entry.notification.getKey(), entry);
|
||||
updateRankingAndSort(ranking);
|
||||
mGroupManager.onEntryAdded(entry);
|
||||
}
|
||||
|
||||
public Entry remove(String key, RankingMap ranking) {
|
||||
Entry removed = mEntries.remove(key);
|
||||
if (removed == null) return null;
|
||||
updateRankingAndSort(ranking);
|
||||
mGroupManager.onEntryRemoved(removed);
|
||||
return removed;
|
||||
}
|
||||
|
||||
@@ -203,7 +209,6 @@ public class NotificationData {
|
||||
// anything changed, and this class should call back the UI so it updates itself.
|
||||
public void filterAndSort() {
|
||||
mSortedAndFiltered.clear();
|
||||
mGroupsWithSummaries.clear();
|
||||
|
||||
final int N = mEntries.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -214,32 +219,12 @@ public class NotificationData {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sbn.getNotification().isGroupSummary()) {
|
||||
mGroupsWithSummaries.add(sbn.getGroupKey());
|
||||
}
|
||||
mSortedAndFiltered.add(entry);
|
||||
}
|
||||
|
||||
// Second pass: Filter out group children with summary.
|
||||
if (!mGroupsWithSummaries.isEmpty()) {
|
||||
final int M = mSortedAndFiltered.size();
|
||||
for (int i = M - 1; i >= 0; i--) {
|
||||
Entry ent = mSortedAndFiltered.get(i);
|
||||
StatusBarNotification sbn = ent.notification;
|
||||
if (sbn.getNotification().isGroupChild() &&
|
||||
mGroupsWithSummaries.contains(sbn.getGroupKey())) {
|
||||
mSortedAndFiltered.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(mSortedAndFiltered, mRankingComparator);
|
||||
}
|
||||
|
||||
public boolean isGroupWithSummary(String groupKey) {
|
||||
return mGroupsWithSummaries.contains(groupKey);
|
||||
}
|
||||
|
||||
boolean shouldFilterOut(StatusBarNotification sbn) {
|
||||
if (!(mEnvironment.isDeviceProvisioned() ||
|
||||
showNotificationEvenIfUnprovisioned(sbn))) {
|
||||
@@ -254,6 +239,10 @@ public class NotificationData {
|
||||
mEnvironment.shouldHideSensitiveContents(sbn.getUserId())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mGroupManager.isChildInGroupWithSummary(sbn)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -328,5 +317,6 @@ public class NotificationData {
|
||||
public boolean isDeviceProvisioned();
|
||||
public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
|
||||
public String getCurrentMediaNotificationKey();
|
||||
public NotificationGroupManager getGroupManager();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.systemui.statusbar.phone;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A class to handle notifications and their corresponding groups.
|
||||
*/
|
||||
public class NotificationGroupManager {
|
||||
|
||||
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
|
||||
private OnGroupChangeListener mListener;
|
||||
private int mBarState = -1;
|
||||
|
||||
public void setOnGroupChangeListener(OnGroupChangeListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public boolean isGroupExpanded(StatusBarNotification sbn) {
|
||||
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
|
||||
if (group == null) {
|
||||
return false;
|
||||
}
|
||||
return group.expanded;
|
||||
}
|
||||
|
||||
public void setGroupExpanded(StatusBarNotification sbn, boolean expanded) {
|
||||
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
|
||||
if (group == null) {
|
||||
return;
|
||||
}
|
||||
setGroupExpanded(group, expanded);
|
||||
}
|
||||
|
||||
private void setGroupExpanded(NotificationGroup group, boolean expanded) {
|
||||
group.expanded = expanded;
|
||||
if (group.summary != null) {
|
||||
mListener.onGroupExpansionChanged(group.summary.row, expanded);
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryRemoved(NotificationData.Entry removed) {
|
||||
onEntryRemovedInternal(removed, removed.notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry was removed.
|
||||
*
|
||||
* @param removed the removed entry
|
||||
* @param sbn the notification the entry has, which doesn't need to be the same as it's internal
|
||||
* notification
|
||||
*/
|
||||
private void onEntryRemovedInternal(NotificationData.Entry removed,
|
||||
final StatusBarNotification sbn) {
|
||||
Notification notif = sbn.getNotification();
|
||||
String groupKey = sbn.getGroupKey();
|
||||
final NotificationGroup group = mGroupMap.get(groupKey);
|
||||
if (notif.isGroupSummary()) {
|
||||
group.summary = null;
|
||||
} else {
|
||||
group.children.remove(removed);
|
||||
}
|
||||
if (group.children.isEmpty()) {
|
||||
if (group.summary == null) {
|
||||
mGroupMap.remove(groupKey);
|
||||
} else {
|
||||
if (group.expanded) {
|
||||
// only the summary is left. Change it to unexpanded in a few ms. We do this to
|
||||
// avoid raceconditions
|
||||
removed.row.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (group.children.isEmpty()) {
|
||||
setGroupExpanded(sbn, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryAdded(NotificationData.Entry added) {
|
||||
StatusBarNotification sbn = added.notification;
|
||||
Notification notif = sbn.getNotification();
|
||||
String groupKey = sbn.getGroupKey();
|
||||
NotificationGroup group = mGroupMap.get(groupKey);
|
||||
if (group == null) {
|
||||
group = new NotificationGroup();
|
||||
mGroupMap.put(groupKey, group);
|
||||
}
|
||||
if (notif.isGroupSummary()) {
|
||||
group.summary = added;
|
||||
if (!group.children.isEmpty()) {
|
||||
mListener.onGroupCreatedFromChildren(group);
|
||||
}
|
||||
} else {
|
||||
group.children.add(added);
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryUpdated(NotificationData.Entry entry,
|
||||
StatusBarNotification oldNotification) {
|
||||
if (mGroupMap.get(oldNotification.getGroupKey()) != null) {
|
||||
onEntryRemovedInternal(entry, oldNotification);
|
||||
}
|
||||
onEntryAdded(entry);
|
||||
}
|
||||
|
||||
public boolean isVisible(StatusBarNotification sbn) {
|
||||
if (!sbn.getNotification().isGroupChild()) {
|
||||
return true;
|
||||
}
|
||||
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
|
||||
if (group != null && group.expanded) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasGroupChildren(StatusBarNotification sbn) {
|
||||
if (areGroupsProhibited()) {
|
||||
return false;
|
||||
}
|
||||
if (!sbn.getNotification().isGroupSummary()) {
|
||||
return false;
|
||||
}
|
||||
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
|
||||
if (group == null) {
|
||||
return false;
|
||||
}
|
||||
return !group.children.isEmpty();
|
||||
}
|
||||
|
||||
public void setStatusBarState(int newState) {
|
||||
if (mBarState == newState) {
|
||||
return;
|
||||
}
|
||||
boolean prohibitedBefore = areGroupsProhibited();
|
||||
mBarState = newState;
|
||||
boolean nowProhibited = areGroupsProhibited();
|
||||
if (nowProhibited != prohibitedBefore) {
|
||||
if (nowProhibited) {
|
||||
for (NotificationGroup group : mGroupMap.values()) {
|
||||
if (group.expanded) {
|
||||
setGroupExpanded(group, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
mListener.onGroupsProhibitedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areGroupsProhibited() {
|
||||
return mBarState == StatusBarState.KEYGUARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether a given notification is a child in a group which has a summary
|
||||
*/
|
||||
public boolean isChildInGroupWithSummary(StatusBarNotification sbn) {
|
||||
if (!sbn.getNotification().isGroupChild()) {
|
||||
return false;
|
||||
}
|
||||
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
|
||||
if (group == null || group.summary == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
|
||||
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
|
||||
return group == null ? null
|
||||
: group.summary == null ? null
|
||||
: group.summary.row;
|
||||
}
|
||||
|
||||
public static class NotificationGroup {
|
||||
public final HashSet<NotificationData.Entry> children = new HashSet<>();
|
||||
public NotificationData.Entry summary;
|
||||
public boolean expanded;
|
||||
}
|
||||
|
||||
public interface OnGroupChangeListener {
|
||||
/**
|
||||
* The expansion of a group has changed.
|
||||
*
|
||||
* @param changedRow the row for which the expansion has changed, which is also the summary
|
||||
* @param expanded a boolean indicating the new expanded state
|
||||
*/
|
||||
void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded);
|
||||
|
||||
/**
|
||||
* Children group policy has changed and children may no be prohibited or allowed.
|
||||
*/
|
||||
void onGroupsProhibitedChanged();
|
||||
|
||||
/**
|
||||
* A group of children just received a summary notification and should therefore become
|
||||
* children of it.
|
||||
*
|
||||
* @param group the group created
|
||||
*/
|
||||
void onGroupCreatedFromChildren(NotificationGroup group);
|
||||
}
|
||||
}
|
||||
@@ -3379,6 +3379,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
}
|
||||
}
|
||||
mState = state;
|
||||
mGroupManager.setStatusBarState(state);
|
||||
mStatusBarWindowManager.setStatusBarState(state);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user