App ops: Support neighboring cells, call phone, improve UI.

Change-Id: I0783c7ae941499cf9847bcf657141c3aea76f59d
This commit is contained in:
Dianne Hackborn
2013-01-31 15:06:57 -08:00
parent 6b99860a9a
commit 27daaab633
5 changed files with 139 additions and 65 deletions

View File

@@ -574,6 +574,8 @@
<item>Write calendar</item> <item>Write calendar</item>
<item>Wi-Fi scan</item> <item>Wi-Fi scan</item>
<item>Post notification</item> <item>Post notification</item>
<item>Cell tower scan</item>
<item>Call phone</item>
</string-array> </string-array>
<!-- Titles for the list of long press timeout options. --> <!-- Titles for the list of long press timeout options. -->

View File

@@ -2702,7 +2702,7 @@
<!-- App Ops Settings --> <skip /> <!-- App Ops Settings --> <skip />
<!-- [CHAR LIMIT=NONE] App ops settings title, on main settings screen. If clicked, the user is taken to a settings screen for app operations --> <!-- [CHAR LIMIT=NONE] App ops settings title, on main settings screen. If clicked, the user is taken to a settings screen for app operations -->
<string name="app_ops_settings">App ops</string> <string name="app_ops_settings">Privacy</string>
<!-- [CHAR LIMIT=NONE] Time label for an operation that is currently running. --> <!-- [CHAR LIMIT=NONE] Time label for an operation that is currently running. -->
<string name="app_ops_running">Running</string> <string name="app_ops_running">Running</string>
<!-- [CHAR LIMIT=NONE] Time label for an operation that has never executed. --> <!-- [CHAR LIMIT=NONE] Time label for an operation that has never executed. -->

View File

@@ -304,10 +304,16 @@ public class AppOpsCategory extends ListFragment implements
// Start out with a progress indicator. // Start out with a progress indicator.
setListShown(false); setListShown(false);
}
// Prepare the loader. Either re-connect with an existing one, @Override
// or start a new one. public void onStart() {
getLoaderManager().initLoader(0, null, this); super.onStart();
// Prepare the loader. We don't monitor for changes while stopped,
// so want to re-start the loader (retrieving a new data set) each
// time we start.
getLoaderManager().restartLoader(0, null, this);
} }
// utility method used to start sub activity // utility method used to start sub activity

View File

@@ -117,39 +117,41 @@ public class AppOpsDetails extends Fragment {
List<AppOpsState.AppOpEntry> entries = mState.buildState(tpl, List<AppOpsState.AppOpEntry> entries = mState.buildState(tpl,
mPackageInfo.applicationInfo.uid, mPackageInfo.packageName); mPackageInfo.applicationInfo.uid, mPackageInfo.packageName);
for (final AppOpsState.AppOpEntry entry : entries) { for (final AppOpsState.AppOpEntry entry : entries) {
for (int i=0; i<entry.getNumOpEntry(); i++) { final AppOpsManager.OpEntry firstOp = entry.getOpEntry(0);
final AppOpsManager.OpEntry op = entry.getOpEntry(i); final View view = mInflater.inflate(R.layout.app_ops_details_item,
final View view = mInflater.inflate(R.layout.app_ops_details_item, mOperationsSection, false);
mOperationsSection, false); mOperationsSection.addView(view);
mOperationsSection.addView(view); String perm = AppOpsManager.opToPermission(firstOp.getOp());
String perm = AppOpsManager.opToPermission(op.getOp()); if (perm != null) {
if (perm != null) { try {
try { PermissionInfo pi = mPm.getPermissionInfo(perm, 0);
PermissionInfo pi = mPm.getPermissionInfo(perm, 0); if (pi.group != null && !lastPermGroup.equals(pi.group)) {
if (pi.group != null && !lastPermGroup.equals(pi.group)) { lastPermGroup = pi.group;
lastPermGroup = pi.group; PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(pi.group, 0);
PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(pi.group, 0); if (pgi.icon != 0) {
if (pgi.icon != 0) { ((ImageView)view.findViewById(R.id.op_icon)).setImageDrawable(
((ImageView)view.findViewById(R.id.op_icon)).setImageDrawable( pgi.loadIcon(mPm));
pgi.loadIcon(mPm));
}
} }
} catch (NameNotFoundException e) {
} }
} catch (NameNotFoundException e) {
} }
((TextView)view.findViewById(R.id.op_name)).setText(mState.getLabelText(op));
((TextView)view.findViewById(R.id.op_time)).setText(mState.getTimeText(op));
Switch sw = (Switch)view.findViewById(R.id.switchWidget);
sw.setChecked(op.getMode() == AppOpsManager.MODE_ALLOWED);
sw.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mAppOps.setMode(op.getOp(), entry.getPackageOps().getUid(),
entry.getPackageOps().getPackageName(), isChecked
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
}
});
} }
((TextView)view.findViewById(R.id.op_name)).setText(
entry.getBriefLabelText(mState));
((TextView)view.findViewById(R.id.op_time)).setText(
entry.getTimeText(res));
Switch sw = (Switch)view.findViewById(R.id.switchWidget);
final int switchOp = AppOpsManager.opToSwitch(firstOp.getOp());
sw.setChecked(mAppOps.checkOp(switchOp, entry.getPackageOps().getUid(),
entry.getPackageOps().getPackageName()) == AppOpsManager.MODE_ALLOWED);
sw.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mAppOps.setMode(switchOp, entry.getPackageOps().getUid(),
entry.getPackageOps().getPackageName(), isChecked
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
}
});
} }
} }

