Loading src/com/android/settings/notification/NotificationBackend.java +18 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY import static com.android.server.notification.Flags.notificationHideUnusedChannels; import android.annotation.FlaggedApi; import android.app.Flags; import android.app.INotificationManager; import android.app.NotificationChannel; Loading Loading @@ -67,10 +68,13 @@ import com.android.settingslib.utils.StringUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; public class NotificationBackend { private static final String TAG = "NotificationBackend"; Loading Loading @@ -368,6 +372,20 @@ public class NotificationBackend { } } /** * Returns a set of all apps that have any notification channels (not including deleted ones). */ @FlaggedApi(Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS) public @NonNull Set<String> getPackagesWithAnyChannels(int userId) { try { List<String> packages = sINM.getPackagesWithAnyChannels(userId); return new HashSet<>(packages); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return Collections.EMPTY_SET; } } public void updateChannel(String pkg, int uid, NotificationChannel channel) { try { sINM.updateNotificationChannelForPackage(pkg, uid, channel); Loading src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java +50 −10 Original line number Diff line number Diff line Loading @@ -17,11 +17,13 @@ package com.android.settings.notification.modes; import android.app.Application; import android.app.Flags; import android.app.settings.SettingsEnums; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; Loading @@ -44,7 +46,11 @@ import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.AppPreference; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** Loading @@ -58,6 +64,8 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere private static final String KEY = "zen_mode_non_bypassing_apps_list"; private static final String KEY_ADD = "zen_mode_bypassing_apps_add"; @Nullable private final NotificationBackend mNotificationBackend; @Nullable private final ZenHelperBackend mHelperBackend; @Nullable private final UserManager mUserManager; @Nullable @VisibleForTesting ApplicationsState mApplicationsState; @VisibleForTesting PreferenceScreen mPreferenceScreen; Loading @@ -69,18 +77,22 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere @Nullable private Fragment mHostFragment; public ZenModeAddBypassingAppsPreferenceController(Context context, @Nullable Application app, @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) { @Nullable Fragment host, @Nullable NotificationBackend notificationBackend, @Nullable ZenHelperBackend helperBackend) { this(context, app == null ? null : ApplicationsState.getInstance(app), host, notificationBackend); notificationBackend, helperBackend); } private ZenModeAddBypassingAppsPreferenceController(Context context, @Nullable ApplicationsState appState, @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) { @Nullable NotificationBackend notificationBackend, @Nullable ZenHelperBackend helperBackend) { super(context); mNotificationBackend = notificationBackend; mApplicationsState = appState; mHostFragment = host; mHelperBackend = helperBackend; mUserManager = context.getSystemService(UserManager.class); } @Override Loading Loading @@ -147,7 +159,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere @VisibleForTesting void updateAppList(List<ApplicationsState.AppEntry> apps) { if (apps == null) { if (apps == null || mNotificationBackend == null) { return; } Loading @@ -157,21 +169,49 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere mPreferenceScreen.addPreference(mPreferenceCategory); } Map<Integer, Set<String>> packagesByUser = new HashMap<>(); Map<Integer, Map<String, Boolean>> packagesBypassingDndByUser = new HashMap<>(); if (Flags.nmBinderPerfGetAppsWithChannels()) { if (mHelperBackend == null || mUserManager == null) { return; } for (UserHandle userHandle : mUserManager.getUserProfiles()) { int userId = userHandle.getIdentifier(); packagesByUser.put(userId, mNotificationBackend.getPackagesWithAnyChannels(userId)); packagesBypassingDndByUser.put(userId, mHelperBackend.getPackagesBypassingDnd(userId)); } } boolean doAnyAppsPassCriteria = false; for (ApplicationsState.AppEntry app : apps) { String pkg = app.info.packageName; final String key = getKey(pkg, app.info.uid); int userId = UserHandle.getUserId(app.info.uid); boolean doesAppBypassDnd, doesAppHaveAnyChannels; if (Flags.nmBinderPerfGetAppsWithChannels()) { Set<String> packagesWithChannels = packagesByUser.getOrDefault(userId, Collections.EMPTY_SET); Map<String, Boolean> packagesBypassingDnd = packagesBypassingDndByUser.getOrDefault(userId, new HashMap<>()); doesAppBypassDnd = packagesBypassingDnd.containsKey(pkg); doesAppHaveAnyChannels = packagesWithChannels.contains(pkg); } else { final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid); final int appChannelsBypassingDnd = mNotificationBackend .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size(); if (appChannelsBypassingDnd == 0 && appChannels > 0) { doesAppBypassDnd = (appChannelsBypassingDnd != 0); doesAppHaveAnyChannels = (appChannels > 0); } if (!doesAppBypassDnd && doesAppHaveAnyChannels) { doAnyAppsPassCriteria = true; } Preference pref = mPreferenceCategory.findPreference(key); if (pref == null) { if (appChannelsBypassingDnd == 0 && appChannels > 0) { if (!doesAppBypassDnd && doesAppHaveAnyChannels) { // does not exist but should pref = new AppPreference(mPrefContext); pref.setKey(key); Loading @@ -193,7 +233,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere updateIcon(pref, app); mPreferenceCategory.addPreference(pref); } } else if (appChannelsBypassingDnd != 0 || appChannels == 0) { } else if (doesAppBypassDnd || !doesAppHaveAnyChannels) { // exists but shouldn't anymore mPreferenceCategory.removePreference(pref); } Loading src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase impl controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host, zenHelperBackend)); controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host, notificationBackend)); notificationBackend, zenHelperBackend)); return controllers; } Loading tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java +27 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.settings.notification.modes; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; Loading @@ -30,6 +31,8 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.UsesFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import androidx.fragment.app.Fragment; Loading @@ -46,36 +49,50 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.ParameterizedRobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @RunWith(RobolectricTestRunner.class) @RunWith(ParameterizedRobolectricTestRunner.class) @UsesFlags(android.app.Flags.class) @EnableFlags(Flags.FLAG_MODES_UI) public class ZenModeAddBypassingAppsPreferenceControllerTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private NotificationBackend mBackend; @Mock private ZenHelperBackend mHelperBackend; @Mock private PreferenceCategory mPreferenceCategory; @Mock private ApplicationsState mApplicationState; private ZenModeAddBypassingAppsPreferenceController mController; private Context mContext; @ParameterizedRobolectricTestRunner.Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return FlagsParameterization.allCombinationsOf( Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS); } public ZenModeAddBypassingAppsPreferenceControllerTest(FlagsParameterization flags) { mSetFlagsRule.setFlagsParameterization(flags); } @Before public void setup() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mController = new ZenModeAddBypassingAppsPreferenceController( mContext, null, mock(Fragment.class), mBackend); mContext, null, mock(Fragment.class), mBackend, mHelperBackend); mController.mPreferenceCategory = mPreferenceCategory; mController.mApplicationsState = mApplicationState; mController.mPrefContext = mContext; Loading Loading @@ -132,6 +149,12 @@ public class ZenModeAddBypassingAppsPreferenceControllerTest { appWithChannelsNoneBypassing.info.uid)) .thenReturn(new ParceledListSlice<>(new ArrayList<>())); // used when NM_BINDER_PERF_GET_APPS_WITH_CHANNELS flag is true when(mBackend.getPackagesWithAnyChannels(anyInt())).thenReturn( Set.of("appWithBypassingChannels", "appWithChannelsNoneBypassing")); when(mHelperBackend.getPackagesBypassingDnd(anyInt())).thenReturn( Map.of("appWithBypassingChannels", false)); List<ApplicationsState.AppEntry> appEntries = new ArrayList<>(); appEntries.add(appWithBypassingChannels); appEntries.add(appWithoutChannels); Loading Loading
src/com/android/settings/notification/NotificationBackend.java +18 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY import static com.android.server.notification.Flags.notificationHideUnusedChannels; import android.annotation.FlaggedApi; import android.app.Flags; import android.app.INotificationManager; import android.app.NotificationChannel; Loading Loading @@ -67,10 +68,13 @@ import com.android.settingslib.utils.StringUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; public class NotificationBackend { private static final String TAG = "NotificationBackend"; Loading Loading @@ -368,6 +372,20 @@ public class NotificationBackend { } } /** * Returns a set of all apps that have any notification channels (not including deleted ones). */ @FlaggedApi(Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS) public @NonNull Set<String> getPackagesWithAnyChannels(int userId) { try { List<String> packages = sINM.getPackagesWithAnyChannels(userId); return new HashSet<>(packages); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return Collections.EMPTY_SET; } } public void updateChannel(String pkg, int uid, NotificationChannel channel) { try { sINM.updateNotificationChannelForPackage(pkg, uid, channel); Loading
src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java +50 −10 Original line number Diff line number Diff line Loading @@ -17,11 +17,13 @@ package com.android.settings.notification.modes; import android.app.Application; import android.app.Flags; import android.app.settings.SettingsEnums; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; Loading @@ -44,7 +46,11 @@ import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.AppPreference; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** Loading @@ -58,6 +64,8 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere private static final String KEY = "zen_mode_non_bypassing_apps_list"; private static final String KEY_ADD = "zen_mode_bypassing_apps_add"; @Nullable private final NotificationBackend mNotificationBackend; @Nullable private final ZenHelperBackend mHelperBackend; @Nullable private final UserManager mUserManager; @Nullable @VisibleForTesting ApplicationsState mApplicationsState; @VisibleForTesting PreferenceScreen mPreferenceScreen; Loading @@ -69,18 +77,22 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere @Nullable private Fragment mHostFragment; public ZenModeAddBypassingAppsPreferenceController(Context context, @Nullable Application app, @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) { @Nullable Fragment host, @Nullable NotificationBackend notificationBackend, @Nullable ZenHelperBackend helperBackend) { this(context, app == null ? null : ApplicationsState.getInstance(app), host, notificationBackend); notificationBackend, helperBackend); } private ZenModeAddBypassingAppsPreferenceController(Context context, @Nullable ApplicationsState appState, @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) { @Nullable NotificationBackend notificationBackend, @Nullable ZenHelperBackend helperBackend) { super(context); mNotificationBackend = notificationBackend; mApplicationsState = appState; mHostFragment = host; mHelperBackend = helperBackend; mUserManager = context.getSystemService(UserManager.class); } @Override Loading Loading @@ -147,7 +159,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere @VisibleForTesting void updateAppList(List<ApplicationsState.AppEntry> apps) { if (apps == null) { if (apps == null || mNotificationBackend == null) { return; } Loading @@ -157,21 +169,49 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere mPreferenceScreen.addPreference(mPreferenceCategory); } Map<Integer, Set<String>> packagesByUser = new HashMap<>(); Map<Integer, Map<String, Boolean>> packagesBypassingDndByUser = new HashMap<>(); if (Flags.nmBinderPerfGetAppsWithChannels()) { if (mHelperBackend == null || mUserManager == null) { return; } for (UserHandle userHandle : mUserManager.getUserProfiles()) { int userId = userHandle.getIdentifier(); packagesByUser.put(userId, mNotificationBackend.getPackagesWithAnyChannels(userId)); packagesBypassingDndByUser.put(userId, mHelperBackend.getPackagesBypassingDnd(userId)); } } boolean doAnyAppsPassCriteria = false; for (ApplicationsState.AppEntry app : apps) { String pkg = app.info.packageName; final String key = getKey(pkg, app.info.uid); int userId = UserHandle.getUserId(app.info.uid); boolean doesAppBypassDnd, doesAppHaveAnyChannels; if (Flags.nmBinderPerfGetAppsWithChannels()) { Set<String> packagesWithChannels = packagesByUser.getOrDefault(userId, Collections.EMPTY_SET); Map<String, Boolean> packagesBypassingDnd = packagesBypassingDndByUser.getOrDefault(userId, new HashMap<>()); doesAppBypassDnd = packagesBypassingDnd.containsKey(pkg); doesAppHaveAnyChannels = packagesWithChannels.contains(pkg); } else { final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid); final int appChannelsBypassingDnd = mNotificationBackend .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size(); if (appChannelsBypassingDnd == 0 && appChannels > 0) { doesAppBypassDnd = (appChannelsBypassingDnd != 0); doesAppHaveAnyChannels = (appChannels > 0); } if (!doesAppBypassDnd && doesAppHaveAnyChannels) { doAnyAppsPassCriteria = true; } Preference pref = mPreferenceCategory.findPreference(key); if (pref == null) { if (appChannelsBypassingDnd == 0 && appChannels > 0) { if (!doesAppBypassDnd && doesAppHaveAnyChannels) { // does not exist but should pref = new AppPreference(mPrefContext); pref.setKey(key); Loading @@ -193,7 +233,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere updateIcon(pref, app); mPreferenceCategory.addPreference(pref); } } else if (appChannelsBypassingDnd != 0 || appChannels == 0) { } else if (doesAppBypassDnd || !doesAppHaveAnyChannels) { // exists but shouldn't anymore mPreferenceCategory.removePreference(pref); } Loading
src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase impl controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host, zenHelperBackend)); controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host, notificationBackend)); notificationBackend, zenHelperBackend)); return controllers; } Loading
tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java +27 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.settings.notification.modes; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; Loading @@ -30,6 +31,8 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.UsesFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import androidx.fragment.app.Fragment; Loading @@ -46,36 +49,50 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.ParameterizedRobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @RunWith(RobolectricTestRunner.class) @RunWith(ParameterizedRobolectricTestRunner.class) @UsesFlags(android.app.Flags.class) @EnableFlags(Flags.FLAG_MODES_UI) public class ZenModeAddBypassingAppsPreferenceControllerTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private NotificationBackend mBackend; @Mock private ZenHelperBackend mHelperBackend; @Mock private PreferenceCategory mPreferenceCategory; @Mock private ApplicationsState mApplicationState; private ZenModeAddBypassingAppsPreferenceController mController; private Context mContext; @ParameterizedRobolectricTestRunner.Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return FlagsParameterization.allCombinationsOf( Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS); } public ZenModeAddBypassingAppsPreferenceControllerTest(FlagsParameterization flags) { mSetFlagsRule.setFlagsParameterization(flags); } @Before public void setup() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mController = new ZenModeAddBypassingAppsPreferenceController( mContext, null, mock(Fragment.class), mBackend); mContext, null, mock(Fragment.class), mBackend, mHelperBackend); mController.mPreferenceCategory = mPreferenceCategory; mController.mApplicationsState = mApplicationState; mController.mPrefContext = mContext; Loading Loading @@ -132,6 +149,12 @@ public class ZenModeAddBypassingAppsPreferenceControllerTest { appWithChannelsNoneBypassing.info.uid)) .thenReturn(new ParceledListSlice<>(new ArrayList<>())); // used when NM_BINDER_PERF_GET_APPS_WITH_CHANNELS flag is true when(mBackend.getPackagesWithAnyChannels(anyInt())).thenReturn( Set.of("appWithBypassingChannels", "appWithChannelsNoneBypassing")); when(mHelperBackend.getPackagesBypassingDnd(anyInt())).thenReturn( Map.of("appWithBypassingChannels", false)); List<ApplicationsState.AppEntry> appEntries = new ArrayList<>(); appEntries.add(appWithBypassingChannels); appEntries.add(appWithoutChannels); Loading