Change zen schedules page to use switches.

Also add clearer summaries for schedule and calendar events (rather than just "Off" and "On"): for schedules, list the days & times for which the schedule is active, and for events, the calendar whose events it triggers on.

Bug: 190180868
Bug: 215564123
Test: manual for switches, ZenRuleScheduleHelperTest for descriptions

Change-Id: I3ad579503adae0a66dfa3093b4e3df77db0ede31
diff --git a/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettings.java
index 5b02b3b..d46d31a 100644
--- a/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettings.java
+++ b/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettings.java
@@ -42,7 +42,6 @@
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
 
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -56,8 +55,7 @@
 
     public static final String ACTION = Settings.ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS;
 
-    // per-instance to ensure we're always using the current locale
-    private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE");
+    private final ZenRuleScheduleHelper mScheduleHelper = new ZenRuleScheduleHelper();
 
     private Preference mDays;
     private TimePickerPreference mStart;
@@ -149,30 +147,11 @@
     }
 
     private void updateDays() {
-        // Compute an ordered, delimited list of day names based on the persisted user config.
-        final int[] days = mSchedule.days;
-        if (days != null && days.length > 0) {
-            final StringBuilder sb = new StringBuilder();
-            final Calendar c = Calendar.getInstance();
-            int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(c);
-            for (int i = 0; i < daysOfWeek.length; i++) {
-                final int day = daysOfWeek[i];
-                for (int j = 0; j < days.length; j++) {
-                    if (day == days[j]) {
-                        c.set(Calendar.DAY_OF_WEEK, day);
-                        if (sb.length() > 0) {
-                            sb.append(mContext.getString(R.string.summary_divider_text));
-                        }
-                        sb.append(mDayFormat.format(c.getTime()));
-                        break;
-                    }
-                }
-            }
-            if (sb.length() > 0) {
-                mDays.setSummary(sb);
-                mDays.notifyDependencyChange(false);
-                return;
-            }
+        String desc = mScheduleHelper.getDaysDescription(mContext, mSchedule);
+        if (desc != null) {
+            mDays.setSummary(desc);
+            mDays.notifyDependencyChange(false);
+            return;
         }
         mDays.setSummary(R.string.zen_mode_schedule_rule_days_none);
         mDays.notifyDependencyChange(true);
diff --git a/src/com/android/settings/notification/zen/ZenRulePreference.java b/src/com/android/settings/notification/zen/ZenRulePreference.java
index a265a07..8cf3106 100644
--- a/src/com/android/settings/notification/zen/ZenRulePreference.java
+++ b/src/com/android/settings/notification/zen/ZenRulePreference.java
@@ -23,22 +23,20 @@
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.service.notification.ZenModeConfig;
-import android.view.View;
-import android.widget.CheckBox;
+import android.service.notification.ZenModeConfig.ScheduleInfo;
 
 import androidx.fragment.app.Fragment;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
 
 import com.android.settings.R;
 import com.android.settings.utils.ManagedServiceSettings;
 import com.android.settings.utils.ZenServiceListing;
+import com.android.settingslib.PrimarySwitchPreference;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.widget.TwoTargetPreference;
 
 import java.util.Map;
 
-public class ZenRulePreference extends TwoTargetPreference {
+public class ZenRulePreference extends PrimarySwitchPreference {
     private static final ManagedServiceSettings.Config CONFIG =
             ZenModeAutomationSettings.getConditionProviderConfig();
     final String mId;
@@ -53,14 +51,13 @@
     CharSequence mName;
 
     private Intent mIntent;
-    private boolean mChecked;
-    private CheckBox mCheckBox;
+
+    private final ZenRuleScheduleHelper mScheduleHelper = new ZenRuleScheduleHelper();
 
     public ZenRulePreference(Context context,
             final Map.Entry<String, AutomaticZenRule> ruleEntry,
             Fragment parent, MetricsFeatureProvider metricsProvider) {
         super(context);
-        setLayoutResource(R.layout.preference_checkable_two_target);
         mBackend = ZenModeBackend.getInstance(context);
         mContext = context;
         mRule = ruleEntry.getValue();
@@ -72,50 +69,11 @@
         mServiceListing.reloadApprovedServices();
         mPref = this;
         mMetricsFeatureProvider = metricsProvider;
-        mChecked = mRule.isEnabled();
         setAttributes(mRule);
         setWidgetLayoutResource(getSecondTargetResId());
-    }
 
-    protected int getSecondTargetResId() {
-        if (mIntent != null) {
-            return R.layout.zen_rule_widget;
-        }
-        return 0;
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder view) {
-        super.onBindViewHolder(view);
-        View settingsWidget = view.findViewById(android.R.id.widget_frame);
-        View divider = view.findViewById(R.id.two_target_divider);
-        if (mIntent != null) {
-            divider.setVisibility(View.VISIBLE);
-            settingsWidget.setVisibility(View.VISIBLE);
-            settingsWidget.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    mContext.startActivity(mIntent);
-                }
-            });
-        } else {
-            divider.setVisibility(View.GONE);
-            settingsWidget.setVisibility(View.GONE);
-            settingsWidget.setOnClickListener(null);
-        }
-
-        View checkboxContainer = view.findViewById(R.id.checkbox_container);
-        if (checkboxContainer != null) {
-            checkboxContainer.setOnClickListener(mOnCheckBoxClickListener);
-        }
-        mCheckBox = (CheckBox) view.findViewById(com.android.internal.R.id.checkbox);
-        if (mCheckBox != null) {
-            mCheckBox.setChecked(mChecked);
-        }
-    }
-
-    public boolean isChecked() {
-        return mChecked;
+        // initialize the checked state of the preference
+        super.setChecked(mRule.isEnabled());
     }
 
     public void updatePreference(AutomaticZenRule rule) {
@@ -126,34 +84,24 @@
 
         if (mRule.isEnabled() != rule.isEnabled()) {
             setChecked(rule.isEnabled());
-            setSummary(computeRuleSummary(rule));
         }
-
+        setSummary(computeRuleSummary(rule));
         mRule = rule;
     }
 
     @Override
     public void onClick() {
-        mOnCheckBoxClickListener.onClick(null);
+        mContext.startActivity(mIntent);
     }
 
-    private void setChecked(boolean checked) {
-        mChecked = checked;
-        if (mCheckBox != null) {
-            mCheckBox.setChecked(checked);
-        }
+    @Override
+    public void setChecked(boolean checked) {
+        mRule.setEnabled(checked);
+        mBackend.updateZenRule(mId, mRule);
+        setAttributes(mRule);
+        super.setChecked(checked);
     }
 
-    private View.OnClickListener mOnCheckBoxClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mRule.setEnabled(!mChecked);
-            mBackend.updateZenRule(mId, mRule);
-            setChecked(mRule.isEnabled());
-            setAttributes(mRule);
-        }
-    };
-
     protected void setAttributes(AutomaticZenRule rule) {
         final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
                 rule.getConditionId(), true);