View File

@@ -60,13 +60,16 @@ public class AppOpsState {
public static class OpsTemplate implements Parcelable { public static class OpsTemplate implements Parcelable {
public final int[] ops; public final int[] ops;
public final boolean[] showPerms;
public OpsTemplate(int[] _ops) { public OpsTemplate(int[] _ops, boolean[] _showPerms) {
ops = _ops; ops = _ops;
showPerms = _showPerms;
} }
OpsTemplate(Parcel src) { OpsTemplate(Parcel src) {
ops = src.createIntArray(); ops = src.createIntArray();
showPerms = src.createBooleanArray();
} }
@Override @Override
@@ -77,6 +80,7 @@ public class AppOpsState {
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeIntArray(ops); dest.writeIntArray(ops);
dest.writeBooleanArray(showPerms);
} }
public static final Creator<OpsTemplate> CREATOR = new Creator<OpsTemplate>() { public static final Creator<OpsTemplate> CREATOR = new Creator<OpsTemplate>() {
@@ -94,7 +98,13 @@ public class AppOpsState {
new int[] { AppOpsManager.OP_COARSE_LOCATION, new int[] { AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION, AppOpsManager.OP_FINE_LOCATION,
AppOpsManager.OP_GPS, AppOpsManager.OP_GPS,
AppOpsManager.OP_WIFI_SCAN } AppOpsManager.OP_WIFI_SCAN,
AppOpsManager.OP_NEIGHBORING_CELLS },
new boolean[] { true,
true,
false,
false,
false }
); );
public static final OpsTemplate PERSONAL_TEMPLATE = new OpsTemplate( public static final OpsTemplate PERSONAL_TEMPLATE = new OpsTemplate(
@@ -103,12 +113,22 @@ public class AppOpsState {
AppOpsManager.OP_READ_CALL_LOG, AppOpsManager.OP_READ_CALL_LOG,
AppOpsManager.OP_WRITE_CALL_LOG, AppOpsManager.OP_WRITE_CALL_LOG,
AppOpsManager.OP_READ_CALENDAR, AppOpsManager.OP_READ_CALENDAR,
AppOpsManager.OP_WRITE_CALENDAR } AppOpsManager.OP_WRITE_CALENDAR },
new boolean[] { true,
true,
true,
true,
true,
true }
); );
public static final OpsTemplate DEVICE_TEMPLATE = new OpsTemplate( public static final OpsTemplate DEVICE_TEMPLATE = new OpsTemplate(
new int[] { AppOpsManager.OP_VIBRATE, new int[] { AppOpsManager.OP_VIBRATE,
AppOpsManager.OP_POST_NOTIFICATION } AppOpsManager.OP_POST_NOTIFICATION,
AppOpsManager.OP_CALL_PHONE },
new boolean[] { false,
false,
true }
); );
public static final OpsTemplate[] ALL_TEMPLATES = new OpsTemplate[] { public static final OpsTemplate[] ALL_TEMPLATES = new OpsTemplate[] {
@@ -124,6 +144,8 @@ public class AppOpsState {
private final File mApkFile; private final File mApkFile;
private final SparseArray<AppOpsManager.OpEntry> mOps private final SparseArray<AppOpsManager.OpEntry> mOps
= new SparseArray<AppOpsManager.OpEntry>(); = new SparseArray<AppOpsManager.OpEntry>();
private final SparseArray<AppOpEntry> mOpSwitches
= new SparseArray<AppOpEntry>();
private String mLabel; private String mLabel;
private Drawable mIcon; private Drawable mIcon;
private boolean mMounted; private boolean mMounted;
@@ -134,14 +156,19 @@ public class AppOpsState {
mApkFile = new File(info.sourceDir); mApkFile = new File(info.sourceDir);
} }
public void addOp(AppOpsManager.OpEntry op) { public void addOp(AppOpEntry entry, AppOpsManager.OpEntry op) {
mOps.put(op.getOp(), op); mOps.put(op.getOp(), op);
mOpSwitches.put(AppOpsManager.opToSwitch(op.getOp()), entry);
} }
public boolean hasOp(int op) { public boolean hasOp(int op) {
return mOps.indexOfKey(op) >= 0; return mOps.indexOfKey(op) >= 0;
} }
public AppOpEntry getOpSwitch(int op) {
return mOpSwitches.get(AppOpsManager.opToSwitch(op));
}
public ApplicationInfo getApplicationInfo() { public ApplicationInfo getApplicationInfo() {
return mInfo; return mInfo;
} }
@@ -199,32 +226,45 @@ public class AppOpsState {
private final AppOpsManager.PackageOps mPkgOps; private final AppOpsManager.PackageOps mPkgOps;
private final ArrayList<AppOpsManager.OpEntry> mOps private final ArrayList<AppOpsManager.OpEntry> mOps
= new ArrayList<AppOpsManager.OpEntry>(); = new ArrayList<AppOpsManager.OpEntry>();
private final ArrayList<AppOpsManager.OpEntry> mBriefOps
= new ArrayList<AppOpsManager.OpEntry>();
private final AppEntry mApp; private final AppEntry mApp;
public AppOpEntry(AppOpsManager.PackageOps pkg, AppOpsManager.OpEntry op, AppEntry app) { public AppOpEntry(AppOpsManager.PackageOps pkg, AppOpsManager.OpEntry op, AppEntry app,
boolean brief) {
mPkgOps = pkg; mPkgOps = pkg;
mApp = app; mApp = app;
mApp.addOp(op); mApp.addOp(this, op);
mOps.add(op); mOps.add(op);
if (brief) {
mBriefOps.add(op);
}
} }
public void addOp(AppOpsManager.OpEntry op) { private static void addOp(ArrayList<AppOpsManager.OpEntry> list, AppOpsManager.OpEntry op) {
mApp.addOp(op); for (int i=0; i<list.size(); i++) {
for (int i=0; i<mOps.size(); i++) { AppOpsManager.OpEntry pos = list.get(i);
AppOpsManager.OpEntry pos = mOps.get(i);
if (pos.isRunning() != op.isRunning()) { if (pos.isRunning() != op.isRunning()) {
if (op.isRunning()) { if (op.isRunning()) {
mOps.add(i, op); list.add(i, op);
return; return;
} }
continue; continue;
} }
if (pos.getTime() < op.getTime()) { if (pos.getTime() < op.getTime()) {
mOps.add(i, op); list.add(i, op);
return; return;
} }
} }
mOps.add(op); list.add(op);
}
public void addOp(AppOpsManager.OpEntry op, boolean brief) {
mApp.addOp(this, op);
addOp(mOps, op);
if (brief) {
addOp(mBriefOps, op);
}
} }
public AppEntry getAppEntry() { public AppEntry getAppEntry() {
@@ -243,21 +283,34 @@ public class AppOpsState {
return mOps.get(pos); return mOps.get(pos);
} }
public CharSequence getLabelText(AppOpsState state) { private CharSequence getLabelText(ArrayList<AppOpsManager.OpEntry> ops,
if (getNumOpEntry() == 1) { AppOpsState state) {
return state.mOpNames[getOpEntry(0).getOp()]; if (ops.size() == 1) {
return state.mOpNames[ops.get(0).getOp()];
} else { } else {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (int i=0; i<getNumOpEntry(); i++) { for (int i=0; i<ops.size(); i++) {
if (i > 0) { if (i > 0) {
builder.append(", "); builder.append(", ");
} }
builder.append(state.mOpNames[getOpEntry(i).getOp()]); builder.append(state.mOpNames[ops.get(i).getOp()]);
} }
return builder.toString(); return builder.toString();
} }
} }
public CharSequence getLabelText(AppOpsState state) {
return getLabelText(mOps, state);
}
public CharSequence getBriefLabelText(AppOpsState state) {
if (mBriefOps.size() > 0) {
return getLabelText(mBriefOps, state);
} else {
return getLabelText(mOps, state);
}
}
public CharSequence getTimeText(Resources res) { public CharSequence getTimeText(Resources res) {
if (isRunning()) { if (isRunning()) {
return res.getText(R.string.app_ops_running); return res.getText(R.string.app_ops_running);
@@ -305,8 +358,8 @@ public class AppOpsState {
}; };
private void addOp(List<AppOpEntry> entries, AppOpsManager.PackageOps pkgOps, private void addOp(List<AppOpEntry> entries, AppOpsManager.PackageOps pkgOps,
AppEntry appEntry, AppOpsManager.OpEntry opEntry) { AppEntry appEntry, AppOpsManager.OpEntry opEntry, boolean brief, boolean allowMerge) {
if (entries.size() > 0) { if (allowMerge && entries.size() > 0) {
AppOpEntry last = entries.get(entries.size()-1); AppOpEntry last = entries.get(entries.size()-1);
if (last.getAppEntry() == appEntry) { if (last.getAppEntry() == appEntry) {
boolean lastExe = last.getTime() != 0; boolean lastExe = last.getTime() != 0;
@@ -314,12 +367,17 @@ public class AppOpsState {
if (lastExe == entryExe) { if (lastExe == entryExe) {
if (DEBUG) Log.d(TAG, "Add op " + opEntry.getOp() + " to package " if (DEBUG) Log.d(TAG, "Add op " + opEntry.getOp() + " to package "
+ pkgOps.getPackageName() + ": append to " + last); + pkgOps.getPackageName() + ": append to " + last);
last.addOp(opEntry); last.addOp(opEntry, brief);
return; return;
} }
} }
} }
AppOpEntry entry = new AppOpEntry(pkgOps, opEntry, appEntry); AppOpEntry entry = appEntry.getOpSwitch(opEntry.getOp());
if (entry != null) {
entry.addOp(opEntry, brief);
return;
}
entry = new AppOpEntry(pkgOps, opEntry, appEntry, brief);
if (DEBUG) Log.d(TAG, "Add op " + opEntry.getOp() + " to package " if (DEBUG) Log.d(TAG, "Add op " + opEntry.getOp() + " to package "
+ pkgOps.getPackageName() + ": making new " + entry); + pkgOps.getPackageName() + ": making new " + entry);
entries.add(entry); entries.add(entry);
@@ -354,15 +412,19 @@ public class AppOpsState {
final Context context = mContext; final Context context = mContext;
final HashMap<String, AppEntry> appEntries = new HashMap<String, AppEntry>(); final HashMap<String, AppEntry> appEntries = new HashMap<String, AppEntry>();
List<AppOpEntry> entries = new ArrayList<AppOpEntry>(); final List<AppOpEntry> entries = new ArrayList<AppOpEntry>();
ArrayList<String> perms = new ArrayList<String>(); final ArrayList<String> perms = new ArrayList<String>();
ArrayList<Integer> permOps = new ArrayList<Integer>(); final ArrayList<Integer> permOps = new ArrayList<Integer>();
final boolean[] brief = new boolean[AppOpsManager._NUM_OP];
for (int i=0; i<tpl.ops.length; i++) { for (int i=0; i<tpl.ops.length; i++) {
String perm = AppOpsManager.opToPermission(tpl.ops[i]); brief[tpl.ops[i]] = tpl.showPerms[i];
if (perm != null && !perms.contains(perm)) { if (tpl.showPerms[i]) {
perms.add(perm); String perm = AppOpsManager.opToPermission(tpl.ops[i]);
permOps.add(tpl.ops[i]); if (perm != null && !perms.contains(perm)) {
perms.add(perm);
permOps.add(tpl.ops[i]);
}
} }
} }
@@ -382,7 +444,8 @@ public class AppOpsState {
} }
for (int j=0; j<pkgOps.getOps().size(); j++) { for (int j=0; j<pkgOps.getOps().size(); j++) {
AppOpsManager.OpEntry opEntry = pkgOps.getOps().get(j); AppOpsManager.OpEntry opEntry = pkgOps.getOps().get(j);
addOp(entries, pkgOps, appEntry, opEntry); addOp(entries, pkgOps, appEntry, opEntry, brief[opEntry.getOp()],
packageName == null);
} }
} }
} }
@@ -439,7 +502,8 @@ public class AppOpsState {
AppOpsManager.OpEntry opEntry = new AppOpsManager.OpEntry( AppOpsManager.OpEntry opEntry = new AppOpsManager.OpEntry(
permOps.get(k), AppOpsManager.MODE_ALLOWED, 0, 0, 0); permOps.get(k), AppOpsManager.MODE_ALLOWED, 0, 0, 0);
dummyOps.add(opEntry); dummyOps.add(opEntry);
addOp(entries, pkgOps, appEntry, opEntry); addOp(entries, pkgOps, appEntry, opEntry, brief[opEntry.getOp()],
packageName == null);
} }
} }
} }