summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Mortimer <sam@mortimer.me.uk>2014-11-25 14:18:22 -0800
committerMichael Bestas <mikeioannina@gmail.com>2016-01-03 14:44:44 -0800
commit7822cf9f28703bc48ab257ffe31ef2e22ae11149 (patch)
tree056dcbe9c4e798253990cc4ffc238b79d55e7566
parent556d77feaa524c5e9108139ef076dd73268b71d7 (diff)
downloadpackages_apps_Settings-7822cf9f28703bc48ab257ffe31ef2e22ae11149.zip
packages_apps_Settings-7822cf9f28703bc48ab257ffe31ef2e22ae11149.tar.gz
packages_apps_Settings-7822cf9f28703bc48ab257ffe31ef2e22ae11149.tar.bz2
[1/2] Settings AppOps: show/hide app type and allow/deny counters
Squash of: Author: Sam Mortimer <sam@mortimer.me.uk> Date: Thu Oct 3 13:51:31 2013 -0700 AppOps: menu options to show/hide user and system apps Change-Id: Ia55364c361db949a891e4b53a757ee4dff519eb6 Author: Sam Mortimer <sam@mortimer.me.uk> Author: Danny Baumann <dannybaumann@web.de> Date: Wed Oct 2 22:24:14 2013 -0700 [1/2] AppOps: Show allowed/ignored counts Change-Id: Ia27737155c33e934df9a2a1fcff88379240fd096 Author: Sam Mortimer <sam@mortimer.me.uk> Date: Sun Dec 1 21:39:07 2013 -0800 AppOps: only apply app type filters to summary screen *) If buildState() is called for a single package, do not apply hide user/system app filters. *) Fixes show detail display when called via long press in privacyguard and an AppOp hide filter is ticked. Change-Id: If31d094fb989836ab6829daeefe820695032c0ed Change-Id: I4d6f37b3923eb9bc98d67101d6f0ef481a427682
-rw-r--r--res/layout/app_ops_details_item.xml18
-rw-r--r--res/menu/appops_manager.xml26
-rw-r--r--res/values/cm_plurals.xml10
-rw-r--r--res/values/cm_strings.xml14
-rw-r--r--src/com/android/settings/applications/AppOpsDetails.java2
-rw-r--r--src/com/android/settings/applications/AppOpsState.java117
-rw-r--r--src/com/android/settings/applications/AppOpsSummary.java114
7 files changed, 266 insertions, 35 deletions
diff --git a/res/layout/app_ops_details_item.xml b/res/layout/app_ops_details_item.xml
index 1124ec7..eb206ae 100644
--- a/res/layout/app_ops_details_item.xml
+++ b/res/layout/app_ops_details_item.xml
@@ -54,6 +54,18 @@
android:id="@+id/op_time"
android:layout_column="1"
android:layout_row="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_horizontal|top"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart" />
+
+ <TextView
+ android:id="@+id/op_counts"
+ android:layout_column="1"
+ android:layout_row="2"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal|top"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart" />
@@ -65,7 +77,8 @@
android:layout_gravity="center_vertical"
android:layout_marginStart="8dip"
android:layout_column="2"
- android:layout_row="1"
+ android:layout_row="0"
+ android:layout_rowSpan="3"
android:padding="8dip"
android:focusable="false"
android:entries="@+array/app_ops_permissions" />
@@ -77,7 +90,8 @@
android:layout_gravity="center_vertical"
android:layout_marginStart="8dip"
android:layout_column="2"
- android:layout_row="1"
+ android:layout_row="0"
+ android:layout_rowSpan="3"
android:padding="8dip"
android:focusable="false"
android:clickable="true" />
diff --git a/res/menu/appops_manager.xml b/res/menu/appops_manager.xml
new file mode 100644
index 0000000..f89b738
--- /dev/null
+++ b/res/menu/appops_manager.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The CyanogenMod 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/show_user_apps"
+ android:title="@string/app_ops_show_user_apps"
+ android:checkable="true" />
+ <item android:id="@+id/show_system_apps"
+ android:title="@string/app_ops_show_system_apps"
+ android:checkable="true" />
+ <item android:id="@+id/reset_counters"
+ android:title="@string/app_ops_reset_counters" />
+</menu>
diff --git a/res/values/cm_plurals.xml b/res/values/cm_plurals.xml
index 6fb1e3e..d9b3a31 100644
--- a/res/values/cm_plurals.xml
+++ b/res/values/cm_plurals.xml
@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013-2014 The CyanogenMod 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.
@@ -16,4 +19,9 @@
<item quantity="one"><xliff:g id="seconds">%d</xliff:g> second</item>
<item quantity="other"><xliff:g id="seconds">%d</xliff:g> seconds</item>
</plurals>
-</resources> \ No newline at end of file
+
+ <plurals name="app_ops_count">
+ <item quantity="one">once</item>
+ <item quantity="other">%d times</item>
+ </plurals>
+</resources>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 90baf4d..80dcfc4 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -490,6 +490,19 @@
<string name="app_ops_permissions_ignored">Ignored</string>
<string name="app_ops_permissions_always_ask">Always ask</string>
+ <!-- App ops detail -->
+ <string name="app_ops_entry_summary"><xliff:g id="op">%1$s</xliff:g> (used <xliff:g id="count">%2$s</xliff:g>)</string>
+ <string name="app_ops_allowed_count">Allowed <xliff:g id="count" example="2 times">%s</xliff:g></string>
+ <string name="app_ops_ignored_count">Denied <xliff:g id="count" example="2 times">%s</xliff:g></string>
+ <string name="app_ops_both_count">Allowed <xliff:g id="count">%1$s</xliff:g>, denied <xliff:g id="count">%2$s</xliff:g></string>
+
+ <!-- App ops menu options -->
+ <string name="app_ops_show_user_apps">Show user apps</string>
+ <string name="app_ops_show_system_apps">Show built-in apps</string>
+ <string name="app_ops_reset_counters">Reset allow/deny counters</string>
+ <string name="app_ops_reset_confirm_title">Confirm counters reset</string>
+ <string name="app_ops_reset_confirm_mesg">Are you sure you wish to reset counters?</string>
+
<string name="ok">OK</string>
<!-- Hostname setting -->
@@ -939,7 +952,6 @@
<string name="block_notifications_title">Filter notifications</string>
<string name="block_notifications_summary">Manage ignored notifications and filters</string>
<string name="no_filters_title">No filters set</string>
- <string name="app_ops_ignored_count">Denied <xliff:g id="count" example="2 times">%s</xliff:g></string>
<!-- Anonymous Statistics #CM -->
<!-- About device screen, list item title. Takes the user to the screen about opting in or out of anonymous statistics. -->
diff --git a/src/com/android/settings/applications/AppOpsDetails.java b/src/com/android/settings/applications/AppOpsDetails.java
index 13e4831..12da8b8 100644
--- a/src/com/android/settings/applications/AppOpsDetails.java
+++ b/src/com/android/settings/applications/AppOpsDetails.java
@@ -172,6 +172,8 @@ public class AppOpsDetails extends InstrumentedFragment {
}
((TextView)view.findViewById(R.id.op_name)).setText(
entry.getSwitchText(mState));
+ ((TextView)view.findViewById(R.id.op_counts)).setText(
+ entry.getCountsText(res));
((TextView)view.findViewById(R.id.op_time)).setText(
entry.getTimeText(res, true));
diff --git a/src/com/android/settings/applications/AppOpsState.java b/src/com/android/settings/applications/AppOpsState.java
index ae58ddd..e46d94a 100644
--- a/src/com/android/settings/applications/AppOpsState.java
+++ b/src/com/android/settings/applications/AppOpsState.java
@@ -16,6 +16,7 @@
package com.android.settings.applications;
+import android.app.Activity;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -23,6 +24,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -52,12 +54,15 @@ public class AppOpsState {
List<AppOpEntry> mApps;
+ private SharedPreferences mPreferences;
+
public AppOpsState(Context context) {
mContext = context;
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
mPm = context.getPackageManager();
mOpSummaries = context.getResources().getTextArray(R.array.app_ops_summaries_cm);
mOpLabels = context.getResources().getTextArray(R.array.app_ops_labels_cm);
+ mPreferences = context.getSharedPreferences("appops_manager", Activity.MODE_PRIVATE);
}
public static class OpsTemplate implements Parcelable {
@@ -383,30 +388,59 @@ public class AppOpsState {
}
private CharSequence getCombinedText(ArrayList<AppOpsManager.OpEntry> ops,
- CharSequence[] items) {
- if (ops.size() == 1) {
- return items[ops.get(0).getOp()];
- } else {
- StringBuilder builder = new StringBuilder();
- for (int i=0; i<ops.size(); i++) {
- if (i > 0) {
- builder.append(", ");
- }
- builder.append(items[ops.get(i).getOp()]);
+ CharSequence[] items, Resources res, boolean withTerseCounts) {
+ StringBuilder builder = new StringBuilder();
+ for (int i=0; i<ops.size(); i++) {
+ if (i > 0) {
+ builder.append(", ");
}
- return builder.toString();
+ AppOpsManager.OpEntry op = ops.get(i);
+ int count = op.getAllowedCount() + op.getIgnoredCount();
+
+ if (withTerseCounts && count > 0) {
+ String quantity = res.getQuantityString(R.plurals.app_ops_count,
+ count, count);
+ builder.append(res.getString(R.string.app_ops_entry_summary,
+ items[op.getOp()], quantity));
+ } else {
+ builder.append(items[op.getOp()]);
+ }
+ }
+ return builder.toString();
+ }
+
+ public CharSequence getCountsText(Resources res) {
+ AppOpsManager.OpEntry op = mOps.get(0);
+ int allowed = op.getAllowedCount();
+ int denied = op.getIgnoredCount();
+
+ if (allowed == 0 && denied == 0) {
+ return null;
}
+
+ CharSequence allowedQuantity = res.getQuantityString(R.plurals.app_ops_count,
+ allowed, allowed);
+ CharSequence deniedQuantity = res.getQuantityString(R.plurals.app_ops_count,
+ denied, denied);
+
+ if (denied == 0) {
+ return res.getString(R.string.app_ops_allowed_count, allowedQuantity);
+ } else if (allowed == 0) {
+ return res.getString(R.string.app_ops_ignored_count, deniedQuantity);
+ }
+ return res.getString(R.string.app_ops_both_count, allowedQuantity, deniedQuantity);
}
public CharSequence getSummaryText(AppOpsState state) {
- return getCombinedText(mOps, state.mOpSummaries);
+ return getCombinedText(mOps, state.mOpSummaries, state.mContext.getResources(), true);
}
public CharSequence getSwitchText(AppOpsState state) {
+ final Resources res = state.mContext.getResources();
if (mSwitchOps.size() > 0) {
- return getCombinedText(mSwitchOps, state.mOpLabels);
+ return getCombinedText(mSwitchOps, state.mOpLabels, res, false);
} else {
- return getCombinedText(mOps, state.mOpLabels);
+ return getCombinedText(mOps, state.mOpLabels, res, false);
}
}
@@ -490,19 +524,34 @@ public class AppOpsState {
}
private AppEntry getAppEntry(final Context context, final HashMap<String, AppEntry> appEntries,
- final String packageName, ApplicationInfo appInfo) {
+ final String packageName, ApplicationInfo appInfo, boolean applyFilters) {
+
+ if (appInfo == null) {
+ try {
+ appInfo = mPm.getApplicationInfo(packageName,
+ PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to find info for package " + packageName);
+ return null;
+ }
+ }
+
+ if (applyFilters) {
+ // Hide user apps if needed
+ if (!shouldShowUserApps() &&
+ (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return null;
+ }
+ // Hide system apps if needed
+ if (!shouldShowSystemApps() &&
+ (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return null;
+ }
+ }
+
AppEntry appEntry = appEntries.get(packageName);
if (appEntry == null) {
- if (appInfo == null) {
- try {
- appInfo = mPm.getApplicationInfo(packageName,
- PackageManager.GET_DISABLED_COMPONENTS
- | PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Unable to find info for package " + packageName);
- return null;
- }
- }
appEntry = new AppEntry(this, appInfo);
appEntry.loadLabel(context);
appEntries.put(packageName, appEntry);
@@ -510,6 +559,14 @@ public class AppOpsState {
return appEntry;
}
+ private boolean shouldShowUserApps() {
+ return mPreferences.getBoolean("show_user_apps", true);
+ }
+
+ private boolean shouldShowSystemApps() {
+ return mPreferences.getBoolean("show_system_apps", true);
+ }
+
public List<AppOpEntry> buildState(OpsTemplate tpl, int uid, String packageName) {
final Context context = mContext;
@@ -530,6 +587,9 @@ public class AppOpsState {
}
}
+ // Whether to apply hide user / system app filters
+ final boolean applyFilters = (packageName == null);
+
List<AppOpsManager.PackageOps> pkgs;
if (packageName != null) {
pkgs = mAppOps.getOpsForPackage(uid, packageName, tpl.ops);
@@ -540,7 +600,8 @@ public class AppOpsState {
if (pkgs != null) {
for (int i=0; i<pkgs.size(); i++) {
AppOpsManager.PackageOps pkgOps = pkgs.get(i);
- AppEntry appEntry = getAppEntry(context, appEntries, pkgOps.getPackageName(), null);
+ AppEntry appEntry = getAppEntry(context, appEntries, pkgOps.getPackageName(), null,
+ applyFilters);
if (appEntry == null) {
continue;
}
@@ -568,7 +629,7 @@ public class AppOpsState {
for (int i=0; i<apps.size(); i++) {
PackageInfo appInfo = apps.get(i);
AppEntry appEntry = getAppEntry(context, appEntries, appInfo.packageName,
- appInfo.applicationInfo);
+ appInfo.applicationInfo, applyFilters);
if (appEntry == null) {
continue;
}
@@ -602,7 +663,7 @@ public class AppOpsState {
}
AppOpsManager.OpEntry opEntry = new AppOpsManager.OpEntry(
- permOps.get(k), AppOpsManager.MODE_ALLOWED, 0, 0, 0, -1, null);
+ permOps.get(k), AppOpsManager.MODE_ALLOWED, 0, 0, 0, -1, null, 0, 0);
dummyOps.add(opEntry);
addOp(entries, pkgOps, appEntry, opEntry, packageName == null,
packageName == null ? 0 : opToOrder[opEntry.getOp()]);
diff --git a/src/com/android/settings/applications/AppOpsSummary.java b/src/com/android/settings/applications/AppOpsSummary.java
index 0d47dc1..71af7f4 100644
--- a/src/com/android/settings/applications/AppOpsSummary.java
+++ b/src/com/android/settings/applications/AppOpsSummary.java
@@ -16,10 +16,16 @@
package com.android.settings.applications;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.AppOpsManager;
import android.app.Fragment;
import android.app.FragmentManager;
+import android.content.Context;
+import android.content.DialogInterface;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceFrameLayout;
import android.support.v13.app.FragmentPagerAdapter;
@@ -27,6 +33,9 @@ import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.util.TypedValue;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -42,6 +51,11 @@ public class AppOpsSummary extends InstrumentedFragment {
private View mRootView;
private ViewPager mViewPager;
+ private MyPagerAdapter mAdapter;
+
+ private Activity mActivity;
+ private SharedPreferences mPreferences;
+
CharSequence[] mPageNames;
static AppOpsState.OpsTemplate[] sPageTemplates = new AppOpsState.OpsTemplate[] {
AppOpsState.LOCATION_TEMPLATE,
@@ -88,6 +102,10 @@ public class AppOpsSummary extends InstrumentedFragment {
mCurPos = position;
}
+ public int getCurrentPage() {
+ return mCurPos;
+ }
+
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
@@ -96,6 +114,14 @@ public class AppOpsSummary extends InstrumentedFragment {
}
}
+ private void resetAdapter() {
+ // trigger adapter load, preserving the selected page
+ int curPos = mAdapter.getCurrentPage();
+ mViewPager.setAdapter(mAdapter);
+ mViewPager.setOnPageChangeListener(mAdapter);
+ mViewPager.setCurrentItem(curPos);
+ }
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// initialize the inflater
@@ -109,9 +135,9 @@ public class AppOpsSummary extends InstrumentedFragment {
mPageNames = getResources().getTextArray(R.array.app_ops_categories_cm);
mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
- MyPagerAdapter adapter = new MyPagerAdapter(getChildFragmentManager());
- mViewPager.setAdapter(adapter);
- mViewPager.setOnPageChangeListener(adapter);
+ mAdapter = new MyPagerAdapter(getChildFragmentManager());
+ mViewPager.setAdapter(mAdapter);
+ mViewPager.setOnPageChangeListener(mAdapter);
PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs);
Resources.Theme theme = tabs.getContext().getTheme();
@@ -126,6 +152,88 @@ public class AppOpsSummary extends InstrumentedFragment {
((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;
}
+ mActivity = getActivity();
+
return rootView;
}
+
+ private boolean shouldShowUserApps() {
+ return mPreferences.getBoolean("show_user_apps", true);
+ }
+
+ private boolean shouldShowSystemApps() {
+ return mPreferences.getBoolean("show_system_apps", true);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ // get shared preferences
+ mPreferences = mActivity.getSharedPreferences("appops_manager", Activity.MODE_PRIVATE);
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.appops_manager, menu);
+ menu.findItem(R.id.show_user_apps).setChecked(shouldShowUserApps());
+ menu.findItem(R.id.show_system_apps).setChecked(shouldShowSystemApps());
+ }
+
+ private void resetCounters() {
+ final AppOpsManager appOps =
+ (AppOpsManager) mActivity.getSystemService(Context.APP_OPS_SERVICE);
+ if (appOps == null) {
+ return;
+ }
+ appOps.resetCounters();
+ // reload content
+ resetAdapter();
+ }
+
+ private void resetCountersConfirm() {
+ new AlertDialog.Builder(getActivity())
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.app_ops_reset_confirm_title)
+ .setMessage(R.string.app_ops_reset_confirm_mesg)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
+ {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ resetCounters();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.show_user_apps:
+ final String prefNameUserApps = "show_user_apps";
+ // set the menu checkbox and save it in shared preference
+ item.setChecked(!item.isChecked());
+ mPreferences.edit().putBoolean(prefNameUserApps, item.isChecked()).commit();
+ // reload content
+ resetAdapter();
+ return true;
+ case R.id.show_system_apps:
+ final String prefNameSysApps = "show_system_apps";
+ // set the menu checkbox and save it in shared preference
+ item.setChecked(!item.isChecked());
+ mPreferences.edit().putBoolean(prefNameSysApps, item.isChecked()).commit();
+ // reload view content
+ resetAdapter();
+ return true;
+ case R.id.reset_counters:
+ resetCountersConfirm();
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
}