DND Bypassing Apps redesign
- Add link in DND Conversations Page to the overall conversations list Settings page - Add custom_rule xml pages for custom schedule rule settings for messages and calls (so the UI is the same as before the message/calls redesign) - Change app exceptions to display apps with subtext indicating which notitfication channels are allowed to bypass dnd (previously, would display each channel individually) - Add individual AppBypassDnd channel pages where users can decide which channels will bypass DND for an app on a single page (AppChannelsBypassingDndSettings) - Only remove dnd bypassing apps preferences from the preference list if the list changed, else just update the preference itself to avoid the list from flashing Test: make RunSettingsRoboTests7 Bug: 151845457 Change-Id: If12d8921e1405aefb1066acc2ef5c55d216fe47a
This commit is contained in:
@@ -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) {
|
||||
|
Reference in New Issue
Block a user