Add notification channel settings.

If a user has set a preference at an app wide level, it cannot be
undone at a channel level, but channels can be more restrictive.

Tests: changing settings for apps and channels.

Change-Id: I7907b8179ffc24d68197c917f182e63d8e1ff7a7
This commit is contained in:
Julia Reynolds
2016-08-23 08:51:04 -04:00
parent b349c0dc97
commit b5ec2d71a2
9 changed files with 568 additions and 72 deletions

View File

@@ -6274,12 +6274,21 @@
<!-- [CHAR LIMIT=NONE] Text when loading app list in notification settings --> <!-- [CHAR LIMIT=NONE] Text when loading app list in notification settings -->
<string name="loading_notification_apps">Loading apps...</string> <string name="loading_notification_apps">Loading apps...</string>
<!-- [CHAR LIMIT=NONE] App notification settings: channels title -->
<string name="notification_channels">Channels</string>
<!-- [CHAR LIMIT=NONE] App notification settings: Block option title --> <!-- [CHAR LIMIT=NONE] App notification settings: Block option title -->
<string name="app_notification_block_title">Block all</string> <string name="app_notification_block_title">Block all</string>
<!-- [CHAR LIMIT=NONE] App notification settings: Block option description--> <!-- [CHAR LIMIT=NONE] App notification settings: Block option description-->
<string name="app_notification_block_summary">Never show notifications from this app</string> <string name="app_notification_block_summary">Never show notifications from this app</string>
<!-- [CHAR LIMIT=NONE] Channel notification settings: Block option title -->
<string name="channel_notification_block_title">Block all</string>
<!-- [CHAR LIMIT=NONE] Channel notification settings: Block option description-->
<string name="channel_notification_block_summary">Never show notifications from this channel</string>
<!-- [CHAR LIMIT=NONE] App notification settings: Override DND option title --> <!-- [CHAR LIMIT=NONE] App notification settings: Override DND option title -->
<string name="app_notification_override_dnd_title">Override Do Not Disturb</string> <string name="app_notification_override_dnd_title">Override Do Not Disturb</string>
@@ -6304,6 +6313,12 @@
<!-- [CHAR LIMIT=150] App notification settings: App notifications Importance title --> <!-- [CHAR LIMIT=150] App notification settings: App notifications Importance title -->
<string name="app_notification_importance_title">Importance</string> <string name="app_notification_importance_title">Importance</string>
<!-- [CHAR LIMIT=180] Notification settings: lights -->
<string name="notification_show_lights_title">Always pulse notification light</string>
<!-- [CHAR LIMIT=180] Notification settings: vibration -->
<string name="notification_vibrate_title">Always vibrate</string>
<!-- [CHAR LIMIT=40] Zen mode settings: Rule name option and edit dialog title --> <!-- [CHAR LIMIT=40] Zen mode settings: Rule name option and edit dialog title -->
<string name="zen_mode_rule_name">Rule name</string> <string name="zen_mode_rule_name">Rule name</string>

View File

