PreferenceControllers are the way of the future.

Migrates Notification settings (app level, group level, and channel
level) into PreferenceControllers (and most importantly:
PreferenceControllerTest)

Note: this removes the 'Advanced' preference group, but does
not yet use the standard system 'Advanced' grouping as it does
not currently support our use case (where we don't know how many
fields to show outside of 'Advanced' until onResume() and also
where we need fields to show below the 'Advanced' group).

Test: make RunSettingsRoboTests
Change-Id: Iddd1b4771922db322e5f73562e9d63ed077c5396
This commit is contained in:
Julia Reynolds
2017-10-13 15:12:07 -04:00
parent 187ff21a7c
commit ac3f7e80dc
44 changed files with 5332 additions and 1024 deletions

View File

@@ -16,12 +16,10 @@
package com.android.settings.notification;
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
@@ -29,43 +27,29 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.widget.MasterSwitchPreference;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
/** These settings are per app, so should not be returned in global search results. */
public class AppNotificationSettings extends NotificationSettingsBase {
private static final String TAG = "AppNotificationSettings";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static String KEY_GENERAL_CATEGORY = "categories";
private static String KEY_DELETED = "deleted";
private List<NotificationChannelGroup> mChannelGroupList;
private List<PreferenceCategory> mChannelGroups = new ArrayList();
private FooterPreference mDeletedChannels;
@Override
public int getMetricsCategory() {
@@ -84,24 +68,13 @@ public class AppNotificationSettings extends NotificationSettingsBase {
if (getPreferenceScreen() != null) {
getPreferenceScreen().removeAll();
mChannelGroups.clear();
mDeletedChannels = null;
mShowLegacyChannelConfig = false;
mDynamicPreferences.clear();
}
addPreferencesFromResource(R.xml.notification_settings);
getPreferenceScreen().setOrderingAsAdded(true);
setupBlock();
addHeaderPref();
mShowLegacyChannelConfig = mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid);
if (mShowLegacyChannelConfig) {
mChannel = mBackend.getChannel(
mAppRow.pkg, mAppRow.uid, NotificationChannel.DEFAULT_CHANNEL_ID);
populateDefaultChannelPrefs();
addPreferencesFromResource(R.xml.channel_notification_settings);
} else {
addPreferencesFromResource(R.xml.upgraded_app_notification_settings);
setupBadge();
addPreferencesFromResource(R.xml.app_notification_settings);
// Load channel settings
new AsyncTask<Void, Void, Void>() {
@Override
@@ -117,41 +90,59 @@ public class AppNotificationSettings extends NotificationSettingsBase {
return;
}
populateList();
addAppLinkPref();
}
}.execute();
}
getPreferenceScreen().setOrderingAsAdded(true);
updateDependents(mAppRow.banned);
for (NotificationPreferenceController controller : mControllers) {
controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
controller.displayPreference(getPreferenceScreen());
}
updatePreferenceStates();
}
private void addHeaderPref() {
ArrayMap<String, AppRow> rows = new ArrayMap<>();
rows.put(mAppRow.pkg, mAppRow);
collectConfigActivities(rows);
final Activity activity = getActivity();
final Preference pref = EntityHeaderController
.newInstance(activity, this /* fragment */, null /* header */)
.setRecyclerView(getListView(), getLifecycle())
.setIcon(mAppRow.icon)
.setLabel(mAppRow.label)
.setPackageName(mAppRow.pkg)
.setUid(mAppRow.uid)
.setHasAppInfoLink(true)
.setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
.done(activity, getPrefContext());
pref.setKey(KEY_HEADER);
getPreferenceScreen().addPreference(pref);
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.notification_settings;
}
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
mControllers.add(new HeaderPreferenceController(context, this));
mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
mControllers.add(new BadgePreferenceController(context, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new ImportancePreferenceController(context));
mControllers.add(new SoundPreferenceController(context, this,
mImportanceListener, mBackend));
mControllers.add(new LightsPreferenceController(context, mBackend));
mControllers.add(new VibrationPreferenceController(context, mBackend));
mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
mBackend));
mControllers.add(new DndPreferenceController(context, getLifecycle(), mBackend));
mControllers.add(new AppLinkPreferenceController(context));
mControllers.add(new DescriptionPreferenceController(context));
mControllers.add(new NotificationsOffPreferenceController(context));
mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
return new ArrayList<>(mControllers);
}
private void populateList() {
if (!mChannelGroups.isEmpty()) {
if (!mDynamicPreferences.isEmpty()) {
// If there's anything in mChannelGroups, we've called populateChannelList twice.
// Clear out existing channels and log.
Log.w(TAG, "Notification channel group posted twice to settings - old size " +
mChannelGroups.size() + ", new size " + mChannelGroupList.size());
for (Preference p : mChannelGroups) {
mDynamicPreferences.size() + ", new size " + mChannelGroupList.size());
for (Preference p : mDynamicPreferences) {
getPreferenceScreen().removePreference(p);
}
}
@@ -160,7 +151,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
groupCategory.setTitle(R.string.notification_channels);
groupCategory.setKey(KEY_GENERAL_CATEGORY);
getPreferenceScreen().addPreference(groupCategory);
mChannelGroups.add(groupCategory);
mDynamicPreferences.add(groupCategory);
Preference empty = new Preference(getPrefContext());
empty.setTitle(R.string.no_channels);
@@ -168,20 +159,8 @@ public class AppNotificationSettings extends NotificationSettingsBase {
groupCategory.addPreference(empty);
} else {
populateGroupList();
int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
if (deletedChannelCount > 0 &&
getPreferenceScreen().findPreference(KEY_DELETED) == null) {
mDeletedChannels = new FooterPreference(getPrefContext());
mDeletedChannels.setSelectable(false);
mDeletedChannels.setTitle(getResources().getQuantityString(
R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
mDeletedChannels.setEnabled(false);
mDeletedChannels.setKey(KEY_DELETED);
mDeletedChannels.setOrder(ORDER_LAST);
getPreferenceScreen().addPreference(mDeletedChannels);
}
mImportanceListener.onImportanceChanged();
}
updateDependents(mAppRow.banned);
}
private void populateGroupList() {
@@ -190,7 +169,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
groupCategory.setKey(KEY_GENERAL_CATEGORY);
groupCategory.setOrderingAsAdded(true);
getPreferenceScreen().addPreference(groupCategory);
mChannelGroups.add(groupCategory);
mDynamicPreferences.add(groupCategory);
for (NotificationChannelGroup group : mChannelGroupList) {
final List<NotificationChannel> channels = group.getChannels();
int N = channels.size();
@@ -240,91 +219,6 @@ public class AppNotificationSettings extends NotificationSettingsBase {
parent.addPreference(groupPref);
}
void setupBadge() {
mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
if (mChannel == null) {
mBadge.setChecked(mAppRow.showBadge);
} else {
mBadge.setChecked(mChannel.canShowBadge());
}
mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean value = (Boolean) newValue;
if (mChannel == null) {
mBackend.setShowBadge(mPkg, mUid, value);
} else {
mChannel.setShowBadge(value);
mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
mBackend.updateChannel(mPkg, mUid, mChannel);
}
return true;
}
});
}
protected void setupBlock() {
View switchBarContainer = LayoutInflater.from(
getPrefContext()).inflate(R.layout.styled_switch_bar, null);
mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
mSwitchBar.show();
mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
mSwitchBar.setChecked(!mAppRow.banned);
mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
if (mShowLegacyChannelConfig && mChannel != null) {
final int importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
mChannel.setImportance(importance);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
mBackend.updateChannel(mPkg, mUid, mChannel);
}
mBackend.setNotificationsEnabledForPackage(mPkgInfo.packageName, mUid, isChecked);
mAppRow.banned = true;
updateDependents(!isChecked);
}
});
mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
mBlockBar.setOrder(ORDER_FIRST);
mBlockBar.setKey(KEY_BLOCK);
getPreferenceScreen().addPreference(mBlockBar);
if (mAppRow.systemApp && !mAppRow.banned) {
setVisible(mBlockBar, false);
}
setupBlockDesc(R.string.app_notifications_off_desc);
}
protected void updateDependents(boolean banned) {
for (PreferenceCategory category : mChannelGroups) {
setVisible(category, !banned);
}
if (mDeletedChannels != null) {
setVisible(mDeletedChannels, !banned);
}
setVisible(mBlockedDesc, banned);
setVisible(mBadge, !banned);
if (mShowLegacyChannelConfig) {
setVisible(mImportanceToggle, !banned);
setVisible(mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
|| (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
&& mDndVisualEffectsSuppressed));
setVisible(mVisibilityOverride, !banned &&
checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && isLockScreenSecure());
}
if (mAppLink != null) {
setVisible(mAppLink, !banned);
}
if (mAppRow.systemApp && !mAppRow.banned) {
setVisible(mBlockBar, false);
}
}
private Comparator<NotificationChannelGroup> mChannelGroupComparator =
new Comparator<NotificationChannelGroup>() {