Modify Summary for Mode's Apps settings page
Adds call to SummaryHelper to set Apps preference summary. Bug: 308819928 Test: atest ZenModeAppsLinkPreferenceControllerTest Flag: android.app.modes_ui Change-Id: Iebec11afc62ecb79386e1866af57cd4e68461a95
This commit is contained in:
@@ -357,6 +357,19 @@ public class NotificationBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all of a user's packages that have at least one channel that will bypass DND
|
||||||
|
*/
|
||||||
|
public List<String> getPackagesBypassingDnd(int userId,
|
||||||
|
boolean includeConversationChannels) {
|
||||||
|
try {
|
||||||
|
return sINM.getPackagesBypassingDnd(userId, includeConversationChannels);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error calling NoMan", e);
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateChannel(String pkg, int uid, NotificationChannel channel) {
|
public void updateChannel(String pkg, int uid, NotificationChannel channel) {
|
||||||
try {
|
try {
|
||||||
sINM.updateNotificationChannelForPackage(pkg, uid, channel);
|
sINM.updateNotificationChannelForPackage(pkg, uid, channel);
|
||||||
|
@@ -20,23 +20,44 @@ import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_I
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.core.text.BidiFormatter;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
|
import com.android.settings.notification.NotificationBackend;
|
||||||
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preference with a link and summary about what apps can break through the mode
|
* Preference with a link and summary about what apps can break through the mode
|
||||||
*/
|
*/
|
||||||
public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController {
|
class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController {
|
||||||
|
|
||||||
|
private static final String TAG = "ZenModeAppsLinkPreferenceController";
|
||||||
|
|
||||||
private final ZenModeSummaryHelper mSummaryHelper;
|
private final ZenModeSummaryHelper mSummaryHelper;
|
||||||
|
private ApplicationsState.Session mAppSession;
|
||||||
|
private NotificationBackend mNotificationBackend = new NotificationBackend();
|
||||||
|
private ZenMode mZenMode;
|
||||||
|
private Preference mPreference;
|
||||||
|
|
||||||
public ZenModeAppsLinkPreferenceController(Context context, String key,
|
ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host,
|
||||||
ZenModesBackend backend) {
|
ApplicationsState applicationsState, ZenModesBackend backend) {
|
||||||
super(context, key, backend);
|
super(context, key, backend);
|
||||||
mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
|
mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
|
||||||
|
if (applicationsState != null && host != null) {
|
||||||
|
mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,6 +70,84 @@ public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferen
|
|||||||
.setSourceMetricsCategory(0)
|
.setSourceMetricsCategory(0)
|
||||||
.setArguments(bundle)
|
.setArguments(bundle)
|
||||||
.toIntent());
|
.toIntent());
|
||||||
preference.setSummary(mSummaryHelper.getAppsSummary(zenMode));
|
mZenMode = zenMode;
|
||||||
|
mPreference = preference;
|
||||||
|
triggerUpdateAppsBypassingDndSummaryText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void triggerUpdateAppsBypassingDndSummaryText() {
|
||||||
|
if (mAppSession == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures()
|
||||||
|
&& android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
|
||||||
|
? ApplicationsState.FILTER_ENABLED_NOT_QUIET
|
||||||
|
: ApplicationsState.FILTER_ALL_ENABLED;
|
||||||
|
// We initiate a rebuild in the background here. Once the rebuild is completed,
|
||||||
|
// the onRebuildComplete() callback will be invoked, which will trigger the summary text
|
||||||
|
// to be initialized.
|
||||||
|
mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAppsBypassingDndSummaryText(List<ApplicationsState.AppEntry> apps) {
|
||||||
|
Set<String> appNames = getAppsBypassingDnd(apps);
|
||||||
|
mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, appNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
ArraySet<String> getAppsBypassingDnd(@NonNull List<ApplicationsState.AppEntry> apps) {
|
||||||
|
ArraySet<String> appsBypassingDnd = new ArraySet<>();
|
||||||
|
|
||||||
|
Map<String, String> pkgLabelMap = new HashMap<String, String>();
|
||||||
|
for (ApplicationsState.AppEntry entry : apps) {
|
||||||
|
if (entry.info != null) {
|
||||||
|
pkgLabelMap.put(entry.info.packageName, entry.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String pkg : mNotificationBackend.getPackagesBypassingDnd(mContext.getUserId(),
|
||||||
|
/* includeConversationChannels= */ false)) {
|
||||||
|
// Settings may hide some packages from the user, so if they're not present here
|
||||||
|
// we skip displaying them, even if they bypass dnd.
|
||||||
|
if (pkgLabelMap.get(pkg) == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
appsBypassingDnd.add(BidiFormatter.getInstance().unicodeWrap(pkgLabelMap.get(pkg)));
|
||||||
|
}
|
||||||
|
return appsBypassingDnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting final ApplicationsState.Callbacks mAppSessionCallbacks =
|
||||||
|
new ApplicationsState.Callbacks() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRunningStateChanged(boolean running) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPackageListChanged() {
|
||||||
|
triggerUpdateAppsBypassingDndSummaryText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
|
||||||
|
updateAppsBypassingDndSummaryText(apps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPackageIconChanged() { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPackageSizeChanged(String packageName) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllSizesComputed() { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLauncherInfoChanged() { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadEntriesCompleted() {
|
||||||
|
triggerUpdateAppsBypassingDndSummaryText();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
package com.android.settings.notification.modes;
|
package com.android.settings.notification.modes;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
import android.app.AutomaticZenRule;
|
import android.app.AutomaticZenRule;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -42,7 +44,9 @@ public class ZenModeFragment extends ZenModeFragmentBase {
|
|||||||
prefControllers.add(new ZenModePeopleLinkPreferenceController(
|
prefControllers.add(new ZenModePeopleLinkPreferenceController(
|
||||||
context, "zen_mode_people", mBackend));
|
context, "zen_mode_people", mBackend));
|
||||||
prefControllers.add(new ZenModeAppsLinkPreferenceController(
|
prefControllers.add(new ZenModeAppsLinkPreferenceController(
|
||||||
context, "zen_mode_apps", mBackend));
|
context, "zen_mode_apps", this,
|
||||||
|
ApplicationsState.getInstance((Application) context.getApplicationContext()),
|
||||||
|
mBackend));
|
||||||
prefControllers.add(new ZenModeOtherLinkPreferenceController(
|
prefControllers.add(new ZenModeOtherLinkPreferenceController(
|
||||||
context, "zen_other_settings", mBackend));
|
context, "zen_other_settings", mBackend));
|
||||||
prefControllers.add(new ZenModeDisplayLinkPreferenceController(
|
prefControllers.add(new ZenModeDisplayLinkPreferenceController(
|
||||||
|
@@ -16,28 +16,51 @@
|
|||||||
|
|
||||||
package com.android.settings.notification.modes;
|
package com.android.settings.notification.modes;
|
||||||
|
|
||||||
|
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
|
||||||
|
|
||||||
|
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.AutomaticZenRule;
|
import android.app.AutomaticZenRule;
|
||||||
import android.app.Flags;
|
import android.app.Flags;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.platform.test.annotations.EnableFlags;
|
import android.platform.test.annotations.EnableFlags;
|
||||||
import android.platform.test.flag.junit.SetFlagsRule;
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
import android.service.notification.ZenPolicy;
|
import android.service.notification.ZenPolicy;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.notification.NotificationBackend;
|
||||||
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settingslib.widget.SelectorWithWidgetPreference;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@EnableFlags(Flags.FLAG_MODES_UI)
|
@EnableFlags(Flags.FLAG_MODES_UI)
|
||||||
@@ -47,7 +70,15 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
|||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
@Mock
|
@Mock
|
||||||
private ZenModesBackend mBackend;
|
private ZenModesBackend mZenModesBackend;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private NotificationBackend mNotificationBackend;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ApplicationsState mApplicationsState;
|
||||||
|
@Mock
|
||||||
|
private ApplicationsState.Session mSession;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
@@ -56,21 +87,109 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
|||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mContext = RuntimeEnvironment.application;
|
mContext = RuntimeEnvironment.application;
|
||||||
|
when(mApplicationsState.newSession(any(), any())).thenReturn(mSession);
|
||||||
mController = new ZenModeAppsLinkPreferenceController(
|
mController = new ZenModeAppsLinkPreferenceController(
|
||||||
mContext, "controller_key", mBackend);
|
mContext, "controller_key", mock(Fragment.class), mApplicationsState,
|
||||||
|
mZenModesBackend);
|
||||||
|
ReflectionHelpers.setField(mController, "mNotificationBackend", mNotificationBackend);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplicationsState.AppEntry createAppEntry(String packageName, String label) {
|
||||||
|
ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
|
||||||
|
entry.info = new ApplicationInfo();
|
||||||
|
entry.info.packageName = packageName;
|
||||||
|
entry.label = label;
|
||||||
|
entry.info.uid = 0;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZenMode createPriorityChannelsZenMode() {
|
||||||
|
return new ZenMode("id", new AutomaticZenRule.Builder("Bedtime",
|
||||||
|
Uri.parse("bed"))
|
||||||
|
.setType(AutomaticZenRule.TYPE_BEDTIME)
|
||||||
|
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
|
||||||
|
.setZenPolicy(new ZenPolicy.Builder()
|
||||||
|
.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
|
||||||
|
.build())
|
||||||
|
.build(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@EnableFlags(Flags.FLAG_MODES_UI)
|
public void testIsAvailable() {
|
||||||
public void testHasSummary() {
|
assertThat(mController.isAvailable()).isTrue();
|
||||||
Preference pref = mock(Preference.class);
|
|
||||||
ZenMode zenMode = new ZenMode("id",
|
|
||||||
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
|
|
||||||
.setType(AutomaticZenRule.TYPE_DRIVING)
|
|
||||||
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
|
|
||||||
.build(), true);
|
|
||||||
mController.updateZenMode(pref, zenMode);
|
|
||||||
verify(pref).setSummary(any());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateSetsIntent() {
|
||||||
|
// Creates the preference
|
||||||
|
SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
|
||||||
|
// Create a zen mode that allows priority channels to breakthrough.
|
||||||
|
ZenMode zenMode = createPriorityChannelsZenMode();
|
||||||
|
|
||||||
|
// Capture the intent
|
||||||
|
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
|
||||||
|
mController.updateState((Preference) preference, zenMode);
|
||||||
|
verify(preference).setIntent(captor.capture());
|
||||||
|
Intent launcherIntent = captor.getValue();
|
||||||
|
|
||||||
|
assertThat(launcherIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo("com.android.settings.notification.modes.ZenModeAppsFragment");
|
||||||
|
assertThat(launcherIntent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
|
||||||
|
-1)).isEqualTo(0);
|
||||||
|
|
||||||
|
Bundle bundle = launcherIntent.getBundleExtra(
|
||||||
|
SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
|
||||||
|
assertThat(bundle).isNotNull();
|
||||||
|
assertThat(bundle.getString(MODE_ID)).isEqualTo("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAppsBypassingDnd() {
|
||||||
|
ApplicationsState.AppEntry entry = createAppEntry("test", "testLabel");
|
||||||
|
ApplicationsState.AppEntry entryConv = createAppEntry("test_conv", "test_convLabel");
|
||||||
|
List<ApplicationsState.AppEntry> appEntries = List.of(entry, entryConv);
|
||||||
|
|
||||||
|
when(mNotificationBackend.getPackagesBypassingDnd(mContext.getUserId(),
|
||||||
|
false)).thenReturn(List.of("test"));
|
||||||
|
|
||||||
|
assertThat(mController.getAppsBypassingDnd(appEntries)).containsExactly("testLabel");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateTriggersRebuild() {
|
||||||
|
// Creates the preference
|
||||||
|
SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
|
||||||
|
// Create a zen mode that allows priority channels to breakthrough.
|
||||||
|
ZenMode zenMode = createPriorityChannelsZenMode();
|
||||||
|
|
||||||
|
// Create some applications.
|
||||||
|
ArrayList<ApplicationsState.AppEntry> appEntries =
|
||||||
|
new ArrayList<ApplicationsState.AppEntry>();
|
||||||
|
appEntries.add(createAppEntry("test", "pkgLabel"));
|
||||||
|
|
||||||
|
when(mNotificationBackend.getPackagesBypassingDnd(
|
||||||
|
mContext.getUserId(), false))
|
||||||
|
.thenReturn(List.of("test"));
|
||||||
|
|
||||||
|
// Updates the preference with the zen mode. We expect that this causes the app session
|
||||||
|
// to trigger a rebuild.
|
||||||
|
mController.updateZenMode((Preference) preference, zenMode);
|
||||||
|
verify(mSession).rebuild(any(), any(), eq(false));
|
||||||
|
|
||||||
|
// Manually triggers the callback that will happen on rebuild.
|
||||||
|
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
|
||||||
|
verify(preference).setSummary("pkgLabel can interrupt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnPackageListChangedTriggersRebuild() {
|
||||||
|
mController.mAppSessionCallbacks.onPackageListChanged();
|
||||||
|
verify(mSession).rebuild(any(), any(), eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnLoadEntriesCompletedTriggersRebuild() {
|
||||||
|
mController.mAppSessionCallbacks.onLoadEntriesCompleted();
|
||||||
|
verify(mSession).rebuild(any(), any(), eq(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user