am 610f310b: Merge "New permissions UI." into jb-dev

* commit '610f310b199ec0d93cb6e78106b029d7269015df':
  New permissions UI.
This commit is contained in:
Dianne Hackborn
2012-05-03 19:05:24 -07:00
committed by Android Git Automerger
8 changed files with 381 additions and 409 deletions

View File

@@ -5783,7 +5783,7 @@ package android.content {
field public static final java.lang.String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
field public static final android.os.Parcelable.Creator CREATOR;
field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";

View File

@@ -1240,7 +1240,11 @@ public class Intent implements Parcelable, Cloneable {
* Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} to install a
* package. Tells the installer UI to skip the confirmation with the user
* if the .apk is replacing an existing one.
* @deprecated As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, Android
* will no longer show an interstitial message about updating existing
* applications so this is no longer needed.
*/
@Deprecated
public static final String EXTRA_ALLOW_REPLACE
= "android.intent.extra.ALLOW_REPLACE";

View File

@@ -18,7 +18,9 @@ package android.widget;
import com.android.internal.R;
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -27,14 +29,13 @@ import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.text.Collator;
import java.util.ArrayList;
@@ -42,7 +43,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -58,16 +58,51 @@ import java.util.Set;
*
* {@hide}
*/
public class AppSecurityPermissions implements View.OnClickListener {
public class AppSecurityPermissions {
private enum State {
NO_PERMS,
DANGEROUS_ONLY,
NORMAL_ONLY,
BOTH
public static final int WHICH_PERSONAL = 1<<0;
public static final int WHICH_DEVICE = 1<<1;
public static final int WHICH_NEW = 1<<2;
public static final int WHICH_ALL = 0xffff;
private final static String TAG = "AppSecurityPermissions";
private boolean localLOGV = false;
private Context mContext;
private LayoutInflater mInflater;
private PackageManager mPm;
private PackageInfo mInstalledPackageInfo;
private final Map<String, MyPermissionGroupInfo> mPermGroups
= new HashMap<String, MyPermissionGroupInfo>();
private final List<MyPermissionGroupInfo> mPermGroupsList
= new ArrayList<MyPermissionGroupInfo>();
private final PermissionGroupInfoComparator mPermGroupComparator;
private final PermissionInfoComparator mPermComparator;
private List<MyPermissionInfo> mPermsList;
private CharSequence mNewPermPrefix;
private Drawable mNormalIcon;
private Drawable mDangerousIcon;
static class MyPermissionGroupInfo extends PermissionGroupInfo {
CharSequence mLabel;
final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>();
final ArrayList<MyPermissionInfo> mPersonalPermissions = new ArrayList<MyPermissionInfo>();
final ArrayList<MyPermissionInfo> mDevicePermissions = new ArrayList<MyPermissionInfo>();
final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>();
MyPermissionGroupInfo(PermissionInfo perm) {
name = perm.packageName;
packageName = perm.packageName;
}
MyPermissionGroupInfo(PermissionGroupInfo info) {
super(info);
}
}
static class MyPermissionInfo extends PermissionInfo {
CharSequence mLabel;
/**
* PackageInfo.requestedPermissionsFlags for the new package being installed.
*/
@@ -99,46 +134,99 @@ public class AppSecurityPermissions implements View.OnClickListener {
}
}
private final static String TAG = "AppSecurityPermissions";
private boolean localLOGV = false;
private Context mContext;
private LayoutInflater mInflater;
private PackageManager mPm;
private LinearLayout mPermsView;
private Map<String, CharSequence> mNewMap;
private Map<String, CharSequence> mDangerousMap;
private Map<String, CharSequence> mNormalMap;
private List<MyPermissionInfo> mPermsList;
private String mDefaultGrpLabel;
private String mDefaultGrpName="DefaultGrp";
private String mPermFormat;
private CharSequence mNewPermPrefix;
private Drawable mNormalIcon;
private Drawable mDangerousIcon;
private boolean mExpanded;
private Drawable mShowMaxIcon;
private Drawable mShowMinIcon;
private View mShowMore;
private TextView mShowMoreText;
private ImageView mShowMoreIcon;
private State mCurrentState;
private LinearLayout mNonDangerousList;
private LinearLayout mDangerousList;
private LinearLayout mNewList;
private HashMap<String, CharSequence> mGroupLabelCache;
private View mNoPermsView;
public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
MyPermissionGroupInfo mGroup;
MyPermissionInfo mPerm;
AlertDialog mDialog;
public PermissionItemView(Context context, AttributeSet attrs) {
super(context, attrs);
setClickable(true);
}
public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
boolean first, CharSequence newPermPrefix) {
mGroup = grp;
mPerm = perm;
ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon);
TextView permNameView = (TextView) findViewById(R.id.perm_name);
PackageManager pm = getContext().getPackageManager();
Drawable icon = null;
if (first) {
if (grp.icon != 0) {
icon = grp.loadIcon(pm);
} else {
ApplicationInfo appInfo;
try {
appInfo = pm.getApplicationInfo(grp.packageName, 0);
icon = appInfo.loadIcon(pm);
} catch (NameNotFoundException e) {
}
}
}
CharSequence label = perm.mLabel;
if (perm.mNew && newPermPrefix != null) {
// If this is a new permission, format it appropriately.
SpannableStringBuilder builder = new SpannableStringBuilder();
Parcel parcel = Parcel.obtain();
TextUtils.writeToParcel(newPermPrefix, parcel, 0);
parcel.setDataPosition(0);
CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
parcel.recycle();
builder.append(newStr);
builder.append(label);
label = builder;
}
permGrpIcon.setImageDrawable(icon);
permNameView.setText(label);
setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (mGroup != null && mPerm != null) {
if (mDialog != null) {
mDialog.dismiss();
}
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(mGroup.mLabel);
builder.setMessage(mPerm.loadDescription(getContext().getPackageManager()));
builder.setCancelable(true);
mDialog = builder.show();
mDialog.setCanceledOnTouchOutside(true);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mDialog != null) {
mDialog.dismiss();
}
}
}
public AppSecurityPermissions(Context context, List<PermissionInfo> permList) {
mContext = context;
mPm = mContext.getPackageManager();
loadResources();
mPermComparator = new PermissionInfoComparator();
mPermGroupComparator = new PermissionGroupInfoComparator();
for (PermissionInfo pi : permList) {
mPermsList.add(new MyPermissionInfo(pi));
}
setPermissions(mPermsList);
}
public AppSecurityPermissions(Context context, String packageName) {
mContext = context;
mPm = mContext.getPackageManager();
loadResources();
mPermComparator = new PermissionInfoComparator();
mPermGroupComparator = new PermissionGroupInfoComparator();
mPermsList = new ArrayList<MyPermissionInfo>();
Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
PackageInfo pkgInfo;
@@ -155,11 +243,15 @@ public class AppSecurityPermissions implements View.OnClickListener {
for(MyPermissionInfo tmpInfo : permSet) {
mPermsList.add(tmpInfo);
}
setPermissions(mPermsList);
}
public AppSecurityPermissions(Context context, PackageParser.Package pkg) {
mContext = context;
mPm = mContext.getPackageManager();
loadResources();
mPermComparator = new PermissionInfoComparator();
mPermGroupComparator = new PermissionGroupInfoComparator();
mPermsList = new ArrayList<MyPermissionInfo>();
Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
if(pkg == null) {
@@ -193,10 +285,20 @@ public class AppSecurityPermissions implements View.OnClickListener {
for (MyPermissionInfo tmpInfo : permSet) {
mPermsList.add(tmpInfo);
}
setPermissions(mPermsList);
}
private void loadResources() {
// Pick up from framework resources instead.
mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);
mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);
}
/**
* Utility to retrieve a view displaying a single permission.
* Utility to retrieve a view displaying a single permission. This provides
* the old UI layout for permissions; it is only here for the device admin
* settings to continue to use.
*/
public static View getPermissionItemView(Context context,
CharSequence grpName, CharSequence description, boolean dangerous) {
@@ -204,10 +306,14 @@ public class AppSecurityPermissions implements View.OnClickListener {
Context.LAYOUT_INFLATER_SERVICE);
Drawable icon = context.getResources().getDrawable(dangerous
? R.drawable.ic_bullet_key_permission : R.drawable.ic_text_dot);
return getPermissionItemView(context, inflater, grpName,
return getPermissionItemViewOld(context, inflater, grpName,
description, dangerous, icon);
}
public PackageInfo getInstalledPackageInfo() {
return mInstalledPackageInfo;
}
private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
if(sharedPkgList == null || (sharedPkgList.length == 0)) {
@@ -239,6 +345,7 @@ public class AppSecurityPermissions implements View.OnClickListener {
if ((strList == null) || (strList.length == 0)) {
return;
}
mInstalledPackageInfo = installedPkgInfo;
for (int i=0; i<strList.length; i++) {
String permName = strList[i];
// If we are only looking at an existing app, then we only
@@ -270,13 +377,42 @@ public class AppSecurityPermissions implements View.OnClickListener {
// to see, so skip it.
continue;
}
final String origGroupName = tmpPermInfo.group;
String groupName = origGroupName;
if (groupName == null) {
groupName = tmpPermInfo.packageName;
tmpPermInfo.group = groupName;
}
MyPermissionGroupInfo group = mPermGroups.get(groupName);
if (group == null) {
PermissionGroupInfo grp = null;
if (origGroupName != null) {
grp = mPm.getPermissionGroupInfo(origGroupName, 0);
}
if (grp != null) {
group = new MyPermissionGroupInfo(grp);
} else {
// We could be here either because the permission
// didn't originally specify a group or the group it
// gave couldn't be found. In either case, we consider
// its group to be the permission's package name.
tmpPermInfo.group = tmpPermInfo.packageName;
group = mPermGroups.get(tmpPermInfo.group);
if (group == null) {
group = new MyPermissionGroupInfo(tmpPermInfo);
}
group = new MyPermissionGroupInfo(tmpPermInfo);
}
mPermGroups.put(tmpPermInfo.group, group);
}
final boolean newPerm = installedPkgInfo != null
&& (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo);
myPerm.mNewReqFlags = flagsList[i];
myPerm.mExistingReqFlags = existingFlags;
// This is a new permission if the app is already installed and
// doesn't currently hold this permission.
myPerm.mNew = installedPkgInfo != null
&& (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
myPerm.mNew = newPerm;
permSet.add(myPerm);
} catch (NameNotFoundException e) {
Log.i(TAG, "Ignoring unknown permission:"+permName);
@@ -285,149 +421,99 @@ public class AppSecurityPermissions implements View.OnClickListener {
}
public int getPermissionCount() {
return mPermsList.size();
return getPermissionCount(WHICH_ALL);
}
private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) {
if (which == WHICH_NEW) {
return grp.mNewPermissions;
} else if (which == WHICH_PERSONAL) {
return grp.mPersonalPermissions;
} else if (which == WHICH_DEVICE) {
return grp.mDevicePermissions;
} else {
return grp.mAllPermissions;
}
}
public int getPermissionCount(int which) {
int N = 0;
for (int i=0; i<mPermGroupsList.size(); i++) {
N += getPermissionList(mPermGroupsList.get(i), which).size();
}
return N;
}
public View getPermissionsView() {
return getPermissionsView(WHICH_ALL);
}
public View getPermissionsView(int which) {
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
mShowMore = mPermsView.findViewById(R.id.show_more);
mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);
mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);
mNewList = (LinearLayout) mPermsView.findViewById(R.id.new_perms_list);
mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);
mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);
mNoPermsView = mPermsView.findViewById(R.id.no_permissions);
// Set up the LinearLayout that acts like a list item.
mShowMore.setClickable(true);
mShowMore.setOnClickListener(this);
mShowMore.setFocusable(true);
LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
View noPermsView = permsView.findViewById(R.id.no_permissions);
// Pick up from framework resources instead.
mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);
mPermFormat = mContext.getString(R.string.permissions_format);
mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);
mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);
mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);
mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark);
// Set permissions view
setPermissions(mPermsList);
return mPermsView;
}
displayPermissions(mPermGroupsList, displayList, which);
if (displayList.getChildCount() <= 0) {
noPermsView.setVisibility(View.VISIBLE);
}
/**
* Utility method that concatenates two strings defined by mPermFormat.
* a null value is returned if both str1 and str2 are null, if one of the strings
* is null the other non null value is returned without formatting
* this is to placate initial error checks
*/
private CharSequence formatPermissions(CharSequence groupDesc, CharSequence permDesc,
boolean newPerms) {
if (permDesc == null) {
return groupDesc;
}
// Sometimes people write permission names with a trailing period;
// strip that if it appears.
int len = permDesc.length();
if (len > 0 && permDesc.charAt(len-1) == '.') {
permDesc = (permDesc.toString()).substring(0, len-1);
}
if (newPerms) {
if (true) {
// If this is a new permission, format it appropriately.
SpannableStringBuilder builder = new SpannableStringBuilder();
if (groupDesc != null) {
// The previous permissions go in front, with a newline
// separating them.
builder.append(groupDesc);
builder.append("\n");
}
Parcel parcel = Parcel.obtain();
TextUtils.writeToParcel(mNewPermPrefix, parcel, 0);
parcel.setDataPosition(0);
CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
parcel.recycle();
builder.append(newStr);
builder.append(permDesc);
return builder;
} else {
// If this is a new permission, format it appropriately.
SpannableStringBuilder builder = new SpannableStringBuilder(permDesc);
builder.insert(0, mNewPermPrefix);
if (groupDesc != null) {
// The previous permissions go in front, with a newline
// separating them.
builder.insert(0, "\n");
builder.insert(0, groupDesc);
}
return builder;
}
}
if (groupDesc == null) {
return permDesc;
}
// groupDesc and permDesc are non null
return String.format(mPermFormat, groupDesc, permDesc.toString());
}
private CharSequence getGroupLabel(String grpName) {
if (grpName == null) {
//return default label
return mDefaultGrpLabel;
}
CharSequence cachedLabel = mGroupLabelCache.get(grpName);
if (cachedLabel != null) {
return cachedLabel;
}
PermissionGroupInfo pgi;
try {
pgi = mPm.getPermissionGroupInfo(grpName, 0);
} catch (NameNotFoundException e) {
Log.i(TAG, "Invalid group name:" + grpName);
return null;
}
CharSequence label = pgi.loadLabel(mPm).toString();
mGroupLabelCache.put(grpName, label);
return label;
return permsView;
}
/**
* Utility method that displays permissions from a map containing group name and
* list of permission descriptions.
*/
private void displayPermissions(Map<String, CharSequence> permInfoMap,
LinearLayout permListView, boolean dangerous) {
private void displayPermissions(List<MyPermissionGroupInfo> groups,
LinearLayout permListView, int which) {
permListView.removeAllViews();
Set<String> permInfoStrSet = permInfoMap.keySet();
for (String loopPermGrpInfoStr : permInfoStrSet) {
CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr);
//guaranteed that grpLabel wont be null since permissions without groups
//will belong to the default group
if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:"
+ permInfoMap.get(loopPermGrpInfoStr));
permListView.addView(getPermissionItemView(grpLabel,
permInfoMap.get(loopPermGrpInfoStr), dangerous));
int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density);
for (int i=0; i<groups.size(); i++) {
MyPermissionGroupInfo grp = groups.get(i);
final List<MyPermissionInfo> perms = getPermissionList(grp, which);
for (int j=0; j<perms.size(); j++) {
MyPermissionInfo perm = perms.get(j);
View view = getPermissionItemView(grp, perm, j == 0,
which != WHICH_NEW ? mNewPermPrefix : null);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
if (j == 0) {
lp.topMargin = spacing;
}
if (j == grp.mAllPermissions.size()-1) {
lp.bottomMargin = spacing;
}
if (permListView.getChildCount() == 0) {
lp.topMargin *= 2;
}
permListView.addView(view, lp);
}
}
}
private void displayNoPermissions() {
mNoPermsView.setVisibility(View.VISIBLE);
private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) {
return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix);
}
private View getPermissionItemView(CharSequence grpName, CharSequence permList,
boolean dangerous) {
return getPermissionItemView(mContext, mInflater, grpName, permList,
dangerous, dangerous ? mDangerousIcon : mNormalIcon);
private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater,
MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first,
CharSequence newPermPrefix) {
PermissionItemView permView = (PermissionItemView)inflater.inflate(
R.layout.app_permission_item, null);
permView.setPermission(grp, perm, first, newPermPrefix);
return permView;
}
private static View getPermissionItemView(Context context, LayoutInflater inflater,
private static View getPermissionItemViewOld(Context context, LayoutInflater inflater,
CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) {
View permView = inflater.inflate(R.layout.app_permission_item, null);
View permView = inflater.inflate(R.layout.app_permission_item_old, null);
TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group);
TextView permDescView = (TextView) permView.findViewById(R.id.permission_list);
@@ -444,41 +530,6 @@ public class AppSecurityPermissions implements View.OnClickListener {
return permView;
}
private void showPermissions() {
switch(mCurrentState) {
case NO_PERMS:
displayNoPermissions();
break;
case DANGEROUS_ONLY:
displayPermissions(mNewMap, mNewList, true);
displayPermissions(mDangerousMap, mDangerousList, true);
break;
case NORMAL_ONLY:
displayPermissions(mNewMap, mNewList, true);
displayPermissions(mNormalMap, mNonDangerousList, false);
break;
case BOTH:
displayPermissions(mNewMap, mNewList, true);
displayPermissions(mDangerousMap, mDangerousList, true);
if (mExpanded) {
displayPermissions(mNormalMap, mNonDangerousList, false);
mShowMoreIcon.setImageDrawable(mShowMaxIcon);
mShowMoreText.setText(R.string.perms_hide);
mNonDangerousList.setVisibility(View.VISIBLE);
} else {
mShowMoreIcon.setImageDrawable(mShowMinIcon);
mShowMoreText.setText(R.string.perms_show_all);
mNonDangerousList.setVisibility(View.GONE);
}
mShowMore.setVisibility(View.VISIBLE);
break;
}
}
private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
int existingReqFlags) {
final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
@@ -496,79 +547,45 @@ public class AppSecurityPermissions implements View.OnClickListener {
}
return false;
}
/*
* Utility method that aggregates all permission descriptions categorized by group
* Say group1 has perm11, perm12, perm13, the group description will be
* perm11_Desc, perm12_Desc, perm13_Desc
*/
private void aggregateGroupDescs(Map<String, List<MyPermissionInfo> > map,
Map<String, CharSequence> retMap, boolean newPerms) {
if(map == null) {
return;
}
if(retMap == null) {
return;
}
Set<String> grpNames = map.keySet();
Iterator<String> grpNamesIter = grpNames.iterator();
while(grpNamesIter.hasNext()) {
CharSequence grpDesc = null;
String grpNameKey = grpNamesIter.next();
List<MyPermissionInfo> grpPermsList = map.get(grpNameKey);
if(grpPermsList == null) {
continue;
}
for(PermissionInfo permInfo: grpPermsList) {
CharSequence permDesc = permInfo.loadLabel(mPm);
grpDesc = formatPermissions(grpDesc, permDesc, newPerms);
}
// Insert grpDesc into map
if(grpDesc != null) {
if(localLOGV) Log.i(TAG, "Group:"+grpNameKey+" description:"+grpDesc.toString());
retMap.put(grpNameKey, grpDesc);
}
}
}
private static class PermissionInfoComparator implements Comparator<PermissionInfo> {
private PackageManager mPm;
private static class PermissionGroupInfoComparator implements Comparator<MyPermissionGroupInfo> {
private final Collator sCollator = Collator.getInstance();
PermissionInfoComparator(PackageManager pm) {
mPm = pm;
PermissionGroupInfoComparator() {
}
public final int compare(PermissionInfo a, PermissionInfo b) {
CharSequence sa = a.loadLabel(mPm);
CharSequence sb = b.loadLabel(mPm);
return sCollator.compare(sa, sb);
public final int compare(MyPermissionGroupInfo a, MyPermissionGroupInfo b) {
if (((a.flags^b.flags)&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) {
return ((a.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) ? -1 : 1;
}
if (a.priority != b.priority) {
return a.priority > b.priority ? -1 : 1;
}
return sCollator.compare(a.mLabel, b.mLabel);
}
}
private static class PermissionInfoComparator implements Comparator<MyPermissionInfo> {
private final Collator sCollator = Collator.getInstance();
PermissionInfoComparator() {
}
public final int compare(MyPermissionInfo a, MyPermissionInfo b) {
return sCollator.compare(a.mLabel, b.mLabel);
}
}
private void addPermToList(List<MyPermissionInfo> permList,
MyPermissionInfo pInfo) {
if (pInfo.mLabel == null) {
pInfo.mLabel = pInfo.loadLabel(mPm);
}
int idx = Collections.binarySearch(permList, pInfo, mPermComparator);
if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size());
if (idx < 0) {
idx = -idx-1;
permList.add(idx, pInfo);
}
}
private void setPermissions(List<MyPermissionInfo> permList) {
mGroupLabelCache = new HashMap<String, CharSequence>();
//add the default label so that uncategorized permissions can go here
mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);
// Map containing group names and a list of permissions under that group
// that are new from the current install
mNewMap = new HashMap<String, CharSequence>();
// Map containing group names and a list of permissions under that group
// categorized as dangerous
mDangerousMap = new HashMap<String, CharSequence>();
// Map containing group names and a list of permissions under that group
// categorized as normal
mNormalMap = new HashMap<String, CharSequence>();
// Additional structures needed to ensure that permissions are unique under
// each group
Map<String, List<MyPermissionInfo>> newMap =
new HashMap<String, List<MyPermissionInfo>>();
Map<String, List<MyPermissionInfo>> dangerousMap =
new HashMap<String, List<MyPermissionInfo>>();
Map<String, List<MyPermissionInfo> > normalMap =
new HashMap<String, List<MyPermissionInfo>>();
PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);
if (permList != null) {
// First pass to group permissions
for (MyPermissionInfo pInfo : permList) {
@@ -577,51 +594,26 @@ public class AppSecurityPermissions implements View.OnClickListener {
if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
continue;
}
Map<String, List<MyPermissionInfo> > permInfoMap;
if (pInfo.mNew) {
permInfoMap = newMap;
} else if ((pInfo.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS) {
permInfoMap = dangerousMap;
} else {
permInfoMap = normalMap;
}
String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group;
if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName);
List<MyPermissionInfo> grpPermsList = permInfoMap.get(grpName);
if(grpPermsList == null) {
grpPermsList = new ArrayList<MyPermissionInfo>();
permInfoMap.put(grpName, grpPermsList);
grpPermsList.add(pInfo);
} else {
int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator);
if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size());
if (idx < 0) {
idx = -idx-1;
grpPermsList.add(idx, pInfo);
MyPermissionGroupInfo group = mPermGroups.get(pInfo.group);
if (group != null) {
pInfo.mLabel = pInfo.loadLabel(mPm);
addPermToList(group.mAllPermissions, pInfo);
if (pInfo.mNew) {
addPermToList(group.mNewPermissions, pInfo);
}
if ((group.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) {
addPermToList(group.mPersonalPermissions, pInfo);
} else {
addPermToList(group.mDevicePermissions, pInfo);
}
}
}
// Second pass to actually form the descriptions
// Look at dangerous permissions first
aggregateGroupDescs(newMap, mNewMap, true);
aggregateGroupDescs(dangerousMap, mDangerousMap, false);
aggregateGroupDescs(normalMap, mNormalMap, false);
}
mCurrentState = State.NO_PERMS;
if (mNewMap.size() > 0 || mDangerousMap.size() > 0) {
mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY;
} else if(mNormalMap.size() > 0) {
mCurrentState = State.NORMAL_ONLY;
for (MyPermissionGroupInfo pgrp : mPermGroups.values()) {
pgrp.mLabel = pgrp.loadLabel(mPm);
mPermGroupsList.add(pgrp);
}
if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState);
showPermissions();
}
public void onClick(View v) {
if(localLOGV) Log.i(TAG, "mExpanded="+mExpanded);
mExpanded = !mExpanded;
showPermissions();
Collections.sort(mPermGroupsList, mPermGroupComparator);
}
}

View File

@@ -19,37 +19,32 @@
Contains the group name and a list of permission labels under the group.
-->
<RelativeLayout
<view class="android.widget.AppSecurityPermissions$PermissionItemView"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/perm_icon"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_alignParentLeft="true"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="8dp"
android:scaleType="fitCenter" />
<TextView
android:id="@+id/permission_group"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingLeft="6dip"
android:layout_toRightOf="@id/perm_icon"
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="match_parent"
android:background="?android:attr/dividerVertical" />
<TextView
android:id="@+id/permission_list"
android:id="@+id/perm_name"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginTop="-4dip"
android:paddingBottom="8dip"
android:paddingLeft="6dip"
android:layout_below="@id/permission_group"
android:layout_toRightOf="@id/perm_icon"
android:layout_marginLeft="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:layout_gravity="top|left" />
</RelativeLayout>
</view>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source 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.
-->
<!--
Defines the layout of a single permission item.
Contains the group name and a list of permission labels under the group.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/perm_icon"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_alignParentLeft="true"
android:scaleType="fitCenter" />
<TextView
android:id="@+id/permission_group"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingLeft="6dip"
android:layout_toRightOf="@id/perm_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/permission_list"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginTop="-4dip"
android:paddingBottom="8dip"
android:paddingLeft="6dip"
android:layout_below="@id/permission_group"
android:layout_toRightOf="@id/perm_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>

View File

@@ -26,88 +26,17 @@
android:id="@+id/no_permissions"
android:text="@string/no_permissions"
android:textAppearance="?android:attr/textAppearanceMedium"
android:paddingLeft="16dip"
android:paddingRight="12dip"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- List view containing list of new permissions categorized by groups. -->
<!-- Populated with all permissions. -->
<LinearLayout
android:id="@+id/new_perms_list"
android:id="@+id/perms_list"
android:orientation="vertical"
android:layout_width="match_parent"
android:paddingLeft="16dip"
android:paddingRight="12dip"
android:layout_height="wrap_content" />
<!-- List view containing list of dangerous permissions categorized by groups. -->
<LinearLayout
android:id="@+id/dangerous_perms_list"
android:orientation="vertical"
android:layout_width="match_parent"
android:paddingLeft="16dip"
android:paddingRight="12dip"
android:layout_height="wrap_content" />
<!-- Clickable area letting user display additional permissions. -->
<LinearLayout
android:id="@+id/show_more"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginTop="12dip"
android:layout_marginBottom="16dip">
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dip"
android:paddingBottom="12dip"
android:paddingLeft="16dip"
android:duplicateParentState="true"
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/show_more_text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:duplicateParentState="true"
android:layout_alignTop="@+id/show_more_icon"
android:layout_gravity="center_vertical"
android:paddingLeft="36dip"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@id/show_more_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="12dip" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
</LinearLayout>
<!-- List view containing list of permissions that aren't dangerous. -->
<LinearLayout
android:id="@+id/non_dangerous_perms_list"
android:orientation="vertical"
android:paddingLeft="16dip"
android:paddingRight="12dip"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -63,7 +63,6 @@
<java-symbol type="id" name="clearDefaultHint" />
<java-symbol type="id" name="contentPanel" />
<java-symbol type="id" name="customPanel" />
<java-symbol type="id" name="dangerous_perms_list" />
<java-symbol type="id" name="datePicker" />
<java-symbol type="id" name="day" />
<java-symbol type="id" name="day_names" />
@@ -117,9 +116,7 @@
<java-symbol type="id" name="new_app_action" />
<java-symbol type="id" name="new_app_description" />
<java-symbol type="id" name="new_app_icon" />
<java-symbol type="id" name="new_perms_list" />
<java-symbol type="id" name="no_permissions" />
<java-symbol type="id" name="non_dangerous_perms_list" />
<java-symbol type="id" name="numberpicker_input" />
<java-symbol type="id" name="old_app_action" />
<java-symbol type="id" name="old_app_description" />
@@ -127,7 +124,9 @@
<java-symbol type="id" name="package_label" />
<java-symbol type="id" name="packages_list" />
<java-symbol type="id" name="pause" />
<java-symbol type="id" name="perms_list" />
<java-symbol type="id" name="perm_icon" />
<java-symbol type="id" name="perm_name" />
<java-symbol type="id" name="permission_group" />
<java-symbol type="id" name="permission_list" />
<java-symbol type="id" name="pickers" />
@@ -163,9 +162,6 @@
<java-symbol type="id" name="sha256_fingerprint" />
<java-symbol type="id" name="share" />
<java-symbol type="id" name="shortcut" />
<java-symbol type="id" name="show_more" />
<java-symbol type="id" name="show_more_icon" />
<java-symbol type="id" name="show_more_text" />
<java-symbol type="id" name="skip_button" />
<java-symbol type="id" name="slider_group" />
<java-symbol type="id" name="split_action_bar" />
@@ -1044,6 +1040,7 @@
<java-symbol type="layout" name="alert_dialog_progress" />
<java-symbol type="layout" name="always_use_checkbox" />
<java-symbol type="layout" name="app_permission_item" />
<java-symbol type="layout" name="app_permission_item_old" />
<java-symbol type="layout" name="app_perms_summary" />
<java-symbol type="layout" name="calendar_view" />
<java-symbol type="layout" name="character_picker" />

View File

@@ -2981,7 +2981,7 @@
<!-- Do not translate. -->
<string name="permissions_format"><xliff:g id="perm_line1">%1$s</xliff:g>, <xliff:g id="perm_line2">%2$s</xliff:g></string>
<!-- Text that is placed at the front of a permission name that is being added to an app [CHAR LIMIT=NONE] -->
<string name="perms_new_perm_prefix"><font size="12" fgcolor="#ffffa3a3">NEW: </font></string>
<string name="perms_new_perm_prefix"><font size="12" fgcolor="#ff900000">NEW: </font></string>
<!-- Shown for an application when it doesn't require any permission grants. -->
<string name="no_permissions">No permissions required</string>
<!-- When installing an application, the less-dangerous permissions are hidden. If the user showed those, this is the text to hide them again. -->