Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2014 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.ActivityManager;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.app.DialogFragment;
     23 import android.app.ListFragment;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.ContentResolver;
     27 import android.content.Context;
     28 import android.content.DialogInterface;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.pm.PackageItemInfo;
     32 import android.content.pm.PackageManager;
     33 import android.content.pm.ResolveInfo;
     34 import android.content.pm.ServiceInfo;
     35 import android.database.ContentObserver;
     36 import android.net.Uri;
     37 import android.os.Bundle;
     38 import android.os.Handler;
     39 import android.provider.Settings;
     40 import android.util.Slog;
     41 import android.view.LayoutInflater;
     42 import android.view.View;
     43 import android.view.ViewGroup;
     44 import android.widget.ArrayAdapter;
     45 import android.widget.CheckBox;
     46 import android.widget.ImageView;
     47 import android.widget.ListView;
     48 import android.widget.TextView;
     49 
     50 import com.android.settings.R;
     51 
     52 import java.util.HashSet;
     53 import java.util.List;
     54 
     55 public abstract class ManagedServiceSettings extends ListFragment {
     56     private static final boolean SHOW_PACKAGE_NAME = false;
     57 
     58     private final Config mConfig;
     59     private PackageManager mPM;
     60     private ContentResolver mCR;
     61 
     62     private final HashSet<ComponentName> mEnabledServices = new HashSet<ComponentName>();
     63     private ServiceListAdapter mListAdapter;
     64 
     65     abstract protected Config getConfig();
     66 
     67     public ManagedServiceSettings() {
     68         mConfig = getConfig();
     69     }
     70 
     71     private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
     72         @Override
     73         public void onChange(boolean selfChange, Uri uri) {
     74             updateList();
     75         }
     76     };
     77 
     78     private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
     79         @Override
     80         public void onReceive(Context context, Intent intent) {
     81             updateList();
     82         }
     83     };
     84 
     85     public class ScaryWarningDialogFragment extends DialogFragment {
     86         static final String KEY_COMPONENT = "c";
     87         static final String KEY_LABEL = "l";
     88 
     89         public ScaryWarningDialogFragment setServiceInfo(ComponentName cn, String label) {
     90             Bundle args = new Bundle();
     91             args.putString(KEY_COMPONENT, cn.flattenToString());
     92             args.putString(KEY_LABEL, label);
     93             setArguments(args);
     94             return this;
     95         }
     96 
     97         @Override
     98         public Dialog onCreateDialog(Bundle savedInstanceState) {
     99             super.onCreate(savedInstanceState);
    100             final Bundle args = getArguments();
    101             final String label = args.getString(KEY_LABEL);
    102             final ComponentName cn = ComponentName.unflattenFromString(args.getString(KEY_COMPONENT));
    103 
    104             final String title = getResources().getString(mConfig.warningDialogTitle, label);
    105             final String summary = getResources().getString(mConfig.warningDialogSummary, label);
    106             return new AlertDialog.Builder(getActivity())
    107                     .setMessage(summary)
    108                     .setTitle(title)
    109                     .setCancelable(true)
    110                     .setPositiveButton(android.R.string.ok,
    111                             new DialogInterface.OnClickListener() {
    112                                 public void onClick(DialogInterface dialog, int id) {
    113                                     mEnabledServices.add(cn);
    114                                     saveEnabledServices();
    115                                 }
    116                             })
    117                     .setNegativeButton(android.R.string.cancel,
    118                             new DialogInterface.OnClickListener() {
    119                                 public void onClick(DialogInterface dialog, int id) {
    120                                     // pass
    121                                 }
    122                             })
    123                     .create();
    124         }
    125     }
    126 
    127     @Override
    128     public void onCreate(Bundle icicle) {
    129         super.onCreate(icicle);
    130 
    131         mPM = getActivity().getPackageManager();
    132         mCR = getActivity().getContentResolver();
    133         mListAdapter = new ServiceListAdapter(getActivity());
    134     }
    135 
    136     @Override
    137     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    138             Bundle savedInstanceState) {
    139         View v =  inflater.inflate(R.layout.managed_service_settings, container, false);
    140         TextView empty = (TextView) v.findViewById(android.R.id.empty);
    141         empty.setText(mConfig.emptyText);
    142         return v;
    143     }
    144 
    145     @Override
    146     public void onResume() {
    147         super.onResume();
    148         updateList();
    149 
    150         // listen for package changes
    151         IntentFilter filter = new IntentFilter();
    152         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
    153         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    154         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    155         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
    156         filter.addDataScheme("package");
    157         getActivity().registerReceiver(mPackageReceiver, filter);
    158 
    159         mCR.registerContentObserver(Settings.Secure.getUriFor(mConfig.setting),
    160                 false, mSettingsObserver);
    161     }
    162 
    163     @Override
    164     public void onPause() {
    165         super.onPause();
    166 
    167         getActivity().unregisterReceiver(mPackageReceiver);
    168         mCR.unregisterContentObserver(mSettingsObserver);
    169     }
    170 
    171     private void loadEnabledServices() {
    172         mEnabledServices.clear();
    173         final String flat = Settings.Secure.getString(mCR, mConfig.setting);
    174         if (flat != null && !"".equals(flat)) {
    175             final String[] names = flat.split(":");
    176             for (int i = 0; i < names.length; i++) {
    177                 final ComponentName cn = ComponentName.unflattenFromString(names[i]);
    178                 if (cn != null) {
    179                     mEnabledServices.add(cn);
    180                 }
    181             }
    182         }
    183     }
    184 
    185     private void saveEnabledServices() {
    186         StringBuilder sb = null;
    187         for (ComponentName cn : mEnabledServices) {
    188             if (sb == null) {
    189                 sb = new StringBuilder();
    190             } else {
    191                 sb.append(':');
    192             }
    193             sb.append(cn.flattenToString());
    194         }
    195         Settings.Secure.putString(mCR,
    196                 mConfig.setting,
    197                 sb != null ? sb.toString() : "");
    198     }
    199 
    200     private void updateList() {
    201         loadEnabledServices();
    202 
    203         getServices(mConfig, mListAdapter, mPM);
    204         mListAdapter.sort(new PackageItemInfo.DisplayNameComparator(mPM));
    205 
    206         getListView().setAdapter(mListAdapter);
    207     }
    208 
    209     protected static int getEnabledServicesCount(Config config, Context context) {
    210         final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting);
    211         if (flat == null || "".equals(flat)) return 0;
    212         final String[] components = flat.split(":");
    213         return components.length;
    214     }
    215 
    216     protected static int getServicesCount(Config c, PackageManager pm) {
    217         return getServices(c, null, pm);
    218     }
    219 
    220     private static int getServices(Config c, ArrayAdapter<ServiceInfo> adapter, PackageManager pm) {
    221         int services = 0;
    222         if (adapter != null) {
    223             adapter.clear();
    224         }
    225         final int user = ActivityManager.getCurrentUser();
    226 
    227         List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
    228                 new Intent(c.intentAction),
    229                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
    230                 user);
    231 
    232         for (int i = 0, count = installedServices.size(); i < count; i++) {
    233             ResolveInfo resolveInfo = installedServices.get(i);
    234             ServiceInfo info = resolveInfo.serviceInfo;
    235 
    236             if (!c.permission.equals(info.permission)) {
    237                 Slog.w(c.tag, "Skipping " + c.noun + " service "
    238                         + info.packageName + "/" + info.name
    239                         + ": it does not require the permission "
    240                         + c.permission);
    241                 continue;
    242             }
    243             if (adapter != null) {
    244                 adapter.add(info);
    245             }
    246             services++;
    247         }
    248         return services;
    249     }
    250 
    251     private boolean isServiceEnabled(ServiceInfo info) {
    252         final ComponentName cn = new ComponentName(info.packageName, info.name);
    253         return mEnabledServices.contains(cn);
    254     }
    255 
    256     @Override
    257     public void onListItemClick(ListView l, View v, int position, long id) {
    258         ServiceInfo info = mListAdapter.getItem(position);
    259         final ComponentName cn = new ComponentName(info.packageName, info.name);
    260         if (mEnabledServices.contains(cn)) {
    261             // the simple version: disabling
    262             mEnabledServices.remove(cn);
    263             saveEnabledServices();
    264         } else {
    265             // show a scary dialog
    266             new ScaryWarningDialogFragment()
    267                 .setServiceInfo(cn, info.loadLabel(mPM).toString())
    268                 .show(getFragmentManager(), "dialog");
    269         }
    270     }
    271 
    272     private static class ViewHolder {
    273         ImageView icon;
    274         TextView name;
    275         CheckBox checkbox;
    276         TextView description;
    277     }
    278 
    279     private class ServiceListAdapter extends ArrayAdapter<ServiceInfo> {
    280         final LayoutInflater mInflater;
    281 
    282         ServiceListAdapter(Context context) {
    283             super(context, 0, 0);
    284             mInflater = (LayoutInflater)
    285                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    286         }
    287 
    288         public boolean hasStableIds() {
    289             return true;
    290         }
    291 
    292         public long getItemId(int position) {
    293             return position;
    294         }
    295 
    296         public View getView(int position, View convertView, ViewGroup parent) {
    297             View v;
    298             if (convertView == null) {
    299                 v = newView(parent);
    300             } else {
    301                 v = convertView;
    302             }
    303             bindView(v, position);
    304             return v;
    305         }
    306 
    307         public View newView(ViewGroup parent) {
    308             View v = mInflater.inflate(R.layout.managed_service_item, parent, false);
    309             ViewHolder h = new ViewHolder();
    310             h.icon = (ImageView) v.findViewById(R.id.icon);
    311             h.name = (TextView) v.findViewById(R.id.name);
    312             h.checkbox = (CheckBox) v.findViewById(R.id.checkbox);
    313             h.description = (TextView) v.findViewById(R.id.description);
    314             v.setTag(h);
    315             return v;
    316         }
    317 
    318         public void bindView(View view, int position) {
    319             ViewHolder vh = (ViewHolder) view.getTag();
    320             ServiceInfo info = getItem(position);
    321 
    322             vh.icon.setImageDrawable(info.loadIcon(mPM));
    323             vh.name.setText(info.loadLabel(mPM));
    324             if (SHOW_PACKAGE_NAME) {
    325                 vh.description.setText(info.packageName);
    326                 vh.description.setVisibility(View.VISIBLE);
    327             } else {
    328                 vh.description.setVisibility(View.GONE);
    329             }
    330             vh.checkbox.setChecked(isServiceEnabled(info));
    331         }
    332     }
    333 
    334     protected static class Config {
    335         String tag;
    336         String setting;
    337         String intentAction;
    338         String permission;
    339         String noun;
    340         int warningDialogTitle;
    341         int warningDialogSummary;
    342         int emptyText;
    343     }
    344 }
    345