Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85a63957 authored by Julia Reynolds's avatar Julia Reynolds Committed by Android (Google) Code Review
Browse files

Merge "Add notification sent count to channel settings"

parents 50c8cdbf 8ebfb12c
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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));

@@ -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;
    }

@@ -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;
    }
@@ -278,5 +371,6 @@ public class NotificationBackend {
        public int userId;
        public int blockedChannelCount;
        public int channelCount;
        public Map<String, NotificationsSentState> sentByChannel;
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);

+57 −0
Original line number Diff line number Diff line
@@ -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 @@ 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);
    }
}