OSDN Git Service

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