diff options
author | Lars Greiss <kufikugel@googlemail.com> | 2013-07-02 01:46:10 +0200 |
---|---|---|
committer | Danny Baumann <dannybaumann@web.de> | 2013-07-03 13:04:57 +0200 |
commit | deff1bd108333b3c997751a5dfb2f7731d7f6bda (patch) | |
tree | d7ff6a96504a0bfed19591deccdf5a9f6d512035 /src/com | |
parent | 5aacfcbbe55f638d1f69933acb0821492c354b48 (diff) | |
download | packages_apps_settings-deff1bd108333b3c997751a5dfb2f7731d7f6bda.zip packages_apps_settings-deff1bd108333b3c997751a5dfb2f7731d7f6bda.tar.gz packages_apps_settings-deff1bd108333b3c997751a5dfb2f7731d7f6bda.tar.bz2 |
Settings: Introduce Privacy Guard Manager
Due that on the one hand a lot user are not familiar
with permissions and how to read them and on the
other user want to have a faster easy access to
setup privacy guard for several apps this small
manager assist the user to setup.
Privacy Guard Manager shows a list off all installed apps
Per default it shows only user apps which have critical
permissions privacy guard can protect.
The user can optional show in the list as well all system apps
or turn off the filter to show all apps undependent of the
permission the app has. To activate privacy guard for an
app a simple click on the entry activates it so that the user
can go fast trough the list. Longpress opens app detail page.
Initial help dialog tells the user how to use it.
Due the filter user who are unfimiliar with permissions can
fast see which apps are effected without deeper knowledge
cheers
SlimRoms
PatchSet3:
- go inline with AOSP style
- move PrivacyGuard by default into manager
- change behaviour off active icons
PatchSet4:
- change app icon size to 36dip
PatchSet5:
- string change
PatchSet6:
- add reset option to disable privacy guard for
all shown apps in the list
PatchSet7:
- Cleaned up the intro dialog a bit.
PatchSet8+9:
- cleanup
- fix alignment of top checkbox
PatchSet10:
- fix permission filter
PatchSet11:
- improve icon loading
Change-Id: I6bc886393bf90f8daed92b66d5fee1ce9e1accd0
Diffstat (limited to 'src/com')
4 files changed, 544 insertions, 13 deletions
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index b01dc74..b966bc7 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -94,7 +94,6 @@ public class SecuritySettings extends SettingsPreferenceFragment private static final String LOCKSCREEN_QUICK_UNLOCK_CONTROL = "quick_unlock_control"; private static final String KEY_VIBRATE_PREF = "lockscreen_vibrate"; private static final String KEY_SMS_SECURITY_CHECK_PREF = "sms_security_check_limit"; - private static final String KEY_PRIVACY_GUARD_DEFAULT = "privacy_guard_default"; private static final String KEY_APP_SECURITY_CATEGORY = "app_security"; DevicePolicyManager mDPM; @@ -128,7 +127,6 @@ public class SecuritySettings extends SettingsPreferenceFragment private CheckBoxPreference mHomeUnlock; private CheckBoxPreference mQuickUnlockScreen; private ListPreference mSmsSecurityCheck; - private CheckBoxPreference mPrivacyGuardDefault; @Override public void onCreate(Bundle savedInstanceState) { @@ -410,14 +408,6 @@ public class SecuritySettings extends SettingsPreferenceFragment root.findPreference(KEY_APP_SECURITY_CATEGORY); appCategory.removePreference(mSmsSecurityCheck); } - - mPrivacyGuardDefault = (CheckBoxPreference) findPreference(KEY_PRIVACY_GUARD_DEFAULT); - try { - mPrivacyGuardDefault.setChecked(Settings.Secure.getInt(getContentResolver(), - Settings.Secure.PRIVACY_GUARD_DEFAULT) == 1); - } catch (SettingNotFoundException e) { - mPrivacyGuardDefault.setChecked(false); - } } return root; @@ -708,9 +698,6 @@ public class SecuritySettings extends SettingsPreferenceFragment } else if (KEY_TOGGLE_VERIFY_APPLICATIONS.equals(key)) { Settings.Global.putInt(getContentResolver(), Settings.Global.PACKAGE_VERIFIER_ENABLE, mToggleVerifyApps.isChecked() ? 1 : 0); - } else if (KEY_PRIVACY_GUARD_DEFAULT.equals(key)) { - Settings.Secure.putInt(getContentResolver(), Settings.Secure.PRIVACY_GUARD_DEFAULT, - mPrivacyGuardDefault.isChecked() ? 1 : 0); } else { // If we didn't handle it, let preferences handle it. return super.onPreferenceTreeClick(preferenceScreen, preference); diff --git a/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardAppListAdapter.java b/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardAppListAdapter.java new file mode 100644 index 0000000..4c7e815 --- /dev/null +++ b/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardAppListAdapter.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 SlimRoms Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.cyanogenmod.privacyguard; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.cyanogenmod.privacyguard.PrivacyGuardManager.AppInfo; + +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public class PrivacyGuardAppListAdapter extends BaseAdapter { + + private LayoutInflater mInflater; + private PackageManager mPm; + + private List<AppInfo> mApps; + private ConcurrentHashMap<String, Drawable> mIcons; + private Drawable mDefaultImg; + + private Context mContext; + + //constructor + public PrivacyGuardAppListAdapter(Context context, List<AppInfo> apps) { + mContext = context; + mInflater = LayoutInflater.from(mContext); + mPm = context.getPackageManager(); + mApps = apps; + + // set the default icon till the actual app icon is loaded in async task + mDefaultImg = mContext.getResources().getDrawable(android.R.mipmap.sym_def_app_icon); + mIcons = new ConcurrentHashMap<String, Drawable>(); + + new LoadIconsTask().execute(apps.toArray(new PrivacyGuardManager.AppInfo[]{})); + } + + @Override + public int getCount() { + return mApps.size(); + } + + @Override + public Object getItem(int position) { + return mApps.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + PrivacyGuardAppViewHolder appHolder; + + if (convertView == null) { + convertView = mInflater.inflate(R.layout.privacy_guard_manager_list_row, null); + + // creates a ViewHolder and children references + appHolder = new PrivacyGuardAppViewHolder(); + appHolder.title = (TextView) convertView.findViewById(R.id.app_title); + appHolder.icon = (ImageView) convertView.findViewById(R.id.app_icon); + appHolder.privacyGuardIcon = (ImageView) convertView.findViewById(R.id.app_privacy_guard_icon); + convertView.setTag(appHolder); + } else { + appHolder = (PrivacyGuardAppViewHolder) convertView.getTag(); + } + + PrivacyGuardManager.AppInfo app = mApps.get(position); + + appHolder.title.setText(app.title); + + Drawable icon = mIcons.get(app.packageName); + appHolder.icon.setImageDrawable(icon != null ? icon : mDefaultImg); + + int privacyGuardDrawableResId = app.privacyGuardEnabled + ? R.drawable.ic_privacy_guard : R.drawable.ic_privacy_guard_off; + appHolder.privacyGuardIcon.setImageResource(privacyGuardDrawableResId); + + return convertView; + } + + /** + * An asynchronous task to load the icons of the installed applications. + */ + private class LoadIconsTask extends AsyncTask<PrivacyGuardManager.AppInfo, Void, Void> { + @Override + protected Void doInBackground(PrivacyGuardManager.AppInfo... apps) { + for (PrivacyGuardManager.AppInfo app : apps) { + try { + Drawable icon = mPm.getApplicationIcon(app.packageName); + mIcons.put(app.packageName, icon); + publishProgress(); + } catch (PackageManager.NameNotFoundException e) { + // ignored; app will show up with default image + } + } + + return null; + } + + @Override + protected void onProgressUpdate(Void... progress) { + notifyDataSetChanged(); + } + } + + /** + * App view holder used to reuse the views inside the list. + */ + public static class PrivacyGuardAppViewHolder { + TextView title; + ImageView icon; + ImageView privacyGuardIcon; + } +} diff --git a/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardManager.java b/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardManager.java new file mode 100644 index 0000000..efb05a4 --- /dev/null +++ b/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardManager.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2013 SlimRoms Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.cyanogenmod.privacyguard; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.content.ActivityNotFoundException; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.ListView; +import android.widget.TextView; + +import com.android.settings.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class PrivacyGuardManager extends Fragment + implements OnItemClickListener, OnItemLongClickListener { + + private static final String TAG = "PrivacyGuardManager"; + + private TextView mNoUserAppsInstalled; + private ListView mAppsList; + private PrivacyGuardAppListAdapter mAdapter; + private List<AppInfo> mApps; + + private PackageManager mPm; + private Activity mActivity; + + private SharedPreferences mPreferences; + + // array of critical permissions where privacy guard + // can hide the information + private static final String[] PERMISSION_FILTER = new String[] { + android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, + android.Manifest.permission.READ_HISTORY_BOOKMARKS, + android.Manifest.permission.WRITE_HISTORY_BOOKMARKS, + android.Manifest.permission.READ_CALENDAR, + android.Manifest.permission.WRITE_CALENDAR, + android.Manifest.permission.READ_CONTACTS, + android.Manifest.permission.WRITE_CONTACTS, + android.Manifest.permission.READ_CALL_LOG, + android.Manifest.permission.WRITE_CALL_LOG, + "com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL", + android.Manifest.permission.READ_SMS, + android.Manifest.permission.RECEIVE_SMS, + android.Manifest.permission.SEND_SMS, + android.Manifest.permission.WRITE_SMS, + android.Manifest.permission.BROADCAST_SMS + }; + + // holder for package data passed into the adapter + public static final class AppInfo { + String title; + String packageName; + boolean privacyGuardEnabled; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + mActivity = getActivity(); + mPm = mActivity.getPackageManager(); + + return inflater.inflate(R.layout.privacy_guard_manager, container, false); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mNoUserAppsInstalled = (TextView) mActivity.findViewById(R.id.error); + + mAppsList = (ListView) mActivity.findViewById(R.id.apps_list); + mAppsList.setOnItemClickListener(this); + mAppsList.setOnItemLongClickListener(this); + + // get shared preference + mPreferences = mActivity.getSharedPreferences("privacy_guard_manager", Activity.MODE_PRIVATE); + if (!mPreferences.getBoolean("first_help_shown", false)) { + showHelp(); + } + + // load apps and construct the list + loadApps(); + setHasOptionsMenu(true); + } + + private void loadApps() { + mApps = loadInstalledApps(); + + // if app list is empty inform the user + // else go ahead and construct the list + if (mApps == null || mApps.isEmpty()) { + if (shouldFilterByPermission()) { + mNoUserAppsInstalled.setText(R.string.privacy_guard_filter_does_not_match); + } else { + mNoUserAppsInstalled.setText(R.string.privacy_guard_no_user_apps); + } + mNoUserAppsInstalled.setVisibility(View.VISIBLE); + mAppsList.setVisibility(View.GONE); + } else { + mNoUserAppsInstalled.setVisibility(View.GONE); + mAppsList.setVisibility(View.VISIBLE); + mAdapter = new PrivacyGuardAppListAdapter(mActivity, mApps); + mAppsList.setAdapter(mAdapter); + } + } + + private void resetPrivacyGuard() { + if (mApps == null || mApps.isEmpty()) { + return; + } + // turn off privacy guard for all apps shown in the current list + for (AppInfo app : mApps) { + if (app.privacyGuardEnabled) { + mPm.setPrivacyGuardSetting(app.packageName, false); + app.privacyGuardEnabled = false; + } + } + mAdapter.notifyDataSetChanged(); + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + // on click change the privacy guard status for this item + final AppInfo app = (AppInfo) parent.getItemAtPosition(position); + + app.privacyGuardEnabled = !app.privacyGuardEnabled; + mPm.setPrivacyGuardSetting(app.packageName, app.privacyGuardEnabled); + + mAdapter.notifyDataSetChanged(); + } + + @Override + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + // on long click open app details window + final AppInfo app = (AppInfo) parent.getItemAtPosition(position); + + try { + startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.parse("package:" + app.packageName))); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "Couldn't open app details activity", e); + } + + return true; + } + + /** + * Uses the package manager to query for all currently installed apps + * for the list. + * + * @return the complete List off installed applications (@code PrivacyGuardAppInfo) + */ + private List<AppInfo> loadInstalledApps() { + List<AppInfo> apps = new ArrayList<AppInfo>(); + List<PackageInfo> packages = mPm.getInstalledPackages(PackageManager.GET_PERMISSIONS); + boolean showSystemApps = shouldShowSystemApps(); + boolean filterByPermission = shouldFilterByPermission(); + + for (PackageInfo info : packages) { + final ApplicationInfo appInfo = info.applicationInfo; + + // skip system apps if they shall not be included + if (!showSystemApps && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + continue; + } + + if (filterByPermission) { + final String[] requestedPermissions = info.requestedPermissions; + if (requestedPermissions == null || !filterAppPermissions(requestedPermissions)) { + continue; + } + } + + AppInfo app = new AppInfo(); + app.title = appInfo.loadLabel(mPm).toString(); + app.packageName = info.packageName; + app.privacyGuardEnabled = mPm.getPrivacyGuardSetting(app.packageName); + apps.add(app); + } + + // sort the apps by title + Collections.sort(apps, new Comparator<AppInfo>() { + @Override + public int compare(AppInfo lhs, AppInfo rhs) { + return lhs.title.compareToIgnoreCase(rhs.title); + } + }); + + return apps; + } + + private boolean filterAppPermissions(final String[] requestedPermissions) { + for (String requested : requestedPermissions) { + for (String filtered : PERMISSION_FILTER) { + if (requested.equals(filtered)) { + return true; + } + } + } + return false; + } + + private boolean shouldShowSystemApps() { + return mPreferences.getBoolean("show_system_apps", false); + } + + private boolean shouldFilterByPermission() { + return mPreferences.getBoolean("filter_by_permission", true); + } + + private class HelpDialogFragment extends DialogFragment { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.privacy_guard_help_title) + .setMessage(R.string.privacy_guard_help_text) + .setNegativeButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }) + .create(); + } + + @Override + public void onCancel(DialogInterface dialog) { + mPreferences.edit().putBoolean("first_help_shown", true).commit(); + } + } + + private void showHelp() { + HelpDialogFragment fragment = new HelpDialogFragment(); + fragment.show(getFragmentManager(), "help_dialog"); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.privacy_guard_manager, menu); + menu.findItem(R.id.show_system_apps).setChecked(shouldShowSystemApps()); + menu.findItem(R.id.filter_app_permissions).setChecked(shouldFilterByPermission()); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.help: + showHelp(); + return true; + case R.id.reset: + resetPrivacyGuard(); + return true; + case R.id.filter_app_permissions: + case R.id.show_system_apps: + final String prefName = item.getItemId() == R.id.filter_app_permissions + ? "filter_by_permission" : "show_system_apps"; + // set the menu checkbox and save it in + // shared preference and rebuild the list + item.setChecked(!item.isChecked()); + mPreferences.edit().putBoolean(prefName, item.isChecked()).commit(); + loadApps(); + return true; + default: + return super.onContextItemSelected(item); + } + } + + @Override + public void onResume() { + super.onResume(); + // rebuild the list; the user might have changed settings inbetween + loadApps(); + } +} diff --git a/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardPrefs.java b/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardPrefs.java new file mode 100644 index 0000000..6d18952 --- /dev/null +++ b/src/com/android/settings/cyanogenmod/privacyguard/PrivacyGuardPrefs.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 Slimroms + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.cyanogenmod.privacyguard; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.Utils; + +public class PrivacyGuardPrefs extends SettingsPreferenceFragment implements + OnPreferenceChangeListener { + + private static final String TAG = "PrivacyGuardPrefs"; + + private static final String KEY_PRIVACY_GUARD_DEFAULT = "privacy_guard_default"; + + private CheckBoxPreference mPrivacyGuardDefault; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.privacy_guard_prefs); + PreferenceScreen prefSet = getPreferenceScreen(); + + mPrivacyGuardDefault = (CheckBoxPreference) findPreference(KEY_PRIVACY_GUARD_DEFAULT); + mPrivacyGuardDefault.setOnPreferenceChangeListener(this); + + try { + mPrivacyGuardDefault.setChecked(Settings.Secure.getInt(getContentResolver(), + Settings.Secure.PRIVACY_GUARD_DEFAULT) == 1); + } catch (SettingNotFoundException e) { + mPrivacyGuardDefault.setChecked(false); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + final View view = super.onCreateView(inflater, container, savedInstanceState); + final ListView list = (ListView) view.findViewById(android.R.id.list); + // our container already takes care of the padding + int paddingTop = list.getPaddingTop(); + int paddingBottom = list.getPaddingBottom(); + list.setPadding(0, paddingTop, 0, paddingBottom); + return view; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mPrivacyGuardDefault) { + boolean value = (Boolean) newValue; + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.PRIVACY_GUARD_DEFAULT, value ? 1 : 0); + return true; + } + return false; + } +} |