Add notification sent count to channel settings
Bug: 79607096
Test: robotests
Change-Id: I3c1d8fd1cbd9f5b8e997f1bfd50926121a5040fb
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 5e8ef19..314a99e 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -21,6 +21,8 @@
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.app.usage.IUsageStatsManager;
+import android.app.usage.UsageEvents;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -28,21 +30,30 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.notification.NotifyingApp;
+import android.text.format.DateUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.R;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.StringUtil;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class NotificationBackend {
private static final String TAG = "NotificationBackend";
+ static IUsageStatsManager sUsageStatsManager = IUsageStatsManager.Stub.asInterface(
+ ServiceManager.getService(Context.USAGE_STATS_SERVICE));
+ private static final int DAYS_TO_CHECK = 7;
static INotificationManager sINM = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
@@ -62,6 +73,7 @@
row.userId = UserHandle.getUserId(row.uid);
row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
row.channelCount = getChannelCount(row.pkg, row.uid);
+ row.sentByChannel = getAggregatedUsageEvents(context, row.userId, row.pkg);
return row;
}
@@ -259,6 +271,87 @@
}
}
+ protected Map<String, NotificationsSentState> getAggregatedUsageEvents(
+ Context context, int userId, String pkg) {
+ long now = System.currentTimeMillis();
+ long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
+ UsageEvents events = null;
+ try {
+ events = sUsageStatsManager.queryEventsForPackageForUser(
+ startTime, now, userId, pkg, context.getPackageName());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return getAggregatedUsageEvents(events);
+ }
+
+ protected Map<String, NotificationsSentState> getAggregatedUsageEvents(UsageEvents events) {
+ Map<String, NotificationsSentState> sentByChannel = new HashMap<>();
+ if (events != null) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ while (events.hasNextEvent()) {
+ events.getNextEvent(event);
+
+ if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ String channelId = event.mNotificationChannelId;
+ if (channelId != null) {
+ NotificationsSentState stats = sentByChannel.get(channelId);
+ if (stats == null) {
+ stats = new NotificationsSentState();
+ sentByChannel.put(channelId, stats);
+ }
+ if (event.getTimeStamp() > stats.lastSent) {
+ stats.lastSent = event.getTimeStamp();
+ }
+ stats.sentCount++;
+ calculateAvgSentCounts(stats);
+ }
+ }
+
+ }
+ }
+ return sentByChannel;
+ }
+
+ public static CharSequence getSentSummary(Context context, NotificationsSentState state,
+ boolean sortByRecency) {
+ if (state == null) {
+ return null;
+ }
+ if (sortByRecency) {
+ if (state.lastSent == 0) {
+ return context.getString(R.string.notifications_sent_never);
+ }
+ return StringUtil.formatRelativeTime(
+ context, System.currentTimeMillis() - state.lastSent, true);
+ } else {
+ if (state.avgSentWeekly > 0) {
+ return context.getString(R.string.notifications_sent_weekly, state.avgSentWeekly);
+ }
+ return context.getString(R.string.notifications_sent_daily, state.avgSentDaily);
+ }
+ }
+
+ private void calculateAvgSentCounts(NotificationsSentState stats) {
+ if (stats != null) {
+ stats.avgSentDaily = Math.round((float) stats.sentCount / DAYS_TO_CHECK);
+ if (stats.sentCount < DAYS_TO_CHECK) {
+ stats.avgSentWeekly = stats.sentCount;
+ }
+ }
+ }
+
+ /**
+ * NotificationsSentState contains how often an app sends notifications and how recently it sent
+ * one.
+ */
+ public static class NotificationsSentState {
+ public int avgSentDaily = 0;
+ public int avgSentWeekly = 0;
+ public long lastSent = 0;
+ public int sentCount = 0;
+ }
+
static class Row {
public String section;
}
@@ -278,5 +371,6 @@
public int userId;
public int blockedChannelCount;
public int channelCount;
+ public Map<String, NotificationsSentState> sentByChannel;
}
}
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 9f53334..e1c5876 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -276,6 +276,8 @@
&& !groupBlocked);
channelPref.setKey(channel.getId());
channelPref.setTitle(channel.getName());
+ channelPref.setSummary(NotificationBackend.getSentSummary(
+ mContext, mAppRow.sentByChannel.get(channel.getId()), false));
channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
Bundle channelArgs = new Bundle();
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
index 9678ecb..8b1190e 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.when;
import android.app.Activity;
+import android.app.usage.IUsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -78,6 +79,8 @@
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
+ NotificationBackend backend = new NotificationBackend();
+ ReflectionHelpers.setField(backend, "sUsageStatsManager", mock(IUsageStatsManager.class));
ReflectionHelpers.setField(mController, "mBackend", new NotificationBackend());
mController.displayPreference(mScreen);
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
index 4d7b07c..c725962 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
@@ -16,17 +16,31 @@
package com.android.settings.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.UsageEvents;
+import android.os.Parcel;
+
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
@RunWith(SettingsRobolectricTestRunner.class)
public class NotificationBackendTest {
@@ -118,4 +132,47 @@
assertTrue(appRow.lockedImportance);
assertEquals("SpecificChannel", appRow.lockedChannelId);
}
+
+ @Test
+ public void testGetAggregatedUsageEvents_multipleEventsAgg() {
+ List<UsageEvents.Event> events = new ArrayList<>();
+ UsageEvents.Event good = new UsageEvents.Event();
+ good.mEventType = UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+ good.mPackage = "pkg";
+ good.mNotificationChannelId = "channel1";
+ good.mTimeStamp = 2;
+ events.add(good);
+ UsageEvents.Event good1 = new UsageEvents.Event();
+ good1.mEventType = UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+ good1.mPackage = "pkg";
+ good1.mNotificationChannelId = "channel1";
+ good1.mTimeStamp = 6;
+ events.add(good1);
+ UsageEvents.Event good2 = new UsageEvents.Event();
+ good2.mEventType = UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+ good2.mPackage = "pkg";
+ good2.mNotificationChannelId = "channel2";
+ good2.mTimeStamp = 3;
+ events.add(good2);
+ NotificationBackend backend = new NotificationBackend();
+
+ Map<String, NotificationBackend.NotificationsSentState> stats =
+ backend.getAggregatedUsageEvents(getUsageEvents(events));
+
+ assertThat(stats.get("channel1").sentCount).isEqualTo(2);
+ assertThat(stats.get("channel1").lastSent).isEqualTo(6);
+ assertThat(stats.get("channel1").avgSentWeekly).isEqualTo(2);
+ assertThat(stats.get("channel2").sentCount).isEqualTo(1);
+ assertThat(stats.get("channel2").lastSent).isEqualTo(3);
+ assertThat(stats.get("channel2").avgSentWeekly).isEqualTo(1);
+ }
+
+ private UsageEvents getUsageEvents(List<UsageEvents.Event> events) {
+ UsageEvents usageEvents = new UsageEvents(events, new String[] {"pkg"});
+ Parcel parcel = Parcel.obtain();
+ parcel.setDataPosition(0);
+ usageEvents.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return UsageEvents.CREATOR.createFromParcel(parcel);
+ }
}