@@ -55,4 +55,9 @@
android:order="6" android:order="6"
settings:useAdditionalSummary="true" /> settings:useAdditionalSummary="true" />
<PreferenceCategory
android:key="channels"
android:title="@string/notification_channels"
android:order="7" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" >
<!-- Importance -->
<!-- Block -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="block"
android:title="@string/channel_notification_block_title"
android:summary="@string/app_notification_block_summary"
android:order="2"
settings:useAdditionalSummary="true"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<!-- Silent -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="silent"
android:title="@string/show_silently"
android:summary="@string/show_silently_summary"
android:order="3"
settings:useAdditionalSummary="true" />
<!-- Slider -->
<com.android.settings.notification.ImportanceSeekBarPreference
android:key="importance"
android:title="@string/notification_importance_title"
android:order="4"/>
<!-- Visibility Override -->
<com.android.settings.notification.RestrictedDropDownPreference
android:key="visibility_override"
android:title="@string/app_notification_visibility_override_title"
android:order="5" />
<!-- Bypass DND -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="bypass_dnd"
android:title="@string/app_notification_override_dnd_title"
android:summary="@string/app_notification_override_dnd_summary"
android:order="6"
settings:useAdditionalSummary="true" />
<!-- Lights -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="lights"
android:title="@string/notification_show_lights_title"
android:order="7"
settings:useAdditionalSummary="true" />
<!-- Vibration -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="vibrate"
android:title="@string/notification_vibrate_title"
android:order="8"
settings:useAdditionalSummary="true" />
<!-- Default ringtone -->
<com.android.settings.notification.DefaultNotificationTonePreference
android:key="ringtone"
android:title="@string/notification_ringtone_title"
android:dialogTitle="@string/notification_ringtone_title"
android:order="9"
android:ringtoneType="notification" />
</PreferenceScreen>

View File

