Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      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
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     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.
     15  */
     16 
     17 package com.android.settings.notification;
     18 
     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;
     38 
     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;
     49 
     50 import static android.app.NotificationManager.IMPORTANCE_LOW;
     51 import static android.app.NotificationManager.IMPORTANCE_NONE;
     52 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
     53 
     54 public class ChannelNotificationSettings extends NotificationSettingsBase {
     55     private static final String TAG = "ChannelSettings";
     56 
     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";
     62 
     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;
     71 
     72     @Override
     73     public int getMetricsCategory() {
     74         return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
     75     }
     76 
     77     @Override
     78     public void onResume() {
     79         super.onResume();
     80         if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
     81             Log.w(TAG, "Missing package or uid or packageinfo or channel");
     82             finish();
     83             return;
     84         }
     85 
     86         if (getPreferenceScreen() != null) {
     87             getPreferenceScreen().removeAll();
     88         }
     89         addPreferencesFromResource(R.xml.notification_settings);
     90         setupBlock();
     91         addHeaderPref();
     92         addAppLinkPref();
     93         addFooterPref();
     94 
     95         if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
     96             populateDefaultChannelPrefs();
     97             mShowLegacyChannelConfig = true;
     98         } else {
     99             populateUpgradedChannelPrefs();
    100 
    101             if (mChannel.getGroup() != null) {
    102                 // Go look up group name
    103                 new AsyncTask<Void, Void, Void>() {
    104                     @Override
    105                     protected Void doInBackground(Void... unused) {
    106                         if (mChannel.getGroup() != null) {
    107                             mChannelGroup = mBackend.getGroup(mChannel.getGroup(), mPkg, mUid);
    108                         }
    109                         return null;
    110                     }
    111 
    112                     @Override
    113                     protected void onPostExecute(Void unused) {
    114                         if (getHost() == null || mChannelGroup == null) {
    115                             return;
    116                         }
    117                         setChannelGroupLabel(mChannelGroup.getName());
    118                     }
    119                 }.execute();
    120             }
    121         }
    122 
    123         updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
    124     }
    125 
    126     private void populateUpgradedChannelPrefs() {
    127         addPreferencesFromResource(R.xml.upgraded_channel_notification_settings);
    128         setupBadge();
    129         setupPriorityPref(mChannel.canBypassDnd());
    130         setupVisOverridePref(mChannel.getLockscreenVisibility());
    131         setupLights();
    132         setupVibrate();
    133         setupRingtone();
    134         setupImportance();
    135         mAdvanced = (PreferenceGroup) findPreference(KEY_ADVANCED);
    136     }
    137 
    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)
    151                 .setUid(mAppRow.uid)
    152                 .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
    153                         EntityHeaderController.ActionType.ACTION_NONE)
    154                 .setHasAppInfoLink(true)
    155                 .done(activity, getPrefContext());
    156         getPreferenceScreen().addPreference(pref);
    157     }
    158 
    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()));
    167         }
    168         final Activity activity = getActivity();
    169         mHeaderPref.setSummary(summary.toString());
    170         mHeaderPref.done(activity, getPrefContext());
    171     }
    172 
    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);
    180         }
    181     }
    182 
    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());
    188 
    189         mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
    190             @Override
    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);
    196                 return true;
    197             }
    198         });
    199     }
    200 
    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() {
    206             @Override
    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);
    212                 return true;
    213             }
    214         });
    215     }
    216 
    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() {
    223             @Override
    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);
    229                 return true;
    230             }
    231         });
    232     }
    233 
    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() {
    239             @Override
    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);
    244                 return false;
    245             }
    246         });
    247     }
    248 
    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);
    253         mSwitchBar.show();
    254         mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
    255         mSwitchBar.setChecked(mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
    256         mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
    257             @Override
    258             public void onSwitchChanged(Switch switchView, boolean isChecked) {
    259                 int importance = 0;
    260                 if (mShowLegacyChannelConfig) {
    261                     importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
    262                     mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
    263                 } else {
    264                     importance = isChecked ? IMPORTANCE_LOW : IMPORTANCE_NONE;
    265                     mImportance.setSummary(getImportanceSummary(importance));
    266                 }
    267                 mChannel.setImportance(importance);
    268                 mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
    269                 mBackend.updateChannel(mPkg, mUid, mChannel);
    270                 updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
    271             }
    272         });
    273 
    274         mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
    275         mBlockBar.setOrder(ORDER_FIRST);
    276         mBlockBar.setKey(KEY_BLOCK);
    277         getPreferenceScreen().addPreference(mBlockBar);
    278 
    279         if (!isChannelBlockable(mAppRow.systemApp, mChannel)) {
    280             setVisible(mBlockBar, false);
    281         }
    282 
    283         setupBlockDesc(R.string.channel_notifications_off_desc);
    284     }
    285 
    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);
    300         }
    301         mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
    302     }
    303 
    304     private String getImportanceSummary(int importance) {
    305         String title;
    306         String summary = null;
    307         switch (importance) {
    308             case IMPORTANCE_UNSPECIFIED:
    309                 title = getContext().getString(R.string.notification_importance_unspecified);
    310                 break;
    311             case NotificationManager.IMPORTANCE_MIN:
    312                 title = getContext().getString(R.string.notification_importance_min_title);
    313                 summary = getContext().getString(R.string.notification_importance_min);
    314                 break;
    315             case NotificationManager.IMPORTANCE_LOW:
    316                 title = getContext().getString(R.string.notification_importance_low_title);
    317                 summary = getContext().getString(R.string.notification_importance_low);
    318                 break;
    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);
    323                 } else {
    324                     summary = getContext().getString(R.string.notification_importance_low);
    325                 }
    326                 break;
    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);
    332                 } else {
    333                     summary = getContext().getString(R.string.notification_importance_high_silent);
    334                 }
    335                 break;
    336             default:
    337                 return "";
    338         }
    339 
    340         if (summary != null) {
    341             return getContext().getString(R.string.notification_importance_divider, title, summary);
    342         } else {
    343             return title;
    344         }
    345     }
    346 
    347     @Override
    348     public boolean onPreferenceTreeClick(Preference preference) {
    349         if (preference instanceof RingtonePreference) {
    350             mRingtone.onPrepareRingtonePickerIntent(mRingtone.getIntent());
    351             startActivityForResult(preference.getIntent(), 200);
    352             return true;
    353         }
    354         return super.onPreferenceTreeClick(preference);
    355     }
    356 
    357     @Override
    358     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    359         if (mRingtone != null) {
    360             mRingtone.onActivityResult(requestCode, resultCode, data);
    361         }
    362         if (mChannel != null) {
    363             mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
    364         }
    365     }
    366 
    367     boolean canPulseLight() {
    368         if (!getResources()
    369                 .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
    370             return false;
    371         }
    372         return Settings.System.getInt(getContentResolver(),
    373                 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
    374     }
    375 
    376     void updateDependents(boolean banned) {
    377         PreferenceGroup parent;
    378         if (mShowLegacyChannelConfig) {
    379             parent = getPreferenceScreen();
    380             setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
    381         } else {
    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));
    388             parent = mAdvanced;
    389         }
    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));
    399         }
    400         if (mFooter != null) {
    401             setVisible(mFooter, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
    402         }
    403     }
    404 }
    405