Merge changes from topics "add-mode-dialog", "nicer-icon-header" into main

* changes:
  Icon picker: Styling improvements
  Add mode: Support for app-provided modes
This commit is contained in:
Matías Hernández
2024-07-02 12:26:42 +00:00
committed by Android (Google) Code Review
40 changed files with 1435 additions and 374 deletions

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.modes;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_RULE_ID;
import static android.service.notification.ConditionProviderService.EXTRA_RULE_ID;
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.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.service.notification.ConditionProviderService;
import com.android.settingslib.notification.modes.ZenMode;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@RunWith(RobolectricTestRunner.class)
public class ConfigurationActivityHelperTest {
private Context mContext;
private ConfigurationActivityHelper mHelper;
@Mock private PackageManager mPm;
@Mock private Function<ComponentName, ComponentInfo> mApprovedServiceFinder;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.getApplication();
mHelper = new ConfigurationActivityHelper(mPm);
when(mPm.queryIntentActivities(any(), anyInt())).thenReturn(List.of(new ResolveInfo()));
}
@Test
public void getConfigurationActivityIntentForMode_configActivity() throws Exception {
ZenMode mode = new TestModeBuilder()
.setId("id")
.setPackage(mContext.getPackageName())
.setConfigurationActivity(new ComponentName(mContext.getPackageName(), "test"))
.build();
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mHelper.getConfigurationActivityIntentForMode(mode, mApprovedServiceFinder);
assertThat(res).isNotNull();
assertThat(res.getStringExtra(EXTRA_RULE_ID)).isEqualTo("id");
assertThat(res.getStringExtra(EXTRA_AUTOMATIC_RULE_ID)).isEqualTo("id");
assertThat(res.getComponent()).isEqualTo(
new ComponentName(mContext.getPackageName(), "test"));
}
@Test
public void getConfigurationActivityIntentForMode_configActivityNotResolvable_returnsNull()
throws Exception {
ZenMode mode = new TestModeBuilder()
.setId("id")
.setPackage(mContext.getPackageName())
.setConfigurationActivity(new ComponentName(mContext.getPackageName(), "test"))
.build();
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
when(mPm.queryIntentActivities(any(), anyInt())).thenReturn(new ArrayList<>());
Intent res = mHelper.getConfigurationActivityIntentForMode(mode, mApprovedServiceFinder);
assertThat(res).isNull();
}
@Test
public void getConfigurationActivityIntentForMode_configActivityAndWrongPackage_returnsNull()
throws Exception {
ZenMode mode = new TestModeBuilder()
.setPackage(mContext.getPackageName())
.setConfigurationActivity(new ComponentName("another", "test"))
.build();
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mHelper.getConfigurationActivityIntentForMode(mode, mApprovedServiceFinder);
assertThat(res).isNull();
}
@Test
public void getConfigurationActivityIntentForMode_configActivityAndUnspecifiedOwner()
throws Exception {
ZenMode mode = new TestModeBuilder()
.setId("id")
.setPackage(null)
.setConfigurationActivity(new ComponentName("another", "test"))
.build();
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mHelper.getConfigurationActivityIntentForMode(mode, mApprovedServiceFinder);
assertThat(res).isNotNull();
assertThat(res.getStringExtra(EXTRA_RULE_ID)).isEqualTo("id");
assertThat(res.getStringExtra(EXTRA_AUTOMATIC_RULE_ID)).isEqualTo("id");
assertThat(res.getComponent()).isEqualTo(new ComponentName("another", "test"));
}
@Test
public void getConfigurationActivityIntentForMode_cps() throws Exception {
ZenMode mode = new TestModeBuilder()
.setId("id")
.setPackage(mContext.getPackageName())
.setOwner(new ComponentName(mContext.getPackageName(), "service"))
.build();
ComponentInfo ci = new ComponentInfo();
ci.packageName = mContext.getPackageName();
ci.metaData = new Bundle();
ci.metaData.putString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY,
ComponentName.flattenToShortString(
new ComponentName(mContext.getPackageName(), "activity")));
when(mApprovedServiceFinder.apply(new ComponentName(mContext.getPackageName(), "service")))
.thenReturn(ci);
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mHelper.getConfigurationActivityIntentForMode(mode, mApprovedServiceFinder);
assertThat(res).isNotNull();
assertThat(res.getStringExtra(EXTRA_RULE_ID)).isEqualTo("id");
assertThat(res.getStringExtra(EXTRA_AUTOMATIC_RULE_ID)).isEqualTo("id");
assertThat(res.getComponent()).isEqualTo(
new ComponentName(mContext.getPackageName(), "activity"));
}
@Test
public void getConfigurationActivityIntentForMode_cpsAndWrongPackage_returnsNull()
throws Exception {
ZenMode mode = new TestModeBuilder()
.setPackage("other")
.setOwner(new ComponentName(mContext.getPackageName(), "service"))
.build();
ComponentInfo ci = new ComponentInfo();
ci.packageName = mContext.getPackageName();
ci.metaData = new Bundle();
ci.metaData.putString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY,
ComponentName.flattenToShortString(
new ComponentName(mContext.getPackageName(), "activity")));
when(mApprovedServiceFinder.apply(new ComponentName(mContext.getPackageName(), "service")))
.thenReturn(ci);
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mHelper.getConfigurationActivityIntentForMode(mode, mApprovedServiceFinder);
assertThat(res).isNull();
}
}

