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 static android.app.NotificationManager.IMPORTANCE_LOW;
20 import static android.app.NotificationManager.IMPORTANCE_NONE;
21 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
23 import android.app.Activity;
24 import android.app.NotificationChannel;
25 import android.app.NotificationChannelGroup;
26 import android.app.NotificationManager;
27 import android.content.Intent;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.os.AsyncTask;
31 import android.provider.Settings;
32 import android.support.v7.preference.Preference;
33 import android.text.TextUtils;
34 import android.text.BidiFormatter;
35 import android.text.SpannableStringBuilder;
36 import android.util.ArrayMap;
37 import android.util.Log;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.widget.Switch;
42 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
43 import com.android.settings.AppHeader;
44 import com.android.settings.R;
45 import com.android.settings.RingtonePreference;
46 import com.android.settings.Utils;
47 import com.android.settings.applications.AppHeaderController;
48 import com.android.settings.applications.AppInfoBase;
49 import com.android.settings.applications.LayoutPreference;
50 import com.android.settings.overlay.FeatureFactory;
51 import com.android.settings.widget.FooterPreference;
52 import com.android.settings.widget.SwitchBar;
53 import com.android.settingslib.RestrictedSwitchPreference;
55 public class ChannelNotificationSettings extends NotificationSettingsBase {
56 private static final String TAG = "ChannelSettings";
58 private static final String KEY_LIGHTS = "lights";
59 private static final String KEY_VIBRATE = "vibrate";
60 private static final String KEY_RINGTONE = "ringtone";
61 private static final String KEY_IMPORTANCE = "importance";
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 AppHeaderController mHeaderPref;
72 public int getMetricsCategory() {
73 return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
77 public void onResume() {
79 if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
80 Log.w(TAG, "Missing package or uid or packageinfo or channel");
85 if (getPreferenceScreen() != null) {
86 getPreferenceScreen().removeAll();
88 addPreferencesFromResource(R.xml.notification_settings);
94 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
95 populateDefaultChannelPrefs();
96 mShowLegacyChannelConfig = true;
98 populateUpgradedChannelPrefs();
100 if (mChannel.getGroup() != null) {
101 // Go look up group name
102 new AsyncTask<Void, Void, Void>() {
104 protected Void doInBackground(Void... unused) {
105 if (mChannel.getGroup() != null) {
106 mChannelGroup = mBackend.getGroup(mChannel.getGroup(), mPkg, mUid);
112 protected void onPostExecute(Void unused) {
113 if (getHost() == null || mChannelGroup == null) {
116 setChannelGroupLabel(mChannelGroup.getName());
122 updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
125 private void populateUpgradedChannelPrefs() {
126 addPreferencesFromResource(R.xml.upgraded_channel_notification_settings);
128 setupPriorityPref(mChannel.canBypassDnd());
129 setupVisOverridePref(mChannel.getLockscreenVisibility());
136 private void addHeaderPref() {
137 ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<String, NotificationBackend.AppRow>();
138 rows.put(mAppRow.pkg, mAppRow);
139 collectConfigActivities(rows);
140 final Activity activity = getActivity();
141 mHeaderPref = FeatureFactory.getFactory(activity)
142 .getApplicationFeatureProvider(activity)
143 .newAppHeaderController(this /* fragment */, null /* appHeader */);
144 final Preference pref = mHeaderPref
145 .setIcon(mAppRow.icon)
146 .setLabel(mChannel.getName())
147 .setSummary(mAppRow.label)
148 .setPackageName(mAppRow.pkg)
150 .setButtonActions(AppHeaderController.ActionType.ACTION_APP_INFO,
151 AppHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
152 .done(activity, getPrefContext());
153 getPreferenceScreen().addPreference(pref);
156 private void setChannelGroupLabel(CharSequence groupName) {
157 final SpannableStringBuilder summary = new SpannableStringBuilder();
158 BidiFormatter bidi = BidiFormatter.getInstance();
159 summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
160 if (groupName != null) {
161 summary.append(bidi.unicodeWrap(mContext.getText(
162 R.string.notification_header_divider_symbol_with_spaces)));
163 summary.append(bidi.unicodeWrap(groupName.toString()));
165 final Activity activity = getActivity();
166 mHeaderPref.setSummary(summary.toString());
167 mHeaderPref.done(activity, getPrefContext());
170 private void addFooterPref() {
171 if (!TextUtils.isEmpty(mChannel.getDescription())) {
172 FooterPreference descPref = new FooterPreference(getPrefContext());
173 descPref.setOrder(ORDER_LAST);
174 descPref.setSelectable(false);
175 descPref.setTitle(mChannel.getDescription());
176 getPreferenceScreen().addPreference(descPref);
180 protected void setupBadge() {
181 mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
182 mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
183 mBadge.setEnabled(mAppRow.showBadge);
184 mBadge.setChecked(mChannel.canShowBadge());
186 mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
188 public boolean onPreferenceChange(Preference preference, Object newValue) {
189 final boolean value = (Boolean) newValue;
190 mChannel.setShowBadge(value);
191 mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
192 mBackend.updateChannel(mPkg, mUid, mChannel);
198 private void setupLights() {
199 mLights = (RestrictedSwitchPreference) findPreference(KEY_LIGHTS);
200 mLights.setDisabledByAdmin(mSuspendedAppsAdmin);
201 mLights.setChecked(mChannel.shouldShowLights());
202 mLights.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
204 public boolean onPreferenceChange(Preference preference, Object newValue) {
205 final boolean lights = (Boolean) newValue;
206 mChannel.enableLights(lights);
207 mChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
208 mBackend.updateChannel(mPkg, mUid, mChannel);
214 private void setupVibrate() {
215 mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
216 mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
217 mVibrate.setEnabled(!(mAppRow.lockedImportance || mVibrate.isDisabledByAdmin()));
218 mVibrate.setChecked(mChannel.shouldVibrate());
219 mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
221 public boolean onPreferenceChange(Preference preference, Object newValue) {
222 final boolean vibrate = (Boolean) newValue;
223 mChannel.enableVibration(vibrate);
224 mChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
225 mBackend.updateChannel(mPkg, mUid, mChannel);
231 private void setupRingtone() {
232 mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
233 mRingtone.setRingtone(mChannel.getSound());
234 mRingtone.setEnabled(!(mAppRow.lockedImportance));
235 mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
237 public boolean onPreferenceChange(Preference preference, Object newValue) {
238 mChannel.setSound((Uri) newValue, mChannel.getAudioAttributes());
239 mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
240 mBackend.updateChannel(mPkg, mUid, mChannel);
246 private void setupBlock() {
247 View switchBarContainer = LayoutInflater.from(
248 getPrefContext()).inflate(R.layout.styled_switch_bar, null);
249 mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
251 mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
252 mSwitchBar.setChecked(mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
253 mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
255 public void onSwitchChanged(Switch switchView, boolean isChecked) {
257 if (mShowLegacyChannelConfig) {
258 importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
259 mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
261 importance = isChecked ? IMPORTANCE_LOW : IMPORTANCE_NONE;
262 mImportance.setSummary(getImportanceSummary(importance));
264 mChannel.setImportance(importance);
265 mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
266 mBackend.updateChannel(mPkg, mUid, mChannel);
267 updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
271 mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
272 mBlockBar.setOrder(ORDER_FIRST);
273 mBlockBar.setKey(KEY_BLOCK);
274 getPreferenceScreen().addPreference(mBlockBar);
276 if (!isChannelBlockable(mAppRow.systemApp, mChannel)) {
277 setVisible(mBlockBar, false);
280 setupBlockDesc(R.string.channel_notifications_off_desc);
283 private void setupImportance() {
284 mImportance = findPreference(KEY_IMPORTANCE);
285 Bundle channelArgs = new Bundle();
286 channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
287 channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
288 channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
289 channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
290 mImportance.setEnabled(mSuspendedAppsAdmin == null && !mAppRow.lockedImportance);
291 // Set up intent to show importance selection only if this setting is enabled.
292 if (mImportance.isEnabled()) {
293 Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
294 ChannelImportanceSettings.class.getName(),
295 channelArgs, null, R.string.notification_importance_title, null,
296 false, getMetricsCategory());
297 mImportance.setIntent(channelIntent);
299 mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
302 private String getImportanceSummary(int importance) {
304 String summary = null;
305 switch (importance) {
306 case IMPORTANCE_UNSPECIFIED:
307 title = getContext().getString(R.string.notification_importance_unspecified);
309 case NotificationManager.IMPORTANCE_MIN:
310 title = getContext().getString(R.string.notification_importance_min_title);
311 summary = getContext().getString(R.string.notification_importance_min);
313 case NotificationManager.IMPORTANCE_LOW:
314 title = getContext().getString(R.string.notification_importance_low_title);
315 summary = getContext().getString(R.string.notification_importance_low);
317 case NotificationManager.IMPORTANCE_DEFAULT:
318 title = getContext().getString(R.string.notification_importance_default_title);
319 if (hasValidSound()) {
320 summary = getContext().getString(R.string.notification_importance_default);
323 case NotificationManager.IMPORTANCE_HIGH:
324 case NotificationManager.IMPORTANCE_MAX:
325 title = getContext().getString(R.string.notification_importance_high_title);
326 if (hasValidSound()) {
327 summary = getContext().getString(R.string.notification_importance_high);
334 if (summary != null) {
335 return getContext().getString(R.string.notification_importance_divider, title, summary);
342 public boolean onPreferenceTreeClick(Preference preference) {
343 if (preference instanceof RingtonePreference) {
344 mRingtone.onPrepareRingtonePickerIntent(mRingtone.getIntent());
345 startActivityForResult(preference.getIntent(), 200);
348 return super.onPreferenceTreeClick(preference);
352 public void onActivityResult(int requestCode, int resultCode, Intent data) {
353 if (mRingtone != null) {
354 mRingtone.onActivityResult(requestCode, resultCode, data);
356 mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
359 boolean canPulseLight() {
361 .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
364 return Settings.System.getInt(getContentResolver(),
365 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
368 boolean hasValidSound() {
369 return mChannel.getSound() != null && !Uri.EMPTY.equals(mChannel.getSound());
372 void updateDependents(boolean banned) {
373 if (mShowLegacyChannelConfig) {
374 setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
376 setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
377 setVisible(mLights, checkCanBeVisible(
378 NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
379 setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
380 setVisible(mRingtone, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
382 setVisible(mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
383 setVisible(mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
384 || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
385 && mDndVisualEffectsSuppressed));
386 setVisible(mVisibilityOverride, checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
387 && isLockScreenSecure());
388 setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE);
389 if (mAppLink != null) {
390 setVisible(mAppLink, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
392 if (mFooter != null) {
393 setVisible(mFooter, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));