SysUI: Reflect notification ranking

Respect the ranking received via NotificationListenerService.

Bug: 15131411
Change-Id: I9e3a1530ffb5f4c29eeeccdbc910261d2eb72216
This commit is contained in:
Christoph Studer
2014-05-26 13:10:30 +02:00
parent 5d423844f2
commit 37fe693225
5 changed files with 185 additions and 115 deletions

View File

@@ -48,6 +48,7 @@ import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -77,11 +78,12 @@ import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -194,7 +196,7 @@ public abstract class BaseStatusBar extends SystemUI implements
mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
if (provisioned != mDeviceProvisioned) {
mDeviceProvisioned = provisioned;
updateNotificationIcons();
updateNotifications();
}
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
@@ -209,7 +211,7 @@ public abstract class BaseStatusBar extends SystemUI implements
// so we just dump our cache ...
mUsersAllowingPrivateNotifications.clear();
// ... and refresh all the notifications
updateNotificationIcons();
updateNotifications();
}
};
@@ -280,11 +282,12 @@ public abstract class BaseStatusBar extends SystemUI implements
public void onListenerConnected() {
if (DEBUG) Log.d(TAG, "onListenerConnected");
final StatusBarNotification[] notifications = getActiveNotifications();
final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
for (StatusBarNotification sbn : notifications) {
addNotificationInternal(sbn);
addNotificationInternal(sbn, currentRanking);
}
}
});
@@ -293,13 +296,14 @@ public abstract class BaseStatusBar extends SystemUI implements
@Override
public void onNotificationPosted(final StatusBarNotification sbn) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
if (mNotificationData.findByKey(sbn.getKey()) != null) {
updateNotificationInternal(sbn);
updateNotificationInternal(sbn, currentRanking);
} else {
addNotificationInternal(sbn);
addNotificationInternal(sbn, currentRanking);
}
}
});
@@ -308,10 +312,24 @@ public abstract class BaseStatusBar extends SystemUI implements
@Override
public void onNotificationRemoved(final StatusBarNotification sbn) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
removeNotificationInternal(sbn.getKey());
removeNotificationInternal(sbn.getKey(), currentRanking);
}
});
}
@Override
public void onNotificationRankingUpdate() {
if (DEBUG) Log.d(TAG, "onRankingUpdate");
final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
mNotificationData.updateRanking(currentRanking);
updateNotifications();
}
});
}
@@ -1113,19 +1131,13 @@ public abstract class BaseStatusBar extends SystemUI implements
}
}
protected StatusBarNotification removeNotificationViews(String key) {
NotificationData.Entry entry = mNotificationData.remove(key);
protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) {
NotificationData.Entry entry = mNotificationData.remove(key, ranking);
if (entry == null) {
Log.w(TAG, "removeNotification for unknown key: " + key);
return null;
}
// Remove the expanded view.
ViewGroup rowParent = (ViewGroup)entry.row.getParent();
if (rowParent != null) rowParent.removeView(entry.row);
updateRowStates();
updateNotificationIcons();
updateSpeedBump();
updateNotifications();
return entry.notification;
}
@@ -1159,35 +1171,17 @@ public abstract class BaseStatusBar extends SystemUI implements
return entry;
}
protected void addNotificationViews(NotificationData.Entry entry) {
protected void addNotificationViews(Entry entry, Ranking ranking) {
if (entry == null) {
return;
}
// Add the expanded view and icon.
int pos = mNotificationData.add(entry);
if (DEBUG) {
Log.d(TAG, "addNotificationViews: added at " + pos);
}
updateRowStates();
updateNotificationIcons();
updateSpeedBump();
mNotificationData.add(entry, ranking);
updateNotifications();
}
protected void updateSpeedBump() {
int n = mNotificationData.size();
int speedBumpIndex = -1;
for (int i = n-1; i >= 0; i--) {
NotificationData.Entry entry = mNotificationData.get(i);
if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
&& entry.row.isBelowSpeedBump() ) {
speedBumpIndex = n - 1 - i;
}
}
mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
}
private void addNotificationViews(StatusBarNotification notification) {
addNotificationViews(createNotificationViews(notification));
private void addNotificationViews(StatusBarNotification notification, Ranking ranking) {
addNotificationViews(createNotificationViews(notification), ranking);
}
/**
@@ -1201,17 +1195,17 @@ public abstract class BaseStatusBar extends SystemUI implements
protected void updateRowStates() {
int maxKeyguardNotifications = getMaxKeyguardNotifications();
mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
int n = mNotificationData.size();
final int N = mNotificationData.size();
int visibleNotifications = 0;
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
for (int i = n-1; i >= 0; i--) {
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = mNotificationData.get(i);
if (onKeyguard) {
entry.row.setExpansionDisabled(true);
} else {
entry.row.setExpansionDisabled(false);
if (!entry.row.isUserLocked()) {
boolean top = (i == n-1);
boolean top = (i == 0);
entry.row.setSystemExpanded(top);
}
}
@@ -1238,6 +1232,9 @@ public abstract class BaseStatusBar extends SystemUI implements
} else {
mKeyguardIconOverflowContainer.setVisibility(View.GONE);
}
// Move overflow container to last position.
mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
mStackScroller.getChildCount() - 1);
}
private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1247,46 +1244,42 @@ public abstract class BaseStatusBar extends SystemUI implements
protected void setZenMode(int mode) {
if (!isDeviceProvisioned()) return;
mZenMode = mode;
updateNotificationIcons();
updateNotifications();
}
protected abstract void haltTicker();
protected abstract void setAreThereNotifications();
protected abstract void updateNotificationIcons();
protected abstract void updateNotifications();
protected abstract void tick(StatusBarNotification n, boolean firstTime);
protected abstract void updateExpandedViewPos(int expandedPosition);
protected abstract boolean shouldDisableNavbarGestures();
protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
return parent != null && parent.indexOfChild(entry.row) == 0;
}
@Override
public void addNotification(StatusBarNotification notification) {
if (!USE_NOTIFICATION_LISTENER) {
addNotificationInternal(notification);
addNotificationInternal(notification, null);
}
}
public abstract void addNotificationInternal(StatusBarNotification notification);
public abstract void addNotificationInternal(StatusBarNotification notification,
Ranking ranking);
@Override
public void removeNotification(String key) {
if (!USE_NOTIFICATION_LISTENER) {
removeNotificationInternal(key);
removeNotificationInternal(key, null);
}
}
protected abstract void removeNotificationInternal(String key);
protected abstract void removeNotificationInternal(String key, Ranking ranking);
public void updateNotification(StatusBarNotification notification) {
if (!USE_NOTIFICATION_LISTENER) {
updateNotificationInternal(notification);
updateNotificationInternal(notification, null);
}
}
public void updateNotificationInternal(StatusBarNotification notification) {
public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
@@ -1358,18 +1351,12 @@ public abstract class BaseStatusBar extends SystemUI implements
&& oldPublicContentView.getPackage().equals(publicContentView.getPackage())
&& oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
boolean orderUnchanged =
notification.getNotification().when == oldNotification.getNotification().when
&& notification.getScore() == oldNotification.getScore();
// score now encompasses/supersedes isOngoing()
boolean updateTicker = notification.getNotification().tickerText != null
&& !TextUtils.equals(notification.getNotification().tickerText,
oldEntry.notification.getNotification().tickerText);
boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
&& (orderUnchanged || isTopAnyway)) {
if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
&& publicUnchanged) {
if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
oldEntry.notification = notification;
try {
@@ -1397,22 +1384,20 @@ public abstract class BaseStatusBar extends SystemUI implements
handleNotificationError(notification, "Couldn't update icon: " + ic);
return;
}
updateRowStates();
updateSpeedBump();
mNotificationData.updateRanking(ranking);
updateNotifications();
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
removeNotificationViews(notification.getKey());
addNotificationViews(notification);
removeNotificationViews(notification.getKey(), ranking);
addNotificationViews(notification, ranking);
}
} else {
if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey());
if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
removeNotificationViews(notification.getKey());
addNotificationViews(notification); // will also replace the heads up
removeNotificationViews(notification.getKey(), ranking);
addNotificationViews(notification, ranking); // will also replace the heads up
final NotificationData.Entry newEntry = mNotificationData.findByKey(
notification.getKey());
final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
@@ -1559,5 +1544,12 @@ public abstract class BaseStatusBar extends SystemUI implements
mWindowManager.removeViewImmediate(mSearchPanelView);
}
mContext.unregisterReceiver(mBroadcastReceiver);
if (USE_NOTIFICATION_LISTENER) {
try {
mNotificationListener.unregisterAsSystemService();
} catch (RemoteException e) {
// Ignore.
}
}
}
}

View File

@@ -50,7 +50,7 @@ public class InterceptedNotifications {
for (int i = 0; i < n; i++) {
final StatusBarNotification sbn = mIntercepted.valueAt(i);
sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
mBar.addNotificationInternal(sbn);
mBar.addNotificationInternal(sbn, null);
}
mIntercepted.clear();
updateSyntheticNotification();
@@ -88,7 +88,7 @@ public class InterceptedNotifications {
private void updateSyntheticNotification() {
if (mIntercepted.isEmpty()) {
if (mSynKey != null) {
mBar.removeNotificationInternal(mSynKey);
mBar.removeNotificationInternal(mSynKey, null);
mSynKey = null;
}
return;
@@ -107,9 +107,9 @@ public class InterceptedNotifications {
mBar.getCurrentUserHandle());
if (mSynKey == null) {
mSynKey = sbn.getKey();
mBar.addNotificationInternal(sbn);
mBar.addNotificationInternal(sbn, null);
} else {
mBar.updateNotificationInternal(sbn);
mBar.updateNotificationInternal(sbn, null);
}
final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
entry.row.setOnClickListener(mSynClickListener);

View File

@@ -16,15 +16,19 @@
package com.android.systemui.statusbar;
import android.app.Notification;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.view.View;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* The list of currently displaying notifications.
*
* TODO: Rename to NotificationList.
*/
public class NotificationData {
public static final class Entry {
@@ -34,7 +38,6 @@ public class NotificationData {
public ExpandableNotificationRow row; // the outer expanded view
public View expanded; // the inflated RemoteViews
public View expandedPublic; // for insecure lockscreens
public ImageView largeIcon;
public View expandedBig;
private boolean interruption;
public Entry() {}
@@ -64,18 +67,23 @@ public class NotificationData {
}
private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
// sort first by score, then by when
private Ranking mRanking;
private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
@Override
public int compare(Entry a, Entry b) {
if (mRanking != null) {
return mRanking.getRank(a.key) - mRanking.getRank(b.key);
}
final StatusBarNotification na = a.notification;
final StatusBarNotification nb = b.notification;
int d = na.getScore() - nb.getScore();
int d = nb.getScore() - na.getScore();
if (a.interruption != b.interruption) {
return a.interruption ? 1 : -1;
return a.interruption ? -1 : 1;
} else if (d != 0) {
return d;
} else {
return (int) (na.getNotification().when - nb.getNotification().when);
return (int) (nb.getNotification().when - na.getNotification().when);
}
}
};
@@ -97,26 +105,47 @@ public class NotificationData {
return null;
}
public int add(Entry entry) {
int i;
int N = mEntries.size();
for (i = 0; i < N; i++) {
if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
break;
}
}
mEntries.add(i, entry);
return i;
public void add(Entry entry, Ranking ranking) {
mEntries.add(entry);
updateRankingAndSort(ranking);
}
public Entry remove(String key) {
public Entry remove(String key, Ranking ranking) {
Entry e = findByKey(key);
if (e != null) {
mEntries.remove(e);
if (e == null) {
return null;
}
mEntries.remove(e);
updateRankingAndSort(ranking);
return e;
}
public void updateRanking(Ranking ranking) {
updateRankingAndSort(ranking);
}
public boolean isAmbient(String key) {
// TODO: Remove when switching to NotificationListener.
if (mRanking == null) {
for (Entry entry : mEntries) {
if (key.equals(entry.key)) {
return entry.notification.getNotification().priority ==
Notification.PRIORITY_MIN;
}
}
} else {
return mRanking.isAmbient(key);
}
return false;
}
private void updateRankingAndSort(Ranking ranking) {
if (ranking != null) {
mRanking = ranking;
}
Collections.sort(mEntries, mRankingComparator);
}
/**
* Return whether there are any visible items (i.e. items without an error).
*/

View File

@@ -64,6 +64,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.DisplayMetrics;
@@ -86,7 +87,6 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -1029,13 +1029,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
public void addNotificationInternal(StatusBarNotification notification) {
public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
Entry shadeEntry = createNotificationViews(notification);
if (shadeEntry == null) {
return;
}
if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
// Forward the ranking so we can sort the new notification.
mNotificationData.updateRanking(ranking);
return;
}
if (mUseHeadsUp && shouldInterrupt(notification)) {
@@ -1075,7 +1077,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
tick(notification, true);
}
}
addNotificationViews(shadeEntry);
addNotificationViews(shadeEntry, ranking);
// Recalculate the position of the sliding windows and the titles.
setAreThereNotifications();
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
@@ -1097,8 +1099,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
public void removeNotificationInternal(String key) {
StatusBarNotification old = removeNotificationViews(key);
public void removeNotificationInternal(String key, Ranking ranking) {
StatusBarNotification old = removeNotificationViews(key, ranking);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
if (old != null) {
@@ -1135,7 +1137,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
R.integer.config_show_search_delay);
}
private void loadNotificationShade() {
private void updateNotificationShade() {
if (mStackScroller == null) return;
int N = mNotificationData.size();
@@ -1145,7 +1147,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
final boolean provisioned = isDeviceProvisioned();
// If the device hasn't been through Setup, we only show system notifications
for (int i=0; i<N; i++) {
Entry ent = mNotificationData.get(N-i-1);
Entry ent = mNotificationData.get(i);
if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
// TODO How do we want to badge notifcations from profiles.
@@ -1176,26 +1178,75 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
for (int i=0; i<toShow.size(); i++) {
View v = toShow.get(i);
if (v.getParent() == null) {
mStackScroller.addView(v, i);
mStackScroller.addView(v);
}
}
// So after all this work notifications still aren't sorted correctly.
// Let's do that now by advancing through toShow and mStackScroller in
// lock-step, making sure mStackScroller matches what we see in toShow.
int j = 0;
for (int i = 0; i < mStackScroller.getChildCount(); i++) {
View child = mStackScroller.getChildAt(i);
if (!(child instanceof ExpandableNotificationRow)) {
// We don't care about non-notification views.
continue;
}
if (child == toShow.get(j)) {
// Everything is well, advance both lists.
j++;
continue;
}
// Oops, wrong notification at this position. Put the right one
// here and advance both lists.
mStackScroller.changeViewPosition(toShow.get(j), i);
j++;
}
updateRowStates();
updateSpeedbump();
mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
}
private void updateSpeedbump() {
int speedbumpIndex = -1;
int currentIndex = 0;
for (int i = 0; i < mNotificationData.size(); i++) {
Entry entry = mNotificationData.get(i);
if (entry.row.getParent() == null) {
// This view isn't even added, so the stack scroller doesn't
// know about it. Ignore completely.
continue;
}
if (entry.row.getVisibility() != View.GONE &&
mNotificationData.isAmbient(entry.key)) {
speedbumpIndex = currentIndex;
break;
}
currentIndex++;
}
mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
}
@Override
protected void updateNotificationIcons() {
protected void updateNotifications() {
// TODO: Move this into updateNotificationIcons()?
if (mNotificationIcons == null) return;
loadNotificationShade();
updateNotificationShade();
updateNotificationIcons();
}
private void updateNotificationIcons() {
final LinearLayout.LayoutParams params
= new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
int N = mNotificationData.size();
if (DEBUG) {
Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" +
mNotificationIcons);
}
ArrayList<View> toShow = new ArrayList<View>();
@@ -1203,7 +1254,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
final boolean provisioned = isDeviceProvisioned();
// If the device hasn't been through Setup, we only show system notifications
for (int i=0; i<N; i++) {
Entry ent = mNotificationData.get(N-i-1);
Entry ent = mNotificationData.get(i);
if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
|| showNotificationEvenIfUnprovisioned(ent.notification))) continue;
if (!notificationIsForCurrentProfiles(ent.notification)) continue;
@@ -2376,7 +2427,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public void userSwitched(int newUserId) {
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
animateCollapsePanels();
updateNotificationIcons();
updateNotifications();
resetUserSetupObserver();
}
@@ -2777,10 +2828,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
updateStackScrollerState();
updatePublicMode();
updateRowStates();
updateSpeedBump();
updateNotifications();
checkBarModes();
updateNotificationIcons();
updateCarrierLabelVisibility(false);
}

View File

@@ -17,12 +17,12 @@
package com.android.systemui.statusbar.tv;
import android.os.IBinder;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import com.android.internal.policy.IKeyguardShowCallback;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -50,7 +50,7 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
public void addNotificationInternal(StatusBarNotification notification) {
public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
}
@Override
@@ -58,7 +58,7 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
protected void removeNotificationInternal(String key) {
protected void removeNotificationInternal(String key, Ranking ranking) {
}
@Override
@@ -117,7 +117,7 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
protected void updateNotificationIcons() {
protected void updateNotifications() {
}
@Override