Update notification log

- Change notification presentation to better match notification shade
- Stop constantly removing and readding all of the preferences
- Update individual preferences in a kind of hacky way (to be improved
later)
- Link notification entries to notification preferences screen
- Hide notification summaries

Bug: 137396965
Test: manual
Change-Id: Icdd54488b8bf659f6fe42af93e42cd21912fa4fe
This commit is contained in:
Julia Reynolds
2019-09-18 09:19:04 -04:00
parent 5f10a9a9ea
commit b294b82c84
3 changed files with 371 additions and 270 deletions

View File

@@ -26,8 +26,7 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@*android:dimen/status_bar_icon_size" android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
> >
<ImageView <ImageView
@@ -46,24 +45,38 @@
android:scaleType="fitCenter" /> android:scaleType="fitCenter" />
<TextView <TextView
android:id="@+id/title" android:id="@+id/pkgname"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_toStartOf="@+id/timestamp"
android:layout_toEndOf="@id/icon" android:layout_toEndOf="@id/icon"
android:ellipsize="end" android:ellipsize="end"
android:singleLine="true" android:singleLine="true"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textStyle="bold" android:textStyle="bold"
android:textAlignment="viewStart" android:textAlignment="viewStart" />
android:labelFor="@android:id/button2" />
<ImageView
android:id="@+id/alerted_icon"
android:layout_width="@*android:dimen/status_bar_icon_size"
android:layout_height="@*android:dimen/status_bar_icon_size"
android:layout_centerVertical="true"
android:layout_marginStart="6dp"
android:paddingTop="1dp"
android:scaleType="fitCenter"
android:visibility="gone"
android:layout_toEndOf="@id/pkgname"
android:tint="?android:attr/textColorSecondary"
android:src="@drawable/ic_notifications_alert"
/>
<DateTimeView <DateTimeView
android:id="@+id/timestamp" android:id="@+id/timestamp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="13dp"
android:paddingBottom="13dp"
android:layout_alignBottom="@android:id/widget_frame" android:layout_alignBottom="@android:id/widget_frame"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignTop="@android:id/widget_frame" android:layout_alignTop="@android:id/widget_frame"
@@ -78,26 +91,25 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@*android:dimen/status_bar_icon_size" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="vertical"
android:layout_marginStart="30dp" android:layout_marginStart="30dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
> >
<ImageView <TextView
android:id="@+id/pkgicon" android:id="@+id/title"
android:layout_width="@*android:dimen/status_bar_icon_size" android:layout_width="match_parent"
android:layout_height="@*android:dimen/status_bar_icon_size" android:layout_height="wrap_content"
android:layout_marginStart="0dp" android:layout_gravity="left|center_vertical"
android:layout_marginEnd="6dp" android:ellipsize="end"
android:contentDescription="@null" android:singleLine="true"
android:adjustViewBounds="true" android:textColor="?android:attr/textColorPrimary"
android:maxHeight="@*android:dimen/status_bar_icon_size" android:textAlignment="viewStart"
android:maxWidth="@*android:dimen/status_bar_icon_size" />
android:scaleType="fitCenter" />
<TextView <TextView
android:id="@+id/pkgname" android:id="@+id/text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical" android:layout_gravity="left|center_vertical"
@@ -106,12 +118,30 @@
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart" android:textAlignment="viewStart"
/> />
</LinearLayout> </LinearLayout>
<TextView <LinearLayout
android:id="@+id/extra" android:id="@+id/extra"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/notification_extra"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginStart="30dp"
android:singleLine="false"
android:textColor="?android:attr/textColorPrimary"
android:textSize="10sp"
android:fontFamily="monospace"
android:textAlignment="viewStart"
/>
<TextView
android:id="@+id/ranking_extra"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
@@ -122,6 +152,7 @@
android:textSize="10sp" android:textSize="10sp"
android:fontFamily="monospace" android:fontFamily="monospace"
android:textAlignment="viewStart" android:textAlignment="viewStart"
/> />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -9997,14 +9997,7 @@
<string name="notification_log_details_ashmem">ashmem</string> <string name="notification_log_details_ashmem">ashmem</string>
<!-- Notification log debug tool: header: notification alert info --> <!-- Notification log debug tool: header: notification alert info -->
<string name="notification_log_details_alerted">notification alerted</string> <string name="notification_log_details_alerted">notification alerted</string>
<!-- Notification log debug tool: header: notification sound info --> <string name="notification_log_channel">channel</string>
<string name="notification_log_details_sound">sound</string>
<!-- Notification log debug tool: header: notification vibration info -->
<string name="notification_log_details_vibrate">vibrate</string>
<!-- Notification log debug tool: header: notification vibration info -->
<string name="notification_log_details_vibrate_pattern">pattern</string>
<!-- Notification log debug tool: the word 'default' -->
<string name="notification_log_details_default">default</string>
<!-- Notification log debug tool: the word 'none' --> <!-- Notification log debug tool: the word 'none' -->
<string name="notification_log_details_none">none</string> <string name="notification_log_details_none">none</string>
<!-- Notification log debug tool: missing ranking information --> <!-- Notification log debug tool: missing ranking information -->

