Loading src/com/android/settings/notification/NotificationBackend.java +94 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 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; Loading @@ -28,21 +30,30 @@ import android.content.pm.PackageInfo; 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)); Loading @@ -62,6 +73,7 @@ public class NotificationBackend { 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; } Loading Loading @@ -259,6 +271,87 @@ public class NotificationBackend { } } 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; } Loading @@ -278,5 +371,6 @@ public class NotificationBackend { public int userId; public int blockedChannelCount; public int channelCount; public Map<String, NotificationsSentState> sentByChannel; } } src/com/android/settings/notification/NotificationSettingsBase.java +2 −0 Original line number Diff line number Diff line Loading @@ -276,6 +276,8 @@ abstract public class NotificationSettingsBase extends DashboardFragment { && !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); Loading tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java +3 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify; 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; Loading Loading @@ -78,6 +79,8 @@ public class AppNotificationPreferenceControllerTest { 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); Loading tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java +57 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -118,4 +132,47 @@ public class NotificationBackendTest { 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); } } Loading
src/com/android/settings/notification/NotificationBackend.java +94 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 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; Loading @@ -28,21 +30,30 @@ import android.content.pm.PackageInfo; 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)); Loading @@ -62,6 +73,7 @@ public class NotificationBackend { 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; } Loading Loading @@ -259,6 +271,87 @@ public class NotificationBackend { } } 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; } Loading @@ -278,5 +371,6 @@ public class NotificationBackend { public int userId; public int blockedChannelCount; public int channelCount; public Map<String, NotificationsSentState> sentByChannel; } }
src/com/android/settings/notification/NotificationSettingsBase.java +2 −0 Original line number Diff line number Diff line Loading @@ -276,6 +276,8 @@ abstract public class NotificationSettingsBase extends DashboardFragment { && !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); Loading
tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java +3 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify; 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; Loading Loading @@ -78,6 +79,8 @@ public class AppNotificationPreferenceControllerTest { 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); Loading
tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java +57 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -118,4 +132,47 @@ public class NotificationBackendTest { 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); } }