View File

@@ -84,6 +84,18 @@ class TestModeBuilder {
return this;
}
TestModeBuilder setOwner(ComponentName owner) {
mRule.setOwner(owner);
mConfigZenRule.component = owner;
return this;
}
TestModeBuilder setConfigurationActivity(ComponentName configActivity) {
mRule.setConfigurationActivity(configActivity);
mConfigZenRule.configurationActivity = configActivity;
return this;
}
TestModeBuilder setConditionId(Uri conditionId) {
mRule.setConditionId(conditionId);
mConfigZenRule.conditionId = conditionId;
@@ -150,18 +162,6 @@ class TestModeBuilder {
return this;
}
TestModeBuilder setConfigurationActivity(ComponentName configActivity) {
mRule.setConfigurationActivity(configActivity);
mConfigZenRule.configurationActivity = configActivity;
return this;
}
TestModeBuilder setOwner(ComponentName owner) {
mRule.setOwner(owner);
mConfigZenRule.component = owner;
return this;
}
ZenMode build() {
return new ZenMode(mId, mRule, mConfigZenRule);
}

View File

@@ -27,8 +27,6 @@ import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -47,7 +45,6 @@ public final class ZenModeCallsLinkPreferenceControllerTest {
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
@Mock private ZenModesBackend mBackend;
@Mock private ZenHelperBackend mHelperBackend;
@Before
@@ -57,7 +54,7 @@ public final class ZenModeCallsLinkPreferenceControllerTest {
mContext = RuntimeEnvironment.application;
mController = new ZenModeCallsLinkPreferenceController(
mContext, "something", mBackend, mHelperBackend);
mContext, "something", mHelperBackend);
}
@Test

View File

@@ -19,7 +19,6 @@ package com.android.settings.notification.modes;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,7 +33,6 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
import com.google.common.collect.ImmutableList;
@@ -64,7 +62,7 @@ public class ZenModeIconPickerListPreferenceControllerTest {
mController = new ZenModeIconPickerListPreferenceController(
RuntimeEnvironment.getApplication(), "icon_list", mListener,
new TestIconOptionsProvider(), mock(ZenModesBackend.class));
new TestIconOptionsProvider());
mRecyclerView = new RecyclerView(mContext);
mRecyclerView.setId(R.id.icon_list);

View File

@@ -27,8 +27,6 @@ import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -47,7 +45,6 @@ public final class ZenModeMessagesLinkPreferenceControllerTest {
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
@Mock private ZenModesBackend mBackend;
@Mock private ZenHelperBackend mHelperBackend;
@Before
@@ -57,7 +54,7 @@ public final class ZenModeMessagesLinkPreferenceControllerTest {
mContext = RuntimeEnvironment.application;
mController = new ZenModeMessagesLinkPreferenceController(
mContext, "something", mBackend, mHelperBackend);
mContext, "something", mHelperBackend);
}
@Test

View File

@@ -27,8 +27,6 @@ import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -47,7 +45,6 @@ public final class ZenModeNotifVisLinkPreferenceControllerTest {
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
@Mock private ZenModesBackend mBackend;
@Mock private ZenHelperBackend mHelperBackend;
@Before
@@ -57,7 +54,7 @@ public final class ZenModeNotifVisLinkPreferenceControllerTest {
mContext = RuntimeEnvironment.application;
mController = new ZenModeNotifVisLinkPreferenceController(
mContext, "something", mBackend, mHelperBackend);
mContext, "something", mHelperBackend);
}
@Test

View File

@@ -27,8 +27,6 @@ import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -48,7 +46,6 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
@Mock private ZenModesBackend mBackend;
@Mock private ZenHelperBackend mHelperBackend;
@Before
@@ -58,7 +55,7 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
mContext = RuntimeEnvironment.application;
mController = new ZenModeOtherLinkPreferenceController(
mContext, "something", mBackend, mHelperBackend);
mContext, "something", mHelperBackend);
}
@Test