View File

@@ -16,7 +16,8 @@
package com.android.settings.notification; package com.android.settings.notification;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.provider.Settings.EXTRA_APP_PACKAGE;
import static android.provider.Settings.EXTRA_CHANNEL_ID;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
@@ -27,18 +28,19 @@ import android.app.PendingIntent;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.graphics.PorterDuff;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.NotificationListenerService.RankingMap;
@@ -49,6 +51,7 @@ import android.text.TextUtils;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.DateTimeView; import android.widget.DateTimeView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -62,8 +65,8 @@ import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils; import com.android.settings.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
public class NotificationStation extends SettingsPreferenceFragment { public class NotificationStation extends SettingsPreferenceFragment {
@@ -72,33 +75,44 @@ public class NotificationStation extends SettingsPreferenceFragment {
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final boolean DUMP_EXTRAS = true; private static final boolean DUMP_EXTRAS = true;
private static final boolean DUMP_PARCEL = true; private static final boolean DUMP_PARCEL = true;
private Handler mHandler;
private static class HistoricalNotificationInfo { private static class HistoricalNotificationInfo {
public String key; public String key;
public String channel; public NotificationChannel channel;
public String pkg; public String pkg;
public Drawable pkgicon; public Drawable pkgicon;
public CharSequence pkgname; public CharSequence pkgname;
public Drawable icon; public Drawable icon;
public CharSequence title; public CharSequence title;
public CharSequence text;
public int priority; public int priority;
public int user; public int user;
public long timestamp; public long timestamp;
public boolean active; public boolean active;
public CharSequence extra; public CharSequence notificationExtra;
public CharSequence rankingExtra;
public boolean alerted;
public boolean visuallyInterruptive;
public void updateFrom(HistoricalNotificationInfo updatedInfo) {
this.channel = updatedInfo.channel;
this.icon = updatedInfo.icon;
this.title = updatedInfo.title;
this.text = updatedInfo.text;
this.priority = updatedInfo.priority;
this.timestamp = updatedInfo.timestamp;
this.active = updatedInfo.active;
this.alerted = updatedInfo.alerted;
this.visuallyInterruptive = updatedInfo.visuallyInterruptive;
this.notificationExtra = updatedInfo.notificationExtra;
this.rankingExtra = updatedInfo.rankingExtra;
}
} }
private PackageManager mPm; private PackageManager mPm;
private INotificationManager mNoMan; private INotificationManager mNoMan;
private RankingMap mRanking; private RankingMap mRanking;
private LinkedList<HistoricalNotificationInfo> mNotificationInfos;
private Runnable mRefreshListRunnable = new Runnable() {
@Override
public void run() {
refreshList();
}
};
private final NotificationListenerService mListener = new NotificationListenerService() { private final NotificationListenerService mListener = new NotificationListenerService() {
@Override @Override
@@ -106,15 +120,21 @@ public class NotificationStation extends SettingsPreferenceFragment {
logd("onNotificationPosted: %s, with update for %d", sbn.getNotification(), logd("onNotificationPosted: %s, with update for %d", sbn.getNotification(),
ranking == null ? 0 : ranking.getOrderedKeys().length); ranking == null ? 0 : ranking.getOrderedKeys().length);
mRanking = ranking; mRanking = ranking;
scheduleRefreshList(); if (sbn.getNotification().isGroupSummary()) {
return;
}
addOrUpdateNotification(sbn);
} }
@Override @Override
public void onNotificationRemoved(StatusBarNotification notification, RankingMap ranking) { public void onNotificationRemoved(StatusBarNotification sbn, RankingMap ranking) {
logd("onNotificationRankingUpdate with update for %d", logd("onNotificationRankingUpdate with update for %d",
ranking == null ? 0 : ranking.getOrderedKeys().length); ranking == null ? 0 : ranking.getOrderedKeys().length);
mRanking = ranking; mRanking = ranking;
scheduleRefreshList(); if (sbn.getNotification().isGroupSummary()) {
return;
}
markNotificationAsDismissed(sbn);
} }
@Override @Override
@@ -122,7 +142,7 @@ public class NotificationStation extends SettingsPreferenceFragment {
logd("onNotificationRankingUpdate with update for %d", logd("onNotificationRankingUpdate with update for %d",
ranking == null ? 0 : ranking.getOrderedKeys().length); ranking == null ? 0 : ranking.getOrderedKeys().length);
mRanking = ranking; mRanking = ranking;
scheduleRefreshList(); updateNotificationsFromRanking();
} }
@Override @Override
@@ -130,44 +150,29 @@ public class NotificationStation extends SettingsPreferenceFragment {
mRanking = getCurrentRanking(); mRanking = getCurrentRanking();
logd("onListenerConnected with update for %d", logd("onListenerConnected with update for %d",
mRanking == null ? 0 : mRanking.getOrderedKeys().length); mRanking == null ? 0 : mRanking.getOrderedKeys().length);
scheduleRefreshList(); populateNotifications();
} }
}; };
private void scheduleRefreshList() {
if (mHandler != null) {
mHandler.removeCallbacks(mRefreshListRunnable);
mHandler.postDelayed(mRefreshListRunnable, 100);
}
}
private Context mContext; private Context mContext;
private final Comparator<HistoricalNotificationInfo> mNotificationSorter private final Comparator<HistoricalNotificationInfo> mNotificationSorter
= new Comparator<HistoricalNotificationInfo>() { = (lhs, rhs) -> Long.compare(rhs.timestamp, lhs.timestamp);
@Override
public int compare(HistoricalNotificationInfo lhs,
HistoricalNotificationInfo rhs) {
return Long.compare(rhs.timestamp, lhs.timestamp);
}
};
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Activity activity) {
logd("onAttach(%s)", activity.getClass().getSimpleName()); logd("onAttach(%s)", activity.getClass().getSimpleName());
super.onAttach(activity); super.onAttach(activity);
mHandler = new Handler(activity.getMainLooper());
mContext = activity; mContext = activity;
mPm = mContext.getPackageManager(); mPm = mContext.getPackageManager();
mNoMan = INotificationManager.Stub.asInterface( mNoMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE)); ServiceManager.getService(Context.NOTIFICATION_SERVICE));
mNotificationInfos = new LinkedList<>();
} }
@Override @Override
public void onDetach() { public void onDetach() {
logd("onDetach()"); logd("onDetach()");
mHandler.removeCallbacks(mRefreshListRunnable);
mHandler = null;
super.onDetach(); super.onDetach();
} }
@@ -205,24 +210,84 @@ public class NotificationStation extends SettingsPreferenceFragment {
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Cannot register listener", e); Log.e(TAG, "Cannot register listener", e);
} }
refreshList();
} }
private void refreshList() { /**
List<HistoricalNotificationInfo> infos = loadNotifications(); * Adds all current and historical notifications when the NLS connects.
if (infos != null) { */
final int N = infos.size(); private void populateNotifications() {
logd("adding %d infos", N); loadNotifications();
Collections.sort(infos, mNotificationSorter); final int N = mNotificationInfos.size();
if (getPreferenceScreen() == null) { logd("adding %d infos", N);
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext())); if (getPreferenceScreen() == null) {
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
getPreferenceScreen().removeAll();
for (int i = 0; i < N; i++) {
getPreferenceScreen().addPreference(new HistoricalNotificationPreference(
getPrefContext(), mNotificationInfos.get(i), i));
}
}
/**
* Finds and dims the given notification in the preferences list.
*/
private void markNotificationAsDismissed(StatusBarNotification sbn) {
final int N = mNotificationInfos.size();
for (int i = 0; i < N; i++) {
final HistoricalNotificationInfo info = mNotificationInfos.get(i);
if (TextUtils.equals(info.key, sbn.getKey())) {
info.active = false;
((HistoricalNotificationPreference) getPreferenceScreen().findPreference(
sbn.getKey())).updatePreference(info);
break;
} }
getPreferenceScreen().removeAll(); }
for (int i = 0; i < N; i++) { }
getPreferenceScreen().addPreference(
new HistoricalNotificationPreference(getPrefContext(), infos.get(i))); /**
* Either updates a notification with its latest information or (if it's something the user
* would consider a new notification) adds a new entry at the start of the list.
*/
private void addOrUpdateNotification(StatusBarNotification sbn) {
HistoricalNotificationInfo newInfo = createFromSbn(sbn, true);
boolean needsAdd = true;
final int N = mNotificationInfos.size();
for (int i = 0; i < N; i++) {
final HistoricalNotificationInfo info = mNotificationInfos.get(i);
if (TextUtils.equals(info.key, sbn.getKey()) && info.active
&& !newInfo.alerted && !newInfo.visuallyInterruptive) {
info.updateFrom(newInfo);
((HistoricalNotificationPreference) getPreferenceScreen().findPreference(
sbn.getKey())).updatePreference(info);
needsAdd = false;
break;
} }
} }
if (needsAdd) {
mNotificationInfos.addFirst(newInfo);
getPreferenceScreen().addPreference(new HistoricalNotificationPreference(
getPrefContext(),
mNotificationInfos.peekFirst(), -1 * mNotificationInfos.size()));
}
}
/**
* Updates all notifications in the list based on new information in the ranking.
*/
private void updateNotificationsFromRanking() {
Ranking rank = new Ranking();
for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
final HistoricalNotificationPreference p =
(HistoricalNotificationPreference) getPreferenceScreen().getPreference(i);
final HistoricalNotificationInfo info = mNotificationInfos.get(i);
mRanking.getRanking(p.getKey(), rank);
updateFromRanking(info);
((HistoricalNotificationPreference) getPreferenceScreen().findPreference(
info.key)).updatePreference(info);
}
} }
private static void logd(String msg, Object... args) { private static void logd(String msg, Object... args) {
@@ -242,14 +307,46 @@ public class NotificationStation extends SettingsPreferenceFragment {
CharSequence title = null; CharSequence title = null;
if (n.extras != null) { if (n.extras != null) {
title = n.extras.getCharSequence(Notification.EXTRA_TITLE); title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
if (TextUtils.isEmpty(title)) { }
title = n.extras.getCharSequence(Notification.EXTRA_TEXT); return title == null? null : String.valueOf(title);
}
/**
* Returns the appropriate substring for this notification based on the style of notification.
*/
private static String getTextString(Context appContext, Notification n) {
CharSequence text = null;
if (n.extras != null) {
text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
if (nb.getStyle() instanceof Notification.BigTextStyle) {
text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
} else if (nb.getStyle() instanceof Notification.MessagingStyle) {
Notification.MessagingStyle ms = (Notification.MessagingStyle) nb.getStyle();
final List<Notification.MessagingStyle.Message> messages = ms.getMessages();
if (messages != null && messages.size() > 0) {
text = messages.get(messages.size() - 1).getText();
}
}
if (TextUtils.isEmpty(text)) {
text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
} }
} }
if (TextUtils.isEmpty(title) && !TextUtils.isEmpty(n.tickerText)) { return text == null ? null : String.valueOf(text);
title = n.tickerText; }
private static Drawable loadIcon(Context context, StatusBarNotification sbn) {
Drawable draw = sbn.getNotification().getSmallIcon().loadDrawableAsUser(
sbn.getPackageContext(context), sbn.getUserId());
if (draw == null) {
return null;
} }
return String.valueOf(title); draw.mutate();
draw.setColorFilter(sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
return draw;
} }
private static String formatPendingIntent(PendingIntent pi) { private static String formatPendingIntent(PendingIntent pi) {
@@ -265,7 +362,11 @@ public class NotificationStation extends SettingsPreferenceFragment {
return sb.toString(); return sb.toString();
} }
private List<HistoricalNotificationInfo> loadNotifications() { /**
* Reads all current and past notifications (up to the system limit, since the device was
* booted), stores the data we need to present them, and sorts them chronologically for display.
*/
private void loadNotifications() {
final int currentUserId = ActivityManager.getCurrentUser(); final int currentUserId = ActivityManager.getCurrentUser();
try { try {
StatusBarNotification[] active = mNoMan.getActiveNotifications( StatusBarNotification[] active = mNoMan.getActiveNotifications(
@@ -274,51 +375,125 @@ public class NotificationStation extends SettingsPreferenceFragment {
mContext.getPackageName(), 50); mContext.getPackageName(), 50);
List<HistoricalNotificationInfo> list List<HistoricalNotificationInfo> list
= new ArrayList<HistoricalNotificationInfo>(active.length + dismissed.length); = new ArrayList<>(active.length + dismissed.length);
for (StatusBarNotification[] resultset for (StatusBarNotification[] resultSet
: new StatusBarNotification[][] { active, dismissed }) { : new StatusBarNotification[][] { active, dismissed }) {
for (StatusBarNotification sbn : resultset) { for (StatusBarNotification sbn : resultSet) {
if (sbn.getUserId() != UserHandle.USER_ALL & sbn.getUserId() != currentUserId) { if (sbn.getUserId() != UserHandle.USER_ALL & sbn.getUserId() != currentUserId) {
continue; continue;
} }
if (sbn.getNotification().isGroupSummary()) {
final Notification n = sbn.getNotification(); continue;
final HistoricalNotificationInfo info = new HistoricalNotificationInfo();
info.pkg = sbn.getPackageName();
info.user = sbn.getUserId();
info.icon = loadIconDrawable(info.pkg, info.user, n.icon);
info.pkgicon = loadPackageIconDrawable(info.pkg, info.user);
info.pkgname = loadPackageName(info.pkg);
info.title = getTitleString(n);
if (TextUtils.isEmpty(info.title)) {
info.title = getString(R.string.notification_log_no_title);
} }
info.timestamp = sbn.getPostTime(); final HistoricalNotificationInfo info = createFromSbn(sbn, resultSet == active);
info.priority = n.priority;
info.channel = n.getChannelId();
info.key = sbn.getKey();
info.active = (resultset == active);
info.extra = generateExtraText(sbn, info);
logd(" [%d] %s: %s", info.timestamp, info.pkg, info.title); logd(" [%d] %s: %s", info.timestamp, info.pkg, info.title);
list.add(info); list.add(info);
} }
} }
return list; // notifications are given to us in the same order as the shade; sorted by inferred
// priority. Resort chronologically for our display.
list.sort(mNotificationSorter);
mNotificationInfos = new LinkedList<>(list);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Cannot load Notifications: ", e); Log.e(TAG, "Cannot load Notifications: ", e);
} }
return null;
} }
private HistoricalNotificationInfo createFromSbn(StatusBarNotification sbn, boolean active) {
final Notification n = sbn.getNotification();
final HistoricalNotificationInfo info = new HistoricalNotificationInfo();
info.pkg = sbn.getPackageName();
info.user = sbn.getUserId();
info.icon = loadIcon(mContext, sbn);
if (info.icon == null) {
info.icon = loadPackageIconDrawable(info.pkg, info.user);
}
info.pkgname = loadPackageName(info.pkg);
info.title = getTitleString(n);
info.text = getTextString(sbn.getPackageContext(mContext), n);
info.timestamp = sbn.getPostTime();
info.priority = n.priority;
info.key = sbn.getKey();
info.active = active;
info.notificationExtra = generateExtraText(sbn, info);
updateFromRanking(info);
return info;
}
private void updateFromRanking(HistoricalNotificationInfo info) {
Ranking rank = new Ranking();
if (mRanking == null) {
return;
}
mRanking.getRanking(info.key, rank);
info.alerted = rank.getLastAudiblyAlertedMillis() > 0;
info.visuallyInterruptive = rank.visuallyInterruptive();
info.channel = rank.getChannel();
info.rankingExtra = generateRankingExtraText(info);
}
/**
* Generates a string of debug information for this notification based on the RankingMap
*/
private CharSequence generateRankingExtraText(HistoricalNotificationInfo info) {
final SpannableStringBuilder sb = new SpannableStringBuilder();
final String delim = getString(R.string.notification_log_details_delimiter);
Ranking rank = new Ranking();
if (mRanking != null && mRanking.getRanking(info.key, rank)) {
if (info.active && info.alerted) {
sb.append("\n")
.append(bold(getString(R.string.notification_log_details_alerted)));
}
sb.append("\n")
.append(bold(getString(R.string.notification_log_channel)))
.append(delim)
.append(info.channel.toString());
if (info.active) {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_importance)))
.append(delim)
.append(Ranking.importanceToString(rank.getImportance()));
if (rank.getImportanceExplanation() != null) {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_explanation)))
.append(delim)
.append(rank.getImportanceExplanation());
}
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_badge)))
.append(delim)
.append(Boolean.toString(rank.canShowBadge()));
}
} else {
if (mRanking == null) {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_ranking_null)));
} else {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_ranking_none)));
}
}
return sb;
}
/**
* Generates a string of debug information for this notification
*/
private CharSequence generateExtraText(StatusBarNotification sbn, private CharSequence generateExtraText(StatusBarNotification sbn,
HistoricalNotificationInfo info) { HistoricalNotificationInfo info) {
final Ranking rank = new Ranking();
final Notification n = sbn.getNotification(); final Notification n = sbn.getNotification();
final SpannableStringBuilder sb = new SpannableStringBuilder(); final SpannableStringBuilder sb = new SpannableStringBuilder();
final String delim = getString(R.string.notification_log_details_delimiter); final String delim = getString(R.string.notification_log_details_delimiter);
@@ -333,10 +508,6 @@ public class NotificationStation extends SettingsPreferenceFragment {
.append(bold(getString(R.string.notification_log_details_icon))) .append(bold(getString(R.string.notification_log_details_icon)))
.append(delim) .append(delim)
.append(String.valueOf(n.getSmallIcon())); .append(String.valueOf(n.getSmallIcon()));
sb.append("\n")
.append(bold("channelId"))
.append(delim)
.append(String.valueOf(n.getChannelId()));
sb.append("\n") sb.append("\n")
.append(bold("postTime")) .append(bold("postTime"))
.append(delim) .append(delim)
@@ -357,58 +528,6 @@ public class NotificationStation extends SettingsPreferenceFragment {
getString(R.string.notification_log_details_group_summary))); getString(R.string.notification_log_details_group_summary)));
} }
} }
if (info.active) {
// mRanking only applies to active notifications
if (mRanking != null && mRanking.getRanking(sbn.getKey(), rank)) {
if (rank.getLastAudiblyAlertedMillis() > 0) {
sb.append("\n")
.append(bold(getString(R.string.notification_log_details_alerted)));
}
}
}
try {
NotificationChannel channel = mNoMan.getNotificationChannelForPackage(
sbn.getPackageName(), sbn.getUid(), n.getChannelId(), false);
sb.append("\n")
.append(bold(getString(R.string.notification_log_details_sound)))
.append(delim);
if (channel == null || channel.getImportance() == IMPORTANCE_UNSPECIFIED) {
if (0 != (n.defaults & Notification.DEFAULT_SOUND)) {
sb.append(getString(R.string.notification_log_details_default));
} else if (n.sound != null) {
sb.append(n.sound.toString());
} else {
sb.append(getString(R.string.notification_log_details_none));
}
} else {
sb.append(String.valueOf(channel.getSound()));
}
sb.append("\n")
.append(bold(getString(R.string.notification_log_details_vibrate)))
.append(delim);
if (channel == null || channel.getImportance() == IMPORTANCE_UNSPECIFIED) {
if (0 != (n.defaults & Notification.DEFAULT_VIBRATE)) {
sb.append(getString(R.string.notification_log_details_default));
} else if (n.vibrate != null) {
sb.append(getString(R.string.notification_log_details_vibrate_pattern));
} else {
sb.append(getString(R.string.notification_log_details_none));
}
} else {
if (channel.getVibrationPattern() != null) {
sb.append(getString(R.string.notification_log_details_vibrate_pattern));
} else {
sb.append(getString(R.string.notification_log_details_none));
}
}
} catch (RemoteException e) {
Log.d(TAG, "cannot read channel info", e);
}
sb.append("\n")
.append(bold(getString(R.string.notification_log_details_visibility)))
.append(delim)
.append(Notification.visibilityToString(n.visibility));
if (n.publicVersion != null) { if (n.publicVersion != null) {
sb.append("\n") sb.append("\n")
.append(bold(getString( .append(bold(getString(
@@ -416,42 +535,7 @@ public class NotificationStation extends SettingsPreferenceFragment {
.append(delim) .append(delim)
.append(getTitleString(n.publicVersion)); .append(getTitleString(n.publicVersion));
} }
sb.append("\n")
.append(bold(getString(R.string.notification_log_details_priority)))
.append(delim)
.append(Notification.priorityToString(n.priority));
if (info.active) {
// mRanking only applies to active notifications
if (mRanking != null && mRanking.getRanking(sbn.getKey(), rank)) {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_importance)))
.append(delim)
.append(Ranking.importanceToString(rank.getImportance()));
if (rank.getImportanceExplanation() != null) {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_explanation)))
.append(delim)
.append(rank.getImportanceExplanation());
}
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_badge)))
.append(delim)
.append(Boolean.toString(rank.canShowBadge()));
} else {
if (mRanking == null) {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_ranking_null)));
} else {
sb.append("\n")
.append(bold(getString(
R.string.notification_log_details_ranking_none)));
}
}
}
if (n.contentIntent != null) { if (n.contentIntent != null) {
sb.append("\n") sb.append("\n")
.append(bold(getString( .append(bold(getString(
@@ -535,25 +619,6 @@ public class NotificationStation extends SettingsPreferenceFragment {
return sb; return sb;
} }
private Resources getResourcesForUserPackage(String pkg, int userId) {
Resources r = null;
if (pkg != null) {
try {
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
}
r = mPm.getResourcesForApplicationAsUser(pkg, userId);
} catch (PackageManager.NameNotFoundException ex) {
Log.e(TAG, "Icon package not found: " + pkg, ex);
return null;
}
} else {
r = mContext.getResources();
}
return r;
}
private Drawable loadPackageIconDrawable(String pkg, int userId) { private Drawable loadPackageIconDrawable(String pkg, int userId) {
Drawable icon = null; Drawable icon = null;
try { try {
@@ -576,31 +641,17 @@ public class NotificationStation extends SettingsPreferenceFragment {
return pkg; return pkg;
} }
private Drawable loadIconDrawable(String pkg, int userId, int resId) {
Resources r = getResourcesForUserPackage(pkg, userId);
if (resId == 0) {
return null;
}
try {
return r.getDrawable(resId, null);
} catch (RuntimeException e) {
Log.w(TAG, "Icon not found in "
+ (pkg != null ? resId : "<system>")
+ ": " + Integer.toHexString(resId), e);
}
return null;
}
private static class HistoricalNotificationPreference extends Preference { private static class HistoricalNotificationPreference extends Preference {
private final HistoricalNotificationInfo mInfo; private final HistoricalNotificationInfo mInfo;
private static long sLastExpandedTimestamp; // quick hack to keep things from collapsing private static long sLastExpandedTimestamp; // quick hack to keep things from collapsing
public ViewGroup mItemView; // hack to update prefs fast;
public HistoricalNotificationPreference(Context context, HistoricalNotificationInfo info) { public HistoricalNotificationPreference(Context context, HistoricalNotificationInfo info,
int order) {
super(context); super(context);
setLayoutResource(R.layout.notification_log_row); setLayoutResource(R.layout.notification_log_row);
setOrder(order);
setKey(info.key);
mInfo = info; mInfo = info;
} }
@@ -608,41 +659,67 @@ public class NotificationStation extends SettingsPreferenceFragment {
public void onBindViewHolder(PreferenceViewHolder row) { public void onBindViewHolder(PreferenceViewHolder row) {
super.onBindViewHolder(row); super.onBindViewHolder(row);
if (mInfo.icon != null) { mItemView = (ViewGroup) row.itemView;
((ImageView) row.findViewById(R.id.icon)).setImageDrawable(mInfo.icon);
updatePreference(mInfo);
row.findViewById(R.id.timestamp).setOnLongClickListener(v -> {
final View extras = row.findViewById(R.id.extra);
extras.setVisibility(extras.getVisibility() == View.VISIBLE
? View.GONE : View.VISIBLE);
sLastExpandedTimestamp = mInfo.timestamp;
return false;
});
}
public void updatePreference(HistoricalNotificationInfo info) {
if (mItemView == null) {
return;
} }
if (mInfo.pkgicon != null) { if (info.icon != null) {
((ImageView) row.findViewById(R.id.pkgicon)).setImageDrawable(mInfo.pkgicon); ((ImageView) mItemView.findViewById(R.id.icon)).setImageDrawable(mInfo.icon);
}
((TextView) mItemView.findViewById(R.id.pkgname)).setText(mInfo.pkgname);
((DateTimeView) mItemView.findViewById(R.id.timestamp)).setTime(info.timestamp);
if (!TextUtils.isEmpty(info.title)) {
((TextView) mItemView.findViewById(R.id.title)).setText(info.title);
mItemView.findViewById(R.id.title).setVisibility(View.VISIBLE);
} else {
mItemView.findViewById(R.id.title).setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(info.text)) {
((TextView) mItemView.findViewById(R.id.text)).setText(info.text);
mItemView.findViewById(R.id.text).setVisibility(View.VISIBLE);
} else {
mItemView.findViewById(R.id.text).setVisibility(View.GONE);
}
if (info.icon != null) {
((ImageView) mItemView.findViewById(R.id.icon)).setImageDrawable(info.icon);
} }
((DateTimeView) row.findViewById(R.id.timestamp)).setTime(mInfo.timestamp); ((DateTimeView) mItemView.findViewById(R.id.timestamp)).setTime(mInfo.timestamp);
((TextView) row.findViewById(R.id.title)).setText(mInfo.title);
((TextView) row.findViewById(R.id.pkgname)).setText(mInfo.pkgname);
final TextView extra = (TextView) row.findViewById(R.id.extra); ((TextView) mItemView.findViewById(R.id.notification_extra))
extra.setText(mInfo.extra); .setText(mInfo.notificationExtra);
extra.setVisibility(mInfo.timestamp == sLastExpandedTimestamp ((TextView) mItemView.findViewById(R.id.ranking_extra))
? View.VISIBLE : View.GONE); .setText(mInfo.rankingExtra);
row.itemView.setOnClickListener( mItemView.findViewById(R.id.extra).setVisibility(
new View.OnClickListener() { mInfo.timestamp == sLastExpandedTimestamp ? View.VISIBLE : View.GONE);
@Override
public void onClick(View view) {
extra.setVisibility(extra.getVisibility() == View.VISIBLE
? View.GONE : View.VISIBLE);
sLastExpandedTimestamp = mInfo.timestamp;
}
});
row.itemView.setAlpha(mInfo.active ? 1.0f : 0.5f); mItemView.setAlpha(mInfo.active ? 1.0f : 0.5f);
mItemView.findViewById(R.id.alerted_icon).setVisibility(
mInfo.alerted ? View.VISIBLE : View.GONE);
} }
@Override @Override
public void performClick() { public void performClick() {
// Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
// Uri.fromParts("package", mInfo.pkg, null)); .putExtra(EXTRA_APP_PACKAGE, mInfo.pkg)
// intent.setComponent(intent.resolveActivity(getContext().getPackageManager())); .putExtra(EXTRA_CHANNEL_ID, mInfo.channel.getId());
// getContext().startActivity(intent); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
} }
} }
} }