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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user