@@ -31,7 +31,7 @@ public class DefaultRingtonePreference extends RingtonePreference {
private static final String TAG = "DefaultRingtonePreference"; private static final String TAG = "DefaultRingtonePreference";
private int mUserId; private int mUserId;
private Context mUserContext; protected Context mUserContext;
public DefaultRingtonePreference(Context context, AttributeSet attrs) { public DefaultRingtonePreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);

View File

@@ -17,23 +17,24 @@
package com.android.settings.notification; package com.android.settings.notification;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.support.v7.preference.PreferenceCategory;
import android.service.notification.NotificationListenerService.Ranking;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.AppHeader; import com.android.settings.AppHeader;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.notification.NotificationBackend.AppRow; import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.RestrictedSwitchPreference;
@@ -48,8 +49,9 @@ public class AppNotificationSettings extends NotificationSettingsBase {
= new Intent(Intent.ACTION_MAIN) = new Intent(Intent.ACTION_MAIN)
.addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES); .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES);
private AppRow mAppRow; private static final String KEY_CHANNELS = "channels";
private boolean mDndVisualEffectsSuppressed; private PreferenceCategory mChannels;
List<NotificationChannel> mChannelList;
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
@@ -77,51 +79,52 @@ public class AppNotificationSettings extends NotificationSettingsBase {
KEY_VISIBILITY_OVERRIDE); KEY_VISIBILITY_OVERRIDE);
mBlock = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BLOCK); mBlock = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BLOCK);
mSilent = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_SILENT); mSilent = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_SILENT);
mChannels = (PreferenceCategory) findPreference(KEY_CHANNELS);
if (mPkgInfo != null) { if (mPkgInfo != null) {
mAppRow = mBackend.loadAppRow(mContext, mPm, mPkgInfo);
NotificationManager.Policy policy =
NotificationManager.from(mContext).getNotificationPolicy();
mDndVisualEffectsSuppressed = policy == null ? false : policy.suppressedVisualEffects != 0;
// load settings intent // load settings intent
ArrayMap<String, AppRow> rows = new ArrayMap<String, AppRow>(); ArrayMap<String, AppRow> rows = new ArrayMap<String, AppRow>();
rows.put(mAppRow.pkg, mAppRow); rows.put(mAppRow.pkg, mAppRow);
collectConfigActivities(rows); collectConfigActivities(rows);
mChannelList = mBackend.getChannels(mPkg, mUid).getList();
setupImportancePrefs(mAppRow.systemApp, mAppRow.appImportance, mAppRow.banned); setupImportancePrefs(mAppRow.systemApp, mAppRow.appImportance, mAppRow.banned,
NotificationManager.IMPORTANCE_MAX);
setupPriorityPref(mAppRow.appBypassDnd); setupPriorityPref(mAppRow.appBypassDnd);
setupVisOverridePref(mAppRow.appVisOverride); setupVisOverridePref(mAppRow.appVisOverride);
if (mChannelList.isEmpty()) {
setVisible(mChannels, false);
} else {
int N = mChannelList.size();
for (int i = 0; i < N; i++) {
final NotificationChannel channel = mChannelList.get(i);
RestrictedPreference channelPref = new RestrictedPreference(getPrefContext());
channelPref.setDisabledByAdmin(mSuspendedAppsAdmin);
channelPref.setKey(channel.getId());
channelPref.setTitle(channel.getName());
Bundle channelArgs = new Bundle();
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
channelArgs.putString(ARG_CHANNEL, channel.getId());
Intent topicIntent = Utils.onBuildStartFragmentIntent(getActivity(),
ChannelNotificationSettings.class.getName(),
channelArgs, null, 0, null, false);
channelPref.setIntent(topicIntent);
mChannels.addPreference(channelPref);
}
}
updateDependents(mAppRow.appImportance); updateDependents(mAppRow.appImportance);
} }
} }
@Override @Override
protected void updateDependents(int importance) { protected void updateDependents(int importance) {
LockPatternUtils utils = new LockPatternUtils(getActivity()); super.updateDependents(importance);
boolean lockscreenSecure = utils.isSecure(UserHandle.myUserId()); setVisible(mChannels,
UserInfo parentUser = mUm.getProfileParent(UserHandle.myUserId()); !(mChannelList.isEmpty() || importance == NotificationManager.IMPORTANCE_NONE));
if (parentUser != null){
lockscreenSecure |= utils.isSecure(parentUser.id);
}
if (getPreferenceScreen().findPreference(mBlock.getKey()) != null) {
setVisible(mSilent, checkCanBeVisible(Ranking.IMPORTANCE_MIN, importance));
mSilent.setChecked(importance == Ranking.IMPORTANCE_LOW);
}
setVisible(mPriority, checkCanBeVisible(Ranking.IMPORTANCE_DEFAULT, importance)
|| (checkCanBeVisible(Ranking.IMPORTANCE_LOW, importance)
&& mDndVisualEffectsSuppressed));
setVisible(mVisibilityOverride,
checkCanBeVisible(Ranking.IMPORTANCE_MIN, importance) && lockscreenSecure);
}
protected boolean checkCanBeVisible(int minImportanceVisible, int importance) {
if (importance == Ranking.IMPORTANCE_UNSPECIFIED) {
return true;
}
return importance >= minImportanceVisible;
} }
private List<ResolveInfo> queryNotificationConfigActivities() { private List<ResolveInfo> queryNotificationConfigActivities() {

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2016 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;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.settings.AppHeader;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
import android.app.NotificationManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.Ranking;
import android.support.v7.preference.Preference;
public class ChannelNotificationSettings extends NotificationSettingsBase {
protected static final String KEY_LIGHTS = "lights";
protected static final String KEY_VIBRATE = "vibrate";
protected static final String KEY_RINGTONE = "ringtone";
protected RestrictedSwitchPreference mLights;
protected RestrictedSwitchPreference mVibrate;
protected DefaultNotificationTonePreference mRingtone;
private int mMaxImportance;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mAppRow == null || mChannel == null) return;
AppHeader.createAppHeader(
this, mAppRow.icon, mChannel.getName(), mAppRow.pkg, mAppRow.uid);
}
@Override
public int getMetricsCategory() {
return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.channel_notification_settings);
mImportance = (ImportanceSeekBarPreference) findPreference(KEY_IMPORTANCE);
mPriority =
(RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
mVisibilityOverride =
(RestrictedDropDownPreference) findPreference(KEY_VISIBILITY_OVERRIDE);
mBlock = (RestrictedSwitchPreference) findPreference(KEY_BLOCK);
mSilent = (RestrictedSwitchPreference) findPreference(KEY_SILENT);
mLights = (RestrictedSwitchPreference) findPreference(KEY_LIGHTS);
mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
mRingtone = (DefaultNotificationTonePreference) findPreference(KEY_RINGTONE);
if (mPkgInfo != null) {
setupPriorityPref(mChannel.canBypassDnd());
if (mAppRow.appBypassDnd) {
mPriority.setShouldDisableView(true);
}
setupVisOverridePref(mChannel.getLockscreenVisibility());
if (mAppRow.appVisOverride != Ranking.VISIBILITY_NO_OVERRIDE) {
mVisibilityOverride.setShouldDisableView(true);
}
setupLights();
setupVibrate();
setupRingtone();
mMaxImportance = mAppRow.appImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
? NotificationManager.IMPORTANCE_MAX : mAppRow.appImportance;
setupImportancePrefs(false, mChannel.getImportance(),
mChannel.getImportance() == NotificationManager.IMPORTANCE_NONE,
mMaxImportance);
}
}
@Override
public void onResume() {
super.onResume();
if ((mUid != -1 && getPackageManager().getPackagesForUid(mUid) == null)) {
// App isn't around anymore, must have been removed.
finish();
return;
}
mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
mContext, mPkg, mUserId);
if (mLights != null) {
mLights.setDisabledByAdmin(mSuspendedAppsAdmin);
}
if (mVibrate != null) {
mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
}
}
private void setupLights() {
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.setLights(lights);
mBackend.updateChannel(mPkg, mUid, mChannel);
return true;
}
});
}
private void setupVibrate() {
mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
mVibrate.setChecked(mChannel.shouldVibrate());
mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean vibrate = (Boolean) newValue;
mChannel.setVibration(vibrate);
mBackend.updateChannel(mPkg, mUid, mChannel);
return true;
}
});
}
private void setupRingtone() {
mRingtone.setRingtone(mChannel.getDefaultRingtone());
mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Uri ringtone = Uri.parse((String) newValue);
mRingtone.setRingtone(ringtone);
mChannel.setDefaultRingtone(ringtone);
mBackend.updateChannel(mPkg, mUid, mChannel);
return false;
}
});
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference instanceof RingtonePreference) {
mRingtone.onPrepareRingtonePickerIntent(mRingtone.getIntent());
startActivityForResult(preference.getIntent(), 200);
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mRingtone != null) {
mRingtone.onActivityResult(requestCode, resultCode, data);
}
}
private 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 void updateDependents(int importance) {
if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
importance = mMaxImportance;
}
importance = Math.min(mMaxImportance, importance);
super.updateDependents(importance);
setVisible(mLights, checkCanBeVisible(
NotificationManager.IMPORTANCE_LOW, importance) && canPulseLight());
setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT, importance));
setVisible(mRingtone, checkCanBeVisible(
NotificationManager.IMPORTANCE_DEFAULT, importance));
if (mMaxImportance == NotificationManager.IMPORTANCE_LOW
&& getPreferenceScreen().findPreference(mBlock.getKey()) != null) {
setVisible(mSilent, false);
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2016 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;
import com.android.settings.DefaultRingtonePreference;
import com.android.settings.Utils;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.AttributeSet;
import static android.content.ContentProvider.getUriWithoutUserId;
public class DefaultNotificationTonePreference extends DefaultRingtonePreference {
private Uri mRingtone;
public DefaultNotificationTonePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected Uri onRestoreRingtone() {
return mRingtone;
}
@Override
public void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
mRingtone);
}
public void setRingtone(Uri ringtone) {
mRingtone = ringtone;
updateRingtoneName(mRingtone);
}
private void updateRingtoneName(final Uri uri) {
AsyncTask ringtoneNameTask = new AsyncTask<Object, Void, CharSequence>() {
@Override
protected CharSequence doInBackground(Object... params) {
return Ringtone.getTitle(mUserContext, uri, false /* followSettingsUri */,
true /* allowRemote */);
}
@Override
protected void onPostExecute(CharSequence name) {
setSummary(name);
}
};
ringtoneNameTask.execute();
}
}

