From 9e2ac046e40da2a48f88d4db4bf08fec5e9548e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Tue, 23 Jul 2024 17:50:35 +0200 Subject: [PATCH] Changes to the trigger segment * Switch is always visible, even without a configuration activity * Custom icon, title, and summary for some mode types (such as SCHEDULE_TIME, etc). * Default texts in case of missing trigger description. * Different icons for having/missing configuration activity. * Move the section to the top of the screen. Bug: 349376785 Test: atest ZenModeTriggerLinkPreferenceControllerTest Flag: android.app.modes_ui Change-Id: I960318899cf4da20ffc5765818429d5790d05067 --- .../ic_zen_mode_trigger_with_activity.xml | 26 +++ .../ic_zen_mode_trigger_without_activity.xml | 25 +++ res/values/strings.xml | 17 +- res/xml/modes_rule_settings.xml | 23 +- ...odeSetTriggerLinkPreferenceController.java | 183 +++++++++++---- ...etTriggerLinkPreferenceControllerTest.java | 208 ++++++++++++------ 6 files changed, 356 insertions(+), 126 deletions(-) create mode 100644 res/drawable/ic_zen_mode_trigger_with_activity.xml create mode 100644 res/drawable/ic_zen_mode_trigger_without_activity.xml diff --git a/res/drawable/ic_zen_mode_trigger_with_activity.xml b/res/drawable/ic_zen_mode_trigger_with_activity.xml new file mode 100644 index 00000000000..567f01a87cc --- /dev/null +++ b/res/drawable/ic_zen_mode_trigger_with_activity.xml @@ -0,0 +1,26 @@ + + + + \ No newline at end of file diff --git a/res/drawable/ic_zen_mode_trigger_without_activity.xml b/res/drawable/ic_zen_mode_trigger_without_activity.xml new file mode 100644 index 00000000000..11a97f16953 --- /dev/null +++ b/res/drawable/ic_zen_mode_trigger_without_activity.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index f994fbc911d..08d29ea0678 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8124,9 +8124,9 @@ Allow visual signals - + Stay focused - + Additional actions @@ -9483,6 +9483,19 @@ Mode name + + Calendar events + + Sleep schedule + + While driving + + Linked to app + + Info and settings in %1$s + + Managed by %1$s + Warning diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml index a8ba5530e8b..2464c25be5d 100644 --- a/res/xml/modes_rule_settings.xml +++ b/res/xml/modes_rule_settings.xml @@ -28,6 +28,21 @@ android:selectable="false" android:layout="@layout/modes_activation_button"/> + + + + + + + + @@ -49,14 +64,6 @@ android:title="@string/zen_category_exceptions" /> - - - - - diff --git a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java index 86135a96190..1f979022670 100644 --- a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java @@ -16,14 +16,26 @@ package com.android.settings.notification.modes; +import static android.app.AutomaticZenRule.TYPE_BEDTIME; +import static android.app.AutomaticZenRule.TYPE_DRIVING; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; 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.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.service.notification.SystemZenRules; +import android.service.notification.ZenModeConfig; import android.util.Log; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; @@ -35,6 +47,8 @@ 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. */ @@ -42,26 +56,29 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc private static final String TAG = "ZenModeSetTriggerLink"; @VisibleForTesting - protected static final String AUTOMATIC_TRIGGER_PREF_KEY = "zen_automatic_trigger_settings"; + static final String AUTOMATIC_TRIGGER_KEY = "zen_automatic_trigger_settings"; + static final String ADD_TRIGGER_KEY = "zen_add_automatic_trigger"; + private final DashboardFragment mFragment; + private final PackageManager mPackageManager; private final ConfigurationActivityHelper mConfigurationActivityHelper; private final ZenServiceListing mServiceListing; - private final DashboardFragment mFragment; ZenModeSetTriggerLinkPreferenceController(Context context, String key, DashboardFragment fragment, ZenModesBackend backend) { - this(context, key, fragment, backend, + this(context, key, fragment, backend, context.getPackageManager(), new ConfigurationActivityHelper(context.getPackageManager()), new ZenServiceListing(context)); } @VisibleForTesting ZenModeSetTriggerLinkPreferenceController(Context context, String key, - DashboardFragment fragment, ZenModesBackend backend, + DashboardFragment fragment, ZenModesBackend backend, PackageManager packageManager, ConfigurationActivityHelper configurationActivityHelper, ZenServiceListing serviceListing) { super(context, key, backend); mFragment = fragment; + mPackageManager = packageManager; mConfigurationActivityHelper = configurationActivityHelper; mServiceListing = serviceListing; } @@ -83,64 +100,137 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc // 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). - PrimarySwitchPreference switchPref = ((PreferenceCategory) preference).findPreference( - AUTOMATIC_TRIGGER_PREF_KEY); - if (switchPref == null) { + if (zenMode.isManualDnd()) { return; } - switchPref.setChecked(zenMode.getRule().isEnabled()); - switchPref.setOnPreferenceChangeListener(mSwitchChangeListener); - switchPref.setSummary(zenMode.getRule().getTriggerDescription()); - switchPref.setIcon(null); - switchPref.setOnPreferenceClickListener(null); - switchPref.setIntent(null); + PrimarySwitchPreference triggerPref = checkNotNull( + ((PreferenceCategory) preference).findPreference(AUTOMATIC_TRIGGER_KEY)); + Preference addTriggerPref = checkNotNull( + ((PreferenceCategory) preference).findPreference(ADD_TRIGGER_KEY)); - if (zenMode.isSystemOwned()) { - if (zenMode.getType() == TYPE_SCHEDULE_TIME) { - switchPref.setTitle(R.string.zen_mode_set_schedule_link); - // TODO: b/332937635 - set correct metrics category - switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext, - ZenModeSetScheduleFragment.class, zenMode.getId(), 0).toIntent()); - } else if (zenMode.getType() == TYPE_SCHEDULE_CALENDAR) { - switchPref.setTitle(R.string.zen_mode_set_calendar_link); - switchPref.setIcon(null); - // TODO: b/332937635 - set correct metrics category - switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext, - ZenModeSetCalendarFragment.class, zenMode.getId(), 0).toIntent()); - } else { - switchPref.setTitle(R.string.zen_mode_select_schedule); - switchPref.setIcon(R.drawable.ic_add_24dp); - switchPref.setSummary(""); - // TODO: b/342156843 - Hide the switch (needs support in SettingsLib). - switchPref.setOnPreferenceClickListener(clickedPreference -> { - ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener); - return true; - }); - } + 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; + }); } else { - Intent intent = mConfigurationActivityHelper.getConfigurationActivityIntentForMode( - zenMode, mServiceListing::findService); - if (intent != null) { - preference.setVisible(true); - switchPref.setTitle(R.string.zen_mode_configuration_link_title); - switchPref.setSummary(zenMode.getRule().getTriggerDescription()); - switchPref.setIntent(intent); + addTriggerPref.setVisible(false); + triggerPref.setVisible(true); + triggerPref.setChecked(zenMode.getRule().isEnabled()); + triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener); + + if (zenMode.isSystemOwned()) { + setUpForSystemOwnedTrigger(triggerPref, zenMode); } else { - Log.i(TAG, "No intent found for " + zenMode.getRule().getName()); - preference.setVisible(false); + setUpForAppTrigger(triggerPref, zenMode); } } } + private void setUpForSystemOwnedTrigger(Preference preference, ZenMode mode) { + if (mode.getType() == TYPE_SCHEDULE_TIME) { + // TODO: b/332937635 - set correct metrics category + preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext, + ZenModeSetScheduleFragment.class, mode.getId(), 0).toIntent()); + + // [Clock Icon] 9:00 - 17:00 / Sun-Mon + preference.setIcon(com.android.internal.R.drawable.ic_zen_mode_type_schedule_time); + ZenModeConfig.ScheduleInfo schedule = + tryParseScheduleConditionId(mode.getRule().getConditionId()); + if (schedule != null) { + preference.setTitle(SystemZenRules.getTimeSummary(mContext, schedule)); + preference.setSummary(SystemZenRules.getShortDaysSummary(mContext, schedule)); + } else { + // Fallback, but shouldn't happen. + Log.wtf(TAG, "SCHEDULE_TIME mode without schedule: " + mode); + preference.setTitle(R.string.zen_mode_set_schedule_link); + preference.setSummary(null); + } + } else if (mode.getType() == TYPE_SCHEDULE_CALENDAR) { + // TODO: b/332937635 - set correct metrics category + preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext, + ZenModeSetCalendarFragment.class, mode.getId(), 0).toIntent()); + + // [Event Icon] Calendar Events / + preference.setIcon( + com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar); + preference.setTitle(R.string.zen_mode_trigger_title_schedule_calendar); + preference.setSummary(mode.getTriggerDescription()); + } else { + Log.wtf(TAG, "Unexpected type for system-owned mode: " + mode); + } + } + + @SuppressLint("SwitchIntDef") + private void setUpForAppTrigger(Preference preference, ZenMode mode) { + // App-owned mode may have triggerDescription, configurationActivity, or both/neither. + Intent configurationIntent = + mConfigurationActivityHelper.getConfigurationActivityIntentForMode( + mode, mServiceListing::findService); + + @StringRes int title = switch (mode.getType()) { + case TYPE_BEDTIME -> R.string.zen_mode_trigger_title_bedtime; + case TYPE_DRIVING -> R.string.zen_mode_trigger_title_driving; + default -> R.string.zen_mode_trigger_title_generic; + }; + + String summary; + if (!Strings.isNullOrEmpty(mode.getTriggerDescription())) { + summary = mode.getTriggerDescription(); + } else if (!Strings.isNullOrEmpty(mode.getRule().getPackageName())) { + String appName = null; + try { + ApplicationInfo appInfo = mPackageManager.getApplicationInfo( + mode.getRule().getPackageName(), 0); + appName = appInfo.loadLabel(mPackageManager).toString(); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Couldn't resolve owner for mode: " + mode); + } + + if (appName != null) { + summary = mContext.getString( + configurationIntent != null + ? R.string.zen_mode_trigger_summary_settings_in_app + : R.string.zen_mode_trigger_summary_managed_by_app, + appName); + } else { + summary = null; + } + } else { + Log.e(TAG, "Mode without package! " + mode); + summary = null; + } + + @DrawableRes int icon; + if (mode.getType() == TYPE_BEDTIME) { + icon = com.android.internal.R.drawable.ic_zen_mode_type_schedule_time; // Clock + } else if (mode.getType() == TYPE_DRIVING) { + icon = com.android.internal.R.drawable.ic_zen_mode_type_driving; // Car + } else { + icon = configurationIntent != null ? R.drawable.ic_zen_mode_trigger_with_activity + : R.drawable.ic_zen_mode_trigger_without_activity; + } + + preference.setTitle(title); + preference.setSummary(summary); + preference.setIcon(icon); + 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? }); - @VisibleForTesting - protected Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> { + private final Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> { final boolean newEnabled = (Boolean) newValue; return saveMode((zenMode) -> { if (newEnabled != zenMode.getRule().isEnabled()) { @@ -148,6 +238,5 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc } return zenMode; }); - // TODO: b/342156843 - Do we want to jump to the corresponding schedule editing screen? }; } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java index fc3cef142c5..61ca4d84662 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java @@ -22,11 +22,15 @@ 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.ZenModeSetTriggerLinkPreferenceController.AUTOMATIC_TRIGGER_PREF_KEY; +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.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +39,7 @@ import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.platform.test.annotations.EnableFlags; @@ -42,7 +47,11 @@ import android.platform.test.flag.junit.SetFlagsRule; 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; @@ -53,6 +62,9 @@ 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; @@ -60,6 +72,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; import org.robolectric.RobolectricTestRunner; import java.util.Calendar; @@ -74,32 +87,47 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { private ZenModesBackend mBackend; private Context mContext; - private PrimarySwitchPreference mPreference; - @Mock private PackageManager mPm; @Mock private ConfigurationActivityHelper mConfigurationActivityHelper; - @Mock private PreferenceCategory mPrefCategory; + private PrimarySwitchPreference mConfigPreference; + private Preference mAddPreference; + @Mock private DashboardFragment mFragment; - private ZenModeSetTriggerLinkPreferenceController mPrefController; + private ZenModeSetTriggerLinkPreferenceController mController; @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); - mPrefController = new ZenModeSetTriggerLinkPreferenceController(mContext, - "zen_automatic_trigger_category", mFragment, mBackend, - mConfigurationActivityHelper, - mock(ZenServiceListing.class)); - mPreference = new PrimarySwitchPreference(mContext); + PreferenceManager preferenceManager = new PreferenceManager(mContext); + PreferenceScreen preferenceScreen = preferenceManager.inflateFromResource(mContext, + R.xml.modes_rule_settings, null); - when(mPrefCategory.findPreference(AUTOMATIC_TRIGGER_PREF_KEY)).thenReturn(mPreference); + mController = new ZenModeSetTriggerLinkPreferenceController(mContext, + "zen_automatic_trigger_category", mFragment, 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); + + when(mPm.getApplicationInfo(any(), anyInt())).then( + (Answer) invocationOnMock -> { + ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.packageName = invocationOnMock.getArgument(0); + appInfo.labelRes = 1; // Whatever, but != 0 so that loadLabel calls PM.getText() + return appInfo; + }); + when(mPm.getText(any(), anyInt(), any())).then( + (Answer) invocationOnMock -> + "App named " + invocationOnMock.getArgument(0)); } @Test @@ -110,37 +138,37 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .build(), true); - mPrefController.updateZenMode(mPrefCategory, manualMode); - assertThat(mPrefController.isAvailable()).isFalse(); + mController.updateZenMode(mPrefCategory, manualMode); + assertThat(mController.isAvailable()).isFalse(); // should be available for other modes - mPrefController.updateZenMode(mPrefCategory, TestModeBuilder.EXAMPLE); - assertThat(mPrefController.isAvailable()).isTrue(); + mController.updateZenMode(mPrefCategory, TestModeBuilder.EXAMPLE); + assertThat(mController.isAvailable()).isTrue(); } @Test - public void testUpdateState() { + public void updateState_switchCheckedIfRuleEnabled() { ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); // Update preference controller with a zen mode that is not enabled - mPrefController.updateZenMode(mPrefCategory, zenMode); - assertThat(mPreference.getCheckedState()).isFalse(); + mController.updateZenMode(mPrefCategory, zenMode); + assertThat(mConfigPreference.getCheckedState()).isFalse(); // Now with the rule enabled zenMode.getRule().setEnabled(true); - mPrefController.updateZenMode(mPrefCategory, zenMode); - assertThat(mPreference.getCheckedState()).isTrue(); + mController.updateZenMode(mPrefCategory, zenMode); + assertThat(mConfigPreference.getCheckedState()).isTrue(); } @Test - public void testOnPreferenceChange() { + public void onPreferenceChange_updatesMode() { ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); // start with disabled rule - mPrefController.updateZenMode(mPrefCategory, zenMode); + mController.updateZenMode(mPrefCategory, zenMode); - // then update the preference to be checked - mPrefController.mSwitchChangeListener.onPreferenceChange(mPreference, true); + // then flip the switch + mConfigPreference.callChangeListener(true); // verify the backend got asked to update the mode to be enabled ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); @@ -149,7 +177,7 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { } @Test - public void testRuleLink_calendar() { + public void updateState_scheduleCalendarRule() { ZenModeConfig.EventInfo eventInfo = new ZenModeConfig.EventInfo(); eventInfo.calendarId = 1L; eventInfo.calName = "My events"; @@ -159,23 +187,21 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { .setType(TYPE_SCHEDULE_CALENDAR) .setTriggerDescription("My events") .build(); - mPrefController.updateZenMode(mPrefCategory, mode); - assertThat(mPreference.getTitle()).isNotNull(); - assertThat(mPreference.getTitle().toString()).isEqualTo( - mContext.getString(R.string.zen_mode_set_calendar_link)); - assertThat(mPreference.getSummary()).isNotNull(); - assertThat(mPreference.getSummary().toString()).isEqualTo( - mode.getRule().getTriggerDescription()); - assertThat(mPreference.getIcon()).isNull(); + mController.updateState(mPrefCategory, mode); + assertThat(mAddPreference.isVisible()).isFalse(); + assertThat(mConfigPreference.isVisible()).isTrue(); + assertThat(mConfigPreference.getTitle()).isEqualTo("Calendar events"); + assertThat(mConfigPreference.getSummary()).isEqualTo("My events"); // Destination as written into the intent by SubSettingLauncher - assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + assertThat( + mConfigPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) .isEqualTo(ZenModeSetCalendarFragment.class.getName()); } @Test - public void testRuleLink_schedule() { + public void updateState_scheduleTimeRule() { ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo(); scheduleInfo.days = new int[]{Calendar.MONDAY, Calendar.TUESDAY, Calendar.THURSDAY}; scheduleInfo.startHour = 1; @@ -186,44 +212,41 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { .setType(TYPE_SCHEDULE_TIME) .setTriggerDescription("some schedule") .build(); - mPrefController.updateZenMode(mPrefCategory, mode); - assertThat(mPreference.getTitle()).isNotNull(); - assertThat(mPreference.getTitle().toString()).isEqualTo( - mContext.getString(R.string.zen_mode_set_schedule_link)); - assertThat(mPreference.getSummary()).isNotNull(); - assertThat(mPreference.getSummary().toString()).isEqualTo( - mode.getRule().getTriggerDescription()); - assertThat(mPreference.getIcon()).isNull(); + mController.updateState(mPrefCategory, 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"); // Destination as written into the intent by SubSettingLauncher - assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + assertThat( + mConfigPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) .isEqualTo(ZenModeSetScheduleFragment.class.getName()); } @Test - public void testRuleLink_manual() { + public void updateState_customManualRule() { ZenMode mode = new TestModeBuilder() .setConditionId(ZenModeConfig.toCustomManualConditionId()) .setPackage(SystemZenRules.PACKAGE_ANDROID) .setType(TYPE_OTHER) .setTriggerDescription("Will not be shown") .build(); - mPrefController.updateZenMode(mPrefCategory, mode); - assertThat(mPreference.getTitle()).isNotNull(); - assertThat(mPreference.getTitle().toString()).isEqualTo( + 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(mPreference.getIcon()).isNotNull(); - assertThat(mPreference.getSummary()).isNotNull(); - assertThat(mPreference.getSummary().toString()).isEqualTo(""); - - // Set up a click listener to open the dialog. - assertThat(mPreference.getOnPreferenceClickListener()).isNotNull(); + assertThat(mAddPreference.getSummary()).isNull(); + // Sets up a click listener to open the dialog. + assertThat(mAddPreference.getOnPreferenceClickListener()).isNotNull(); } @Test - public void testRuleLink_appWithConfigActivity_linksToConfigActivity() { + public void updateState_appWithConfigActivity_showsLinkToConfigActivity() { ZenMode mode = new TestModeBuilder() .setPackage("some.package") .setTriggerDescription("When The Music's Over") @@ -232,28 +255,62 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any())) .thenReturn(configurationIntent); - mPrefController.updateZenMode(mPrefCategory, mode); + mController.updateState(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); + 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); } @Test - public void testRuleLink_appWithoutConfigActivity_hidden() { + public void updateState_appWithoutConfigActivity_showsWithoutLinkToConfigActivity() { ZenMode mode = new TestModeBuilder() .setPackage("some.package") - .setTriggerDescription("Will not be shown :(") + .setTriggerDescription("When the saints go marching in") .build(); when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any())) .thenReturn(null); - mPrefController.updateZenMode(mPrefCategory, mode); + mController.updateState(mPrefCategory, mode); - assertThat(mPrefCategory.isVisible()).isFalse(); + 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(); + } + + @Test + public void updateState_appWithoutTriggerDescriptionWithConfigActivity_showsAppNameInSummary() { + ZenMode mode = new TestModeBuilder() + .setPackage("some.package") + .build(); + Intent configurationIntent = new Intent("configure the mode"); + when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any())) + .thenReturn(configurationIntent); + when(mPm.getText(any(), anyInt(), any())).thenReturn("The App Name"); + + mController.updateState(mPrefCategory, mode); + + assertThat(mConfigPreference.isVisible()).isTrue(); + assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app"); + assertThat(mConfigPreference.getSummary()).isEqualTo("Info and settings in The App Name"); + } + + @Test + public void updateState_appWithoutTriggerDescriptionNorConfigActivity_showsAppNameInSummary() { + ZenMode mode = new TestModeBuilder() + .setPackage("some.package") + .build(); + when(mConfigurationActivityHelper.getConfigurationActivityIntentForMode(any(), any())) + .thenReturn(null); + when(mPm.getText(any(), anyInt(), any())).thenReturn("The App Name"); + + mController.updateState(mPrefCategory, mode); + + assertThat(mConfigPreference.isVisible()).isTrue(); + assertThat(mConfigPreference.getTitle()).isEqualTo("Linked to app"); + assertThat(mConfigPreference.getSummary()).isEqualTo("Managed by The App Name"); } @Test @@ -264,7 +321,7 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { .setType(TYPE_OTHER) .setTriggerDescription("") .build(); - mPrefController.updateZenMode(mPrefCategory, originalMode); + mController.updateZenMode(mPrefCategory, originalMode); ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo(); scheduleInfo.days = new int[] { Calendar.MONDAY }; @@ -272,7 +329,7 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { scheduleInfo.endHour = 15; Uri scheduleUri = ZenModeConfig.toScheduleConditionId(scheduleInfo); - mPrefController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri); + mController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri); // verify the backend got asked to update the mode to be schedule-based. ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); @@ -284,4 +341,17 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { 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)); + } + } }