Merge "Allow DND events rule to choose custom calendars"
This commit is contained in:
committed by
Android (Google) Code Review
commit
1e80322df8
@@ -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<CalendarInfo> mCalendars;
|
||||
|
||||
private boolean mCreate;
|
||||
|
||||
@Override
|
||||
@@ -91,24 +93,20 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase {
|
||||
}
|
||||
|
||||
private void reloadCalendar() {
|
||||
mCalendars = getCalendars(mContext);
|
||||
List<CalendarInfo> calendars = getCalendars(mContext);
|
||||
ArrayList<CharSequence> entries = new ArrayList<>();
|
||||
ArrayList<CharSequence> 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<CalendarInfo> getCalendars(Context context) {
|
||||
private List<CalendarInfo> getCalendars(Context context) {
|
||||
final List<CalendarInfo> 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<CalendarInfo> 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<CalendarInfo> 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<CalendarInfo>
|
||||
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<CalendarInfo> CALENDAR_NAME = new Comparator<CalendarInfo>() {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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<ZenModeEventRuleSettings.CalendarInfo> 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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user