Android7.1勿擾功能簡析
- 螢幕開啟時遮蔽:禁止在勿擾模式下被靜音的通知在螢幕上短暫顯示或彈出
- 螢幕關閉時遮蔽:禁止在勿擾模式下被靜音的通知開啟螢幕
點選勿擾選項進入ZenModeSettings中。ZenModeSettings中的佈局檔案為zen_mode_settings,如下: Settings\res\xml\zen_mode_settings.xml<!-- Interruptions --> <com.android.settingslib.RestrictedPreference android:key="zen_mode" android:title="@string/zen_mode_settings_title" settings:useAdminDisabledSummary="true" settings:keywords="@string/keywords_sounds_and_notifications_interruptions" android:fragment="com.android.settings.notification.ZenModeSettings" />
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:key="zen_mode_settings" android:title="@string/zen_mode_settings_title" > <!-- Priority only allows --> <PreferenceScreen android:key="priority_settings" android:title="@string/zen_mode_priority_settings_title" android:fragment="com.android.settings.notification.ZenModePrioritySettings" /> <!-- Automated rules --> <PreferenceScreen android:key="automation_settings" android:title="@string/zen_mode_automation_settings_title" android:fragment="com.android.settings.notification.ZenModeAutomationSettings" /> <!-- Visual interruptions --> <PreferenceScreen android:key="visual_interruptions_settings" android:title="@string/zen_mode_visual_interruptions_settings_title" android:fragment="com.android.settings.notification.ZenModeVisualInterruptionSettings" /> </PreferenceScreen>
上面的佈局檔案和我們從手機上看到的勿擾設定介面一樣。我們可以看出ZenModePrioritySettings 、ZenModeAutomationSettings 、ZenModeVisualInterruptionSettings分別對應著僅允許優先打擾內容、自動規則、遮蔽視覺打擾三種頁面。
ZenModeSettings、ZenModePrioritySettings、ZenModeAutomationSettings 、ZenModeVisualInterruptionSettings都繼承於ZenModeSettingsBase。我們來分別看一下這些類。
ZenModeSettings這個類,我們從程式碼中可以看出它只是三項設定介面的入口,以及對勿擾的更新做一些介面的顯示工作。如下程式碼:
Settings/src/com/android/settings/notification/ZenModeSettings.java
@Override
protected void onZenModeChanged() {
updateControls();
}
@Override
protected void onZenModeConfigChanged() {
mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
updateControls();
}
private void updateControls() {
updatePrioritySettingsSummary();
updateVisualSettingsSummary();
}
private void updatePrioritySettingsSummary() {
String s = getResources().getString(R.string.zen_mode_alarms);
s = appendLowercase(s, isCategoryEnabled(mPolicy, Policy.PRIORITY_CATEGORY_REMINDERS),
R.string.zen_mode_reminders);
s = appendLowercase(s, isCategoryEnabled(mPolicy, Policy.PRIORITY_CATEGORY_EVENTS),
R.string.zen_mode_events);
if (isCategoryEnabled(mPolicy, Policy.PRIORITY_CATEGORY_MESSAGES)) {
if (mPolicy.priorityMessageSenders == Policy.PRIORITY_SENDERS_ANY) {
s = appendLowercase(s, true, R.string.zen_mode_all_messages);
} else {
s = appendLowercase(s, true, R.string.zen_mode_selected_messages);
}
}
if (isCategoryEnabled(mPolicy, Policy.PRIORITY_CATEGORY_CALLS)) {
if (mPolicy.priorityCallSenders == Policy.PRIORITY_SENDERS_ANY) {
s = appendLowercase(s, true, R.string.zen_mode_all_callers);
} else {
s = appendLowercase(s, true, R.string.zen_mode_selected_callers);
}
} else if (isCategoryEnabled(mPolicy, Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)) {
s = appendLowercase(s, true, R.string.zen_mode_repeat_callers);
}
mPrioritySettings.setSummary(s);
}
private void updateVisualSettingsSummary() {
String s = getString(R.string.zen_mode_all_visual_interruptions);
if (isEffectSuppressed(Policy.SUPPRESSED_EFFECT_SCREEN_ON)
&& isEffectSuppressed(Policy.SUPPRESSED_EFFECT_SCREEN_OFF)) {
s = getString(R.string.zen_mode_no_visual_interruptions);
} else if (isEffectSuppressed(Policy.SUPPRESSED_EFFECT_SCREEN_ON)) {
s = getString(R.string.zen_mode_screen_on_visual_interruptions);
} else if (isEffectSuppressed(Policy.SUPPRESSED_EFFECT_SCREEN_OFF)) {
s = getString(R.string.zen_mode_screen_off_visual_interruptions);
}
mVisualSettings.setSummary(s);
}
我們再看ZenModePrioritySettings類,首先它負責自定義優先打擾的內容,其中包括鬧鐘、提醒、活動、訊息、通話(僅限來自聯絡人)、重複來電者(如果同一個人在15分鐘內第二次來電,則允許顯示通知)。這面也僅是一些開關,將最後的更改用NotificationManager.from(mContext).setNotificationPolicy(mPolicy);進行儲存,其中也只有訊息和通話需要選擇,我們來看一下通話的設定以及儲存,其餘的類似:
Settings/src/com/android/settings/notification/ZenModePrioritySettings.java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.zen_mode_priority_settings);
final PreferenceScreen root = getPreferenceScreen();
mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
...
mCalls = (DropDownPreference) root.findPreference(KEY_CALLS);
addSources(mCalls);
mCalls.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mDisableListeners) return false;
final int val = Integer.parseInt((String) newValue);
final boolean allowCalls = val != SOURCE_NONE;
final int allowCallsFrom = val == SOURCE_NONE ? mPolicy.priorityCallSenders : val;
MetricsLogger.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_CALLS, val);
if (DEBUG) Log.d(TAG, "onPrefChange allowCalls=" + allowCalls
+ " allowCallsFrom=" + ZenModeConfig.sourceToString(allowCallsFrom));
savePolicy(getNewPriorityCategories(allowCalls, Policy.PRIORITY_CATEGORY_CALLS),
allowCallsFrom, mPolicy.priorityMessageSenders,
mPolicy.suppressedVisualEffects);
return true;
}
});
...
updateControls();
}
private void updateControls() {
mDisableListeners = true;
if (mCalls != null) {
mCalls.setValue(Integer.toString(
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS)
? mPolicy.priorityCallSenders : SOURCE_NONE));
}
mMessages.setValue(Integer.toString(
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES)
? mPolicy.priorityMessageSenders : SOURCE_NONE));
mReminders.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS));
mEvents.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS));
mRepeatCallers.setChecked(
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS));
mRepeatCallers.setVisible(!isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS)
|| mPolicy.priorityCallSenders != Policy.PRIORITY_SENDERS_ANY);
mDisableListeners = false;
}
private void savePolicy(int priorityCategories, int priorityCallSenders,
int priorityMessageSenders, int suppressedVisualEffects) {
mPolicy = new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
suppressedVisualEffects);
NotificationManager.from(mContext).setNotificationPolicy(mPolicy);
}
ZenModeVisualInterruptionSettings和ZenModePrioritySettings類似,只是換成了螢幕開啟時遮蔽和螢幕關閉時遮蔽兩個開關,所以就不分析了。我們看ZenModeAutomationSettings這個稍微不同的類。
在ZenModeSettingsBase中有一個mRules的集合,儲存著使用者自己定義的AutomaticZenRule,而mRules的值是NotificationManager中獲取的的。也就是這裡規則的維護儲存也還是在NotificationManager中,如下程式碼。
Settings/src/com/android/settings/notification/ZenModeSettingsBase.java
abstract public class ZenModeSettingsBase extends RestrictedSettingsFragment {
...
protected Context mContext;
protected Set<Map.Entry<String, AutomaticZenRule>> mRules;
...
private void updateZenMode(boolean fireChanged) {
final int zenMode = Settings.Global.getInt(getContentResolver(), Global.ZEN_MODE, mZenMode);
if (zenMode == mZenMode) return;
mZenMode = zenMode;
if (DEBUG) Log.d(TAG, "updateZenMode mZenMode=" + mZenMode);
if (fireChanged) {
onZenModeChanged();
}
}
protected String addZenRule(AutomaticZenRule rule) {
try {
String id = NotificationManager.from(mContext).addAutomaticZenRule(rule);
final AutomaticZenRule savedRule =
NotificationManager.from(mContext).getAutomaticZenRule(id);
maybeRefreshRules(savedRule != null, true);
return id;
} catch (Exception e) {
return null;
}
}
protected boolean setZenRule(String id, AutomaticZenRule rule) {
final boolean success =
NotificationManager.from(mContext).updateAutomaticZenRule(id, rule);
maybeRefreshRules(success, true);
return success;
}
protected boolean removeZenRule(String id) {
final boolean success =
NotificationManager.from(mContext).removeAutomaticZenRule(id);
maybeRefreshRules(success, true);
return success;
}
protected void maybeRefreshRules(boolean success, boolean fireChanged) {
if (success) {
mRules = getZenModeRules();
if (DEBUG) Log.d(TAG, "Refreshed mRules=" + mRules);
if (fireChanged) {
onZenModeConfigChanged();
}
}
}
protected void setZenMode(int zenMode, Uri conditionId) {
NotificationManager.from(mContext).setZenMode(zenMode, conditionId, TAG);
}
private Set<Map.Entry<String, AutomaticZenRule>> getZenModeRules() {
Map<String, AutomaticZenRule> ruleMap
= NotificationManager.from(mContext).getAutomaticZenRules();
return ruleMap.entrySet();
}
...
}
知道上面的東西,ZenModeAutomationSettings就簡單多了,就是mRules列表的展示,以及新增刪除等操作。只是這裡面新增可以選擇兩種模式,分別是活動規則和時間規則。分別對應的類是ZenModeEventRuleSettings和ZenModeScheduleRuleSettings,他們都繼承自ZenModeRuleSettingsBase。這裡ZenModeAutomationSettings雖然程式碼很多,但都是各種彈出框的操作,就不分析了。主要看看活動規則和時間規則這兩個裡面分別又有哪些操作吧。
時間規則的列表,分別有規則名稱、星期幾、開始時間、結束時間、勿擾、鬧鐘響鈴時間可覆蓋結束時間(在所設結束時間或下一次鬧鐘響鈴時(兩者選其先)停止)。由private ScheduleInfo mSchedule;中來儲存著時間規則的一些資料。
活動規則的頁面的列表,分別是名字、在以下日曆活動期間、回覆內容如下的活動、勿擾。由private EventInfo mEvent;中來儲存著時間規則的一些資料。以上兩個規則裡面其餘都只是進行一些修改操作。這些操作最後的修改的資料都會通過呼叫ZenModeRuleSettingsBase裡面的updateRule()方法儲存到NotificationManager裡。程式碼如下:
Settings/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
mStart = new TimePickerPreference(getPrefContext(), mgr);
mStart.setKey(KEY_START_TIME);
mStart.setTitle(R.string.zen_mode_start_time);
mStart.setCallback(new TimePickerPreference.Callback() {
@Override
public boolean onSetTime(final int hour, final int minute) {
if (mDisableListeners) return true;
if (!ZenModeConfig.isValidHour(hour)) return false;
if (!ZenModeConfig.isValidMinute(minute)) return false;
if (hour == mSchedule.startHour && minute == mSchedule.startMinute) {
return true;
}
if (DEBUG) Log.d(TAG, "onPrefChange start h=" + hour + " m=" + minute);
mSchedule.startHour = hour;
mSchedule.startMinute = minute;
updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
return true;
}
});
Settings/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
protected void updateRule(Uri newConditionId) {
mRule.setConditionId(newConditionId);
setZenRule(mId, mRule);
}
protected boolean setZenRule(String id, AutomaticZenRule rule) {
final boolean success =
NotificationManager.from(mContext).updateAutomaticZenRule(id, rule);
maybeRefreshRules(success, true);
return success;
}
自此所有在Setting裡面的勿擾模式的程式碼分析結束了,綜上可知,Settings模組中勿擾只是一個介面的展示,其資料都放在NotificationManager進行管理操作。