Merge "DND Bypassing Apps redesign" into rvc-dev am: 7440bf7034
Change-Id: If875b3ad36863daff5e408a142caade2b44a3f18
This commit is contained in:
@@ -6908,7 +6908,8 @@
|
||||
<!-- Summary for add user action, when it's disabled [CHAR LIMIT=100] -->
|
||||
<string name="user_add_max_count">You can add up to <xliff:g id="user_count">%1$d</xliff:g> users</string>
|
||||
|
||||
<!-- Message to secondary users that only owner can manage users [CHAR LIMIT=none] -->
|
||||
|
||||
<!-- Message to secondary users that only owner can manage users [CHAR LIMIT=none] -->
|
||||
<string name="user_cannot_manage_message" product="tablet">Only the tablet\u2019s owner can manage users.</string>
|
||||
<!-- Message to secondary users that only owner can manage users [CHAR LIMIT=none] -->
|
||||
<string name="user_cannot_manage_message" product="default">Only the phone\u2019s owner can manage users.</string>
|
||||
@@ -8973,8 +8974,22 @@
|
||||
<string name="zen_mode_bypassing_apps">Allow apps to override</string>
|
||||
<!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND header -->
|
||||
<string name="zen_mode_bypassing_apps_header">Apps that can interrupt</string>
|
||||
<!-- [CHAR LIMIT=100] Zen mode settings: Add apps to bypass DND header -->
|
||||
<string name="zen_mode_bypassing_apps_add_header">Select more apps</string>
|
||||
<!-- [CHAR LIMIT=120] Zen mode settings: No apps are bypassing DND -->
|
||||
<string name="zen_mode_bypassing_apps_none">No apps selected</string>
|
||||
<!-- [CHAR LIMIT=120] Zen mode settings: No apps are bypassing DND -->
|
||||
<string name="zen_mode_bypassing_apps_subtext_none">No apps can interrupt</string>
|
||||
<!-- [CHAR LIMIT=120] Zen mode settings: Preference to add apps that are allowed to bypass DND -->
|
||||
<string name="zen_mode_bypassing_apps_add">Add apps</string>
|
||||
<!-- [CHAR LIMIT=120] Zen mode settings: Summary indicating all notification channels can
|
||||
bypass DND for this app -->
|
||||
<string name="zen_mode_bypassing_apps_summary_all">All notifications</string>
|
||||
<!-- [CHAR LIMIT=120] Zen mode settings: Summary indicating all notification channels can
|
||||
bypass DND for this app -->
|
||||
<string name="zen_mode_bypassing_apps_summary_some">Some notifications</string>
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode settings: Footer for DND bypassing apps settings -->
|
||||
<string name="zen_mode_bypassing_apps_footer">Selected people can still reach you, even if you don\u2019t allow apps to interrupt</string>
|
||||
<!-- [CHAR LIMIT=120] Zen mode settings: Allow apps to bypass DND -->
|
||||
<plurals name="zen_mode_bypassing_apps_subtext">
|
||||
<item quantity="one"><xliff:g id="app_name" example="Nest">%s</xliff:g> can interrupt</item>
|
||||
@@ -8989,6 +9004,11 @@
|
||||
<string name="zen_mode_bypassing_apps_all_summary">All notifications</string>
|
||||
<!-- [CHAR LIMIT=100] Zen mode settings: App that can bypass DND's secondary text describing which notification channels from the app can bypass DND-->
|
||||
<string name="zen_mode_bypassing_apps_some_summary">Some notifications</string>
|
||||
<!-- [CHAR LIMIT=100] Zen mode settings: Allow notifications from an app to bypass DND header -->
|
||||
<string name="zen_mode_bypassing_app_channels_header">Notifications that can interrupt</string>
|
||||
<!-- [CHAR LIMIT=100] Zen mode settings: Allow all notifications from an app to bypass DND
|
||||
toggle title -->
|
||||
<string name="zen_mode_bypassing_app_channels_toggle_all">Allow all notifications</string>
|
||||
|
||||
<!-- [CHAR LIMIT=120] Zen mode settings: Summary for sound interruption settings -->
|
||||
<plurals name="zen_mode_other_sounds_summary">
|
||||
@@ -9022,9 +9042,9 @@
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Repeat callers (ie: repeat callers are allowed to bypass dnd) -->
|
||||
<string name="zen_mode_repeat_callers_list">repeat callers</string>
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: calls summary -->
|
||||
<string name="zen_mode_calls_summary_one">Allow from <xliff:g id="caller type" example="contacts">%1$s</xliff:g></string>
|
||||
<string name="zen_mode_calls_summary_one"><xliff:g id="caller type" example="contacts">%1$s</xliff:g></string>
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: calls summary -->
|
||||
<string name="zen_mode_calls_summary_two">Allow from <xliff:g id="caller type" example="starred contacts">%1$s</xliff:g> and <xliff:g id="callert tpye" example="repeat callers">%2$s</xliff:g></string>
|
||||
<string name="zen_mode_calls_summary_two"><xliff:g id="caller type" example="starred contacts">%1$s</xliff:g> and <xliff:g id="caller type" example="repeat callers">%2$s</xliff:g></string>
|
||||
<!-- [CHAR LIMIT=200] Zen mode settings: Repeat callers option summary -->
|
||||
<string name="zen_mode_repeat_callers_summary">If the same person calls a second time within a <xliff:g id="minutes">%d</xliff:g> minute period</string>
|
||||
|
||||
|
29
res/xml/app_channels_bypassing_dnd_settings.xml
Normal file
29
res/xml/app_channels_bypassing_dnd_settings.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:title="@string/zen_mode_settings_title">
|
||||
<com.android.settingslib.widget.LayoutPreference
|
||||
android:key="pref_app_header"
|
||||
android:layout="@layout/settings_entity_header" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="zen_mode_bypassing_app_channels_list"
|
||||
android:title="@string/zen_mode_bypassing_app_channels_header">
|
||||
<!-- add app channel toggles here -->
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
@@ -14,10 +14,11 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="conversation_list"
|
||||
android:title="zen_mode_conversations_title">
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="conversation_list"
|
||||
android:title="@string/zen_mode_conversations_title">
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="important_conversations"
|
||||
|
@@ -17,4 +17,21 @@
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:title="@string/zen_mode_bypassing_apps_title" />
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/zen_mode_bypassing_apps_title">
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="zen_mode_bypassing_apps_list"
|
||||
android:title="@string/zen_mode_bypassing_apps_header">
|
||||
<!-- apps that have notifications that can bypass DND are added here -->
|
||||
</PreferenceCategory>
|
||||
|
||||
<Preference
|
||||
android:key="zen_mode_bypassing_apps_add"
|
||||
android:title="@string/zen_mode_bypassing_apps_add"
|
||||
android:icon="@drawable/ic_add_24dp"
|
||||
settings:allowDividerAbove="true" />
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference
|
||||
android:title="@string/zen_mode_bypassing_apps_footer" />
|
||||
</PreferenceScreen>
|
||||
|
45
res/xml/zen_mode_custom_rule_calls_settings.xml
Normal file
45
res/xml/zen_mode_custom_rule_calls_settings.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:key="zen_mode_custom_rule_calls_settings_page"
|
||||
android:title="@string/zen_mode_calls_title" >
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="zen_mode_settings_category_calls"
|
||||
android:title="@string/zen_mode_settings_category">
|
||||
<!-- Calls -->
|
||||
<ListPreference
|
||||
android:key="zen_mode_calls"
|
||||
android:title="@string/zen_mode_calls"
|
||||
android:entries="@array/zen_mode_contacts_calls_entries"
|
||||
android:entryValues="@array/zen_mode_contacts_values"/>
|
||||
|
||||
<Preference
|
||||
android:key="zen_mode_starred_contacts_callers"
|
||||
android:title="@string/zen_mode_starred_contacts_title"/>
|
||||
|
||||
<!-- Repeat callers -->
|
||||
<SwitchPreference
|
||||
android:key="zen_mode_repeat_callers"
|
||||
android:title="@string/zen_mode_repeat_callers_title" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference/>
|
||||
|
||||
</PreferenceScreen>
|
40
res/xml/zen_mode_custom_rule_messages_settings.xml
Normal file
40
res/xml/zen_mode_custom_rule_messages_settings.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:key="zen_mode_custom_rule_messages_settings_page"
|
||||
android:title="@string/zen_mode_messages_title" >
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/zen_mode_settings_category"
|
||||
android:key="zen_mode_settings_category_messages">
|
||||
<!-- Messages -->
|
||||
<ListPreference
|
||||
android:key="zen_mode_messages"
|
||||
android:title="@string/zen_mode_messages"
|
||||
android:entries="@array/zen_mode_contacts_messages_entries"
|
||||
android:entryValues="@array/zen_mode_contacts_values"/>
|
||||
|
||||
<Preference
|
||||
android:key="zen_mode_starred_contacts_messages"
|
||||
android:title="@string/zen_mode_starred_contacts_title"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference/>
|
||||
|
||||
</PreferenceScreen>
|
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.app;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.core.text.BidiFormatter;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settings.widget.MasterSwitchPreference;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Populates the PreferenceCategory with notification channels associated with the given app.
|
||||
* Users can allow/disallow notification channels from bypassing DND on a single settings
|
||||
* page.
|
||||
*/
|
||||
public class AppChannelsBypassingDndPreferenceController extends NotificationPreferenceController
|
||||
implements PreferenceControllerMixin, LifecycleObserver {
|
||||
|
||||
private static final String KEY = "zen_mode_bypassing_app_channels_list";
|
||||
private static final String ARG_FROM_SETTINGS = "fromSettings";
|
||||
|
||||
private RestrictedSwitchPreference mAllNotificationsToggle;
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
private final List<NotificationChannel> mChannels = new ArrayList<>();
|
||||
|
||||
public AppChannelsBypassingDndPreferenceController(
|
||||
Context context,
|
||||
NotificationBackend backend) {
|
||||
super(context, backend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
mPreferenceCategory = screen.findPreference(KEY);
|
||||
|
||||
mAllNotificationsToggle = new RestrictedSwitchPreference(mPreferenceCategory.getContext());
|
||||
mAllNotificationsToggle.setTitle(R.string.zen_mode_bypassing_app_channels_toggle_all);
|
||||
mAllNotificationsToggle.setDisabledByAdmin(mAdmin);
|
||||
mAllNotificationsToggle.setEnabled(
|
||||
(mAdmin == null || !mAllNotificationsToggle.isDisabledByAdmin()));
|
||||
mAllNotificationsToggle.setOnPreferenceClickListener(
|
||||
new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference pref) {
|
||||
SwitchPreference preference = (SwitchPreference) pref;
|
||||
final boolean bypassDnd = preference.isChecked();
|
||||
for (NotificationChannel channel : mChannels) {
|
||||
if (showNotification(channel) && isChannelConfigurable(channel)) {
|
||||
channel.setBypassDnd(bypassDnd);
|
||||
channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
|
||||
mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, channel);
|
||||
}
|
||||
}
|
||||
// the 0th index is the mAllNotificationsToggle which allows users to
|
||||
// toggle all notifications from this app to bypass DND
|
||||
for (int i = 1; i < mPreferenceCategory.getPreferenceCount(); i++) {
|
||||
MasterSwitchPreference childPreference =
|
||||
(MasterSwitchPreference) mPreferenceCategory.getPreference(i);
|
||||
childPreference.setChecked(showNotificationInDnd(mChannels.get(i - 1)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
loadAppChannels();
|
||||
super.displayPreference(screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return mAppRow != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
if (mAppRow != null) {
|
||||
loadAppChannels();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAppChannels() {
|
||||
// Load channel settings
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... unused) {
|
||||
List<NotificationChannelGroup> mChannelGroupList = mBackend.getGroups(mAppRow.pkg,
|
||||
mAppRow.uid).getList();
|
||||
mChannels.clear();
|
||||
for (NotificationChannelGroup channelGroup : mChannelGroupList) {
|
||||
for (NotificationChannel channel : channelGroup.getChannels()) {
|
||||
if (!isConversation(channel)) {
|
||||
mChannels.add(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(mChannels, CHANNEL_COMPARATOR);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void unused) {
|
||||
if (mContext == null) {
|
||||
return;
|
||||
}
|
||||
populateList();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void populateList() {
|
||||
if (mPreferenceCategory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPreferenceCategory.removeAll();
|
||||
mPreferenceCategory.addPreference(mAllNotificationsToggle);
|
||||
for (NotificationChannel channel : mChannels) {
|
||||
MasterSwitchPreference channelPreference = new MasterSwitchPreference(mContext);
|
||||
channelPreference.setDisabledByAdmin(mAdmin);
|
||||
channelPreference.setSwitchEnabled(
|
||||
(mAdmin == null || !channelPreference.isDisabledByAdmin())
|
||||
&& isChannelConfigurable(channel)
|
||||
&& showNotification(channel));
|
||||
channelPreference.setTitle(BidiFormatter.getInstance().unicodeWrap(channel.getName()));
|
||||
channelPreference.setChecked(showNotificationInDnd(channel));
|
||||
channelPreference.setOnPreferenceChangeListener(
|
||||
new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object val) {
|
||||
boolean switchOn = (Boolean) val;
|
||||
channel.setBypassDnd(switchOn);
|
||||
channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
|
||||
mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, channel);
|
||||
mAllNotificationsToggle.setChecked(areAllChannelsBypassing());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Bundle channelArgs = new Bundle();
|
||||
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid);
|
||||
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg);
|
||||
channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
|
||||
channelArgs.putBoolean(ARG_FROM_SETTINGS, true);
|
||||
channelPreference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ChannelNotificationSettings.class.getName())
|
||||
.setArguments(channelArgs)
|
||||
.setTitleRes(com.android.settings.R.string.notification_channel_title)
|
||||
.setSourceMetricsCategory(SettingsEnums.DND_APPS_BYPASSING)
|
||||
.toIntent());
|
||||
mPreferenceCategory.addPreference(channelPreference);
|
||||
}
|
||||
mAllNotificationsToggle.setChecked(areAllChannelsBypassing());
|
||||
}
|
||||
|
||||
private boolean areAllChannelsBypassing() {
|
||||
boolean allChannelsBypassing = true;
|
||||
for (NotificationChannel channel : mChannels) {
|
||||
if (showNotification(channel)) {
|
||||
allChannelsBypassing &= showNotificationInDnd(channel);
|
||||
}
|
||||
}
|
||||
return allChannelsBypassing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether notifications from this channel would show if DND were on.
|
||||
*/
|
||||
private boolean showNotificationInDnd(NotificationChannel channel) {
|
||||
return channel.canBypassDnd() && showNotification(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether notifications from this channel would show if DND weren't on.
|
||||
*/
|
||||
private boolean showNotification(NotificationChannel channel) {
|
||||
return channel.getImportance() != IMPORTANCE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this notification channel is representing a conversation.
|
||||
*/
|
||||
private boolean isConversation(NotificationChannel channel) {
|
||||
return channel.getConversationId() != null && !channel.isDemoted();
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.app;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Per-app Settings page that shows a list of notification channels that a user can toggle for
|
||||
* the channel to bypass DND.
|
||||
*
|
||||
* This can be found at:
|
||||
* Settings > Sound > Do Not Disturb > Apps > (Choose app)
|
||||
*/
|
||||
public class AppChannelsBypassingDndSettings extends NotificationSettings {
|
||||
private static final String TAG = "AppChannelsBypassingDndSettings";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.DND_APPS_BYPASSING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
|
||||
Log.w(TAG, "Missing package or uid or packageinfo");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
for (NotificationPreferenceController controller : mControllers) {
|
||||
controller.onResume(mAppRow, null, null, null, null, mSuspendedAppsAdmin);
|
||||
controller.displayPreference(getPreferenceScreen());
|
||||
}
|
||||
updatePreferenceStates();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.app_channels_bypassing_dnd_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
mControllers = new ArrayList<>();
|
||||
mControllers.add(new HeaderPreferenceController(context, this));
|
||||
mControllers.add(new AppChannelsBypassingDndPreferenceController(context,
|
||||
new NotificationBackend()));
|
||||
return new ArrayList<>(mControllers);
|
||||
}
|
||||
}
|
@@ -33,6 +33,11 @@ import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
@@ -43,14 +48,8 @@ import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
public class ChannelListPreferenceController extends NotificationPreferenceController {
|
||||
|
||||
private static final String KEY = "channels";
|
||||
@@ -94,7 +93,7 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
|
||||
@Override
|
||||
protected Void doInBackground(Void... unused) {
|
||||
mChannelGroupList = mBackend.getGroups(mAppRow.pkg, mAppRow.uid).getList();
|
||||
Collections.sort(mChannelGroupList, mChannelGroupComparator);
|
||||
Collections.sort(mChannelGroupList, CHANNEL_GROUP_COMPARATOR);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -142,7 +141,7 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
|
||||
}
|
||||
if (!group.isBlocked()) {
|
||||
final List<NotificationChannel> channels = group.getChannels();
|
||||
Collections.sort(channels, mChannelComparator);
|
||||
Collections.sort(channels, CHANNEL_COMPARATOR);
|
||||
int N = channels.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationChannel channel = channels.get(i);
|
||||
@@ -274,7 +273,7 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
|
||||
}
|
||||
} else {
|
||||
final List<NotificationChannel> channels = group.getChannels();
|
||||
Collections.sort(channels, mChannelComparator);
|
||||
Collections.sort(channels, CHANNEL_COMPARATOR);
|
||||
int N = channels.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationChannel channel = channels.get(i);
|
||||
@@ -283,33 +282,4 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Comparator<NotificationChannelGroup> mChannelGroupComparator =
|
||||
new Comparator<NotificationChannelGroup>() {
|
||||
|
||||
@Override
|
||||
public int compare(NotificationChannelGroup left, NotificationChannelGroup right) {
|
||||
// Non-grouped channels (in placeholder group with a null id) come last
|
||||
if (left.getId() == null && right.getId() != null) {
|
||||
return 1;
|
||||
} else if (right.getId() == null && left.getId() != null) {
|
||||
return -1;
|
||||
}
|
||||
return left.getId().compareTo(right.getId());
|
||||
}
|
||||
};
|
||||
|
||||
protected Comparator<NotificationChannel> mChannelComparator =
|
||||
(left, right) -> {
|
||||
if (left.isDeleted() != right.isDeleted()) {
|
||||
return Boolean.compare(left.isDeleted(), right.isDeleted());
|
||||
} else if (left.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
// Uncategorized/miscellaneous legacy channel goes last
|
||||
return 1;
|
||||
} else if (right.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return left.getId().compareTo(right.getId());
|
||||
};
|
||||
}
|
||||
|
@@ -35,6 +35,7 @@ import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -172,4 +173,31 @@ public abstract class NotificationPreferenceController extends AbstractPreferenc
|
||||
}
|
||||
return Objects.equals(NotificationChannel.DEFAULT_CHANNEL_ID, mChannel.getId());
|
||||
}
|
||||
|
||||
public static final Comparator<NotificationChannelGroup> CHANNEL_GROUP_COMPARATOR =
|
||||
new Comparator<NotificationChannelGroup>() {
|
||||
@Override
|
||||
public int compare(NotificationChannelGroup left, NotificationChannelGroup right) {
|
||||
// Non-grouped channels (in placeholder group with a null id) come last
|
||||
if (left.getId() == null && right.getId() != null) {
|
||||
return 1;
|
||||
} else if (right.getId() == null && left.getId() != null) {
|
||||
return -1;
|
||||
}
|
||||
return left.getId().compareTo(right.getId());
|
||||
}
|
||||
};
|
||||
|
||||
public static final Comparator<NotificationChannel> CHANNEL_COMPARATOR = (left, right) -> {
|
||||
if (left.isDeleted() != right.isDeleted()) {
|
||||
return Boolean.compare(left.isDeleted(), right.isDeleted());
|
||||
} else if (left.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
// Uncategorized/miscellaneous legacy channel goes last
|
||||
return 1;
|
||||
} else if (right.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return left.getId().compareTo(right.getId());
|
||||
};
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ public class ZenCustomRuleCallsSettings extends ZenCustomRuleSettingsBase {
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return com.android.settings.R.xml.zen_mode_calls_settings;
|
||||
return com.android.settings.R.xml.zen_mode_custom_rule_calls_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -37,7 +37,7 @@ public class ZenCustomRuleMessagesSettings extends ZenCustomRuleSettingsBase {
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return com.android.settings.R.xml.zen_mode_messages_settings;
|
||||
return com.android.settings.R.xml.zen_mode_custom_rule_messages_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.app.Application;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.text.BidiFormatter;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settings.notification.app.AppChannelsBypassingDndSettings;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.widget.apppreference.AppPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* When clicked, populates the PreferenceScreen with apps that aren't already bypassing DND. The
|
||||
* user can click on these Preferences to allow notification channels from the app to bypass DND.
|
||||
*/
|
||||
public class ZenModeAddBypassingAppsPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin {
|
||||
|
||||
private static final String KEY = "zen_mode_non_bypassing_apps_list";
|
||||
private static final String KEY_ADD = "zen_mode_bypassing_apps_add";
|
||||
private final NotificationBackend mNotificationBackend;
|
||||
|
||||
@VisibleForTesting ApplicationsState mApplicationsState;
|
||||
@VisibleForTesting PreferenceScreen mPreferenceScreen;
|
||||
@VisibleForTesting PreferenceCategory mPreferenceCategory;
|
||||
@VisibleForTesting Context mPrefContext;
|
||||
|
||||
private Preference mAddPreference;
|
||||
private ApplicationsState.Session mAppSession;
|
||||
private Fragment mHostFragment;
|
||||
|
||||
public ZenModeAddBypassingAppsPreferenceController(Context context, Application app,
|
||||
Fragment host, NotificationBackend notificationBackend) {
|
||||
this(context, app == null ? null : ApplicationsState.getInstance(app), host,
|
||||
notificationBackend);
|
||||
}
|
||||
|
||||
private ZenModeAddBypassingAppsPreferenceController(Context context, ApplicationsState appState,
|
||||
Fragment host, NotificationBackend notificationBackend) {
|
||||
super(context);
|
||||
mNotificationBackend = notificationBackend;
|
||||
mApplicationsState = appState;
|
||||
mHostFragment = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
mPreferenceScreen = screen;
|
||||
mAddPreference = screen.findPreference(KEY_ADD);
|
||||
mAddPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
mAddPreference.setVisible(false);
|
||||
if (mApplicationsState != null && mHostFragment != null) {
|
||||
mAppSession = mApplicationsState.newSession(mAppSessionCallbacks,
|
||||
mHostFragment.getLifecycle());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mPrefContext = screen.getContext();
|
||||
super.displayPreference(screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to trigger the app list to refresh.
|
||||
*/
|
||||
public void updateAppList() {
|
||||
if (mAppSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED;
|
||||
List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter,
|
||||
ApplicationsState.ALPHA_COMPARATOR);
|
||||
updateAppList(apps);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateAppList(List<ApplicationsState.AppEntry> apps) {
|
||||
if (apps == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPreferenceCategory == null) {
|
||||
mPreferenceCategory = new PreferenceCategory(mPrefContext);
|
||||
mPreferenceCategory.setTitle(R.string.zen_mode_bypassing_apps_add_header);
|
||||
mPreferenceScreen.addPreference(mPreferenceCategory);
|
||||
}
|
||||
|
||||
List<Preference> appsWithNoBypassingDndNotificationChannels = new ArrayList<>();
|
||||
for (ApplicationsState.AppEntry entry : apps) {
|
||||
String pkg = entry.info.packageName;
|
||||
mApplicationsState.ensureIcon(entry);
|
||||
final int appChannels = mNotificationBackend.getChannelCount(pkg, entry.info.uid);
|
||||
final int appChannelsBypassingDnd = mNotificationBackend
|
||||
.getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList().size();
|
||||
if (appChannelsBypassingDnd == 0 && appChannels > 0) {
|
||||
final String key = ZenModeAllBypassingAppsPreferenceController.getKey(pkg);
|
||||
Preference pref = mPreferenceCategory.findPreference("");
|
||||
if (pref == null) {
|
||||
pref = new AppPreference(mPrefContext);
|
||||
pref.setKey(key);
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(AppInfoBase.ARG_PACKAGE_NAME, entry.info.packageName);
|
||||
args.putInt(AppInfoBase.ARG_PACKAGE_UID, entry.info.uid);
|
||||
new SubSettingLauncher(mContext)
|
||||
.setDestination(AppChannelsBypassingDndSettings.class.getName())
|
||||
.setArguments(args)
|
||||
.setResultListener(mHostFragment, 0)
|
||||
.setSourceMetricsCategory(
|
||||
SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP)
|
||||
.launch();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(entry.label));
|
||||
pref.setIcon(entry.icon);
|
||||
appsWithNoBypassingDndNotificationChannels.add(pref);
|
||||
}
|
||||
}
|
||||
|
||||
if (appsWithNoBypassingDndNotificationChannels.size() == 0) {
|
||||
Preference pref = mPreferenceCategory.findPreference(
|
||||
ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS);
|
||||
if (pref == null) {
|
||||
pref = new Preference(mPrefContext);
|
||||
pref.setKey(ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS);
|
||||
pref.setTitle(R.string.zen_mode_bypassing_apps_subtext_none);
|
||||
}
|
||||
mPreferenceCategory.addPreference(pref);
|
||||
}
|
||||
|
||||
if (ZenModeAllBypassingAppsPreferenceController.hasAppListChanged(
|
||||
appsWithNoBypassingDndNotificationChannels, mPreferenceCategory)) {
|
||||
mPreferenceCategory.removeAll();
|
||||
for (Preference prefToAdd : appsWithNoBypassingDndNotificationChannels) {
|
||||
mPreferenceCategory.addPreference(prefToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final ApplicationsState.Callbacks mAppSessionCallbacks =
|
||||
new ApplicationsState.Callbacks() {
|
||||
|
||||
@Override
|
||||
public void onRunningStateChanged(boolean running) {
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageListChanged() {
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
|
||||
updateAppList(apps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageIconChanged() {
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageSizeChanged(String packageName) {
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllSizesComputed() { }
|
||||
|
||||
@Override
|
||||
public void onLauncherInfoChanged() {
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadEntriesCompleted() {
|
||||
updateAppList();
|
||||
}
|
||||
};
|
||||
}
|
@@ -17,57 +17,59 @@
|
||||
package com.android.settings.notification.zen;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.text.BidiFormatter;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.notification.app.ChannelNotificationSettings;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settings.notification.app.AppChannelsBypassingDndSettings;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.widget.apppreference.AppPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a preference to the PreferenceScreen for each notification channel that can bypass DND.
|
||||
*/
|
||||
public class ZenModeAllBypassingAppsPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin {
|
||||
public static final String KEY_NO_APPS = getKey("none");
|
||||
private static final String KEY = "zen_mode_bypassing_apps_list";
|
||||
|
||||
private final String KEY = "zen_mode_bypassing_apps_category";
|
||||
private final NotificationBackend mNotificationBackend;
|
||||
|
||||
@VisibleForTesting ApplicationsState mApplicationsState;
|
||||
@VisibleForTesting PreferenceScreen mPreferenceScreen;
|
||||
@VisibleForTesting PreferenceCategory mPreferenceCategory;
|
||||
@VisibleForTesting Context mPrefContext;
|
||||
|
||||
private ApplicationsState.Session mAppSession;
|
||||
private NotificationBackend mNotificationBackend = new NotificationBackend();
|
||||
private Fragment mHostFragment;
|
||||
|
||||
public ZenModeAllBypassingAppsPreferenceController(Context context, Application app,
|
||||
Fragment host) {
|
||||
|
||||
this(context, app == null ? null : ApplicationsState.getInstance(app), host);
|
||||
Fragment host, NotificationBackend notificationBackend) {
|
||||
this(context, app == null ? null : ApplicationsState.getInstance(app), host,
|
||||
notificationBackend);
|
||||
}
|
||||
|
||||
private ZenModeAllBypassingAppsPreferenceController(Context context, ApplicationsState appState,
|
||||
Fragment host) {
|
||||
Fragment host, NotificationBackend notificationBackend) {
|
||||
super(context);
|
||||
mNotificationBackend = notificationBackend;
|
||||
mApplicationsState = appState;
|
||||
mHostFragment = host;
|
||||
|
||||
@@ -78,9 +80,9 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
mPreferenceScreen = screen;
|
||||
mPrefContext = mPreferenceScreen.getContext();
|
||||
updateNotificationChannelList();
|
||||
mPreferenceCategory = screen.findPreference(KEY);
|
||||
mPrefContext = screen.getContext();
|
||||
updateAppList();
|
||||
super.displayPreference(screen);
|
||||
}
|
||||
|
||||
@@ -95,9 +97,9 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to trigger the notification channels list to refresh.
|
||||
* Call this method to trigger the app list to refresh.
|
||||
*/
|
||||
public void updateNotificationChannelList() {
|
||||
public void updateAppList() {
|
||||
if (mAppSession == null) {
|
||||
return;
|
||||
}
|
||||
@@ -105,64 +107,95 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
|
||||
ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED;
|
||||
List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter,
|
||||
ApplicationsState.ALPHA_COMPARATOR);
|
||||
updateNotificationChannelList(apps);
|
||||
updateAppList(apps);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateNotificationChannelList(List<ApplicationsState.AppEntry> apps) {
|
||||
if (mPreferenceScreen == null || apps == null) {
|
||||
void updateAppList(List<ApplicationsState.AppEntry> apps) {
|
||||
if (mPreferenceCategory == null || apps == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean showEmptyState = true;
|
||||
|
||||
List<Preference> channelsBypassingDnd = new ArrayList<>();
|
||||
for (ApplicationsState.AppEntry entry : apps) {
|
||||
String pkg = entry.info.packageName;
|
||||
mApplicationsState.ensureIcon(entry);
|
||||
for (NotificationChannel channel : mNotificationBackend
|
||||
.getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList()) {
|
||||
if (!TextUtils.isEmpty(channel.getConversationId())) {
|
||||
// conversation channels that bypass dnd will be shown on the People page
|
||||
continue;
|
||||
List<Preference> appsBypassingDnd = new ArrayList<>();
|
||||
for (ApplicationsState.AppEntry app : apps) {
|
||||
String pkg = app.info.packageName;
|
||||
mApplicationsState.ensureIcon(app);
|
||||
final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid);
|
||||
final int appChannelsBypassingDnd = mNotificationBackend
|
||||
.getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size();
|
||||
if (appChannelsBypassingDnd > 0) {
|
||||
final String key = getKey(pkg);
|
||||
// re-use previously created preference when possible
|
||||
Preference pref = mPreferenceCategory.findPreference(key);
|
||||
if (pref == null) {
|
||||
pref = new AppPreference(mPrefContext);
|
||||
pref.setKey(key);
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(AppInfoBase.ARG_PACKAGE_NAME, app.info.packageName);
|
||||
args.putInt(AppInfoBase.ARG_PACKAGE_UID, app.info.uid);
|
||||
new SubSettingLauncher(mContext)
|
||||
.setDestination(AppChannelsBypassingDndSettings.class.getName())
|
||||
.setArguments(args)
|
||||
.setResultListener(mHostFragment, 0)
|
||||
.setSourceMetricsCategory(
|
||||
SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP)
|
||||
.launch();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
Preference pref = new AppPreference(mPrefContext);
|
||||
pref.setKey(pkg + "|" + channel.getId());
|
||||
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(entry.label));
|
||||
pref.setIcon(entry.icon);
|
||||
pref.setSummary(BidiFormatter.getInstance().unicodeWrap(channel.getName()));
|
||||
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(AppInfoBase.ARG_PACKAGE_NAME, entry.info.packageName);
|
||||
args.putInt(AppInfoBase.ARG_PACKAGE_UID, entry.info.uid);
|
||||
args.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
|
||||
new SubSettingLauncher(mContext)
|
||||
.setDestination(ChannelNotificationSettings.class.getName())
|
||||
.setArguments(args)
|
||||
.setTitleRes(R.string.notification_channel_title)
|
||||
.setResultListener(mHostFragment, 0)
|
||||
.setSourceMetricsCategory(
|
||||
SettingsEnums.NOTIFICATION_ZEN_MODE_OVERRIDING_APP)
|
||||
.launch();
|
||||
return true;
|
||||
});
|
||||
channelsBypassingDnd.add(pref);
|
||||
showEmptyState = false;
|
||||
}
|
||||
|
||||
mPreferenceScreen.removeAll();
|
||||
if (channelsBypassingDnd.size() > 0) {
|
||||
for (Preference prefToAdd : channelsBypassingDnd) {
|
||||
mPreferenceScreen.addPreference(prefToAdd);
|
||||
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label));
|
||||
pref.setIcon(app.icon);
|
||||
if (appChannels > appChannelsBypassingDnd) {
|
||||
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some);
|
||||
} else {
|
||||
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_all);
|
||||
}
|
||||
}
|
||||
if (showEmptyState) {
|
||||
Preference pref = new Preference(mPrefContext);
|
||||
pref.setTitle(R.string.zen_mode_bypassing_apps_subtext_none);
|
||||
mPreferenceScreen.addPreference(pref);
|
||||
|
||||
appsBypassingDnd.add(pref);
|
||||
}
|
||||
}
|
||||
|
||||
if (appsBypassingDnd.size() == 0) {
|
||||
Preference pref = mPreferenceCategory.findPreference(KEY_NO_APPS);
|
||||
if (pref == null) {
|
||||
pref = new Preference(mPrefContext);
|
||||
pref.setKey(KEY_NO_APPS);
|
||||
pref.setTitle(R.string.zen_mode_bypassing_apps_none);
|
||||
}
|
||||
appsBypassingDnd.add(pref);
|
||||
}
|
||||
|
||||
if (hasAppListChanged(appsBypassingDnd, mPreferenceCategory)) {
|
||||
mPreferenceCategory.removeAll();
|
||||
for (Preference prefToAdd : appsBypassingDnd) {
|
||||
mPreferenceCategory.addPreference(prefToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean hasAppListChanged(List<Preference> newAppPreferences,
|
||||
PreferenceCategory preferenceCategory) {
|
||||
if (newAppPreferences.size() != preferenceCategory.getPreferenceCount()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < newAppPreferences.size(); i++) {
|
||||
Preference newAppPref = newAppPreferences.get(i);
|
||||
Preference pref = preferenceCategory.getPreference(i);
|
||||
if (!Objects.equals(newAppPref.getKey(), pref.getKey())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a unique key to idenfity an AppPreference
|
||||
*/
|
||||
static String getKey(String pkg) {
|
||||
return pkg;
|
||||
}
|
||||
|
||||
private final ApplicationsState.Callbacks mAppSessionCallbacks =
|
||||
@@ -170,27 +203,27 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
|
||||
|
||||
@Override
|
||||
public void onRunningStateChanged(boolean running) {
|
||||
updateNotificationChannelList();
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageListChanged() {
|
||||
updateNotificationChannelList();
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
|
||||
updateNotificationChannelList(apps);
|
||||
updateAppList(apps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageIconChanged() {
|
||||
updateNotificationChannelList();
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageSizeChanged(String packageName) {
|
||||
updateNotificationChannelList();
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -198,12 +231,12 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
|
||||
|
||||
@Override
|
||||
public void onLauncherInfoChanged() {
|
||||
updateNotificationChannelList();
|
||||
updateAppList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadEntriesCompleted() {
|
||||
updateNotificationChannelList();
|
||||
updateAppList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -287,44 +287,15 @@ public class ZenModeBackend {
|
||||
|
||||
protected int getAlarmsTotalSilencePeopleSummary(int category) {
|
||||
if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
|
||||
return R.string.zen_mode_from_none_messages;
|
||||
return R.string.zen_mode_from_none;
|
||||
} else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){
|
||||
return R.string.zen_mode_from_none_calls;
|
||||
return R.string.zen_mode_from_none;
|
||||
} else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) {
|
||||
return R.string.zen_mode_from_no_conversations;
|
||||
}
|
||||
return R.string.zen_mode_from_none;
|
||||
}
|
||||
|
||||
protected int getContactsSummary(int category) {
|
||||
int contactType = -1;
|
||||
if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
|
||||
if (isPriorityCategoryEnabled(category)) {
|
||||
contactType = getPriorityMessageSenders();
|
||||
}
|
||||
} else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
|
||||
if (isPriorityCategoryEnabled(category)) {
|
||||
contactType = getPriorityCallSenders();
|
||||
}
|
||||
}
|
||||
|
||||
switch (contactType) {
|
||||
case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
|
||||
return R.string.zen_mode_from_anyone;
|
||||
case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
|
||||
return R.string.zen_mode_from_contacts;
|
||||
case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
|
||||
return R.string.zen_mode_from_starred;
|
||||
case SOURCE_NONE:
|
||||
default:
|
||||
if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
|
||||
return R.string.zen_mode_from_none_messages;
|
||||
} else {
|
||||
return R.string.zen_mode_from_none_calls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int getConversationSummary() {
|
||||
int conversationType = getPriorityConversationSenders();
|
||||
|
||||
@@ -366,7 +337,7 @@ public class ZenModeBackend {
|
||||
return R.string.zen_mode_from_starred;
|
||||
case ZenPolicy.PEOPLE_TYPE_NONE:
|
||||
default:
|
||||
return R.string.zen_mode_from_none_messages;
|
||||
return R.string.zen_mode_from_none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,20 +355,6 @@ public class ZenModeBackend {
|
||||
}
|
||||
}
|
||||
|
||||
protected static int getSettingFromPrefKey(String key) {
|
||||
switch (key) {
|
||||
case ZEN_MODE_FROM_ANYONE:
|
||||
return NotificationManager.Policy.PRIORITY_SENDERS_ANY;
|
||||
case ZEN_MODE_FROM_CONTACTS:
|
||||
return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
|
||||
case ZEN_MODE_FROM_STARRED:
|
||||
return NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
|
||||
case ZEN_MODE_FROM_NONE:
|
||||
default:
|
||||
return SOURCE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeZenRule(String ruleId) {
|
||||
return NotificationManager.from(mContext).removeAutomaticZenRule(ruleId);
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import android.content.Context;
|
||||
import android.icu.text.ListFormatter;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.core.text.BidiFormatter;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -21,6 +22,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Controls the summary for preference found at:
|
||||
@@ -102,7 +104,7 @@ public class ZenModeBypassingAppsPreferenceController extends AbstractZenModePre
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> appsBypassingDnd = new ArrayList<>();
|
||||
Set<String> appsBypassingDnd = new ArraySet<>();
|
||||
for (ApplicationsState.AppEntry entry : apps) {
|
||||
String pkg = entry.info.packageName;
|
||||
for (NotificationChannel channel : mNotificationBackend
|
||||
@@ -116,7 +118,8 @@ public class ZenModeBypassingAppsPreferenceController extends AbstractZenModePre
|
||||
}
|
||||
}
|
||||
|
||||
if (appsBypassingDnd.size() == 0) {
|
||||
final int numAppsBypassingDnd = appsBypassingDnd.size();
|
||||
if (numAppsBypassingDnd == 0) {
|
||||
mSummary = mContext.getResources().getString(
|
||||
R.string.zen_mode_bypassing_apps_subtext_none);
|
||||
refreshSummary(mPreference);
|
||||
@@ -124,18 +127,20 @@ public class ZenModeBypassingAppsPreferenceController extends AbstractZenModePre
|
||||
}
|
||||
|
||||
List<String> displayAppsBypassing = new ArrayList<>();
|
||||
if (appsBypassingDnd.size() <= 2) {
|
||||
displayAppsBypassing = appsBypassingDnd;
|
||||
if (numAppsBypassingDnd <= 2) {
|
||||
displayAppsBypassing.addAll(appsBypassingDnd);
|
||||
} else {
|
||||
displayAppsBypassing.add(appsBypassingDnd.get(0));
|
||||
displayAppsBypassing.add(appsBypassingDnd.get(1));
|
||||
String[] appsBypassingDndArr =
|
||||
appsBypassingDnd.toArray(new String[numAppsBypassingDnd]);
|
||||
displayAppsBypassing.add(appsBypassingDndArr[0]);
|
||||
displayAppsBypassing.add(appsBypassingDndArr[1]);
|
||||
displayAppsBypassing.add(mContext.getResources().getString(
|
||||
R.string.zen_mode_apps_bypassing_list_count,
|
||||
appsBypassingDnd.size() - 2));
|
||||
numAppsBypassingDnd - 2));
|
||||
}
|
||||
mSummary = mContext.getResources().getQuantityString(
|
||||
R.plurals.zen_mode_bypassing_apps_subtext,
|
||||
appsBypassingDnd.size(),
|
||||
numAppsBypassingDnd,
|
||||
ListFormatter.getInstance().format(displayAppsBypassing));
|
||||
refreshSummary(mPreference);
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import android.content.Context;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.search.Indexable;
|
||||
@@ -46,13 +47,16 @@ public class ZenModeBypassingAppsSettings extends ZenModeSettingsBase implements
|
||||
} else {
|
||||
app = null;
|
||||
}
|
||||
return buildPreferenceControllers(context, app, this);
|
||||
return buildPreferenceControllers(context, app, this, new NotificationBackend());
|
||||
}
|
||||
|
||||
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
|
||||
Application app, Fragment host) {
|
||||
Application app, Fragment host, NotificationBackend notificationBackend) {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host));
|
||||
controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host,
|
||||
notificationBackend));
|
||||
controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host,
|
||||
notificationBackend));
|
||||
return controllers;
|
||||
}
|
||||
|
||||
@@ -80,7 +84,7 @@ public class ZenModeBypassingAppsSettings extends ZenModeSettingsBase implements
|
||||
@Override
|
||||
public List<AbstractPreferenceController> createPreferenceControllers(
|
||||
Context context) {
|
||||
return buildPreferenceControllers(context, null, null);
|
||||
return buildPreferenceControllers(context, null, null, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Settings > Sound > Do Not Disturb > Conversationss
|
||||
* Settings > Sound > Do Not Disturb > Conversations
|
||||
*/
|
||||
@SearchIndexable
|
||||
public class ZenModeConversationsSettings extends ZenModeSettingsBase {
|
||||
|
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* 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.zen;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
public class ZenModePriorityCallsPreferenceController extends AbstractZenModePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
protected static final String KEY = "zen_mode_calls";
|
||||
private final ZenModeBackend mBackend;
|
||||
private ListPreference mPreference;
|
||||
private final String[] mListValues;
|
||||
|
||||
public ZenModePriorityCallsPreferenceController(Context context, Lifecycle lifecycle) {
|
||||
super(context, KEY, lifecycle);
|
||||
mBackend = ZenModeBackend.getInstance(context);
|
||||
mListValues = context.getResources().getStringArray(R.array.zen_mode_contacts_values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
updateFromContactsValue(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object selectedContactsFrom) {
|
||||
mBackend.saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
|
||||
ZenModeBackend.getSettingFromPrefKey(selectedContactsFrom.toString()));
|
||||
updateFromContactsValue(preference);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateFromContactsValue(Preference preference) {
|
||||
mPreference = (ListPreference) preference;
|
||||
switch (getZenMode()) {
|
||||
case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
|
||||
case Settings.Global.ZEN_MODE_ALARMS:
|
||||
mPreference.setEnabled(false);
|
||||
mPreference.setValue(ZenModeBackend.ZEN_MODE_FROM_NONE);
|
||||
mPreference.setSummary(mBackend.getAlarmsTotalSilencePeopleSummary(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS));
|
||||
break;
|
||||
default:
|
||||
preference.setEnabled(true);
|
||||
preference.setSummary(mBackend.getContactsSummary(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS));
|
||||
|
||||
final String currentVal = ZenModeBackend.getKeyFromSetting(
|
||||
mBackend.getPriorityCallSenders());
|
||||
mPreference.setValue(mListValues[getIndexOfSendersValue(currentVal)]);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected int getIndexOfSendersValue(String currentVal) {
|
||||
int index = 3; // defaults to "none" based on R.array.zen_mode_contacts_values
|
||||
for (int i = 0; i < mListValues.length; i++) {
|
||||
if (TextUtils.equals(currentVal, mListValues[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
@@ -16,11 +16,16 @@
|
||||
|
||||
package com.android.settings.notification.zen;
|
||||
|
||||
import static com.android.settings.widget.RadioButtonPreferenceWithExtraWidget.EXTRA_WIDGET_VISIBILITY_GONE;
|
||||
import static com.android.settings.widget.RadioButtonPreferenceWithExtraWidget.EXTRA_WIDGET_VISIBILITY_SETTING;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.AsyncTask;
|
||||
import android.service.notification.ConversationChannelWrapper;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
@@ -28,7 +33,10 @@ import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settings.notification.app.ConversationListSettings;
|
||||
import com.android.settings.widget.RadioButtonPreferenceWithExtraWidget;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.widget.RadioButtonPreference;
|
||||
|
||||
@@ -51,6 +59,7 @@ public class ZenModePriorityConversationsPreferenceController
|
||||
private int mNumConversations = UNSET;
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
private List<RadioButtonPreference> mRadioButtonPreferences = new ArrayList<>();
|
||||
private Context mPreferenceScreenContext;
|
||||
|
||||
public ZenModePriorityConversationsPreferenceController(Context context, String key,
|
||||
Lifecycle lifecycle, NotificationBackend notificationBackend) {
|
||||
@@ -60,6 +69,7 @@ public class ZenModePriorityConversationsPreferenceController
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
mPreferenceScreenContext = screen.getContext();
|
||||
mPreferenceCategory = screen.findPreference(getPreferenceKey());
|
||||
if (mPreferenceCategory.findPreference(KEY_ALL) == null) {
|
||||
makeRadioPreference(KEY_ALL, R.string.zen_mode_from_all_conversations);
|
||||
@@ -125,7 +135,7 @@ public class ZenModePriorityConversationsPreferenceController
|
||||
R.string.zen_mode_conversations_count_none);
|
||||
} else {
|
||||
return mContext.getResources().getQuantityString(
|
||||
R.plurals.zen_mode_conversations_count, numConversations);
|
||||
R.plurals.zen_mode_conversations_count, numConversations, numConversations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,14 +146,27 @@ public class ZenModePriorityConversationsPreferenceController
|
||||
protected Void doInBackground(Void... unused) {
|
||||
ParceledListSlice<ConversationChannelWrapper> allConversations =
|
||||
mNotificationBackend.getConversations(false);
|
||||
int numConversations = 0;
|
||||
if (allConversations != null) {
|
||||
mNumConversations = allConversations.getList().size();
|
||||
for (ConversationChannelWrapper conversation : allConversations.getList()) {
|
||||
if (!conversation.getNotificationChannel().isDemoted()) {
|
||||
numConversations++;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParceledListSlice<ConversationChannelWrapper> importantConversations =
|
||||
mNumConversations = numConversations;
|
||||
|
||||
ParceledListSlice<ConversationChannelWrapper> impConversations =
|
||||
mNotificationBackend.getConversations(true);
|
||||
if (importantConversations != null) {
|
||||
mNumImportantConversations = importantConversations.getList().size();
|
||||
int numImportantConversations = 0;
|
||||
if (impConversations != null) {
|
||||
for (ConversationChannelWrapper conversation : impConversations.getList()) {
|
||||
if (!conversation.getNotificationChannel().isDemoted()) {
|
||||
numImportantConversations++;
|
||||
}
|
||||
}
|
||||
}
|
||||
mNumImportantConversations = numImportantConversations;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -158,7 +181,14 @@ public class ZenModePriorityConversationsPreferenceController
|
||||
}
|
||||
|
||||
private RadioButtonPreference makeRadioPreference(String key, int titleId) {
|
||||
RadioButtonPreference pref = new RadioButtonPreference(mPreferenceCategory.getContext());
|
||||
RadioButtonPreferenceWithExtraWidget pref =
|
||||
new RadioButtonPreferenceWithExtraWidget(mPreferenceCategory.getContext());
|
||||
if (KEY_ALL.equals(key) || KEY_IMPORTANT.equals(key)) {
|
||||
pref.setExtraWidgetOnClickListener(mConversationSettingsWidgetClickListener);
|
||||
pref.setExtraWidgetVisibility(EXTRA_WIDGET_VISIBILITY_SETTING);
|
||||
} else {
|
||||
pref.setExtraWidgetVisibility(EXTRA_WIDGET_VISIBILITY_GONE);
|
||||
}
|
||||
pref.setKey(key);
|
||||
pref.setTitle(titleId);
|
||||
pref.setOnClickListener(mRadioButtonClickListener);
|
||||
@@ -167,6 +197,17 @@ public class ZenModePriorityConversationsPreferenceController
|
||||
return pref;
|
||||
}
|
||||
|
||||
private View.OnClickListener mConversationSettingsWidgetClickListener =
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new SubSettingLauncher(mPreferenceScreenContext)
|
||||
.setDestination(ConversationListSettings.class.getName())
|
||||
.setSourceMetricsCategory(SettingsEnums.DND_CONVERSATIONS)
|
||||
.launch();
|
||||
}
|
||||
};
|
||||
|
||||
private RadioButtonPreference.OnClickListener mRadioButtonClickListener =
|
||||
new RadioButtonPreference.OnClickListener() {
|
||||
@Override
|
||||
|
@@ -153,7 +153,7 @@ public class ZenModeSettings extends ZenModeSettingsBase {
|
||||
String getCallsSettingSummary(Policy policy) {
|
||||
List<String> enabledCategories = getEnabledCategories(policy,
|
||||
category -> PRIORITY_CATEGORY_CALLS == category
|
||||
|| PRIORITY_CATEGORY_REPEAT_CALLERS == category, false);
|
||||
|| PRIORITY_CATEGORY_REPEAT_CALLERS == category, true);
|
||||
int numCategories = enabledCategories.size();
|
||||
if (numCategories == 0) {
|
||||
return mContext.getString(R.string.zen_mode_from_none_calls);
|
||||
@@ -303,10 +303,19 @@ public class ZenModeSettings extends ZenModeSettingsBase {
|
||||
}
|
||||
} else if (category == Policy.PRIORITY_CATEGORY_CALLS) {
|
||||
if (policy.priorityCallSenders == Policy.PRIORITY_SENDERS_ANY) {
|
||||
if (isFirst) {
|
||||
return mContext.getString(R.string.zen_mode_from_anyone);
|
||||
}
|
||||
return mContext.getString(R.string.zen_mode_all_callers);
|
||||
} else if (policy.priorityCallSenders == Policy.PRIORITY_SENDERS_CONTACTS){
|
||||
if (isFirst) {
|
||||
return mContext.getString(R.string.zen_mode_from_contacts);
|
||||
}
|
||||
return mContext.getString(R.string.zen_mode_contacts_callers);
|
||||
} else {
|
||||
if (isFirst) {
|
||||
return mContext.getString(R.string.zen_mode_from_starred);
|
||||
}
|
||||
return mContext.getString(R.string.zen_mode_starred_callers);
|
||||
}
|
||||
} else if (category == Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) {
|
||||
|
@@ -16,19 +16,8 @@
|
||||
|
||||
package com.android.settings.notification.app;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||
import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.app.NotificationManager.IMPORTANCE_MIN;
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -37,24 +26,17 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.ConversationChannelWrapper;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -67,7 +49,6 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ConversationListPreferenceControllerTest {
|
||||
@@ -116,8 +97,6 @@ public class ConversationListPreferenceControllerTest {
|
||||
list.add(ccw);
|
||||
|
||||
mController.populateList(list, outerContainer);
|
||||
|
||||
verify(outerContainer).setVisible(true);
|
||||
verify(outerContainer, times(1)).addPreference(any());
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ZenModeAddBypassingAppsPreferenceControllerTest {
|
||||
|
||||
@Mock
|
||||
private NotificationBackend mBackend;
|
||||
@Mock
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
@Mock
|
||||
private ApplicationsState mApplicationState;
|
||||
private ZenModeAddBypassingAppsPreferenceController mController;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
mController = new ZenModeAddBypassingAppsPreferenceController(
|
||||
mContext, null, mock(Fragment.class), mBackend);
|
||||
mController.mPreferenceCategory = mPreferenceCategory;
|
||||
mController.mApplicationsState = mApplicationState;
|
||||
mController.mPrefContext = mContext;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAvailable() {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAppList() {
|
||||
// GIVEN there's an app with bypassing channels, app without any channels, and then an app
|
||||
// with notification channels but none that can bypass DND
|
||||
ApplicationsState.AppEntry appWithBypassingChannels =
|
||||
mock(ApplicationsState.AppEntry.class);
|
||||
appWithBypassingChannels.info = new ApplicationInfo();
|
||||
appWithBypassingChannels.info.packageName = "appWithBypassingChannels";
|
||||
appWithBypassingChannels.info.uid = 0;
|
||||
when(mBackend.getNotificationChannelsBypassingDnd(
|
||||
appWithBypassingChannels.info.packageName,
|
||||
appWithBypassingChannels.info.uid))
|
||||
.thenReturn(new ParceledListSlice<>(
|
||||
Arrays.asList(mock(NotificationChannel.class))));
|
||||
when(mBackend.getChannelCount(
|
||||
appWithBypassingChannels.info.packageName,
|
||||
appWithBypassingChannels.info.uid))
|
||||
.thenReturn(5);
|
||||
|
||||
ApplicationsState.AppEntry appWithoutChannels = mock(ApplicationsState.AppEntry.class);
|
||||
appWithoutChannels.info = new ApplicationInfo();
|
||||
appWithoutChannels.info.packageName = "appWithoutChannels";
|
||||
appWithoutChannels.info.uid = 0;
|
||||
when(mBackend.getChannelCount(
|
||||
appWithoutChannels.info.packageName,
|
||||
appWithoutChannels.info.uid))
|
||||
.thenReturn(0);
|
||||
when(mBackend.getNotificationChannelsBypassingDnd(
|
||||
appWithoutChannels.info.packageName,
|
||||
appWithoutChannels.info.uid))
|
||||
.thenReturn(new ParceledListSlice<>(new ArrayList<>()));
|
||||
|
||||
ApplicationsState.AppEntry appWithChannelsNoneBypassing =
|
||||
mock(ApplicationsState.AppEntry.class);
|
||||
appWithChannelsNoneBypassing.info = new ApplicationInfo();
|
||||
appWithChannelsNoneBypassing.info.packageName = "appWithChannelsNoneBypassing";
|
||||
appWithChannelsNoneBypassing.info.uid = 0;
|
||||
when(mBackend.getChannelCount(
|
||||
appWithChannelsNoneBypassing.info.packageName,
|
||||
appWithChannelsNoneBypassing.info.uid))
|
||||
.thenReturn(5);
|
||||
when(mBackend.getNotificationChannelsBypassingDnd(
|
||||
appWithChannelsNoneBypassing.info.packageName,
|
||||
appWithChannelsNoneBypassing.info.uid))
|
||||
.thenReturn(new ParceledListSlice<>(new ArrayList<>()));
|
||||
|
||||
List<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
|
||||
appEntries.add(appWithBypassingChannels);
|
||||
appEntries.add(appWithoutChannels);
|
||||
appEntries.add(appWithChannelsNoneBypassing);
|
||||
|
||||
// WHEN the controller updates the app list with the app entries
|
||||
mController.updateAppList(appEntries);
|
||||
|
||||
// THEN only the appWithChannelsNoneBypassing makes it to the app list
|
||||
ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
|
||||
verify(mPreferenceCategory).addPreference(prefCaptor.capture());
|
||||
|
||||
Preference pref = prefCaptor.getValue();
|
||||
assertThat(pref.getKey()).isEqualTo(
|
||||
ZenModeAllBypassingAppsPreferenceController.getKey(
|
||||
appWithChannelsNoneBypassing.info.packageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAppList_nullApps() {
|
||||
mController.updateAppList(null);
|
||||
verify(mPreferenceCategory, never()).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAppList_emptyAppList() {
|
||||
// WHEN there are no apps
|
||||
mController.updateAppList(new ArrayList<>());
|
||||
|
||||
// THEN only the appWithChannelsNoneBypassing makes it to the app list
|
||||
ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
|
||||
verify(mPreferenceCategory).addPreference(prefCaptor.capture());
|
||||
|
||||
Preference pref = prefCaptor.getValue();
|
||||
assertThat(pref.getKey()).isEqualTo(
|
||||
ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS);
|
||||
}
|
||||
}
|
@@ -19,6 +19,8 @@ package com.android.settings.notification.zen;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -30,25 +32,25 @@ import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settings.notification.zen.ZenModeAllBypassingAppsPreferenceController;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ZenModeAllBypassingAppsPreferenceControllerTest {
|
||||
private ZenModeAllBypassingAppsPreferenceController mController;
|
||||
@@ -57,7 +59,7 @@ public class ZenModeAllBypassingAppsPreferenceControllerTest {
|
||||
@Mock
|
||||
private NotificationBackend mBackend;
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
@Mock
|
||||
private ApplicationsState mApplicationState;
|
||||
|
||||
@@ -67,11 +69,10 @@ public class ZenModeAllBypassingAppsPreferenceControllerTest {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
mController = new ZenModeAllBypassingAppsPreferenceController(
|
||||
mContext, null, mock(Fragment.class));
|
||||
mController.mPreferenceScreen = mPreferenceScreen;
|
||||
mContext, null, mock(Fragment.class), mBackend);
|
||||
mController.mPreferenceCategory = mPreferenceCategory;
|
||||
mController.mApplicationsState = mApplicationState;
|
||||
mController.mPrefContext = mContext;
|
||||
ReflectionHelpers.setField(mController, "mNotificationBackend", mBackend);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,36 +81,49 @@ public class ZenModeAllBypassingAppsPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNotificationChannelList() {
|
||||
ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
|
||||
entry.info = new ApplicationInfo();
|
||||
entry.info.packageName = "test";
|
||||
entry.info.uid = 0;
|
||||
public void testUpdateAppList() {
|
||||
// WHEN there's two apps with notification channels that bypass DND
|
||||
ApplicationsState.AppEntry entry1 = mock(ApplicationsState.AppEntry.class);
|
||||
entry1.info = new ApplicationInfo();
|
||||
entry1.info.packageName = "test";
|
||||
entry1.info.uid = 0;
|
||||
|
||||
ApplicationsState.AppEntry entry2 = mock(ApplicationsState.AppEntry.class);
|
||||
entry2.info = new ApplicationInfo();
|
||||
entry2.info.packageName = "test2";
|
||||
entry2.info.uid = 0;
|
||||
|
||||
List<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
|
||||
appEntries.add(entry);
|
||||
|
||||
appEntries.add(entry1);
|
||||
appEntries.add(entry2);
|
||||
List<NotificationChannel> channelsBypassing = new ArrayList<>();
|
||||
channelsBypassing.add(mock(NotificationChannel.class));
|
||||
channelsBypassing.add(mock(NotificationChannel.class));
|
||||
channelsBypassing.add(mock(NotificationChannel.class));
|
||||
when(mBackend.getNotificationChannelsBypassingDnd(anyString(),
|
||||
anyInt())).thenReturn(new ParceledListSlice<>(channelsBypassing));
|
||||
|
||||
when(mBackend.getNotificationChannelsBypassingDnd(entry.info.packageName,
|
||||
entry.info.uid)).thenReturn(new ParceledListSlice<>(channelsBypassing));
|
||||
|
||||
mController.updateNotificationChannelList(appEntries);
|
||||
verify(mPreferenceScreen, times(3)).addPreference(any());
|
||||
// THEN there's are two preferences
|
||||
mController.updateAppList(appEntries);
|
||||
verify(mPreferenceCategory, times(2)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNotificationChannelList_nullChannels() {
|
||||
mController.updateNotificationChannelList(null);
|
||||
verify(mPreferenceScreen, never()).addPreference(any());
|
||||
public void testUpdateAppList_nullApps() {
|
||||
mController.updateAppList(null);
|
||||
verify(mPreferenceCategory, never()).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNotificationChannelList_emptyChannelsList() {
|
||||
mController.updateNotificationChannelList(new ArrayList<>());
|
||||
verify(mPreferenceScreen, never()).addPreference(any());
|
||||
public void testUpdateAppList_emptyAppList() {
|
||||
// WHEN there are no apps
|
||||
mController.updateAppList(new ArrayList<>());
|
||||
|
||||
// THEN only the appWithChannelsNoneBypassing makes it to the app list
|
||||
ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
|
||||
verify(mPreferenceCategory).addPreference(prefCaptor.capture());
|
||||
|
||||
Preference pref = prefCaptor.getValue();
|
||||
assertThat(pref.getKey()).isEqualTo(
|
||||
ZenModeAllBypassingAppsPreferenceController.KEY_NO_APPS);
|
||||
}
|
||||
}
|
||||
|
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* 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.zen;
|
||||
|
||||
import static android.provider.Settings.Global.ZEN_MODE;
|
||||
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
|
||||
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
|
||||
import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ZenModePriorityCallsPreferenceControllerTest {
|
||||
|
||||
private ZenModePriorityCallsPreferenceController mController;
|
||||
|
||||
@Mock
|
||||
private ZenModeBackend mBackend;
|
||||
@Mock
|
||||
private NotificationManager mNotificationManager;
|
||||
@Mock
|
||||
private ListPreference mockPref;
|
||||
@Mock
|
||||
private NotificationManager.Policy mPolicy;
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
private ContentResolver mContentResolver;
|
||||
private Context mContext;
|
||||
|
||||
/**
|
||||
* Array Values Key
|
||||
* 0: anyone
|
||||
* 1: contacts
|
||||
* 2: starred
|
||||
* 3: none
|
||||
*/
|
||||
private String[] mValues;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
ShadowApplication shadowApplication = ShadowApplication.getInstance();
|
||||
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mValues = mContext.getResources().getStringArray(R.array.zen_mode_contacts_values);
|
||||
mContentResolver = RuntimeEnvironment.application.getContentResolver();
|
||||
when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
|
||||
|
||||
when(mBackend.getPriorityCallSenders())
|
||||
.thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
|
||||
when(mBackend.getAlarmsTotalSilencePeopleSummary(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS)).thenCallRealMethod();
|
||||
when(mBackend.getContactsSummary(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
|
||||
.thenCallRealMethod();
|
||||
|
||||
mController = new ZenModePriorityCallsPreferenceController(mContext, mock(Lifecycle.class));
|
||||
ReflectionHelpers.setField(mController, "mBackend", mBackend);
|
||||
|
||||
when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(mockPref);
|
||||
mController.displayPreference(mPreferenceScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_TotalSilence() {
|
||||
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
|
||||
|
||||
when(mBackend.isPriorityCategoryEnabled(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
|
||||
.thenReturn(false);
|
||||
final ListPreference mockPref = mock(ListPreference.class);
|
||||
mController.updateState(mockPref);
|
||||
|
||||
verify(mockPref).setEnabled(false);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none_calls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_AlarmsOnly() {
|
||||
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
|
||||
|
||||
final ListPreference mockPref = mock(ListPreference.class);
|
||||
mController.updateState(mockPref);
|
||||
|
||||
verify(mockPref).setEnabled(false);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_none_calls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_Priority() {
|
||||
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
|
||||
|
||||
when(mBackend.isPriorityCategoryEnabled(
|
||||
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
|
||||
.thenReturn(true);
|
||||
|
||||
mController.updateState(mockPref);
|
||||
|
||||
verify(mockPref).setEnabled(true);
|
||||
verify(mockPref).setSummary(R.string.zen_mode_from_starred);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_setSelectedContacts_any() {
|
||||
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
|
||||
when(mBackend.getPriorityCallSenders()).thenReturn(
|
||||
NotificationManager.Policy.PRIORITY_SENDERS_ANY);
|
||||
mController.updateState(mockPref);
|
||||
verify(mockPref).setValue(mValues[mController.getIndexOfSendersValue(
|
||||
ZenModeBackend.ZEN_MODE_FROM_ANYONE)]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_setSelectedContacts_none() {
|
||||
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
|
||||
when(mBackend.getPriorityCallSenders()).thenReturn(ZenModeBackend.SOURCE_NONE);
|
||||
mController.updateState(mockPref);
|
||||
verify(mockPref).setValue(mValues[mController.getIndexOfSendersValue(
|
||||
ZenModeBackend.ZEN_MODE_FROM_NONE)]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_setSelectedContacts_starred() {
|
||||
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
|
||||
when(mBackend.getPriorityCallSenders()).thenReturn(
|
||||
NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
|
||||
mController.updateState(mockPref);
|
||||
verify(mockPref).setValue(mValues[mController.getIndexOfSendersValue(
|
||||
ZenModeBackend.ZEN_MODE_FROM_STARRED)]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_setSelectedContacts_contacts() {
|
||||
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
|
||||
when(mBackend.getPriorityCallSenders()).thenReturn(
|
||||
NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS);
|
||||
mController.updateState(mockPref);
|
||||
verify(mockPref).setValue(mValues[mController.getIndexOfSendersValue(
|
||||
ZenModeBackend.ZEN_MODE_FROM_CONTACTS)]);
|
||||
}
|
||||
}
|
@@ -79,13 +79,13 @@ public class ZenModeSettingsTest {
|
||||
public void testGetCallsSettingSummary_contacts() {
|
||||
Policy policy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS | Policy.PRIORITY_CATEGORY_CALLS,
|
||||
Policy.PRIORITY_SENDERS_CONTACTS, 0, 0);
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("Allow from contacts");
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("Contacts");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCallsSettingSummary_repeatCallers() {
|
||||
Policy policy = new Policy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0);
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("Allow from repeat callers");
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("Repeat callers");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,7 +94,7 @@ public class ZenModeSettingsTest {
|
||||
Policy.PRIORITY_CATEGORY_REPEAT_CALLERS | Policy.PRIORITY_CATEGORY_CALLS,
|
||||
Policy.PRIORITY_SENDERS_STARRED, 0, 0);
|
||||
assertThat(mBuilder.getCallsSettingSummary(policy))
|
||||
.isEqualTo("Allow from starred contacts and repeat callers");
|
||||
.isEqualTo("Starred contacts and repeat callers");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user