2 * Copyright (C) 2016 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings.notification;
19 import android.app.Activity;
20 import android.app.NotificationChannel;
21 import android.app.NotificationChannelGroup;
22 import android.app.NotificationManager;
23 import android.content.Intent;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.os.AsyncTask;
27 import android.provider.Settings;
28 import android.support.v7.preference.Preference;
29 import android.support.v7.preference.PreferenceGroup;
30 import android.text.TextUtils;
31 import android.text.BidiFormatter;
32 import android.text.SpannableStringBuilder;
33 import android.util.ArrayMap;
34 import android.util.Log;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.widget.Switch;
39 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
40 import com.android.settings.R;
41 import com.android.settings.RingtonePreference;
42 import com.android.settings.Utils;
43 import com.android.settings.applications.AppInfoBase;
44 import com.android.settings.applications.LayoutPreference;
45 import com.android.settings.widget.EntityHeaderController;
46 import com.android.settings.widget.SwitchBar;
47 import com.android.settingslib.RestrictedSwitchPreference;
48 import com.android.settingslib.widget.FooterPreference;
50 import static android.app.NotificationManager.IMPORTANCE_LOW;
51 import static android.app.NotificationManager.IMPORTANCE_NONE;
52 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
54 public class ChannelNotificationSettings extends NotificationSettingsBase {
55 private static final String TAG = "ChannelSettings";
57 private static final String KEY_LIGHTS = "lights";
58 private static final String KEY_VIBRATE = "vibrate";
59 private static final String KEY_RINGTONE = "ringtone";
60 private static final String KEY_IMPORTANCE = "importance";
61 private static final String KEY_ADVANCED = "advanced";
63 private Preference mImportance;
64 private RestrictedSwitchPreference mLights;
65 private RestrictedSwitchPreference mVibrate;
66 private NotificationSoundPreference mRingtone;
67 private FooterPreference mFooter;
68 private NotificationChannelGroup mChannelGroup;
69 private EntityHeaderController mHeaderPref;
70 private PreferenceGroup mAdvanced;
73 public int getMetricsCategory() {
74 return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
78 public void onResume() {
80 if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
81 Log.w(TAG, "Missing package or uid or packageinfo or channel");
86 if (getPreferenceScreen() != null) {
87 getPreferenceScreen().removeAll();
89 addPreferencesFromResource(R.xml.notification_settings);
95 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
96 populateDefaultChannelPrefs();
97 mShowLegacyChannelConfig = true;
99 populateUpgradedChannelPrefs();
101 if (mChannel.getGroup() != null) {
102 // Go look up group name
103 new AsyncTask<Void, Void, Void>() {
105 protected Void doInBackground(Void... unused) {
106 if (mChannel.getGroup() != null) {
107 mChannelGroup = mBackend.getGroup(mChannel.getGroup(), mPkg, mUid);
113 protected void onPostExecute(Void unused) {
114 if (getHost() == null || mChannelGroup == null) {
117 setChannelGroupLabel(mChannelGroup.getName());
123 updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
126 private void populateUpgradedChannelPrefs() {
127 addPreferencesFromResource(R.xml.upgraded_channel_notification_settings);
129 setupPriorityPref(mChannel.canBypassDnd());
130 setupVisOverridePref(mChannel.getLockscreenVisibility());
135 mAdvanced = (PreferenceGroup) findPreference(KEY_ADVANCED);
138 private void addHeaderPref() {
139 ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<>();
140 rows.put(mAppRow.pkg, mAppRow);
141 collectConfigActivities(rows);
142 final Activity activity = getActivity();
143 mHeaderPref = EntityHeaderController
144 .newInstance(activity, this /* fragment */, null /* header */)
145 .setRecyclerView(getListView(), getLifecycle());
146 final Preference pref = mHeaderPref
147 .setIcon(mAppRow.icon)
148 .setLabel(mChannel.getName())
149 .setSummary(mAppRow.label)
150 .setPackageName(mAppRow.pkg)
152 .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
153 EntityHeaderController.ActionType.ACTION_NONE)
154 .setHasAppInfoLink(true)
155 .done(activity, getPrefContext());
156 getPreferenceScreen().addPreference(pref);
159 private void setChannelGroupLabel(CharSequence groupName) {
160 final SpannableStringBuilder summary = new SpannableStringBuilder();
161 BidiFormatter bidi = BidiFormatter.getInstance();
162 summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
163 if (groupName != null) {
164 summary.append(bidi.unicodeWrap(mContext.getText(
165 R.string.notification_header_divider_symbol_with_spaces)));
166 summary.append(bidi.unicodeWrap(groupName.toString()));
168 final Activity activity = getActivity();
169 mHeaderPref.setSummary(summary.toString());
170 mHeaderPref.done(activity, getPrefContext());
173 private void addFooterPref() {
174 if (!TextUtils.isEmpty(mChannel.getDescription())) {
175 FooterPreference descPref = new FooterPreference(getPrefContext());
176 descPref.setOrder(ORDER_LAST);
177 descPref.setSelectable(false);
178 descPref.setTitle(mChannel.getDescription());
179 getPreferenceScreen().addPreference(descPref);
183 protected void setupBadge() {
184 mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
185 mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
186 mBadge.setEnabled(mAppRow.showBadge);
187 mBadge.setChecked(mChannel.canShowBadge());
189 mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
191 public boolean onPreferenceChange(Preference preference, Object newValue) {
192 final boolean value = (Boolean) newValue;
193 mChannel.setShowBadge(value);
194 mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
195 mBackend.updateChannel(mPkg, mUid, mChannel);
201 private void setupLights() {
202 mLights = (RestrictedSwitchPreference) findPreference(KEY_LIGHTS);
203 mLights.setDisabledByAdmin(mSuspendedAppsAdmin);
204 mLights.setChecked(mChannel.shouldShowLights());
205 mLights.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
207 public boolean onPreferenceChange(Preference preference, Object newValue) {
208 final boolean lights = (Boolean) newValue;
209 mChannel.enableLights(lights);
210 mChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
211 mBackend.updateChannel(mPkg, mUid, mChannel);
217 private void setupVibrate() {
218 mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
219 mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
220 mVibrate.setEnabled(!mVibrate.isDisabledByAdmin() && isChannelConfigurable(mChannel));
221 mVibrate.setChecked(mChannel.shouldVibrate());
222 mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
224 public boolean onPreferenceChange(Preference preference, Object newValue) {
225 final boolean vibrate = (Boolean) newValue;
226 mChannel.enableVibration(vibrate);
227 mChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
228 mBackend.updateChannel(mPkg, mUid, mChannel);
234 private void setupRingtone() {
235 mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
236 mRingtone.setRingtone(mChannel.getSound());
237 mRingtone.setEnabled(isChannelConfigurable(mChannel));
238 mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
240 public boolean onPreferenceChange(Preference preference, Object newValue) {
241 mChannel.setSound((Uri) newValue, mChannel.getAudioAttributes());
242 mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
243 mBackend.updateChannel(mPkg, mUid, mChannel);
249 private void setupBlock() {
250 View switchBarContainer = LayoutInflater.from(
251 getPrefContext()).inflate(R.layout.styled_switch_bar, null);
252 mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
254 mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
255 mSwitchBar.setChecked(mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
256 mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
258 public void onSwitchChanged(Switch switchView, boolean isChecked) {
260 if (mShowLegacyChannelConfig) {
261 importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
262 mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
264 importance = isChecked ? IMPORTANCE_LOW : IMPORTANCE_NONE;
265 mImportance.setSummary(getImportanceSummary(importance));
267 mChannel.setImportance(importance);
268 mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
269 mBackend.updateChannel(mPkg, mUid, mChannel);
270 updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
274 mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
275 mBlockBar.setOrder(ORDER_FIRST);
276 mBlockBar.setKey(KEY_BLOCK);
277 getPreferenceScreen().addPreference(mBlockBar);
279 if (!isChannelBlockable(mAppRow.systemApp, mChannel)) {
280 setVisible(mBlockBar, false);
283 setupBlockDesc(R.string.channel_notifications_off_desc);
286 private void setupImportance() {
287 mImportance = findPreference(KEY_IMPORTANCE);
288 Bundle channelArgs = new Bundle();
289 channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
290 channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
291 channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
292 mImportance.setEnabled(mSuspendedAppsAdmin == null && isChannelConfigurable(mChannel));
293 // Set up intent to show importance selection only if this setting is enabled.
294 if (mImportance.isEnabled()) {
295 Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
296 ChannelImportanceSettings.class.getName(),
297 channelArgs, null, R.string.notification_importance_title, null,
298 false, getMetricsCategory());
299 mImportance.setIntent(channelIntent);
301 mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
304 private String getImportanceSummary(int importance) {
306 String summary = null;
307 switch (importance) {
308 case IMPORTANCE_UNSPECIFIED:
309 title = getContext().getString(R.string.notification_importance_unspecified);
311 case NotificationManager.IMPORTANCE_MIN:
312 title = getContext().getString(R.string.notification_importance_min_title);
313 summary = getContext().getString(R.string.notification_importance_min);
315 case NotificationManager.IMPORTANCE_LOW:
316 title = getContext().getString(R.string.notification_importance_low_title);
317 summary = getContext().getString(R.string.notification_importance_low);
319 case NotificationManager.IMPORTANCE_DEFAULT:
320 title = getContext().getString(R.string.notification_importance_default_title);
321 if (hasValidSound(mChannel)) {
322 summary = getContext().getString(R.string.notification_importance_default);
324 summary = getContext().getString(R.string.notification_importance_low);
327 case NotificationManager.IMPORTANCE_HIGH:
328 case NotificationManager.IMPORTANCE_MAX:
329 title = getContext().getString(R.string.notification_importance_high_title);
330 if (hasValidSound(mChannel)) {
331 summary = getContext().getString(R.string.notification_importance_high);
333 summary = getContext().getString(R.string.notification_importance_high_silent);
340 if (summary != null) {
341 return getContext().getString(R.string.notification_importance_divider, title, summary);
348 public boolean onPreferenceTreeClick(Preference preference) {
349 if (preference instanceof RingtonePreference) {
350 mRingtone.onPrepareRingtonePickerIntent(mRingtone.getIntent());
351 startActivityForResult(preference.getIntent(), 200);
354 return super.onPreferenceTreeClick(preference);
358 public void onActivityResult(int requestCode, int resultCode, Intent data) {
359 if (mRingtone != null) {
360 mRingtone.onActivityResult(requestCode, resultCode, data);
362 if (mChannel != null) {
363 mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
367 boolean canPulseLight() {
369 .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
372 return Settings.System.getInt(getContentResolver(),
373 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
376 void updateDependents(boolean banned) {
377 PreferenceGroup parent;
378 if (mShowLegacyChannelConfig) {
379 parent = getPreferenceScreen();
380 setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
382 setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
383 setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
384 setVisible(mAdvanced, mLights, checkCanBeVisible(
385 NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
386 setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
387 setVisible(mRingtone, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
390 setVisible(parent, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
391 setVisible(parent, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
392 || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
393 && mDndVisualEffectsSuppressed));
394 setVisible(parent, mVisibilityOverride, isLockScreenSecure()
395 &&checkCanBeVisible(NotificationManager.IMPORTANCE_LOW));
396 setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE);
397 if (mAppLink != null) {
398 setVisible(mAppLink, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
400 if (mFooter != null) {
401 setVisible(mFooter, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));