/* * Copyright (C) 2015 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 android.service.notification.ZenModeConfig.ALL_DAYS; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.os.Bundle; import android.os.UserHandle; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceScreen; import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.ConditionProviderService; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; import android.service.notification.ZenModeConfig.ScheduleInfo; import android.service.notification.ZenModeConfig.ZenRule; import android.text.format.DateFormat; import android.util.Log; import com.android.internal.logging.MetricsLogger; import com.android.settings.R; import com.android.settings.notification.ManagedServiceSettings.Config; import com.android.settings.notification.ZenModeEventRuleSettings.CalendarInfo; import com.android.settings.notification.ZenRuleNameDialog.RuleInfo; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Comparator; import java.util.List; import java.util.TreeSet; public class ZenModeAutomationSettings extends ZenModeSettingsBase { static final Config CONFIG = getConditionProviderConfig(); // per-instance to ensure we're always using the current locale private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE"); private final Calendar mCalendar = Calendar.getInstance(); private ServiceListing mServiceListing; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.zen_mode_automation_settings); mServiceListing = new ServiceListing(mContext, CONFIG); mServiceListing.addCallback(mServiceListingCallback); mServiceListing.reload(); mServiceListing.setListening(true); } @Override public void onDestroy() { super.onDestroy(); mServiceListing.setListening(false); mServiceListing.removeCallback(mServiceListingCallback); } @Override protected void onZenModeChanged() { // don't care } @Override protected void onZenModeConfigChanged() { updateControls(); } @Override public void onResume() { super.onResume(); updateControls(); } private void showAddRuleDialog() { new ZenRuleNameDialog(mContext, mServiceListing, null, mConfig.getAutomaticRuleNames()) { @Override public void onOk(String ruleName, RuleInfo ri) { MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ADD_RULE_OK); final ZenRule rule = new ZenRule(); rule.name = ruleName; rule.enabled = true; rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; rule.conditionId = ri.defaultConditionId; rule.component = ri.serviceComponent; final ZenModeConfig newConfig = mConfig.copy(); final String ruleId = newConfig.newRuleId(); newConfig.automaticRules.put(ruleId, rule); if (setZenModeConfig(newConfig)) { showRule(ri.settingsAction, ri.configurationActivity, ruleId, rule.name); } } }.show(); } private void showRule(String settingsAction, ComponentName configurationActivity, String ruleId, String ruleName) { if (DEBUG) Log.d(TAG, "showRule " + ruleId + " name=" + ruleName); mContext.startActivity(new Intent(settingsAction) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) .putExtra(ZenModeRuleSettingsBase.EXTRA_RULE_ID, ruleId)); } private ZenRuleInfo[] sortedRules() { final ZenRuleInfo[] rt = new ZenRuleInfo[mConfig.automaticRules.size()]; for (int i = 0; i < rt.length; i++) { final ZenRuleInfo zri = new ZenRuleInfo(); zri.id = mConfig.automaticRules.keyAt(i); zri.rule = mConfig.automaticRules.valueAt(i); rt[i] = zri; } Arrays.sort(rt, RULE_COMPARATOR); return rt; } private void updateControls() { final PreferenceScreen root = getPreferenceScreen(); root.removeAll(); if (mConfig == null) return; final ZenRuleInfo[] sortedRules = sortedRules(); for (int i = 0; i < sortedRules.length; i++) { final String id = sortedRules[i].id; final ZenRule rule = sortedRules[i].rule; final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(rule.conditionId); final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.conditionId); final Preference p = new Preference(mContext); p.setIcon(isSchedule ? R.drawable.ic_schedule : isEvent ? R.drawable.ic_event : R.drawable.ic_label); p.setTitle(rule.name); p.setSummary(computeRuleSummary(rule)); p.setPersistent(false); p.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION : isEvent ? ZenModeEventRuleSettings.ACTION : ZenModeExternalRuleSettings.ACTION; showRule(action, null, id, rule.name); return true; } }); root.addPreference(p); } final Preference p = new Preference(mContext); p.setIcon(R.drawable.ic_add); p.setTitle(R.string.zen_mode_add_rule); p.setPersistent(false); p.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ADD_RULE); showAddRuleDialog(); return true; } }); root.addPreference(p); } @Override protected int getMetricsCategory() { return MetricsLogger.NOTIFICATION_ZEN_MODE_AUTOMATION; } private String computeRuleSummary(ZenRule rule) { if (rule == null || !rule.enabled) return getString(R.string.switch_off_text); final String mode = computeZenModeCaption(getResources(), rule.zenMode); String summary = getString(R.string.switch_on_text); final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId); final EventInfo event = ZenModeConfig.tryParseEventConditionId(rule.conditionId); if (schedule != null) { summary = computeScheduleRuleSummary(schedule); } else if (event != null) { summary = computeEventRuleSummary(event); } return getString(R.string.zen_mode_rule_summary_combination, summary, mode); } private String computeScheduleRuleSummary(ScheduleInfo schedule) { final String days = computeContiguousDayRanges(schedule.days); final String start = getTime(schedule.startHour, schedule.startMinute); final String end = getTime(schedule.endHour, schedule.endMinute); final String time = getString(R.string.summary_range_verbal_combination, start, end); return getString(R.string.zen_mode_rule_summary_combination, days, time); } private String computeEventRuleSummary(EventInfo event) { final String calendar = getString(R.string.zen_mode_event_rule_summary_calendar_template, computeCalendarName(event)); final String reply = getString(R.string.zen_mode_event_rule_summary_reply_template, getString(computeReply(event))); return getString(R.string.zen_mode_rule_summary_combination, calendar, reply); } private String computeCalendarName(EventInfo event) { return event.calendar != null ? event.calendar : getString(R.string.zen_mode_event_rule_summary_any_calendar); } private int computeReply(EventInfo event) { switch (event.reply) { case EventInfo.REPLY_ANY_EXCEPT_NO: return R.string.zen_mode_event_rule_reply_any_except_no; case EventInfo.REPLY_YES: return R.string.zen_mode_event_rule_reply_yes; case EventInfo.REPLY_YES_OR_MAYBE: return R.string.zen_mode_event_rule_reply_yes_or_maybe; default: throw new IllegalArgumentException("Bad reply: " + event.reply); } } private String getTime(int hour, int minute) { mCalendar.set(Calendar.HOUR_OF_DAY, hour); mCalendar.set(Calendar.MINUTE, minute); return DateFormat.getTimeFormat(mContext).format(mCalendar.getTime()); } private String computeContiguousDayRanges(int[] days) { final TreeSet daySet = new TreeSet<>(); for (int i = 0; days != null && i < days.length; i++) { daySet.add(days[i]); } if (daySet.isEmpty()) { return getString(R.string.zen_mode_schedule_rule_days_none); } final int N = ALL_DAYS.length; if (daySet.size() == N) { return getString(R.string.zen_mode_schedule_rule_days_all); } String rt = null; for (int i = 0; i < N; i++) { final int startDay = ALL_DAYS[i]; final boolean active = daySet.contains(startDay); if (!active) continue; int end = 0; while (daySet.contains(ALL_DAYS[(i + end + 1) % N])) { end++; } if (!(i == 0 && daySet.contains(ALL_DAYS[N - 1]))) { final String v = end == 0 ? dayString(startDay) : getString(R.string.summary_range_symbol_combination, dayString(startDay), dayString(ALL_DAYS[(i + end) % N])); rt = rt == null ? v : getString(R.string.summary_divider_text, rt, v); } i += end; } return rt; } private String dayString(int day) { mCalendar.set(Calendar.DAY_OF_WEEK, day); return mDayFormat.format(mCalendar.getTime()); } private static Config getConditionProviderConfig() { final Config c = new Config(); c.tag = TAG; c.setting = Settings.Secure.ENABLED_CONDITION_PROVIDERS; c.intentAction = ConditionProviderService.SERVICE_INTERFACE; c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE; c.noun = "condition provider"; return c; } private static String computeZenModeCaption(Resources res, int zenMode) { switch (zenMode) { case Global.ZEN_MODE_ALARMS: return res.getString(R.string.zen_mode_option_alarms); case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return res.getString(R.string.zen_mode_option_important_interruptions); case Global.ZEN_MODE_NO_INTERRUPTIONS: return res.getString(R.string.zen_mode_option_no_interruptions); default: return null; } } private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() { @Override public void onServicesReloaded(List services) { for (ServiceInfo service : services) { final RuleInfo ri = ZenModeExternalRuleSettings.getRuleInfo(service); if (ri != null && ri.serviceComponent != null && ri.settingsAction == ZenModeExternalRuleSettings.ACTION) { if (!mServiceListing.isEnabled(ri.serviceComponent)) { Log.i(TAG, "Enabling external condition provider: " + ri.serviceComponent); mServiceListing.setEnabled(ri.serviceComponent, true); } } } } }; private static final Comparator RULE_COMPARATOR = new Comparator() { @Override public int compare(ZenRuleInfo lhs, ZenRuleInfo rhs) { return key(lhs).compareTo(key(rhs)); } private String key(ZenRuleInfo zri) { final ZenRule rule = zri.rule; final int type = ZenModeConfig.isValidScheduleConditionId(rule.conditionId) ? 1 : ZenModeConfig.isValidEventConditionId(rule.conditionId) ? 2 : 3; return type + rule.name; } }; private static class ZenRuleInfo { String id; ZenRule rule; } }