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,59 +16,23 @@
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.Bundle;
import android.os.AsyncTask;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
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.RingtonePreference;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import java.util.ArrayList;
import java.util.List;
public class ChannelNotificationSettings extends NotificationSettingsBase {
private static final String TAG = "ChannelSettings";
private static final String KEY_LIGHTS = "lights";
private static final String KEY_VIBRATE = "vibrate";
private static final String KEY_RINGTONE = "ringtone";
private static final String KEY_IMPORTANCE = "importance";
private static final String KEY_ADVANCED = "advanced";
private Preference mImportance;
private RestrictedSwitchPreference mLights;
private RestrictedSwitchPreference mVibrate;
private NotificationSoundPreference mRingtone;
private FooterPreference mFooter;
private NotificationChannelGroup mChannelGroup;
private EntityHeaderController mHeaderPref;
private PreferenceGroup mAdvanced;
@Override
public int getMetricsCategory() {
return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
@@ -83,308 +47,52 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
return;
}
if (getPreferenceScreen() != null) {
getPreferenceScreen().removeAll();
for (NotificationPreferenceController controller : mControllers) {
controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
controller.displayPreference(getPreferenceScreen());
}
addPreferencesFromResource(R.xml.notification_settings);
setupBlock();
addHeaderPref();
addAppLinkPref();
addFooterPref();
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
populateDefaultChannelPrefs();
mShowLegacyChannelConfig = true;
} else {
populateUpgradedChannelPrefs();
if (mChannel.getGroup() != null) {
mChannelGroup = mBackend.getGroup(mPkg, mUid, mChannel.getGroup());
if (mChannelGroup != null) {
setChannelGroupLabel(mChannelGroup.getName());
}
}
}
updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
}
private void populateUpgradedChannelPrefs() {
addPreferencesFromResource(R.xml.upgraded_channel_notification_settings);
setupBadge();
setupPriorityPref(mChannel.canBypassDnd());
setupVisOverridePref(mChannel.getLockscreenVisibility());
setupLights();
setupVibrate();
setupRingtone();
setupImportance();
mAdvanced = (PreferenceGroup) findPreference(KEY_ADVANCED);
}
private void addHeaderPref() {
ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<>();
rows.put(mAppRow.pkg, mAppRow);
collectConfigActivities(rows);
final Activity activity = getActivity();
mHeaderPref = EntityHeaderController
.newInstance(activity, this /* fragment */, null /* header */)
.setRecyclerView(getListView(), getLifecycle());
final Preference pref = mHeaderPref
.setIcon(mAppRow.icon)
.setLabel(mChannel.getName())
.setSummary(mAppRow.label)
.setPackageName(mAppRow.pkg)
.setUid(mAppRow.uid)
.setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
EntityHeaderController.ActionType.ACTION_NONE)
.setHasAppInfoLink(true)
.done(activity, getPrefContext());
getPreferenceScreen().addPreference(pref);
}
private void setChannelGroupLabel(CharSequence groupName) {
final SpannableStringBuilder summary = new SpannableStringBuilder();
BidiFormatter bidi = BidiFormatter.getInstance();
summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
if (groupName != null) {
summary.append(bidi.unicodeWrap(mContext.getText(
R.string.notification_header_divider_symbol_with_spaces)));
summary.append(bidi.unicodeWrap(groupName.toString()));
}
final Activity activity = getActivity();
mHeaderPref.setSummary(summary.toString());
mHeaderPref.done(activity, getPrefContext());
}
private void addFooterPref() {
if (!TextUtils.isEmpty(mChannel.getDescription())) {
FooterPreference descPref = new FooterPreference(getPrefContext());
descPref.setOrder(ORDER_LAST);
descPref.setSelectable(false);
descPref.setTitle(mChannel.getDescription());
getPreferenceScreen().addPreference(descPref);
}
}
protected void setupBadge() {
mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
mBadge.setEnabled(mAppRow.showBadge);
mBadge.setChecked(mChannel.canShowBadge());
mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean value = (Boolean) newValue;
mChannel.setShowBadge(value);
mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
mBackend.updateChannel(mPkg, mUid, mChannel);
return true;
}
});
}
private void setupLights() {
mLights = (RestrictedSwitchPreference) findPreference(KEY_LIGHTS);
mLights.setDisabledByAdmin(mSuspendedAppsAdmin);
mLights.setChecked(mChannel.shouldShowLights());
mLights.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean lights = (Boolean) newValue;
mChannel.enableLights(lights);
mChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
mBackend.updateChannel(mPkg, mUid, mChannel);
return true;
}
});
}
private void setupVibrate() {
mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
mVibrate.setEnabled(!mVibrate.isDisabledByAdmin() && isChannelConfigurable(mChannel));
mVibrate.setChecked(mChannel.shouldVibrate());
mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean vibrate = (Boolean) newValue;
mChannel.enableVibration(vibrate);
mChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
mBackend.updateChannel(mPkg, mUid, mChannel);
return true;
}
});
}
private void setupRingtone() {
mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
mRingtone.setRingtone(mChannel.getSound());
mRingtone.setEnabled(isChannelConfigurable(mChannel));
mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
mChannel.setSound((Uri) newValue, mChannel.getAudioAttributes());
mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
mBackend.updateChannel(mPkg, mUid, mChannel);
return false;
}
});
}
private 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(mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
int importance = 0;
if (mShowLegacyChannelConfig) {
importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
} else {
importance = isChecked ? IMPORTANCE_LOW : IMPORTANCE_NONE;
mImportance.setSummary(getImportanceSummary(importance));
}
mChannel.setImportance(importance);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
mBackend.updateChannel(mPkg, mUid, mChannel);
updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
}
});
mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
mBlockBar.setOrder(ORDER_FIRST);
mBlockBar.setKey(KEY_BLOCK);
getPreferenceScreen().addPreference(mBlockBar);
if (!isChannelBlockable(mChannel)) {
setVisible(mBlockBar, false);
}
setupBlockDesc(R.string.channel_notifications_off_desc);
}
private void setupImportance() {
mImportance = findPreference(KEY_IMPORTANCE);
Bundle channelArgs = new Bundle();
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
mImportance.setEnabled(mSuspendedAppsAdmin == null && isChannelConfigurable(mChannel));
// Set up intent to show importance selection only if this setting is enabled.
if (mImportance.isEnabled()) {
Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
ChannelImportanceSettings.class.getName(),
channelArgs, null, R.string.notification_importance_title, null,
false, getMetricsCategory());
mImportance.setIntent(channelIntent);
}
mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
}
private String getImportanceSummary(int importance) {
String title;
String summary = null;
switch (importance) {
case IMPORTANCE_UNSPECIFIED:
title = getContext().getString(R.string.notification_importance_unspecified);
break;
case NotificationManager.IMPORTANCE_MIN:
title = getContext().getString(R.string.notification_importance_min_title);
summary = getContext().getString(R.string.notification_importance_min);
break;
case NotificationManager.IMPORTANCE_LOW:
title = getContext().getString(R.string.notification_importance_low_title);
summary = getContext().getString(R.string.notification_importance_low);
break;
case NotificationManager.IMPORTANCE_DEFAULT:
title = getContext().getString(R.string.notification_importance_default_title);
if (hasValidSound(mChannel)) {
summary = getContext().getString(R.string.notification_importance_default);
} else {
summary = getContext().getString(R.string.notification_importance_low);
}
break;
case NotificationManager.IMPORTANCE_HIGH:
case NotificationManager.IMPORTANCE_MAX:
title = getContext().getString(R.string.notification_importance_high_title);
if (hasValidSound(mChannel)) {
summary = getContext().getString(R.string.notification_importance_high);
} else {
summary = getContext().getString(R.string.notification_importance_high_silent);
}
break;
default:
return "";
}
if (summary != null) {
return getContext().getString(R.string.notification_importance_divider, title, summary);
} else {
return title;
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference instanceof RingtonePreference) {
mRingtone.onPrepareRingtonePickerIntent(mRingtone.getIntent());
startActivityForResult(preference.getIntent(), 200);
return true;
}
return super.onPreferenceTreeClick(preference);
updatePreferenceStates();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mRingtone != null) {
mRingtone.onActivityResult(requestCode, resultCode, data);
}
if (mChannel != null) {
mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
for (NotificationPreferenceController controller : mControllers) {
if (controller instanceof PreferenceManager.OnActivityResultListener) {
((PreferenceManager.OnActivityResultListener) controller)
.onActivityResult(requestCode, resultCode, data);
}
}
}
boolean canPulseLight() {
if (!getResources()
.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
return false;
}
return Settings.System.getInt(getContentResolver(),
Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
@Override
protected String getLogTag() {
return TAG;
}
void updateDependents(boolean banned) {
PreferenceGroup parent;
if (mShowLegacyChannelConfig) {
parent = getPreferenceScreen();
setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
} else {
setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
setVisible(mAdvanced, mLights, checkCanBeVisible(
NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
setVisible(mRingtone, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
parent = mAdvanced;
}
setVisible(parent, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
setVisible(parent, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
|| (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
&& mDndVisualEffectsSuppressed));
setVisible(parent, mVisibilityOverride, isLockScreenSecure()
&&checkCanBeVisible(NotificationManager.IMPORTANCE_LOW));
setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE);
if (mAppLink != null) {
setVisible(mAppLink, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
}
if (mFooter != null) {
setVisible(mFooter, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.channel_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 ImportancePreferenceController(context));
mControllers.add(new AllowSoundPreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,
mImportanceListener, mBackend));
mControllers.add(new VibrationPreferenceController(context, mBackend));
mControllers.add(new AppLinkPreferenceController(context));
mControllers.add(new DescriptionPreferenceController(context));
mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
mBackend));
mControllers.add(new LightsPreferenceController(context, mBackend));
mControllers.add(new BadgePreferenceController(context, mBackend));
mControllers.add(new DndPreferenceController(context, getLifecycle(), mBackend));
mControllers.add(new NotificationsOffPreferenceController(context));
return new ArrayList<>(mControllers);
}
}