@@ -178,6 +126,30 @@
     }
 
     private String computeRuleSummary(AutomaticZenRule rule) {
+        if (rule != null) {
+            // handle schedule-based rules
+            ScheduleInfo schedule =
+                    ZenModeConfig.tryParseScheduleConditionId(rule.getConditionId());
+            if (schedule != null) {
+                String desc = mScheduleHelper.getDaysAndTimeSummary(mContext, schedule);
+                return (desc != null) ? desc :
+                        mContext.getResources().getString(
+                                R.string.zen_mode_schedule_rule_days_none);
+            }
+
+            // handle event-based rules
+            ZenModeConfig.EventInfo event =
+                    ZenModeConfig.tryParseEventConditionId(rule.getConditionId());
+            if (event != null) {
+                if (event.calName != null) {
+                    return event.calName;
+                } else {
+                    return mContext.getResources().getString(
+                            R.string.zen_mode_event_rule_calendar_any);
+                }
+            }
+        }
+
         return (rule == null || !rule.isEnabled())
                 ? mContext.getResources().getString(R.string.switch_off_text)
                 : mContext.getResources().getString(R.string.switch_on_text);
diff --git a/src/com/android/settings/notification/zen/ZenRuleScheduleHelper.java b/src/com/android/settings/notification/zen/ZenRuleScheduleHelper.java
new file mode 100644
index 0000000..9a4f108
--- /dev/null
+++ b/src/com/android/settings/notification/zen/ZenRuleScheduleHelper.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 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.zen;
+
+import android.content.Context;
+import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.text.format.DateFormat;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Helper class for shared functionality regarding descriptions of custom zen rule schedules.
+ */
+public class ZenRuleScheduleHelper {
+    // per-instance to ensure we're always using the current locale
+    private SimpleDateFormat mDayFormat;
+
+    // Default constructor, which will use the current locale.
+    public ZenRuleScheduleHelper() {
+        mDayFormat = new SimpleDateFormat("EEE");
+    }
+
+    // Constructor for tests to provide an explicit locale
+    @VisibleForTesting
+    public ZenRuleScheduleHelper(Locale locale) {
+        mDayFormat = new SimpleDateFormat("EEE", locale);
+    }
+
+    /**
+     * Returns an ordered, comma-separated list of the days that a schedule applies, or null if no
+     * days.
+     */
+    public String getDaysDescription(Context context, ScheduleInfo schedule) {
+        // Compute an ordered, delimited list of day names based on the persisted user config.
+        final int[] days = schedule.days;
+        if (days != null && days.length > 0) {
+            final StringBuilder sb = new StringBuilder();
+            final Calendar c = Calendar.getInstance();
+            int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(c);
+            for (int i = 0; i < daysOfWeek.length; i++) {
+                final int day = daysOfWeek[i];
+                for (int j = 0; j < days.length; j++) {
+                    if (day == days[j]) {
+                        c.set(Calendar.DAY_OF_WEEK, day);
+                        if (sb.length() > 0) {
+                            sb.append(context.getString(R.string.summary_divider_text));
+                        }
+                        sb.append(mDayFormat.format(c.getTime()));
+                        break;
+                    }
+                }
+            }
+
+            if (sb.length() > 0) {
+                return sb.toString();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns an ordered summarized list of the days on which this schedule applies, with
+     * adjacent days grouped together ("Sun-Wed" instead of "Sun,Mon,Tue,Wed").
+     */
+    public String getShortDaysSummary(Context context, ScheduleInfo schedule) {
+        // Compute a list of days with contiguous days grouped together, for example: "Sun-Thu" or
+        // "Sun-Mon,Wed,Fri"
+        final int[] days = schedule.days;
+        if (days != null && days.length > 0) {
+            final StringBuilder sb = new StringBuilder();
+            final Calendar cStart = Calendar.getInstance();
+            final Calendar cEnd = Calendar.getInstance();
+            int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(cStart);
+            // the i for loop goes through days in order as determined by locale. as we walk through
+            // the days of the week, keep track of "start" and "last seen"  as indicators for
+            // what's contiguous, and initialize them to something not near actual indices
+            int startDay = Integer.MIN_VALUE;
+            int lastSeenDay = Integer.MIN_VALUE;
+            for (int i = 0; i < daysOfWeek.length; i++) {
+                final int day = daysOfWeek[i];
+
+                // by default, output if this day is *not* included in the schedule, and thus
+                // ends a previously existing block. if this day is included in the schedule
+                // after all (as will be determined in the inner for loop), then output will be set
+                // to false.
+                boolean output = (i == lastSeenDay + 1);
+                for (int j = 0; j < days.length; j++) {
+                    if (day == days[j]) {
+                        // match for this day in the schedule (indicated by counter i)
+                        if (i == lastSeenDay + 1) {
+                            // contiguous to the block we're walking through right now, record it
+                            // (specifically, i, the day index) and move on to the next day
+                            lastSeenDay = i;
+                            output = false;
+                        } else {
+                            // it's a match, but not 1 past the last match, we are starting a new
+                            // block
+                            startDay = i;
+                            lastSeenDay = i;
+                        }
+
+                        // if there is a match on the last day, also make sure to output at the end
+                        // of this loop, and mark the day as the last day we'll have seen in the
+                        // scheduled days.
+                        if (i == daysOfWeek.length - 1) {
+                            output = true;
+                        }
+                        break;
+                    }
+                }
+
+                // output in either of 2 cases: this day is not a match, so has ended any previous
+                // block, or this day *is* a match but is the last day of the week, so we need to
+                // summarize
+                if (output) {
+                    // either describe just the single day if startDay == lastSeenDay, or
+                    // output "startDay - lastSeenDay" as a group
+                    if (sb.length() > 0) {
+                        sb.append(context.getString(R.string.summary_divider_text));
+                    }
+
+                    if (startDay == lastSeenDay) {
+                        // last group was only one day
+                        cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
+                        sb.append(mDayFormat.format(cStart.getTime()));
+                    } else {
+                        // last group was a contiguous group of days, so group them together
+                        cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
+                        cEnd.set(Calendar.DAY_OF_WEEK, daysOfWeek[lastSeenDay]);
+                        sb.append(context.getString(R.string.summary_range_symbol_combination,
+                                mDayFormat.format(cStart.getTime()),
+                                mDayFormat.format(cEnd.getTime())));
+                    }
+                }
+            }
+
+            if (sb.length() > 0) {
+                return sb.toString();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Convenience method for representing the specified time in string format.
+     */
+    private String timeString(Context context, int hour, int minute) {
+        final Calendar c = Calendar.getInstance();
+        c.set(Calendar.HOUR_OF_DAY, hour);
+        c.set(Calendar.MINUTE, minute);
+        return DateFormat.getTimeFormat(context).format(c.getTime());
+    }
+
+    /**
+     * Combination description for a zen rule schedule including both day summary and time bounds.
+     */
+    public String getDaysAndTimeSummary(Context context, ScheduleInfo schedule) {
+        final StringBuilder sb = new StringBuilder();
+        String daysSummary = getShortDaysSummary(context, schedule);
+        if (daysSummary == null) {
+            // no use outputting times without dates
+            return null;
+        }
+        sb.append(daysSummary);
+        sb.append(context.getString(R.string.summary_divider_text));
+        sb.append(context.getString(R.string.summary_range_symbol_combination,
+                timeString(context, schedule.startHour, schedule.startMinute),
+                timeString(context, schedule.endHour, schedule.endMinute)));
+
+        return sb.toString();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/notification/zen/ZenRuleScheduleHelperTest.java b/tests/unit/src/com/android/settings/notification/zen/ZenRuleScheduleHelperTest.java
new file mode 100644
index 0000000..2faee08
--- /dev/null
+++ b/tests/unit/src/com/android/settings/notification/zen/ZenRuleScheduleHelperTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 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.zen;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.LocaleList;
+import android.service.notification.ZenModeConfig.ScheduleInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+public class ZenRuleScheduleHelperTest {
+    private ZenRuleScheduleHelper mScheduleHelper;
+    private ScheduleInfo mScheduleInfo;
+
+    private Context mContext;
+
+    @Mock
+    private Resources mResources;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        // explicitly initialize to Locale.US just for ease of explicitly testing the string values
+        // of the days of the week if the test locale doesn't happen to be in the US
+        mScheduleHelper = new ZenRuleScheduleHelper(Locale.US);
+        mScheduleInfo = new ScheduleInfo();
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getResources()).thenReturn(mResources);
+
+        // Resources will be called upon to join strings together, either to get a divider
+        // or a combination of two strings. Conveniently, these have different signatures.
+        // Divider method calls getString(string divider id)
+        when(mResources.getString(anyInt())).thenReturn(",");
+
+        // Combination method calls getString(combination id, first item, second item)
+        // and returns "first - second"
+        when(mResources.getString(anyInt(), anyString(), anyString())).thenAnswer(
+                invocation -> {
+                    return invocation.getArgument(1).toString() // first item
+                            + "-"
+                            + invocation.getArgument(2).toString();  // second item
+                });
+
+        // for locale used in time format
+        Configuration config = new Configuration();
+        config.setLocales(new LocaleList(Locale.US));
+        when(mResources.getConfiguration()).thenReturn(config);
+    }
+
+    @Test
+    public void getDaysDescription() {
+        // Test various cases of where the days are set.
+        // No days
+        mScheduleInfo.days = new int[] {};
+        assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo)).isNull();
+
+        // one day
+        mScheduleInfo.days = new int[] {Calendar.FRIDAY};
+        assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo)).isEqualTo("Fri");
+
+        // Monday through Friday
+        mScheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY,
+                Calendar.THURSDAY, Calendar.FRIDAY};
+        assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo))
+                .isEqualTo("Mon,Tue,Wed,Thu,Fri");
+
+        // Some scattered days of the week
+        mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.WEDNESDAY, Calendar.THURSDAY,
+                Calendar.SATURDAY};
+        assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo))
+                .isEqualTo("Sun,Wed,Thu,Sat");
+    }
+
+    @Test
+    public void getShortDaysSummary_noOrSingleDays() {
+        // Test various cases for grouping and not-grouping of days.
+        // No days
+        mScheduleInfo.days = new int[]{};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isNull();
+
+        // A single day at the beginning of the week
+        mScheduleInfo.days = new int[]{Calendar.SUNDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isEqualTo("Sun");
+
+        // A single day in the middle of the week
+        mScheduleInfo.days = new int[]{Calendar.THURSDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isEqualTo("Thu");
+
+        // A single day at the end of the week
+        mScheduleInfo.days = new int[]{Calendar.SATURDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isEqualTo("Sat");
+    }
+
+    @Test
+    public void getShortDaysSummary_oneGroup() {
+        // The whole week
+        mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
+                .isEqualTo("Sun-Sat");
+
+        // Various cases of one big group
+        // Sunday through Thursday
+        mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.THURSDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
+                .isEqualTo("Sun-Thu");
+
+        // Wednesday through Saturday
+        mScheduleInfo.days = new int[] {Calendar.WEDNESDAY, Calendar.THURSDAY,
+                Calendar.FRIDAY, Calendar.SATURDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
+                .isEqualTo("Wed-Sat");
+
+        // Monday through Friday
+        mScheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
+                .isEqualTo("Mon-Fri");
+    }
+
+    @Test
+    public void getShortDaysSummary_mixed() {
+        // cases combining groups and single days scattered around
+        mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.SATURDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
+                .isEqualTo("Sun,Tue-Thu,Sat");
+
+        mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.FRIDAY, Calendar.SATURDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
+                .isEqualTo("Sun-Wed,Fri-Sat");
+
+        mScheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.WEDNESDAY,
+                Calendar.FRIDAY, Calendar.SATURDAY};
+        assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
+                .isEqualTo("Mon,Wed,Fri-Sat");
+    }
+
+    @Test
+    public void getDaysAndTimeSummary() {
+        // Combination days & time settings
+        // No days, no output, even if the times are set.
+        mScheduleInfo.startHour = 10;
+        mScheduleInfo.endHour = 16;
+        mScheduleInfo.days = new int[]{};
+        assertThat(mScheduleHelper.getDaysAndTimeSummary(mContext, mScheduleInfo)).isNull();
+
+        // If there are days then they are combined with the time combination
+        mScheduleInfo.days = new int[]{Calendar.SUNDAY, Calendar.MONDAY, Calendar.WEDNESDAY};
+        assertThat(mScheduleHelper.getDaysAndTimeSummary(mContext, mScheduleInfo))
+                .isEqualTo("Sun-Mon,Wed,10:00 AM-4:00 PM");
+    }
+}