Fix jiggle when opening custom manual modes
Looks like setting Preference visibility in updateState() is too late to avoid an animation, and isAvailable() should be used instead. This forces us to split ZenModeSetTriggerLinkPreferenceController (which handled the category and its two children) into separate controllers for the category and each child. Although untangling this code was annoying, the result is arguably cleaner, since the two child preferences deal with different things.
Fixes: 355623101
Test: atest com.android.settings.notification.modes
Flag: android.app.modes_ui
Change-Id: I5fb1b3cbe424973b852f820ecf948491c050421f
diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml
index 7551905..d2f573c 100644
--- a/res/xml/modes_rule_settings.xml
+++ b/res/xml/modes_rule_settings.xml
@@ -51,8 +51,7 @@
<Preference
android:key="zen_add_automatic_trigger"
android:title="@string/zen_mode_select_schedule"
- android:icon="@drawable/ic_add_24dp"
- settings:isPreferenceVisible="false" />
+ android:icon="@drawable/ic_add_24dp" />
</PreferenceCategory>
<PreferenceCategory
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 7c2de97..5aeb34d 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -65,8 +65,13 @@
new ZenModePreferenceCategoryController(context, "modes_additional_actions"));
prefControllers.add(new ZenModeDisplayLinkPreferenceController(
context, "mode_display_settings", mBackend, mHelperBackend));
- prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context,
- "zen_automatic_trigger_category", this, mBackend));
+ prefControllers.add(new ZenModeTriggerCategoryPreferenceController(context,
+ "zen_automatic_trigger_category"));
+ prefControllers.add(new ZenModeTriggerUpdatePreferenceController(context,
+ "zen_automatic_trigger_settings", mBackend));
+ prefControllers.add(
+ new ZenModeTriggerAddPreferenceController(context, "zen_add_automatic_trigger",
+ this, mBackend));
prefControllers.add(new InterruptionFilterPreferenceController(
context, "allow_filtering", mBackend));
prefControllers.add(new ManualDurationPreferenceController(
diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceController.java
new file mode 100644
index 0000000..68cc167
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceController.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+class ZenModeTriggerAddPreferenceController extends AbstractZenModePreferenceController {
+
+ private final DashboardFragment mFragment;
+
+ ZenModeTriggerAddPreferenceController(@NonNull Context context,
+ @NonNull String key, DashboardFragment fragment, ZenModesBackend backend) {
+ super(context, key, backend);
+ mFragment = fragment;
+ }
+
+ @Override
+ public boolean isAvailable(@NonNull ZenMode zenMode) {
+ return zenMode.isCustomManual();
+ }
+
+ @Override
+ void updateState(Preference preference, @NonNull ZenMode zenMode) {
+ if (!isAvailable(zenMode)) {
+ return;
+ }
+
+ preference.setOnPreferenceClickListener(unused -> {
+ ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
+ return true;
+ });
+ }
+
+ @VisibleForTesting
+ final ZenModeScheduleChooserDialog.OnScheduleOptionListener mOnScheduleOptionListener =
+ conditionId -> saveMode(mode -> {
+ mode.setCustomModeConditionId(mContext, conditionId);
+ return mode;
+ // TODO: b/342156843 - Maybe jump to the corresponding schedule editing screen?
+ });
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceController.java
new file mode 100644
index 0000000..5fc3fda
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceController.java
@@ -0,0 +1,44 @@
+/*
+ * 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 android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+
+import com.android.settingslib.notification.modes.ZenMode;
+
+/**
+ * Preference controller for the "Turn on automatically" category
+ */
+class ZenModeTriggerCategoryPreferenceController extends AbstractZenModePreferenceController {
+
+ ZenModeTriggerCategoryPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public boolean isAvailable(@NonNull ZenMode zenMode) {
+ return !zenMode.isManualDnd();
+ }
+
+ @Override
+ public void updateState(Preference preference, @NonNull ZenMode zenMode) {
+ // Nothing to update here (except visibility via isAvailable()).
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
similarity index 74%
rename from src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java
rename to src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
index 24df931..043a38c 100644
--- a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
@@ -22,8 +22,6 @@
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
@@ -39,46 +37,36 @@
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.google.common.base.Strings;
-/**
- * Preference controller for the link to an individual mode's configuration page.
- */
-class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenceController {
- private static final String TAG = "ZenModeSetTriggerLink";
+class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreferenceController {
- @VisibleForTesting
- static final String AUTOMATIC_TRIGGER_KEY = "zen_automatic_trigger_settings";
- static final String ADD_TRIGGER_KEY = "zen_add_automatic_trigger";
+ private static final String TAG = "ZenModeTriggerUpdate";
- private final DashboardFragment mFragment;
private final PackageManager mPackageManager;
private final ConfigurationActivityHelper mConfigurationActivityHelper;
private final ZenServiceListing mServiceListing;
- ZenModeSetTriggerLinkPreferenceController(Context context, String key,
- DashboardFragment fragment, ZenModesBackend backend) {
- this(context, key, fragment, backend, context.getPackageManager(),
+ ZenModeTriggerUpdatePreferenceController(Context context, String key,
+ ZenModesBackend backend) {
+ this(context, key, backend, context.getPackageManager(),
new ConfigurationActivityHelper(context.getPackageManager()),
new ZenServiceListing(context));
}
@VisibleForTesting
- ZenModeSetTriggerLinkPreferenceController(Context context, String key,
- DashboardFragment fragment, ZenModesBackend backend, PackageManager packageManager,
+ ZenModeTriggerUpdatePreferenceController(Context context, String key,
+ ZenModesBackend backend, PackageManager packageManager,
ConfigurationActivityHelper configurationActivityHelper,
ZenServiceListing serviceListing) {
super(context, key, backend);
- mFragment = fragment;
mPackageManager = packageManager;
mConfigurationActivityHelper = configurationActivityHelper;
mServiceListing = serviceListing;
@@ -86,7 +74,7 @@
@Override
public boolean isAvailable(@NonNull ZenMode zenMode) {
- return !zenMode.isManualDnd();
+ return !zenMode.isCustomManual() && !zenMode.isManualDnd();
}
@Override
@@ -97,39 +85,18 @@
}
@Override
- public void updateState(Preference preference, @NonNull ZenMode zenMode) {
- // This controller is expected to govern a preference category so that it controls the
- // availability of the entire preference category if the mode doesn't have a way to
- // automatically trigger (such as manual DND).
- if (zenMode.isManualDnd()) {
+ void updateState(Preference preference, @NonNull ZenMode zenMode) {
+ if (!isAvailable(zenMode)) {
return;
}
- PrimarySwitchPreference triggerPref = checkNotNull(
- ((PreferenceCategory) preference).findPreference(AUTOMATIC_TRIGGER_KEY));
- Preference addTriggerPref = checkNotNull(
- ((PreferenceCategory) preference).findPreference(ADD_TRIGGER_KEY));
- boolean isAddTrigger = zenMode.isSystemOwned() && zenMode.getType() != TYPE_SCHEDULE_TIME
- && zenMode.getType() != TYPE_SCHEDULE_CALENDAR;
-
- if (isAddTrigger) {
- triggerPref.setVisible(false);
- addTriggerPref.setVisible(true);
- addTriggerPref.setOnPreferenceClickListener(unused -> {
- ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
- return true;
- });
+ PrimarySwitchPreference triggerPref = (PrimarySwitchPreference) preference;
+ triggerPref.setChecked(zenMode.getRule().isEnabled());
+ triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener);
+ if (zenMode.isSystemOwned()) {
+ setUpForSystemOwnedTrigger(triggerPref, zenMode);
} else {
- addTriggerPref.setVisible(false);
- triggerPref.setVisible(true);
- triggerPref.setChecked(zenMode.getRule().isEnabled());
- triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener);
-
- if (zenMode.isSystemOwned()) {
- setUpForSystemOwnedTrigger(triggerPref, zenMode);
- } else {
- setUpForAppTrigger(triggerPref, zenMode);
- }
+ setUpForAppTrigger(triggerPref, zenMode);
}
}
@@ -223,14 +190,6 @@
preference.setIntent(configurationIntent);
}
- @VisibleForTesting
- final ZenModeScheduleChooserDialog.OnScheduleOptionListener mOnScheduleOptionListener =
- conditionId -> saveMode(mode -> {
- mode.setCustomModeConditionId(mContext, conditionId);
- return mode;
- // TODO: b/342156843 - Maybe jump to the corresponding schedule editing screen?
- });
-
private final Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
confirmChangeEnabled(p, (boolean) newValue);
return true;
diff --git a/tests/robotests/src/com/android/settings/notification/modes/CharSequenceTruth.java b/tests/robotests/src/com/android/settings/notification/modes/CharSequenceTruth.java
new file mode 100644
index 0000000..94b932f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/CharSequenceTruth.java
@@ -0,0 +1,35 @@
+/*
+ * 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 androidx.annotation.Nullable;
+
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Truth;
+
+class CharSequenceTruth {
+ /**
+ * Shortcut version of {@link Truth#assertThat(String)} suitable for {@link CharSequence}.
+ * {@link CharSequence} doesn't necessarily provide a good {@code equals()} implementation;
+ * however we don't care about formatting in most cases, and we want to assert on the resulting
+ * string (without needing to worry that {@code assertThat(x.getText().toString())} can
+ * throw if the text is null).
+ */
+ static StringSubject assertThat(@Nullable CharSequence actual) {
+ return Truth.assertThat((String) (actual != null ? actual.toString() : null));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java
new file mode 100644
index 0000000..a56e723
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java
@@ -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.AutomaticZenRule.TYPE_OTHER;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.settings.notification.modes.CharSequenceTruth.assertThat;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.SystemZenRules;
+import android.service.notification.ZenModeConfig;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.notification.modes.TestModeBuilder;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Calendar;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public class ZenModeTriggerAddPreferenceControllerTest {
+
+ private static final ZenMode CUSTOM_MANUAL_MODE = new TestModeBuilder()
+ .setConditionId(ZenModeConfig.toCustomManualConditionId())
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_OTHER)
+ .setTriggerDescription("Will not be shown")
+ .build();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
+ private ZenModeTriggerAddPreferenceController mController;
+
+ private Context mContext;
+ private Preference mPreference;
+ @Mock private ZenModesBackend mBackend;
+ @Mock private DashboardFragment mFragment;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen preferenceScreen = preferenceManager.inflateFromResource(mContext,
+ R.xml.modes_rule_settings, null);
+
+ mController = new ZenModeTriggerAddPreferenceController(mContext,
+ "zen_add_automatic_trigger", mFragment, mBackend);
+ mPreference = preferenceScreen.findPreference("zen_add_automatic_trigger");
+ }
+
+ @Test
+ public void isAvailable_customManualMode_true() {
+ mController.setZenMode(CUSTOM_MANUAL_MODE);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_systemMode_false() {
+ ZenMode mode = new TestModeBuilder()
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_SCHEDULE_CALENDAR)
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_appProvidedMode_false() {
+ ZenMode mode = new TestModeBuilder()
+ .setPackage("com.some.package")
+ .setType(TYPE_OTHER)
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_manualDND_false() {
+ ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb",
+ Uri.parse("manual"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(), /* isActive= */ false);
+
+ mController.setZenMode(manualMode);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void updateState_customManualRule() {
+ ZenMode mode = new TestModeBuilder()
+ .setConditionId(ZenModeConfig.toCustomManualConditionId())
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_OTHER)
+ .setTriggerDescription("Will not be shown")
+ .build();
+
+ mController.updateState(mPreference, mode);
+
+ assertThat(mPreference.getTitle()).isEqualTo(
+ mContext.getString(R.string.zen_mode_select_schedule));
+ assertThat(mPreference.getSummary()).isNull();
+ // Sets up a click listener to open the dialog.
+ assertThat(mPreference.getOnPreferenceClickListener()).isNotNull();
+ }
+
+ @Test
+ public void onScheduleChosen_updatesMode() {
+ ZenMode originalMode = new TestModeBuilder()
+ .setConditionId(ZenModeConfig.toCustomManualConditionId())
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_OTHER)
+ .setTriggerDescription("")
+ .build();
+ mController.updateZenMode(mPreference, originalMode);
+
+ ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
+ scheduleInfo.days = new int[] { Calendar.MONDAY };
+ scheduleInfo.startHour = 12;
+ scheduleInfo.endHour = 15;
+ Uri scheduleUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
+
+ mController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri);
+
+ // verify the backend got asked to update the mode to be schedule-based.
+ ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ ZenMode updatedMode = captor.getValue();
+ assertThat(updatedMode.getType()).isEqualTo(TYPE_SCHEDULE_TIME);
+ assertThat(updatedMode.getRule().getConditionId()).isEqualTo(scheduleUri);
+ assertThat(updatedMode.getRule().getTriggerDescription()).isNotEmpty();
+ assertThat(updatedMode.getRule().getOwner()).isEqualTo(
+ ZenModeConfig.getScheduleConditionProvider());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java
new file mode 100644
index 0000000..4510e20
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.AutomaticZenRule.TYPE_OTHER;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.SystemZenRules;
+import android.service.notification.ZenModeConfig;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.notification.modes.TestModeBuilder;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public class ZenModeTriggerCategoryPreferenceControllerTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
+ private ZenModeTriggerCategoryPreferenceController mController;
+
+ private Context mContext;
+ private Preference mPreference;
+ @Mock private ZenModesBackend mBackend;
+ @Mock private DashboardFragment mFragment;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen preferenceScreen = preferenceManager.inflateFromResource(mContext,
+ R.xml.modes_rule_settings, null);
+
+ mController = new ZenModeTriggerCategoryPreferenceController(mContext,
+ "zen_automatic_trigger_category");
+ mPreference = preferenceScreen.findPreference("zen_automatic_trigger_category");
+ }
+
+ @Test
+ public void isAvailable_customManualMode_true() {
+ ZenMode mode = new TestModeBuilder()
+ .setConditionId(ZenModeConfig.toCustomManualConditionId())
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_OTHER)
+ .setTriggerDescription("Will not be shown")
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_systemMode_true() {
+ ZenMode mode = new TestModeBuilder()
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_SCHEDULE_CALENDAR)
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_appProvidedMode_true() {
+ ZenMode mode = new TestModeBuilder()
+ .setPackage("com.some.package")
+ .setType(TYPE_OTHER)
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_manualDND_false() {
+ ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb",
+ Uri.parse("manual"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(), /* isActive= */ false);
+
+ mController.setZenMode(manualMode);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java
similarity index 60%
rename from tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java
rename to tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java
index 93db4be..a3fe57e 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java
@@ -22,11 +22,8 @@
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceController.ADD_TRIGGER_KEY;
-import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceController.AUTOMATIC_TRIGGER_KEY;
-import static com.android.settings.notification.modes.ZenModeSetTriggerLinkPreferenceControllerTest.CharSequenceTruth.assertThat;
+import static com.android.settings.notification.modes.CharSequenceTruth.assertThat;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -51,24 +48,17 @@
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
-import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
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.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.TestModeBuilder;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
-import com.google.common.truth.StringSubject;
-import com.google.common.truth.Truth;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -84,44 +74,31 @@
@RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI)
-public class ZenModeSetTriggerLinkPreferenceControllerTest {
+public class ZenModeTriggerUpdatePreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
- @Mock
- private ZenModesBackend mBackend;
- private Context mContext;
+ private ZenModeTriggerUpdatePreferenceController mController;
- @Mock
- private PackageManager mPm;
- @Mock
- private ConfigurationActivityHelper mConfigurationActivityHelper;
-
- private PreferenceCategory mPrefCategory;
- private PrimarySwitchPreference mConfigPreference;
- private Preference mAddPreference;
-
- @Mock
- private DashboardFragment mFragment;
-
- private ZenModeSetTriggerLinkPreferenceController mController;
+ private PrimarySwitchPreference mPreference;
+ @Mock private ZenModesBackend mBackend;
+ @Mock private PackageManager mPm;
+ @Mock private ConfigurationActivityHelper mConfigurationActivityHelper;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = ApplicationProvider.getApplicationContext();
+ Context context = ApplicationProvider.getApplicationContext();
- PreferenceManager preferenceManager = new PreferenceManager(mContext);
- PreferenceScreen preferenceScreen = preferenceManager.inflateFromResource(mContext,
+ PreferenceManager preferenceManager = new PreferenceManager(context);
+ PreferenceScreen preferenceScreen = preferenceManager.inflateFromResource(context,
R.xml.modes_rule_settings, null);
- mController = new ZenModeSetTriggerLinkPreferenceController(mContext,
- "zen_automatic_trigger_category", mFragment, mBackend, mPm,
+ mController = new ZenModeTriggerUpdatePreferenceController(context,
+ "zen_automatic_trigger_settings", mBackend, mPm,
mConfigurationActivityHelper, mock(ZenServiceListing.class));
- mPrefCategory = preferenceScreen.findPreference("zen_automatic_trigger_category");
- mConfigPreference = checkNotNull(mPrefCategory).findPreference(AUTOMATIC_TRIGGER_KEY);
- mAddPreference = checkNotNull(mPrefCategory).findPreference(ADD_TRIGGER_KEY);
+ mPreference = preferenceScreen.findPreference("zen_automatic_trigger_settings");
when(mPm.getApplicationInfo(any(), anyInt())).then(
(Answer<ApplicationInfo>) invocationOnMock -> {
@@ -136,19 +113,48 @@
}
@Test
- public void testIsAvailable() {
- // should not be available for manual DND
+ public void isAvailable_systemModeNotCustomManual_true() {
+ ZenMode mode = new TestModeBuilder()
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_SCHEDULE_CALENDAR)
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_appProvidedMode_true() {
+ ZenMode mode = new TestModeBuilder()
+ .setPackage("com.some.package")
+ .setType(TYPE_OTHER)
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_customManualMode_false() {
+ ZenMode mode = new TestModeBuilder()
+ .setConditionId(ZenModeConfig.toCustomManualConditionId())
+ .setPackage(SystemZenRules.PACKAGE_ANDROID)
+ .setType(TYPE_OTHER)
+ .build();
+ mController.setZenMode(mode);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_manualDND_false() {
ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb",
Uri.parse("manual"))
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
- .build(), true);
+ .build(), /* isActive= */ false);
- mController.updateZenMode(mPrefCategory, manualMode);
+ mController.setZenMode(manualMode);
assertThat(mController.isAvailable()).isFalse();
-
- // should be available for other modes
- mController.updateZenMode(mPrefCategory, TestModeBuilder.EXAMPLE);
- assertThat(mController.isAvailable()).isTrue();
}
@Test
@@ -156,23 +162,23 @@
ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
// Update preference controller with a zen mode that is not enabled
- mController.updateZenMode(mPrefCategory, zenMode);
- assertThat(mConfigPreference.getCheckedState()).isFalse();
+ mController.updateZenMode(mPreference, zenMode);
+ assertThat(mPreference.getCheckedState()).isFalse();
// Now with the rule enabled
zenMode.getRule().setEnabled(true);
- mController.updateZenMode(mPrefCategory, zenMode);
- assertThat(mConfigPreference.getCheckedState()).isTrue();
+ mController.updateZenMode(mPreference, zenMode);
+ assertThat(mPreference.getCheckedState()).isTrue();
}
@Test
public void onPreferenceChange_toggleOn_enablesModeAfterConfirmation() {
// Start with a disabled mode
ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
- mController.updateZenMode(mPrefCategory, zenMode);
+ mController.updateZenMode(mPreference, zenMode);
// Flip the switch
- mConfigPreference.callChangeListener(true);
+ mPreference.callChangeListener(true);
verify(mBackend, never()).updateMode(any());
// Oh wait, I forgot to confirm! Let's do that
@@ -193,10 +199,10 @@
public void onPreferenceChange_toggleOff_disablesModeAfterConfirmation() {
// Start with an enabled mode
ZenMode zenMode = new TestModeBuilder().setEnabled(true).build();
- mController.updateZenMode(mPrefCategory, zenMode);
+ mController.updateZenMode(mPreference, zenMode);
// Flip the switch
- mConfigPreference.callChangeListener(false);
+ mPreference.callChangeListener(false);
verify(mBackend, never()).updateMode(any());
// Oh wait, I forgot to confirm! Let's do that
@@ -217,17 +223,17 @@
public void onPreferenceChange_ifPressCancelButton_doesNotUpdateMode() {
// Start with a disabled mode
ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
- mController.updateZenMode(mPrefCategory, zenMode);
+ mController.updateZenMode(mPreference, zenMode);
// Flip the switch, then have second thoughts about it
- mConfigPreference.callChangeListener(true);
+ mPreference.callChangeListener(true);
ShadowAlertDialog.getLatestAlertDialog()
.getButton(AlertDialog.BUTTON_NEGATIVE).performClick();
shadowOf(Looper.getMainLooper()).idle();
// Verify nothing changed, and the switch shows the correct (pre-change) value.
verify(mBackend, never()).updateMode(any());
- assertThat(mConfigPreference.isChecked()).isFalse();
+ assertThat(mPreference.isChecked()).isFalse();
assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isFalse();
}
@@ -235,16 +241,16 @@
public void onPreferenceChange_ifExitingDialog_doesNotUpdateMode() {
// Start with a disabled mode
ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
- mController.updateZenMode(mPrefCategory, zenMode);
+ mController.updateZenMode(mPreference, zenMode);
// Flip the switch, but close the dialog without selecting either button.
- mConfigPreference.callChangeListener(true);
+ mPreference.callChangeListener(true);
ShadowAlertDialog.getLatestAlertDialog().dismiss();
shadowOf(Looper.getMainLooper()).idle();
// Verify nothing changed, and the switch shows the correct (pre-change) value.
verify(mBackend, never()).updateMode(any());
- assertThat(mConfigPreference.isChecked()).isFalse();
+ assertThat(mPreference.isChecked()).isFalse();
assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isFalse();
}
@@ -260,15 +266,14 @@
.setTriggerDescription("My events")
.build();
- mController.updateState(mPrefCategory, mode);
+ mController.updateState(mPreference, mode);
- assertThat(mAddPreference.isVisible()).isFalse();
- assertThat(mConfigPreference.isVisible()).isTrue();
- assertThat(mConfigPreference.getTitle()).isEqualTo("Calendar events");
- assertThat(mConfigPreference.getSummary()).isEqualTo("My events");
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getTitle()).isEqualTo("Calendar events");
+ assertThat(mPreference.getSummary()).isEqualTo("My events");
// Destination as written into the intent by SubSettingLauncher
assertThat(
- mConfigPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(ZenModeSetCalendarFragment.class.getName());
}
@@ -285,39 +290,18 @@
.setTriggerDescription("some schedule")
.build();
- mController.updateState(mPrefCategory, mode);
+ mController.updateState(mPreference, mode);
- assertThat(mAddPreference.isVisible()).isFalse();
- assertThat(mConfigPreference.isVisible()).isTrue();
- assertThat(mConfigPreference.getTitle()).isEqualTo("1:00 AM - 3:00 PM");
- assertThat(mConfigPreference.getSummary()).isEqualTo("Mon - Tue, Thu");
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getTitle()).isEqualTo("1:00 AM - 3:00 PM");
+ assertThat(mPreference.getSummary()).isEqualTo("Mon - Tue, Thu");
// Destination as written into the intent by SubSettingLauncher
assertThat(
- mConfigPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(ZenModeSetScheduleFragment.class.getName());
}
@Test
- public void updateState_customManualRule() {
- ZenMode mode = new TestModeBuilder()
- .setConditionId(ZenModeConfig.toCustomManualConditionId())
- .setPackage(SystemZenRules.PACKAGE_ANDROID)
- .setType(TYPE_OTHER)
- .setTriggerDescription("Will not be shown")
- .build();
-
- mController.updateState(mPrefCategory, mode);
-
- assertThat(mConfigPreference.isVisible()).isFalse();
- assertThat(mAddPreference.isVisible()).isTrue();
- assertThat(mAddPreference.getTitle()).isEqualTo(
- mContext.getString(R.string.zen_mode_select_schedule));
- assertThat(mAddPreference.getSummary()).isNull();
- // Sets up a click listener to open the dialog.
- assertThat(mAddPreference.getOnPreferenceClickListener()).isNotNull();
- }
-
- @Test
public void updateState_appWithConfigActivity_showsLinkToConfigActivity() {
ZenMode mode = new TestModeBuilder()
.setPackage("some.package")
@@ -327,12 +311,12 @@
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
.thenReturn(configurationIntent);
- mController.updateState(mPrefCategory, mode);
+ mController.updateState(mPreference, mode);
- assertThat(mConfigPreference.isVisible()).isTrue();
- assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
- assertThat(mConfigPreference.getSummary()).isEqualTo("When The Music's Over");
- assertThat(mConfigPreference.getIntent()).isEqualTo(configurationIntent);
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getTitle()).isEqualTo("Linked to app");
+ assertThat(mPreference.getSummary()).isEqualTo("When The Music's Over");
+ assertThat(mPreference.getIntent()).isEqualTo(configurationIntent);
}
@Test
@@ -344,12 +328,12 @@
when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any()))
.thenReturn(null);
- mController.updateState(mPrefCategory, mode);
+ mController.updateState(mPreference, mode);
- assertThat(mConfigPreference.isVisible()).isTrue();
- assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
- assertThat(mConfigPreference.getSummary()).isEqualTo("When the saints go marching in");
- assertThat(mConfigPreference.getIntent()).isNull();
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getTitle()).isEqualTo("Linked to app");
+ assertThat(mPreference.getSummary()).isEqualTo("When the saints go marching in");
+ assertThat(mPreference.getIntent()).isNull();
}
@Test
@@ -362,11 +346,11 @@
.thenReturn(configurationIntent);
when(mPm.getText(any(), anyInt(), any())).thenReturn("The App Name");
- mController.updateState(mPrefCategory, mode);
+ mController.updateState(mPreference, mode);
- assertThat(mConfigPreference.isVisible()).isTrue();
- assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
- assertThat(mConfigPreference.getSummary()).isEqualTo("Info and settings in The App Name");
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getTitle()).isEqualTo("Linked to app");
+ assertThat(mPreference.getSummary()).isEqualTo("Info and settings in The App Name");
}
@Test
@@ -378,52 +362,10 @@
.thenReturn(null);
when(mPm.getText(any(), anyInt(), any())).thenReturn("The App Name");
- mController.updateState(mPrefCategory, mode);
+ mController.updateState(mPreference, mode);
- assertThat(mConfigPreference.isVisible()).isTrue();
- assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app");
- assertThat(mConfigPreference.getSummary()).isEqualTo("Managed by The App Name");
- }
-
- @Test
- public void onScheduleChosen_updatesMode() {
- ZenMode originalMode = new TestModeBuilder()
- .setConditionId(ZenModeConfig.toCustomManualConditionId())
- .setPackage(SystemZenRules.PACKAGE_ANDROID)
- .setType(TYPE_OTHER)
- .setTriggerDescription("")
- .build();
- mController.updateZenMode(mPrefCategory, originalMode);
-
- ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
- scheduleInfo.days = new int[] { Calendar.MONDAY };
- scheduleInfo.startHour = 12;
- scheduleInfo.endHour = 15;
- Uri scheduleUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
-
- mController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri);
-
- // verify the backend got asked to update the mode to be schedule-based.
- ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
- verify(mBackend).updateMode(captor.capture());
- ZenMode updatedMode = captor.getValue();
- assertThat(updatedMode.getType()).isEqualTo(TYPE_SCHEDULE_TIME);
- assertThat(updatedMode.getRule().getConditionId()).isEqualTo(scheduleUri);
- assertThat(updatedMode.getRule().getTriggerDescription()).isNotEmpty();
- assertThat(updatedMode.getRule().getOwner()).isEqualTo(
- ZenModeConfig.getScheduleConditionProvider());
- }
-
- static class CharSequenceTruth {
- /**
- * Shortcut version of {@link Truth#assertThat(String)} suitable for {@link CharSequence}.
- * {@link CharSequence} doesn't necessarily provide a good {@code equals()} implementation;
- * however we don't care about formatting here, so we want to assert on the resulting
- * string (without needing to worry that {@code assertThat(x.getText().toString())} can
- * throw if the text is null).
- */
- static StringSubject assertThat(@Nullable CharSequence actual) {
- return Truth.assertThat((String) (actual != null ? actual.toString() : null));
- }
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getTitle()).isEqualTo("Linked to app");
+ assertThat(mPreference.getSummary()).isEqualTo("Managed by The App Name");
}
}