View File

@@ -27,8 +27,6 @@ import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -48,8 +46,6 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
private Context mContext;
@Mock
private ZenModesBackend mBackend;
@Mock
private ZenHelperBackend mHelperBackend;
@Before
@@ -59,7 +55,7 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
mContext = RuntimeEnvironment.application;
mController = new ZenModePeopleLinkPreferenceController(
mContext, "something", mBackend, mHelperBackend);
mContext, "something", mHelperBackend);
}
@Test

View File

@@ -19,30 +19,26 @@ package com.android.settings.notification.modes;
import static android.app.AutomaticZenRule.TYPE_OTHER;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_RULE_ID;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.service.notification.ConditionProviderService.EXTRA_RULE_ID;
import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceController.AUTOMATIC_TRIGGER_PREF_KEY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ConditionProviderService;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
@@ -52,7 +48,6 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.utils.ZenServiceListing;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
@@ -80,10 +75,10 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
private PrimarySwitchPreference mPreference;
@Mock
private ZenServiceListing mServiceListing;
@Mock
private PackageManager mPm;
@Mock
private ConfigurationActivityHelper mConfigurationActivityHelper;
@Mock
private PreferenceCategory mPrefCategory;
@@ -98,8 +93,9 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
mContext = ApplicationProvider.getApplicationContext();
mPrefController = new ZenModeSetTriggerLinkPreferenceController(mContext,
"zen_automatic_trigger_category", mFragment, mBackend, mPm);
mPrefController.setServiceListing(mServiceListing);
"zen_automatic_trigger_category", mFragment, mBackend,
mConfigurationActivityHelper,
mock(ZenServiceListing.class));
mPreference = new PrimarySwitchPreference(mContext);
when(mPrefCategory.findPreference(AUTOMATIC_TRIGGER_PREF_KEY)).thenReturn(mPreference);
@@ -225,6 +221,40 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
assertThat(mPreference.getOnPreferenceClickListener()).isNotNull();
}
@Test
public void testRuleLink_appWithConfigActivity_linksToConfigActivity() {
ZenMode mode = new TestModeBuilder()
.setPackage("some.package")
.setTriggerDescription("When The Music's Over")
.build();
Intent configurationIntent = new Intent("configure the mode");
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
.thenReturn(configurationIntent);
mPrefController.updateZenMode(mPrefCategory, mode);
assertThat(mPreference.getTitle()).isNotNull();
assertThat(mPreference.getTitle().toString()).isEqualTo(
mContext.getString(R.string.zen_mode_configuration_link_title));
assertThat(mPreference.getSummary()).isNotNull();
assertThat(mPreference.getSummary().toString()).isEqualTo("When The Music's Over");
assertThat(mPreference.getIntent()).isEqualTo(configurationIntent);
}
@Test
public void testRuleLink_appWithoutConfigActivity_hidden() {
ZenMode mode = new TestModeBuilder()
.setPackage("some.package")
.setTriggerDescription("Will not be shown :(")
.build();
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
.thenReturn(null);
mPrefController.updateZenMode(mPrefCategory, mode);
assertThat(mPrefCategory.isVisible()).isFalse();
}
@Test
public void onScheduleChosen_updatesMode() {
ZenMode originalMode = new TestModeBuilder()
@@ -253,109 +283,4 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
assertThat(updatedMode.getRule().getOwner()).isEqualTo(
ZenModeConfig.getScheduleConditionProvider());
}
@Test
public void testGetAppRuleIntent_configActivity() throws Exception {
ZenMode mode = new TestModeBuilder()
.setId("id")
.setPackage(mContext.getPackageName())
.setConfigurationActivity(new ComponentName(mContext.getPackageName(), "test"))
.setType(TYPE_OTHER)
.setTriggerDescription("some rule")
.build();
when(mPm.getPackageUid(null, 0)).thenReturn(-1);
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mPrefController.getAppRuleIntent(mode);
assertThat(res).isNotNull();
assertThat(res.getStringExtra(EXTRA_RULE_ID)).isEqualTo("id");
assertThat(res.getStringExtra(EXTRA_AUTOMATIC_RULE_ID)).isEqualTo("id");
assertThat(res.getComponent()).isEqualTo(
new ComponentName(mContext.getPackageName(), "test"));
}
@Test
public void testGetAppRuleIntent_configActivity_wrongPackage() throws Exception {
ZenMode mode = new TestModeBuilder()
.setPackage(mContext.getPackageName())
.setConfigurationActivity(new ComponentName("another", "test"))
.setType(TYPE_OTHER)
.build();
when(mPm.getPackageUid(null, 0)).thenReturn(-1);
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mPrefController.getAppRuleIntent(mode);
assertThat(res).isNull();
}
@Test
public void testGetAppRuleIntent_configActivity_unspecifiedOwner() throws Exception {
ZenMode mode = new TestModeBuilder()
.setId("id")
.setPackage(null)
.setConfigurationActivity(new ComponentName("another", "test"))
.setType(TYPE_OTHER)
.build();
when(mPm.getPackageUid(null, 0)).thenReturn(-1);
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mPrefController.getAppRuleIntent(mode);
assertThat(res).isNotNull();
assertThat(res.getStringExtra(EXTRA_RULE_ID)).isEqualTo("id");
assertThat(res.getStringExtra(EXTRA_AUTOMATIC_RULE_ID)).isEqualTo("id");
assertThat(res.getComponent()).isEqualTo(new ComponentName("another", "test"));
}
@Test
public void testGetAppRuleIntent_cps() throws Exception {
ZenMode mode = new TestModeBuilder()
.setId("id")
.setPackage(mContext.getPackageName())
.setOwner(new ComponentName(mContext.getPackageName(), "service"))
.build();
ComponentInfo ci = new ComponentInfo();
ci.packageName = mContext.getPackageName();
ci.metaData = new Bundle();
ci.metaData.putString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY,
ComponentName.flattenToShortString(
new ComponentName(mContext.getPackageName(), "activity")));
when(mServiceListing.findService(new ComponentName(mContext.getPackageName(), "service")))
.thenReturn(ci);
when(mPm.getPackageUid(null, 0)).thenReturn(-1);
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mPrefController.getAppRuleIntent(mode);
assertThat(res).isNotNull();
assertThat(res.getStringExtra(EXTRA_RULE_ID)).isEqualTo("id");
assertThat(res.getStringExtra(EXTRA_AUTOMATIC_RULE_ID)).isEqualTo("id");
assertThat(res.getComponent()).isEqualTo(
new ComponentName(mContext.getPackageName(), "activity"));
}
@Test
public void testGetAppRuleIntent_cps_wrongPackage() throws Exception {
ZenMode mode = new TestModeBuilder()
.setPackage("other")
.setOwner(new ComponentName(mContext.getPackageName(), "service"))
.setType(TYPE_OTHER)
.build();
ComponentInfo ci = new ComponentInfo();
ci.packageName = mContext.getPackageName();
ci.metaData = new Bundle();
ci.metaData.putString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY,
ComponentName.flattenToShortString(
new ComponentName(mContext.getPackageName(), "activity")));
when(mPm.getPackageUid(null, 0)).thenReturn(-1);
when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1);
Intent res = mPrefController.getAppRuleIntent(mode);
assertThat(res).isNull();
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.modes;
import static android.app.NotificationManager.META_DATA_AUTOMATIC_RULE_TYPE;
import static android.app.NotificationManager.META_DATA_RULE_INSTANCE_LIMIT;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import com.android.settings.notification.modes.ZenModesListAddModePreferenceController.ModeType;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
import java.util.function.Function;
@RunWith(RobolectricTestRunner.class)
public class ZenModesListAddModePreferenceControllerTest {
private Context mContext;
private ZenModesListAddModePreferenceController mController;
@Mock private ZenModesListAddModePreferenceController.OnAddModeListener mListener;
@Mock private ZenServiceListing mZenServiceListing;
@Mock private ConfigurationActivityHelper mConfigurationActivityHelper;
@Mock private NotificationManager mNm;
@Mock private PackageManager mPm;
@Captor private ArgumentCaptor<List<ModeType>> mListenerCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.getApplication();
Function<ApplicationInfo, Drawable> appIconRetriever = appInfo -> new ColorDrawable();
mController = new ZenModesListAddModePreferenceController(mContext, mListener,
mZenServiceListing, mConfigurationActivityHelper, mNm, mPm, appIconRetriever,
MoreExecutors.newDirectExecutorService(), MoreExecutors.directExecutor());
when(mConfigurationActivityHelper.getConfigurationActivityFromApprovedComponent(any()))
.thenAnswer((Answer<ComponentName>) invocationOnMock -> {
// By default, assume the ComponentInfo is also the configurationActivity.
ComponentInfo ci = invocationOnMock.getArgument(0);
return ci != null ? ci.getComponentName() : null;
});
}
@Test
public void onClickAddMode_noAppProviders_onlyOptionIsCustom() {
when(mZenServiceListing.loadApprovedComponents()).thenReturn(ImmutableSet.of());
mController.onClickAddMode();
verify(mListener).onAvailableModeTypesForAdd(mListenerCaptor.capture());
List<ModeType> options = mListenerCaptor.getValue();
assertThat(options).hasSize(1);
assertThat(options.get(0).name()).isEqualTo("Custom");
assertThat(options.get(0).summary()).isNull();
assertThat(options.get(0).icon()).isNotNull();
assertThat(options.get(0).creationActivityIntent()).isNull();
}
@Test
public void onClickAddMode_someAppProviders_includedInOptions() {
ImmutableSet<ComponentInfo> approvedComponents = ImmutableSet.of(
newComponentInfoWithValidMetadata("pkg1"),
newComponentInfoWithValidMetadata("pkg2"));
when(mZenServiceListing.loadApprovedComponents()).thenReturn(approvedComponents);
mController.onClickAddMode();
verify(mListener).onAvailableModeTypesForAdd(mListenerCaptor.capture());
List<ModeType> options = mListenerCaptor.getValue();
assertThat(options).hasSize(3);
assertThat(options.get(1).name()).isEqualTo("Rule by pkg1");
assertThat(options.get(1).summary()).isEqualTo("A package called pkg1");
assertThat(options.get(1).icon()).isNotNull();
assertThat(options.get(1).creationActivityIntent()).isNotNull();
assertThat(options.get(1).creationActivityIntent().getComponent()).isEqualTo(
new ComponentName("pkg1", "pkg1.activity"));
assertThat(options.get(0).name()).isEqualTo("Custom");
assertThat(options.get(2).name()).isEqualTo("Rule by pkg2");
}
@Test
public void onClickAddMode_someAppProviders_optionsAreSorted() {
ImmutableSet<ComponentInfo> approvedComponents = ImmutableSet.of(
newComponentInfoWithValidMetadata("pkg_Z"),
newComponentInfoWithValidMetadata("pkg_A"),
newComponentInfoWithValidMetadata("pkg_F"),
newComponentInfoWithValidMetadata("pkg_C"));
when(mZenServiceListing.loadApprovedComponents()).thenReturn(approvedComponents);
mController.onClickAddMode();
verify(mListener).onAvailableModeTypesForAdd(mListenerCaptor.capture());
List<ModeType> options = mListenerCaptor.getValue();
assertThat(options).hasSize(5);
assertThat(options.stream().map(o -> o.name()).toList())
.containsExactly("Custom", "Rule by pkg_A", "Rule by pkg_C", "Rule by pkg_F",
"Rule by pkg_Z")
.inOrder();
}
@Test
public void onClickAddMode_appProviderWithMissingMetadata_notAnOption() {
ComponentInfo componentWithoutRuleType = newComponentInfoWithValidMetadata("pkg1");
componentWithoutRuleType.metaData.remove(META_DATA_AUTOMATIC_RULE_TYPE);
ImmutableSet<ComponentInfo> approvedComponents = ImmutableSet.of(
componentWithoutRuleType, newComponentInfoWithValidMetadata("pkg2"));
when(mZenServiceListing.loadApprovedComponents()).thenReturn(approvedComponents);
mController.onClickAddMode();
verify(mListener).onAvailableModeTypesForAdd(mListenerCaptor.capture());
List<ModeType> options = mListenerCaptor.getValue();
assertThat(options).hasSize(2);
assertThat(options.get(0).name()).isEqualTo("Custom");
assertThat(options.get(1).name()).isEqualTo("Rule by pkg2");
}
@Test
public void onClickAddMode_appProviderWithRuleLimitExceeded_notAnOption() {
ComponentInfo componentWithLimitThreeRules = newComponentInfoWithValidMetadata("pkg1");
componentWithLimitThreeRules.metaData.putInt(META_DATA_RULE_INSTANCE_LIMIT, 3);
ImmutableSet<ComponentInfo> approvedComponents = ImmutableSet.of(
componentWithLimitThreeRules, newComponentInfoWithValidMetadata("pkg2"));
when(mZenServiceListing.loadApprovedComponents()).thenReturn(approvedComponents);
when(mNm.getRuleInstanceCount(any())).thenReturn(3); // Already 3 created rules.
mController.onClickAddMode();
verify(mListener).onAvailableModeTypesForAdd(mListenerCaptor.capture());
List<ModeType> options = mListenerCaptor.getValue();
assertThat(options).hasSize(2);
assertThat(options.get(0).name()).isEqualTo("Custom");
assertThat(options.get(1).name()).isEqualTo("Rule by pkg2");
verify(mNm).getRuleInstanceCount(eq(componentWithLimitThreeRules.getComponentName()));
}
@Test
public void onClickAddMode_appProviderWithoutConfigurationActivity_notAnOption() {
ComponentInfo componentWithoutConfigActivity = newComponentInfoWithValidMetadata("pkg2");
ImmutableSet<ComponentInfo> approvedComponents = ImmutableSet.of(
newComponentInfoWithValidMetadata("pkg1"), componentWithoutConfigActivity);
when(mZenServiceListing.loadApprovedComponents()).thenReturn(approvedComponents);
when(mConfigurationActivityHelper.getConfigurationActivityFromApprovedComponent(any()))
.thenAnswer((Answer<ComponentName>) invocationOnMock -> {
ComponentInfo ci = invocationOnMock.getArgument(0);
if (ci == componentWithoutConfigActivity) {
return null;
} else {
return ci.getComponentName();
}
});
mController.onClickAddMode();
verify(mListener).onAvailableModeTypesForAdd(mListenerCaptor.capture());
List<ModeType> options = mListenerCaptor.getValue();
assertThat(options).hasSize(2);
assertThat(options.get(0).name()).isEqualTo("Custom");
assertThat(options.get(1).name()).isEqualTo("Rule by pkg1");
}
private ComponentInfo newComponentInfoWithValidMetadata(String pkg) {
ComponentInfo ci = new ActivityInfo();
ci.applicationInfo = mock(ApplicationInfo.class);
when(ci.applicationInfo.loadLabel(any())).thenReturn("A package called " + pkg);
when(ci.applicationInfo.loadUnbadgedIcon(any())).thenReturn(new ColorDrawable());
ci.packageName = pkg;
ci.name = pkg + ".activity";
ci.metaData = new Bundle();
ci.metaData.putString(META_DATA_AUTOMATIC_RULE_TYPE, "Rule by " + pkg);
return ci;
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.modes;
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
import static com.android.settings.notification.modes.ZenModesListFragment.REQUEST_NEW_MODE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.testing.EmptyFragmentActivity;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.settings.notification.modes.ZenModesListAddModePreferenceController.ModeType;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowActivity.IntentForResult;
@RunWith(RobolectricTestRunner.class)
public class ZenModesListFragmentTest {
private static final ModeType APP_PROVIDED_MODE_TYPE = new ModeType("Mode", new ColorDrawable(),
"Details", new Intent().setComponent(new ComponentName("pkg", "configActivity")));
private static final ImmutableList<ZenMode> EXISTING_MODES = ImmutableList.of(
new TestModeBuilder().setId("A").build(),
new TestModeBuilder().setId("B").build(),
new TestModeBuilder().setId("C").build());
@Rule
public ActivityScenarioRule<EmptyFragmentActivity> mActivityScenario =
new ActivityScenarioRule<>(EmptyFragmentActivity.class);
private FragmentActivity mActivity;
private ZenModesListFragment mFragment;
@Mock private ZenModesBackend mBackend;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFragment = new ZenModesListFragment();
mActivityScenario.getScenario().onActivity(activity -> {
activity.getSupportFragmentManager().beginTransaction()
.add(mFragment, "tag").commitNow();
mActivity = activity;
});
mFragment.setBackend(mBackend); // after onAttach()
}
@Test
public void onChosenModeTypeForAdd_appProvidedMode_startsCreationActivity() {
when(mBackend.getModes()).thenReturn(EXISTING_MODES);
mFragment.onChosenModeTypeForAdd(APP_PROVIDED_MODE_TYPE);
IntentForResult intent = shadowOf(mActivity).getNextStartedActivityForResult();
assertThat(intent).isNotNull();
assertThat(intent.intent).isEqualTo(APP_PROVIDED_MODE_TYPE.creationActivityIntent());
}
@Test
public void onActivityResult_modeWasCreated_opensIt() {
when(mBackend.getModes()).thenReturn(EXISTING_MODES);
mFragment.onChosenModeTypeForAdd(APP_PROVIDED_MODE_TYPE);
// App creates the new mode.
ZenMode createdMode = new TestModeBuilder().setId("new_id").setPackage("pkg").build();
when(mBackend.getModes()).thenReturn(new ImmutableList.Builder<ZenMode>()
.addAll(EXISTING_MODES)
.add(createdMode)
.build());
mFragment.onActivityResult(REQUEST_NEW_MODE, 0, new Intent());
Intent openModePageIntent = shadowOf(mActivity).getNextStartedActivity();
assertThat(openModePageIntent.getStringExtra(EXTRA_SHOW_FRAGMENT))
.isEqualTo(ZenModeFragment.class.getName());
Bundle fragmentArgs = openModePageIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
assertThat(fragmentArgs).isNotNull();
assertThat(fragmentArgs.getString(EXTRA_AUTOMATIC_ZEN_RULE_ID)).isEqualTo("new_id");
}
@Test
public void onActivityResult_secondTime_doesNothing() {
when(mBackend.getModes()).thenReturn(EXISTING_MODES);
mFragment.onChosenModeTypeForAdd(APP_PROVIDED_MODE_TYPE);
// App creates a new mode, we redirect to its page when coming back.
ZenMode createdMode = new TestModeBuilder().setId("new_id").setPackage("pkg").build();
when(mBackend.getModes()).thenReturn(new ImmutableList.Builder<ZenMode>()
.addAll(EXISTING_MODES)
.add(createdMode)
.build());
mFragment.onActivityResult(REQUEST_NEW_MODE, 0, new Intent());
shadowOf(mActivity).clearNextStartedActivities();
mFragment.onActivityResult(REQUEST_NEW_MODE, 0, new Intent());
Intent nextIntent = shadowOf(mActivity).getNextStartedActivity();
assertThat(nextIntent).isNull();
}
@Test
public void onActivityResult_modeWasNotCreated_doesNothing() {
when(mBackend.getModes()).thenReturn(EXISTING_MODES);
mFragment.onChosenModeTypeForAdd(APP_PROVIDED_MODE_TYPE);
shadowOf(mActivity).clearNextStartedActivities();
// Returning to settings without creating a new mode.
mFragment.onActivityResult(REQUEST_NEW_MODE, 0, new Intent());
Intent nextIntent = shadowOf(mActivity).getNextStartedActivity();
assertThat(nextIntent).isNull();
}
}

View File

@@ -99,7 +99,7 @@ public class ZenModesListPreferenceControllerTest {
PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
preferenceScreen.addPreference(mPreference);
mPrefController = new ZenModesListPreferenceController(mContext, null, mBackend);
mPrefController = new ZenModesListPreferenceController(mContext, mBackend);
}
@Test