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 getConfig()65 abstract protected Config getConfig(); 66 ManagedServiceSettings()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 setServiceInfo(ComponentName cn, String label)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 onCreateDialog(Bundle savedInstanceState)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