View File

@@ -17,11 +17,13 @@ package com.android.settings.notification;
import android.app.INotificationManager; import android.app.INotificationManager;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.UserHandle; import android.os.UserHandle;
@@ -141,6 +143,35 @@ public class NotificationBackend {
} }
} }
public NotificationChannel getChannel(String pkg, int uid, String channelId) {
if (channelId == null) {
return null;
}
try {
return sINM.getNotificationChannelForPackage(pkg, uid, channelId);
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return null;
}
}
public ParceledListSlice<NotificationChannel> getChannels(String pkg, int uid) {
try {
return sINM.getNotificationChannelsForPackage(pkg, uid);
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return ParceledListSlice.emptyList();
}
}
public void updateChannel(String pkg, int uid, NotificationChannel channel) {
try {
sINM.updateNotificationChannelForPackage(pkg, uid, channel);
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
}
}
static class Row { static class Row {
public String section; public String section;
} }
@@ -159,4 +190,8 @@ public class NotificationBackend {
public int appVisOverride; public int appVisOverride;
public boolean lockScreenSecure; public boolean lockScreenSecure;
} }
public static class ChannelRow extends AppRow {
public NotificationChannel channel;
}
} }

View File

@@ -16,6 +16,7 @@
package com.android.settings.notification; package com.android.settings.notification;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppInfoBase;
@@ -23,12 +24,15 @@ import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.RestrictedSwitchPreference;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
@@ -49,6 +53,8 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String TUNER_SETTING = "show_importance_slider"; private static final String TUNER_SETTING = "show_importance_slider";
protected static final String ARG_CHANNEL = "channel";
protected static final String KEY_BYPASS_DND = "bypass_dnd"; protected static final String KEY_BYPASS_DND = "bypass_dnd";
protected static final String KEY_VISIBILITY_OVERRIDE = "visibility_override"; protected static final String KEY_VISIBILITY_OVERRIDE = "visibility_override";
protected static final String KEY_IMPORTANCE = "importance"; protected static final String KEY_IMPORTANCE = "importance";
@@ -71,6 +77,10 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
protected RestrictedSwitchPreference mSilent; protected RestrictedSwitchPreference mSilent;
protected EnforcedAdmin mSuspendedAppsAdmin; protected EnforcedAdmin mSuspendedAppsAdmin;
protected boolean mShowSlider = false; protected boolean mShowSlider = false;
protected boolean mDndVisualEffectsSuppressed;
protected NotificationChannel mChannel;
protected NotificationBackend.AppRow mAppRow;
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
@@ -122,9 +132,16 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return; return;
} }
mAppRow = mBackend.loadAppRow(mContext, mPm, mPkgInfo);
mChannel = (args != null && args.containsKey(ARG_CHANNEL)) ?
mBackend.getChannel(mPkg, mUid, args.getString(ARG_CHANNEL)) : null;
mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended( mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
mContext, mPkg, mUserId); mContext, mPkg, mUserId);
mShowSlider = Settings.Secure.getInt(getContentResolver(), TUNER_SETTING, 0) == 1; mShowSlider = Settings.Secure.getInt(getContentResolver(), TUNER_SETTING, 0) == 1;
NotificationManager.Policy policy =
NotificationManager.from(mContext).getNotificationPolicy();
mDndVisualEffectsSuppressed = policy == null ? false : policy.suppressedVisualEffects != 0;
} }
@Override @Override
@@ -154,21 +171,27 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
} }
} }
protected void setupImportancePrefs(boolean notBlockable, int importance, boolean banned) { protected void setupImportancePrefs(boolean notBlockable, int importance, boolean banned,
int maxImportance) {
if (mShowSlider) { if (mShowSlider) {
setVisible(mBlock, false); setVisible(mBlock, false);
setVisible(mSilent, false); setVisible(mSilent, false);
mImportance.setDisabledByAdmin(mSuspendedAppsAdmin); mImportance.setDisabledByAdmin(mSuspendedAppsAdmin);
mImportance.setMinimumProgress( mImportance.setMinimumProgress(
notBlockable ? Ranking.IMPORTANCE_MIN : Ranking.IMPORTANCE_NONE); notBlockable ? Ranking.IMPORTANCE_MIN : Ranking.IMPORTANCE_NONE);
mImportance.setMax(Ranking.IMPORTANCE_MAX); mImportance.setMax(maxImportance);
mImportance.setProgress(importance); mImportance.setProgress(Math.min(importance, maxImportance));
mImportance.setAutoOn(importance == Ranking.IMPORTANCE_UNSPECIFIED); mImportance.setAutoOn(importance == Ranking.IMPORTANCE_UNSPECIFIED);
mImportance.setCallback(new ImportanceSeekBarPreference.Callback() { mImportance.setCallback(new ImportanceSeekBarPreference.Callback() {
@Override @Override
public void onImportanceChanged(int progress, boolean fromUser) { public void onImportanceChanged(int progress, boolean fromUser) {
if (fromUser) { if (fromUser) {
mBackend.setImportance(mPkg, mUid, progress); if (mChannel != null) {
mChannel.setImportance(progress);
mBackend.updateChannel(mPkg, mUid, mChannel);
} else {
mBackend.setImportance(mPkg, mUid, progress);
}
} }
updateDependents(progress); updateDependents(progress);
} }
@@ -178,33 +201,56 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
if (notBlockable) { if (notBlockable) {
setVisible(mBlock, false); setVisible(mBlock, false);
} else { } else {
boolean blocked = importance == Ranking.IMPORTANCE_NONE || banned; mBlock.setChecked(banned);
mBlock.setChecked(blocked); mBlock.setOnPreferenceChangeListener(
mBlock.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { new Preference.OnPreferenceChangeListener() {
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference,
final boolean blocked = (Boolean) newValue; Object newValue) {
final int importance = final boolean blocked = (Boolean) newValue;
blocked ? Ranking.IMPORTANCE_NONE : Ranking.IMPORTANCE_UNSPECIFIED; final int importance = blocked
mBackend.setImportance(mPkgInfo.packageName, mUid, importance); ? Ranking.IMPORTANCE_NONE
updateDependents(importance); : Ranking.IMPORTANCE_UNSPECIFIED;
return true; if (mChannel != null) {
} mChannel.setImportance(importance);
}); mBackend.updateChannel(mPkg, mUid, mChannel);
} else {
mBackend.setImportance(mPkgInfo.packageName, mUid,
importance);
}
updateDependents(importance);
return true;
}
});
}
// app silenced; cannot un-silence a channel
if (maxImportance == NotificationManager.IMPORTANCE_LOW) {
setVisible(mSilent, false);
updateDependents(banned ? Ranking.IMPORTANCE_NONE : Ranking.IMPORTANCE_LOW);
} else {
mSilent.setChecked(importance == Ranking.IMPORTANCE_LOW);
mSilent.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
final boolean silenced = (Boolean) newValue;
final int importance = silenced
? Ranking.IMPORTANCE_LOW
: Ranking.IMPORTANCE_UNSPECIFIED;
if (mChannel != null) {
mChannel.setImportance(importance);
mBackend.updateChannel(mPkg, mUid, mChannel);
} else {
mBackend.setImportance(mPkgInfo.packageName, mUid,
importance);
}
updateDependents(importance);
return true;
}
});
updateDependents(banned ? Ranking.IMPORTANCE_NONE : importance);
} }
mSilent.setChecked(importance == Ranking.IMPORTANCE_LOW);
mSilent.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean silenced = (Boolean) newValue;
final int importance =
silenced ? Ranking.IMPORTANCE_LOW : Ranking.IMPORTANCE_UNSPECIFIED;
mBackend.setImportance(mPkgInfo.packageName, mUid, importance);
updateDependents(importance);
return true;
}
});
updateDependents(banned ? Ranking.IMPORTANCE_NONE : importance);
} }
} }
@@ -215,7 +261,13 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean bypassZenMode = (Boolean) newValue; final boolean bypassZenMode = (Boolean) newValue;
return mBackend.setBypassZenMode(mPkgInfo.packageName, mUid, bypassZenMode); if (mChannel != null) {
mChannel.setBypassDnd(bypassZenMode);
mBackend.updateChannel(mPkg, mUid, mChannel);
return true;
} else {
return mBackend.setBypassZenMode(mPkgInfo.packageName, mUid, bypassZenMode);
}
} }
}); });
} }
@@ -261,7 +313,12 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
if (sensitive == getGlobalVisibility()) { if (sensitive == getGlobalVisibility()) {
sensitive = Ranking.VISIBILITY_NO_OVERRIDE; sensitive = Ranking.VISIBILITY_NO_OVERRIDE;
} }
mBackend.setVisibilityOverride(mPkgInfo.packageName, mUid, sensitive); if (mChannel != null) {
mChannel.setLockscreenVisibility(sensitive);
mBackend.updateChannel(mPkg, mUid, mChannel);
} else {
mBackend.setVisibilityOverride(mPkgInfo.packageName, mUid, sensitive);
}
return true; return true;
} }
}); });
@@ -287,17 +344,38 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return globalVis; return globalVis;
} }
protected boolean getLockscreenNotificationsEnabled() { private boolean getLockscreenNotificationsEnabled() {
return Settings.Secure.getInt(getContentResolver(), return Settings.Secure.getInt(getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0; Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
} }
protected boolean getLockscreenAllowPrivateNotifications() { private boolean getLockscreenAllowPrivateNotifications() {
return Settings.Secure.getInt(getContentResolver(), return Settings.Secure.getInt(getContentResolver(),
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0; Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
} }
abstract void updateDependents(int progress); private boolean isLockScreenSecure() {
LockPatternUtils utils = new LockPatternUtils(getActivity());
boolean lockscreenSecure = utils.isSecure(UserHandle.myUserId());
UserInfo parentUser = mUm.getProfileParent(UserHandle.myUserId());
if (parentUser != null){
lockscreenSecure |= utils.isSecure(parentUser.id);
}
return lockscreenSecure;
}
protected void updateDependents(int importance) {
if (getPreferenceScreen().findPreference(mBlock.getKey()) != null) {
setVisible(mSilent, checkCanBeVisible(Ranking.IMPORTANCE_MIN, importance));
mSilent.setChecked(importance == Ranking.IMPORTANCE_LOW);
}
setVisible(mPriority, checkCanBeVisible(Ranking.IMPORTANCE_DEFAULT, importance)
|| (checkCanBeVisible(Ranking.IMPORTANCE_LOW, importance)
&& mDndVisualEffectsSuppressed));
setVisible(mVisibilityOverride,
checkCanBeVisible(Ranking.IMPORTANCE_MIN, importance) && isLockScreenSecure());
}
protected void setVisible(Preference p, boolean visible) { protected void setVisible(Preference p, boolean visible) {
final boolean isVisible = getPreferenceScreen().findPreference(p.getKey()) != null; final boolean isVisible = getPreferenceScreen().findPreference(p.getKey()) != null;
@@ -309,6 +387,13 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
} }
} }
protected boolean checkCanBeVisible(int minImportanceVisible, int importance) {
if (importance == Ranking.IMPORTANCE_UNSPECIFIED) {
return true;
}
return importance >= minImportanceVisible;
}
protected void toastAndFinish() { protected void toastAndFinish() {
Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show(); Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show();
getActivity().finish(); getActivity().finish();