Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2015 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.annotation.Nullable;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.app.DialogFragment;
     23 import android.app.NotificationManager;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.pm.ApplicationInfo;
     28 import android.content.pm.PackageItemInfo;
     29 import android.content.pm.PackageManager;
     30 import android.database.ContentObserver;
     31 import android.net.Uri;
     32 import android.os.AsyncTask;
     33 import android.os.Bundle;
     34 import android.os.Handler;
     35 import android.os.Looper;
     36 import android.provider.Settings;
     37 import android.provider.Settings.Secure;
     38 import android.support.v14.preference.SwitchPreference;
     39 import android.support.v7.preference.Preference;
     40 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
     41 import android.support.v7.preference.PreferenceScreen;
     42 import android.text.TextUtils;
     43 import android.util.ArraySet;
     44 import android.view.View;
     45 import android.widget.Toast;
     46 
     47 import com.android.internal.logging.MetricsProto.MetricsEvent;
     48 import com.android.settings.R;
     49 
     50 import java.util.ArrayList;
     51 import java.util.Collections;
     52 import java.util.List;
     53 
     54 public class ZenAccessSettings extends EmptyTextSettings {
     55 
     56     private final SettingObserver mObserver = new SettingObserver();
     57     private static final String ENABLED_SERVICES_SEPARATOR = ":";
     58 
     59     private Context mContext;
     60     private PackageManager mPkgMan;
     61     private NotificationManager mNoMan;
     62 
     63     @Override
     64     protected int getMetricsCategory() {
     65         return MetricsEvent.NOTIFICATION_ZEN_MODE_ACCESS;
     66     }
     67 
     68     @Override
     69     public void onCreate(Bundle icicle) {
     70         super.onCreate(icicle);
     71 
     72         mContext = getActivity();
     73         mPkgMan = mContext.getPackageManager();
     74         mNoMan = mContext.getSystemService(NotificationManager.class);
     75         setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
     76     }
     77 
     78     @Override
     79     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
     80         super.onViewCreated(view, savedInstanceState);
     81         setEmptyText(R.string.zen_access_empty_text);
     82     }
     83 
     84     @Override
     85     public void onResume() {
     86         super.onResume();
     87         reloadList();
     88         getContentResolver().registerContentObserver(
     89                 Secure.getUriFor(Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), false,
     90                 mObserver);
     91         getContentResolver().registerContentObserver(
     92                 Secure.getUriFor(Secure.ENABLED_NOTIFICATION_LISTENERS), false,
     93                 mObserver);
     94     }
     95 
     96     @Override
     97     public void onPause() {
     98         super.onPause();
     99         getContentResolver().unregisterContentObserver(mObserver);
    100     }
    101 
    102     private void reloadList() {
    103         final PreferenceScreen screen = getPreferenceScreen();
    104         screen.removeAll();
    105         final ArrayList<ApplicationInfo> apps = new ArrayList<>();
    106         final ArraySet<String> requesting = mNoMan.getPackagesRequestingNotificationPolicyAccess();
    107         if (!requesting.isEmpty()) {
    108             final List<ApplicationInfo> installed = mPkgMan.getInstalledApplications(0);
    109             if (installed != null) {
    110                 for (ApplicationInfo app : installed) {
    111                     if (requesting.contains(app.packageName)) {
    112                         apps.add(app);
    113                     }
    114                 }
    115             }
    116         }
    117         ArraySet<String> autoApproved = getEnabledNotificationListeners();
    118         requesting.addAll(autoApproved);
    119         Collections.sort(apps, new PackageItemInfo.DisplayNameComparator(mPkgMan));
    120         for (ApplicationInfo app : apps) {
    121             final String pkg = app.packageName;
    122             final CharSequence label = app.loadLabel(mPkgMan);
    123             final SwitchPreference pref = new SwitchPreference(getPrefContext());
    124             pref.setPersistent(false);
    125             pref.setIcon(app.loadIcon(mPkgMan));
    126             pref.setTitle(label);
    127             pref.setChecked(hasAccess(pkg));
    128             if (autoApproved.contains(pkg)) {
    129                 pref.setEnabled(false);
    130                 pref.setSummary(getString(R.string.zen_access_disabled_package_warning));
    131             }
    132             pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    133                 @Override
    134                 public boolean onPreferenceChange(Preference preference, Object newValue) {
    135                     final boolean access = (Boolean) newValue;
    136                     if (access) {
    137                         new ScaryWarningDialogFragment()
    138                                 .setPkgInfo(pkg, label)
    139                                 .show(getFragmentManager(), "dialog");
    140                     } else {
    141                         new FriendlyWarningDialogFragment()
    142                                 .setPkgInfo(pkg, label)
    143                                 .show(getFragmentManager(), "dialog");
    144                     }
    145                     return false;
    146                 }
    147             });
    148             screen.addPreference(pref);
    149         }
    150     }
    151 
    152     private ArraySet<String> getEnabledNotificationListeners() {
    153         ArraySet<String> packages = new ArraySet<>();
    154         String settingValue = Settings.Secure.getString(getContext().getContentResolver(),
    155                 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
    156         if (!TextUtils.isEmpty(settingValue)) {
    157             String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
    158             for (int i = 0; i < restored.length; i++) {
    159                 ComponentName value = ComponentName.unflattenFromString(restored[i]);
    160                 if (null != value) {
    161                     packages.add(value.getPackageName());
    162                 }
    163             }
    164         }
    165         return packages;
    166     }
    167 
    168     private boolean hasAccess(String pkg) {
    169         return mNoMan.isNotificationPolicyAccessGrantedForPackage(pkg);
    170     }
    171 
    172     private static void setAccess(final Context context, final String pkg, final boolean access) {
    173         AsyncTask.execute(new Runnable() {
    174             @Override
    175             public void run() {
    176                 final NotificationManager mgr = context.getSystemService(NotificationManager.class);
    177                 mgr.setNotificationPolicyAccessGranted(pkg, access);
    178             }
    179         });
    180     }
    181 
    182     private static void deleteRules(final Context context, final String pkg) {
    183         AsyncTask.execute(new Runnable() {
    184             @Override
    185             public void run() {
    186                 final NotificationManager mgr = context.getSystemService(NotificationManager.class);
    187                 mgr.removeAutomaticZenRules(pkg);
    188             }
    189         });
    190     }
    191 
    192     private final class SettingObserver extends ContentObserver {
    193         public SettingObserver() {
    194             super(new Handler(Looper.getMainLooper()));
    195         }
    196 
    197         @Override
    198         public void onChange(boolean selfChange, Uri uri) {
    199             reloadList();
    200         }
    201     }
    202 
    203     /**
    204      * Warning dialog when allowing zen access warning about the privileges being granted.
    205      */
    206     public static class ScaryWarningDialogFragment extends DialogFragment {
    207         static final String KEY_PKG = "p";
    208         static final String KEY_LABEL = "l";
    209 
    210         public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
    211             Bundle args = new Bundle();
    212             args.putString(KEY_PKG, pkg);
    213             args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
    214             setArguments(args);
    215             return this;
    216         }
    217 
    218         @Override
    219         public Dialog onCreateDialog(Bundle savedInstanceState) {
    220             super.onCreate(savedInstanceState);
    221             final Bundle args = getArguments();
    222             final String pkg = args.getString(KEY_PKG);
    223             final String label = args.getString(KEY_LABEL);
    224 
    225             final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
    226                     label);
    227             final String summary = getResources()
    228                     .getString(R.string.zen_access_warning_dialog_summary);
    229             return new AlertDialog.Builder(getContext())
    230                     .setMessage(summary)
    231                     .setTitle(title)
    232                     .setCancelable(true)
    233                     .setPositiveButton(R.string.allow,
    234                             new DialogInterface.OnClickListener() {
    235                                 public void onClick(DialogInterface dialog, int id) {
    236                                     setAccess(getContext(), pkg, true);
    237                                 }
    238                             })
    239                     .setNegativeButton(R.string.deny,
    240                             new DialogInterface.OnClickListener() {
    241                                 public void onClick(DialogInterface dialog, int id) {
    242                                     // pass
    243                                 }
    244                             })
    245                     .create();
    246         }
    247     }
    248 
    249     /**
    250      * Warning dialog when revoking zen access warning that zen rule instances will be deleted.
    251      */
    252     public static class FriendlyWarningDialogFragment extends DialogFragment {
    253         static final String KEY_PKG = "p";
    254         static final String KEY_LABEL = "l";
    255 
    256         public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
    257             Bundle args = new Bundle();
    258             args.putString(KEY_PKG, pkg);
    259             args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
    260             setArguments(args);
    261             return this;
    262         }
    263 
    264         @Override
    265         public Dialog onCreateDialog(Bundle savedInstanceState) {
    266             super.onCreate(savedInstanceState);
    267             final Bundle args = getArguments();
    268             final String pkg = args.getString(KEY_PKG);
    269             final String label = args.getString(KEY_LABEL);
    270 
    271             final String title = getResources().getString(
    272                     R.string.zen_access_revoke_warning_dialog_title, label);
    273             final String summary = getResources()
    274                     .getString(R.string.zen_access_revoke_warning_dialog_summary);
    275             return new AlertDialog.Builder(getContext())
    276                     .setMessage(summary)
    277                     .setTitle(title)
    278                     .setCancelable(true)
    279                     .setPositiveButton(R.string.okay,
    280                             new DialogInterface.OnClickListener() {
    281                                 public void onClick(DialogInterface dialog, int id) {
    282                                     deleteRules(getContext(), pkg);
    283                                     setAccess(getContext(), pkg, false);
    284                                 }
    285                             })
    286                     .setNegativeButton(R.string.cancel,
    287                             new DialogInterface.OnClickListener() {
    288                                 public void onClick(DialogInterface dialog, int id) {
    289                                     // pass
    290                                 }
    291                             })
    292                     .create();
    293         }
    294     }
    295 }
    296