diff --git a/src/com/android/settings/notification/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/ZenModeEventRuleSettings.java index e859cdd1b27..c11a6727b32 100644 --- a/src/com/android/settings/notification/ZenModeEventRuleSettings.java +++ b/src/com/android/settings/notification/ZenModeEventRuleSettings.java @@ -27,11 +27,7 @@ import android.provider.Settings; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; -import androidx.preference.DropDownPreference; -import androidx.preference.Preference; -import androidx.preference.Preference.OnPreferenceChangeListener; -import androidx.preference.PreferenceScreen; - +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settingslib.core.AbstractPreferenceController; @@ -40,6 +36,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Objects; + +import androidx.preference.DropDownPreference; +import androidx.preference.Preference; +import androidx.preference.Preference.OnPreferenceChangeListener; +import androidx.preference.PreferenceScreen; public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { private static final String KEY_CALENDAR = "calendar"; @@ -51,7 +53,7 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { private DropDownPreference mReply; private EventInfo mEvent; - private List mCalendars; + private boolean mCreate; @Override @@ -91,24 +93,20 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { } private void reloadCalendar() { - mCalendars = getCalendars(mContext); + List calendars = getCalendars(mContext); ArrayList entries = new ArrayList<>(); ArrayList values = new ArrayList<>(); entries.add(getString(R.string.zen_mode_event_rule_calendar_any)); - values.add(key(0, null)); - final String eventCalendar = mEvent != null ? mEvent.calendar : null; - boolean found = false; - for (CalendarInfo calendar : mCalendars) { + values.add(key(0, null, "")); + final String eventCalendar = mEvent != null ? mEvent.calName : null; + for (CalendarInfo calendar : calendars) { entries.add(calendar.name); values.add(key(calendar)); - if (eventCalendar != null && eventCalendar.equals(calendar.name)) { - found = true; + if (eventCalendar != null && (mEvent.calendarId == null + && eventCalendar.equals(calendar.name))) { + mEvent.calendarId = calendar.calendarId; } } - if (eventCalendar != null && !found) { - entries.add(eventCalendar); - values.add(key(mEvent.userId, eventCalendar)); - } mCalendar.setEntries(entries.toArray(new CharSequence[entries.size()])); mCalendar.setEntryValues(values.toArray(new CharSequence[values.size()])); } @@ -124,12 +122,10 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { public boolean onPreferenceChange(Preference preference, Object newValue) { final String calendarKey = (String) newValue; if (calendarKey.equals(key(mEvent))) return false; - final int i = calendarKey.indexOf(':'); - mEvent.userId = Integer.parseInt(calendarKey.substring(0, i)); - mEvent.calendar = calendarKey.substring(i + 1); - if (mEvent.calendar.isEmpty()) { - mEvent.calendar = null; - } + String[] key = calendarKey.split(":", 3); + mEvent.userId = Integer.parseInt(key[0]); + mEvent.calendarId = key[1].equals("") ? null : Long.parseLong(key[1]); + mEvent.calName = key[2].equals("") ? null : key[2]; updateRule(ZenModeConfig.toEventConditionId(mEvent)); return true; } @@ -172,18 +168,7 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { return MetricsEvent.NOTIFICATION_ZEN_MODE_EVENT_RULE; } - public static CalendarInfo findCalendar(Context context, EventInfo event) { - if (context == null || event == null) return null; - final String eventKey = key(event); - for (CalendarInfo calendar : getCalendars(context)) { - if (eventKey.equals(key(calendar))) { - return calendar; - } - } - return null; - } - - private static List getCalendars(Context context) { + private List getCalendars(Context context) { final List calendars = new ArrayList<>(); for (UserHandle user : UserManager.get(context).getUserProfiles()) { final Context userContext = getContextForUser(context, user); @@ -203,11 +188,11 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { } } - public static void addCalendars(Context context, List outCalendars) { - final String primary = "\"primary\""; - final String[] projection = { Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME, - "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + primary }; - final String selection = primary + " = 1"; + private void addCalendars(Context context, List outCalendars) { + final String[] projection = { Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME }; + final String selection = Calendars.CALENDAR_ACCESS_LEVEL + " >= " + + Calendars.CAL_ACCESS_CONTRIBUTOR + + " AND " + Calendars.SYNC_EVENTS + " = 1"; Cursor cursor = null; try { cursor = context.getContentResolver().query(Calendars.CONTENT_URI, projection, @@ -216,10 +201,8 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { return; } while (cursor.moveToNext()) { - final CalendarInfo ci = new CalendarInfo(); - ci.name = cursor.getString(1); - ci.userId = context.getUserId(); - outCalendars.add(ci); + addCalendar(cursor.getLong(0), cursor.getString(1), + context.getUserId(), outCalendars); } } finally { if (cursor != null) { @@ -228,16 +211,29 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { } } + @VisibleForTesting + void addCalendar(long calendarId, String calName, int userId, List + outCalendars) { + final CalendarInfo ci = new CalendarInfo(); + ci.calendarId = calendarId; + ci.name = calName; + ci.userId = userId; + if (!outCalendars.contains(ci)) { + outCalendars.add(ci); + } + } + private static String key(CalendarInfo calendar) { - return key(calendar.userId, calendar.name); + return key(calendar.userId, calendar.calendarId, calendar.name); } private static String key(EventInfo event) { - return key(event.userId, event.calendar); + return key(event.userId, event.calendarId, event.calName); } - private static String key(int userId, String calendar) { - return EventInfo.resolveUserId(userId) + ":" + (calendar == null ? "" : calendar); + private static String key(int userId, Long calendarId, String displayName) { + return EventInfo.resolveUserId(userId) + ":" + (calendarId == null ? "" : calendarId) + + ":" + displayName; } private static final Comparator CALENDAR_NAME = new Comparator() { @@ -250,5 +246,20 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { public static class CalendarInfo { public String name; public int userId; + public Long calendarId; + + @Override + public boolean equals(Object o) { + if (!(o instanceof CalendarInfo)) return false; + if (o == this) return true; + final CalendarInfo other = (CalendarInfo) o; + return Objects.equals(other.name, name) + && Objects.equals(other.calendarId, calendarId); + } + + @Override + public int hashCode() { + return Objects.hash(name, calendarId); + } } } diff --git a/src/com/android/settings/notification/ZenRuleSelectionDialog.java b/src/com/android/settings/notification/ZenRuleSelectionDialog.java index b076dd0e671..abda3769be8 100644 --- a/src/com/android/settings/notification/ZenRuleSelectionDialog.java +++ b/src/com/android/settings/notification/ZenRuleSelectionDialog.java @@ -36,9 +36,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.Fragment; - import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.utils.ZenServiceListing; @@ -49,6 +46,9 @@ import java.util.Comparator; import java.util.Set; import java.util.TreeSet; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; + public class ZenRuleSelectionDialog extends InstrumentedDialogFragment { private static final String TAG = "ZenRuleSelectionDialog"; private static final boolean DEBUG = ZenModeSettings.DEBUG; @@ -170,7 +170,8 @@ public class ZenRuleSelectionDialog extends InstrumentedDialogFragment { private ZenRuleInfo defaultNewEvent() { final ZenModeConfig.EventInfo event = new ZenModeConfig.EventInfo(); - event.calendar = null; // any calendar + event.calName = null; // any calendar + event.calendarId = null; event.reply = ZenModeConfig.EventInfo.REPLY_ANY_EXCEPT_NO; final ZenRuleInfo rt = new ZenRuleInfo(); rt.settingsAction = ZenModeEventRuleSettings.ACTION; diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeEventRuleSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeEventRuleSettingsTest.java new file mode 100644 index 00000000000..5d8be5a3ac2 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/ZenModeEventRuleSettingsTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2018 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; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.RuntimeEnvironment.application; + +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; + +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowResources; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; +import org.robolectric.shadows.ShadowToast; + +import java.util.ArrayList; +import java.util.List; + +import androidx.fragment.app.FragmentActivity; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = SettingsShadowResources.SettingsShadowTheme.class) +public class ZenModeEventRuleSettingsTest { + + @Mock + private FragmentActivity mActivity; + + @Mock + private Intent mIntent; + + @Mock + private NotificationManager mNotificationManager; + + private TestFragment mFragment; + private Context mContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + ShadowApplication shadowApplication = ShadowApplication.getInstance(); + shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager); + mContext = shadowApplication.getApplicationContext(); + + mFragment = spy(new TestFragment()); + mFragment.onAttach(application); + + doReturn(mActivity).when(mFragment).getActivity(); + + Resources res = application.getResources(); + + doReturn(res).when(mFragment).getResources(); + when(mActivity.getTheme()).thenReturn(res.newTheme()); + when(mActivity.getIntent()).thenReturn(mIntent); + when(mActivity.getResources()).thenReturn(res); + when(mFragment.getContext()).thenReturn(mContext); + } + + @Test + public void onCreate_noRuleId_shouldToastAndFinishAndNoCrash() { + final String expected = mContext.getString(R.string.zen_mode_rule_not_found_text); + + mFragment.onCreate(null); + + // verify the toast + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(expected); + + // verify the finish + verify(mActivity).finish(); + + //should not crash + } + + @Test + public void testNoDuplicateCalendars() { + List calendarsList = new ArrayList<>(); + mFragment.addCalendar(1234, "calName", 1, calendarsList); + mFragment.addCalendar(1234, "calName", 2, calendarsList); + mFragment.addCalendar(1234, "calName", 3, calendarsList); + assertThat(calendarsList.size()).isEqualTo(1); + } + + private static class TestFragment extends ZenModeEventRuleSettings { + + @Override + protected Object getSystemService(final String name) { + return null; + } + } +}