Merge branch 'permissions-ui' into 'master'

Permissions UI in AppDetails

* Removes the "m" prefix and some unnecessary TODOs
* Re-uses the permission list from the privileged installer for the list in AppDetails:

![device-2016-06-09-234253](/uploads/0f3807f05084e763c07c9b4b7c49c481/device-2016-06-09-234253.png)

See merge request !332
This commit is contained in:
Daniel Martí 2016-06-13 18:37:33 +00:00
commit 6b18ab4204
9 changed files with 156 additions and 216 deletions

View File

@ -90,8 +90,9 @@ import org.fdroid.fdroid.installer.InstallerFactory;
import org.fdroid.fdroid.installer.InstallerService; import org.fdroid.fdroid.installer.InstallerService;
import org.fdroid.fdroid.net.Downloader; import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderService; import org.fdroid.fdroid.net.DownloaderService;
import org.fdroid.fdroid.privileged.views.AppDiff;
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class AppDetails extends AppCompatActivity { public class AppDetails extends AppCompatActivity {
@ -1062,7 +1063,7 @@ public class AppDetails extends AppCompatActivity {
private final View.OnClickListener expanderPermissions = new View.OnClickListener() { private final View.OnClickListener expanderPermissions = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
final TextView permissionListView = (TextView) llViewMorePermissions.findViewById(R.id.permissions_list); final View permissionListView = llViewMorePermissions.findViewById(R.id.permission_list);
final TextView permissionHeader = (TextView) llViewMorePermissions.findViewById(R.id.permissions); final TextView permissionHeader = (TextView) llViewMorePermissions.findViewById(R.id.permissions);
if (permissionListView.getVisibility() == View.GONE) { if (permissionListView.getVisibility() == View.GONE) {
@ -1372,29 +1373,11 @@ public class AppDetails extends AppCompatActivity {
} }
private void buildPermissionInfo() { private void buildPermissionInfo() {
final TextView permissionListView = (TextView) llViewMorePermissions.findViewById(R.id.permissions_list); AppDiff appDiff = new AppDiff(appDetails.getPackageManager(), appDetails.getApks().getItem(0));
AppSecurityPermissions perms = new AppSecurityPermissions(appDetails, appDiff.pkgInfo);
ArrayList<String> permsList = appDetails.getApks().getItem(0).getFullPermissionList(); final ViewGroup permList = (ViewGroup) llViewMorePermissions.findViewById(R.id.permission_list);
if (permsList == null) { permList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
permissionListView.setText(R.string.no_permissions);
} else {
StringBuilder sb = new StringBuilder();
for (String permissionName : permsList) {
try {
final Permission permission = new Permission(getActivity(), permissionName);
// TODO: Make this list RTL friendly
sb.append("\t• ").append(permission.getName()).append('\n');
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Permission not yet available: " + permissionName);
}
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
permissionListView.setText(sb.toString());
} else {
permissionListView.setText(R.string.no_permissions);
}
}
} }
private String descAntiFeature(String af) { private String descAntiFeature(String af) {

View File

@ -1,24 +0,0 @@
package org.fdroid.fdroid;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
public class Permission {
private final PackageManager packageManager;
private final PermissionInfo permissionInfo;
Permission(Context context, String permission)
throws PackageManager.NameNotFoundException {
this.packageManager = context.getPackageManager();
this.permissionInfo = this.packageManager.getPermissionInfo(
permission, PackageManager.GET_META_DATA);
}
public CharSequence getName() {
String label = this.permissionInfo.loadLabel(this.packageManager).toString();
return Character.toUpperCase(label.charAt(0)) + label.substring(1);
}
}

View File

@ -111,19 +111,19 @@ public abstract class Installer {
private int newPermissionCount(Apk apk) { private int newPermissionCount(Apk apk) {
// TODO: requires targetSdk in Apk class/database // TODO: requires targetSdk in Apk class/database
//boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion //boolean supportsRuntimePermissions = pkgInfo.applicationInfo.targetSdkVersion
// >= Build.VERSION_CODES.M; // >= Build.VERSION_CODES.M;
//if (supportsRuntimePermissions) { //if (supportsRuntimePermissions) {
// return 0; // return 0;
//} //}
AppDiff appDiff = new AppDiff(context.getPackageManager(), apk); AppDiff appDiff = new AppDiff(context.getPackageManager(), apk);
if (appDiff.mPkgInfo == null) { if (appDiff.pkgInfo == null) {
// could not get diff because we couldn't parse the package // could not get diff because we couldn't parse the package
throw new RuntimeException("cannot parse!"); throw new RuntimeException("cannot parse!");
} }
AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.mPkgInfo); AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo);
if (appDiff.mInstalledAppInfo != null) { if (appDiff.installedAppInfo != null) {
// update to an existing app // update to an existing app
return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW); return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW);
} }

View File

@ -26,34 +26,33 @@ import org.fdroid.fdroid.data.Apk;
public class AppDiff { public class AppDiff {
private final PackageManager mPm; private final PackageManager pm;
public final PackageInfo mPkgInfo; public final PackageInfo pkgInfo;
public ApplicationInfo installedAppInfo;
public ApplicationInfo mInstalledAppInfo;
/** /**
* Constructor based on F-Droids Apk object * Constructor based on F-Droids Apk object
*/ */
public AppDiff(PackageManager mPm, Apk apk) { public AppDiff(PackageManager pm, Apk apk) {
this.mPm = mPm; this.pm = pm;
mPkgInfo = new PackageInfo(); pkgInfo = new PackageInfo();
mPkgInfo.packageName = apk.packageName; pkgInfo.packageName = apk.packageName;
mPkgInfo.applicationInfo = new ApplicationInfo(); pkgInfo.applicationInfo = new ApplicationInfo();
mPkgInfo.requestedPermissions = apk.getFullPermissionsArray(); pkgInfo.requestedPermissions = apk.getFullPermissionsArray();
init(); init();
} }
private void init() { private void init() {
String pkgName = mPkgInfo.packageName; String pkgName = pkgInfo.packageName;
// Check if there is already a package on the device with this name // Check if there is already a package on the device with this name
// but it has been renamed to something else. // but it has been renamed to something else.
final String[] oldName = mPm.canonicalToCurrentPackageNames(new String[]{pkgName}); final String[] oldName = pm.canonicalToCurrentPackageNames(new String[]{pkgName});
if (oldName != null && oldName.length > 0 && oldName[0] != null) { if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0]; pkgName = oldName[0];
mPkgInfo.packageName = pkgName; pkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName; pkgInfo.applicationInfo.packageName = pkgName;
} }
// Check if package is already installed // Check if package is already installed
try { try {
@ -61,13 +60,13 @@ public class AppDiff {
// apps, but this may include apps with just data, and if it is just // apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed". // data we still want to count it as "installed".
//noinspection WrongConstant (lint is actually wrong here!) //noinspection WrongConstant (lint is actually wrong here!)
mInstalledAppInfo = mPm.getApplicationInfo(pkgName, installedAppInfo = pm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES); PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mInstalledAppInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { if ((installedAppInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
mInstalledAppInfo = null; installedAppInfo = null;
} }
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
mInstalledAppInfo = null; installedAppInfo = null;
} }
} }
} }

View File

@ -78,22 +78,22 @@ public class AppSecurityPermissions {
public static final int WHICH_NEW = 1 << 2; public static final int WHICH_NEW = 1 << 2;
public static final int WHICH_ALL = 0xffff; public static final int WHICH_ALL = 0xffff;
private final Context mContext; private final Context context;
private final LayoutInflater mInflater; private final LayoutInflater inflater;
private final PackageManager mPm; private final PackageManager pm;
private final Map<String, MyPermissionGroupInfo> mPermGroups = new HashMap<>(); private final Map<String, MyPermissionGroupInfo> permGroups = new HashMap<>();
private final List<MyPermissionGroupInfo> mPermGroupsList = new ArrayList<>(); private final List<MyPermissionGroupInfo> permGroupsList = new ArrayList<>();
private final PermissionGroupInfoComparator mPermGroupComparator = new PermissionGroupInfoComparator(); private final PermissionGroupInfoComparator permGroupComparator = new PermissionGroupInfoComparator();
private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator(); private final PermissionInfoComparator permComparator = new PermissionInfoComparator();
private final CharSequence mNewPermPrefix; private final CharSequence newPermPrefix;
// PermissionGroupInfo implements Parcelable but its Parcel constructor is private and thus cannot be extended. // PermissionGroupInfo implements Parcelable but its Parcel constructor is private and thus cannot be extended.
@SuppressLint("ParcelCreator") @SuppressLint("ParcelCreator")
static class MyPermissionGroupInfo extends PermissionGroupInfo { static class MyPermissionGroupInfo extends PermissionGroupInfo {
CharSequence mLabel; CharSequence mLabel;
final List<MyPermissionInfo> mNewPermissions = new ArrayList<>(); final List<MyPermissionInfo> newPermissions = new ArrayList<>();
final List<MyPermissionInfo> mAllPermissions = new ArrayList<>(); final List<MyPermissionInfo> allPermissions = new ArrayList<>();
MyPermissionGroupInfo(PermissionInfo perm) { MyPermissionGroupInfo(PermissionInfo perm) {
name = perm.packageName; name = perm.packageName;
@ -116,18 +116,18 @@ public class AppSecurityPermissions {
// PermissionInfo implements Parcelable but its Parcel constructor is private and thus cannot be extended. // PermissionInfo implements Parcelable but its Parcel constructor is private and thus cannot be extended.
@SuppressLint("ParcelCreator") @SuppressLint("ParcelCreator")
private static class MyPermissionInfo extends PermissionInfo { private static class MyPermissionInfo extends PermissionInfo {
CharSequence mLabel; CharSequence label;
/** /**
* PackageInfo.requestedPermissionsFlags for the currently installed * PackageInfo.requestedPermissionsFlags for the currently installed
* package, if it is installed. * package, if it is installed.
*/ */
int mExistingReqFlags; int existingReqFlags;
/** /**
* True if this should be considered a new permission. * True if this should be considered a new permission.
*/ */
boolean mNew; boolean newPerm;
MyPermissionInfo(PermissionInfo info) { MyPermissionInfo(PermissionInfo info) {
super(info); super(info);
@ -135,9 +135,9 @@ public class AppSecurityPermissions {
} }
public static class PermissionItemView extends LinearLayout implements View.OnClickListener { public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
MyPermissionGroupInfo mGroup; MyPermissionGroupInfo group;
MyPermissionInfo mPerm; MyPermissionInfo perm;
AlertDialog mDialog; AlertDialog dialog;
public PermissionItemView(Context context, AttributeSet attrs) { public PermissionItemView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -146,8 +146,8 @@ public class AppSecurityPermissions {
public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm, public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
boolean first, CharSequence newPermPrefix) { boolean first, CharSequence newPermPrefix) {
mGroup = grp; group = grp;
mPerm = perm; this.perm = perm;
ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon); ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon);
TextView permNameView = (TextView) findViewById(R.id.perm_name); TextView permNameView = (TextView) findViewById(R.id.perm_name);
@ -157,8 +157,8 @@ public class AppSecurityPermissions {
if (first) { if (first) {
icon = grp.loadGroupIcon(getContext(), pm); icon = grp.loadGroupIcon(getContext(), pm);
} }
CharSequence label = perm.mLabel; CharSequence label = perm.label;
if (perm.mNew && newPermPrefix != null) { if (perm.newPerm && newPermPrefix != null) {
// If this is a new permission, format it appropriately. // If this is a new permission, format it appropriately.
SpannableStringBuilder builder = new SpannableStringBuilder(); SpannableStringBuilder builder = new SpannableStringBuilder();
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
@ -178,49 +178,49 @@ public class AppSecurityPermissions {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (mGroup != null && mPerm != null) { if (group != null && perm != null) {
if (mDialog != null) { if (dialog != null) {
mDialog.dismiss(); dialog.dismiss();
} }
PackageManager pm = getContext().getPackageManager(); PackageManager pm = getContext().getPackageManager();
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(mGroup.mLabel); builder.setTitle(group.mLabel);
if (mPerm.descriptionRes != 0) { if (perm.descriptionRes != 0) {
builder.setMessage(mPerm.loadDescription(pm)); builder.setMessage(perm.loadDescription(pm));
} else { } else {
CharSequence appName; CharSequence appName;
try { try {
ApplicationInfo app = pm.getApplicationInfo(mPerm.packageName, 0); ApplicationInfo app = pm.getApplicationInfo(perm.packageName, 0);
appName = app.loadLabel(pm); appName = app.loadLabel(pm);
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
appName = mPerm.packageName; appName = perm.packageName;
} }
builder.setMessage(getContext().getString( builder.setMessage(getContext().getString(
R.string.perms_description_app, appName) + "\n\n" + mPerm.name); R.string.perms_description_app, appName) + "\n\n" + perm.name);
} }
builder.setCancelable(true); builder.setCancelable(true);
builder.setIcon(mGroup.loadGroupIcon(getContext(), pm)); builder.setIcon(group.loadGroupIcon(getContext(), pm));
mDialog = builder.show(); dialog = builder.show();
mDialog.setCanceledOnTouchOutside(true); dialog.setCanceledOnTouchOutside(true);
} }
} }
@Override @Override
protected void onDetachedFromWindow() { protected void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
if (mDialog != null) { if (dialog != null) {
mDialog.dismiss(); dialog.dismiss();
} }
} }
} }
private AppSecurityPermissions(Context context) { private AppSecurityPermissions(Context context) {
mContext = context; this.context = context;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mPm = mContext.getPackageManager(); pm = this.context.getPackageManager();
// Pick up from framework resources instead. // Pick up from framework resources instead.
mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix); newPermPrefix = this.context.getText(R.string.perms_new_perm_prefix);
} }
public AppSecurityPermissions(Context context, PackageInfo info) { public AppSecurityPermissions(Context context, PackageInfo info) {
@ -233,7 +233,7 @@ public class AppSecurityPermissions {
PackageInfo installedPkgInfo = null; PackageInfo installedPkgInfo = null;
if (info.requestedPermissions != null) { if (info.requestedPermissions != null) {
try { try {
installedPkgInfo = mPm.getPackageInfo(info.packageName, installedPkgInfo = pm.getPackageInfo(info.packageName,
PackageManager.GET_PERMISSIONS); PackageManager.GET_PERMISSIONS);
} catch (NameNotFoundException ignored) { } catch (NameNotFoundException ignored) {
} }
@ -259,7 +259,7 @@ public class AppSecurityPermissions {
for (String permName : strList) { for (String permName : strList) {
try { try {
PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0); PermissionInfo tmpPermInfo = pm.getPermissionInfo(permName, 0);
if (tmpPermInfo == null) { if (tmpPermInfo == null) {
continue; continue;
} }
@ -288,11 +288,11 @@ public class AppSecurityPermissions {
groupName = tmpPermInfo.packageName; groupName = tmpPermInfo.packageName;
tmpPermInfo.group = groupName; tmpPermInfo.group = groupName;
} }
MyPermissionGroupInfo group = mPermGroups.get(groupName); MyPermissionGroupInfo group = permGroups.get(groupName);
if (group == null) { if (group == null) {
PermissionGroupInfo grp = null; PermissionGroupInfo grp = null;
if (origGroupName != null) { if (origGroupName != null) {
grp = mPm.getPermissionGroupInfo(origGroupName, 0); grp = pm.getPermissionGroupInfo(origGroupName, 0);
} }
if (grp != null) { if (grp != null) {
group = new MyPermissionGroupInfo(grp); group = new MyPermissionGroupInfo(grp);
@ -302,20 +302,20 @@ public class AppSecurityPermissions {
// gave couldn't be found. In either case, we consider // gave couldn't be found. In either case, we consider
// its group to be the permission's package name. // its group to be the permission's package name.
tmpPermInfo.group = tmpPermInfo.packageName; tmpPermInfo.group = tmpPermInfo.packageName;
group = mPermGroups.get(tmpPermInfo.group); group = permGroups.get(tmpPermInfo.group);
if (group == null) { if (group == null) {
group = new MyPermissionGroupInfo(tmpPermInfo); group = new MyPermissionGroupInfo(tmpPermInfo);
} }
} }
mPermGroups.put(tmpPermInfo.group, group); permGroups.put(tmpPermInfo.group, group);
} }
final boolean newPerm = installedPkgInfo != null final boolean newPerm = installedPkgInfo != null
&& (existingFlags & PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0; && (existingFlags & PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo); MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo);
myPerm.mExistingReqFlags = existingFlags; myPerm.existingReqFlags = existingFlags;
// This is a new permission if the app is already installed and // This is a new permission if the app is already installed and
// doesn't currently hold this permission. // doesn't currently hold this permission.
myPerm.mNew = newPerm; myPerm.newPerm = newPerm;
permSet.add(myPerm); permSet.add(myPerm);
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
Log.i(TAG, "Ignoring unknown permission:" + permName); Log.i(TAG, "Ignoring unknown permission:" + permName);
@ -326,26 +326,26 @@ public class AppSecurityPermissions {
private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) { private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) {
switch (which) { switch (which) {
case WHICH_NEW: case WHICH_NEW:
return grp.mNewPermissions; return grp.newPermissions;
default: default:
return grp.mAllPermissions; return grp.allPermissions;
} }
} }
public int getPermissionCount(int which) { public int getPermissionCount(int which) {
int n = 0; int n = 0;
for (MyPermissionGroupInfo grp : mPermGroupsList) { for (MyPermissionGroupInfo grp : permGroupsList) {
n += getPermissionList(grp, which).size(); n += getPermissionList(grp, which).size();
} }
return n; return n;
} }
public View getPermissionsView(int which) { public View getPermissionsView(int which) {
LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); LinearLayout permsView = (LinearLayout) inflater.inflate(R.layout.app_perms_summary, null);
LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list); LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
View noPermsView = permsView.findViewById(R.id.no_permissions); View noPermsView = permsView.findViewById(R.id.no_permissions);
displayPermissions(mPermGroupsList, displayList, which); displayPermissions(permGroupsList, displayList, which);
if (displayList.getChildCount() <= 0) { if (displayList.getChildCount() <= 0) {
noPermsView.setVisibility(View.VISIBLE); noPermsView.setVisibility(View.VISIBLE);
} }
@ -361,21 +361,21 @@ public class AppSecurityPermissions {
LinearLayout permListView, int which) { LinearLayout permListView, int which) {
permListView.removeAllViews(); permListView.removeAllViews();
int spacing = (int) (8 * mContext.getResources().getDisplayMetrics().density); int spacing = (int) (8 * context.getResources().getDisplayMetrics().density);
for (MyPermissionGroupInfo grp : groups) { for (MyPermissionGroupInfo grp : groups) {
final List<MyPermissionInfo> perms = getPermissionList(grp, which); final List<MyPermissionInfo> perms = getPermissionList(grp, which);
for (int j = 0; j < perms.size(); j++) { for (int j = 0; j < perms.size(); j++) {
MyPermissionInfo perm = perms.get(j); MyPermissionInfo perm = perms.get(j);
View view = getPermissionItemView(grp, perm, j == 0, View view = getPermissionItemView(grp, perm, j == 0,
which != WHICH_NEW ? mNewPermPrefix : null); which != WHICH_NEW ? newPermPrefix : null);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT); ViewGroup.LayoutParams.WRAP_CONTENT);
if (j == 0) { if (j == 0) {
lp.topMargin = spacing; lp.topMargin = spacing;
} }
if (j == grp.mAllPermissions.size() - 1) { if (j == grp.allPermissions.size() - 1) {
lp.bottomMargin = spacing; lp.bottomMargin = spacing;
} }
if (permListView.getChildCount() == 0) { if (permListView.getChildCount() == 0) {
@ -388,7 +388,7 @@ public class AppSecurityPermissions {
private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp, private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) { MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) {
PermissionItemView permView = (PermissionItemView) mInflater.inflate( PermissionItemView permView = (PermissionItemView) inflater.inflate(
Build.VERSION.SDK_INT >= 17 && Build.VERSION.SDK_INT >= 17 &&
(perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0 (perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
? R.layout.app_permission_item_money : R.layout.app_permission_item, ? R.layout.app_permission_item_money : R.layout.app_permission_item,
@ -400,17 +400,11 @@ public class AppSecurityPermissions {
private boolean isDisplayablePermission(PermissionInfo pInfo, int existingReqFlags) { private boolean isDisplayablePermission(PermissionInfo pInfo, int existingReqFlags) {
final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
final boolean isNormal = base == PermissionInfo.PROTECTION_NORMAL; final boolean isNormal = base == PermissionInfo.PROTECTION_NORMAL;
// TODO: do we want this in F-Droid?
// We do not show normal permissions in the UI.
//if (isNormal) {
// return false;
//}
final boolean isDangerous = base == PermissionInfo.PROTECTION_DANGEROUS final boolean isDangerous = base == PermissionInfo.PROTECTION_DANGEROUS
|| ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0); || ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0);
// Dangerous and normal permissions are always shown to the user // Dangerous and normal permissions are always shown to the user
// this is matches the permission list in AppDetails
if (isNormal || isDangerous) { if (isNormal || isDangerous) {
return true; return true;
} }
@ -441,16 +435,16 @@ public class AppSecurityPermissions {
} }
public final int compare(MyPermissionInfo a, MyPermissionInfo b) { public final int compare(MyPermissionInfo a, MyPermissionInfo b) {
return sCollator.compare(a.mLabel, b.mLabel); return sCollator.compare(a.label, b.label);
} }
} }
private void addPermToList(List<MyPermissionInfo> permList, private void addPermToList(List<MyPermissionInfo> permList,
MyPermissionInfo pInfo) { MyPermissionInfo pInfo) {
if (pInfo.mLabel == null) { if (pInfo.label == null) {
pInfo.mLabel = pInfo.loadLabel(mPm); pInfo.label = pInfo.loadLabel(pm);
} }
int idx = Collections.binarySearch(permList, pInfo, mPermComparator); int idx = Collections.binarySearch(permList, pInfo, permComparator);
if (idx < 0) { if (idx < 0) {
idx = -idx - 1; idx = -idx - 1;
permList.add(idx, pInfo); permList.add(idx, pInfo);
@ -461,33 +455,33 @@ public class AppSecurityPermissions {
if (permList != null) { if (permList != null) {
// First pass to group permissions // First pass to group permissions
for (MyPermissionInfo pInfo : permList) { for (MyPermissionInfo pInfo : permList) {
if (!isDisplayablePermission(pInfo, pInfo.mExistingReqFlags)) { if (!isDisplayablePermission(pInfo, pInfo.existingReqFlags)) {
continue; continue;
} }
MyPermissionGroupInfo group = mPermGroups.get(pInfo.group); MyPermissionGroupInfo group = permGroups.get(pInfo.group);
if (group != null) { if (group != null) {
pInfo.mLabel = pInfo.loadLabel(mPm); pInfo.label = pInfo.loadLabel(pm);
addPermToList(group.mAllPermissions, pInfo); addPermToList(group.allPermissions, pInfo);
if (pInfo.mNew) { if (pInfo.newPerm) {
addPermToList(group.mNewPermissions, pInfo); addPermToList(group.newPermissions, pInfo);
} }
} }
} }
} }
for (MyPermissionGroupInfo pgrp : mPermGroups.values()) { for (MyPermissionGroupInfo pgrp : permGroups.values()) {
if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) { if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) {
pgrp.mLabel = pgrp.loadLabel(mPm); pgrp.mLabel = pgrp.loadLabel(pm);
} else { } else {
try { try {
ApplicationInfo app = mPm.getApplicationInfo(pgrp.packageName, 0); ApplicationInfo app = pm.getApplicationInfo(pgrp.packageName, 0);
pgrp.mLabel = app.loadLabel(mPm); pgrp.mLabel = app.loadLabel(pm);
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
pgrp.mLabel = pgrp.loadLabel(mPm); pgrp.mLabel = pgrp.loadLabel(pm);
} }
} }
mPermGroupsList.add(pgrp); permGroupsList.add(pgrp);
} }
Collections.sort(mPermGroupsList, mPermGroupComparator); Collections.sort(permGroupsList, permGroupComparator);
} }
} }

View File

@ -26,8 +26,8 @@ import android.widget.ScrollView;
* It's a ScrollView that knows how to stay awake. * It's a ScrollView that knows how to stay awake.
*/ */
public class CaffeinatedScrollView extends ScrollView { public class CaffeinatedScrollView extends ScrollView {
private Runnable mFullScrollAction; private Runnable fullScrollAction;
private int mBottomSlop; private int bottomSlop;
public CaffeinatedScrollView(Context context) { public CaffeinatedScrollView(Context context) {
super(context); super(context);
@ -47,8 +47,8 @@ public class CaffeinatedScrollView extends ScrollView {
} }
public void setFullScrollAction(Runnable action) { public void setFullScrollAction(Runnable action) {
mFullScrollAction = action; fullScrollAction = action;
mBottomSlop = (int) (4 * getResources().getDisplayMetrics().density); bottomSlop = (int) (4 * getResources().getDisplayMetrics().density);
} }
@Override @Override
@ -64,12 +64,12 @@ public class CaffeinatedScrollView extends ScrollView {
} }
private void checkFullScrollAction() { private void checkFullScrollAction() {
if (mFullScrollAction != null) { if (fullScrollAction != null) {
int daBottom = getChildAt(0).getBottom(); int daBottom = getChildAt(0).getBottom();
int screenBottom = getScrollY() + getHeight() - getPaddingBottom(); int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
if ((daBottom - screenBottom) < mBottomSlop) { if ((daBottom - screenBottom) < bottomSlop) {
mFullScrollAction.run(); fullScrollAction.run();
mFullScrollAction = null; fullScrollAction = null;
} }
} }
} }

View File

@ -72,7 +72,7 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
private static final String TAB_ID_ALL = "all"; private static final String TAB_ID_ALL = "all";
private static final String TAB_ID_NEW = "new"; private static final String TAB_ID_NEW = "new";
private App mApp; private App app;
private final DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder() private final DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true) .cacheInMemory(true)
@ -89,8 +89,8 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
ImageView appIcon = (ImageView) appSnippet.findViewById(R.id.app_icon); ImageView appIcon = (ImageView) appSnippet.findViewById(R.id.app_icon);
TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost); TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost);
appName.setText(mApp.name); appName.setText(app.name);
ImageLoader.getInstance().displayImage(mApp.iconUrlLarge, appIcon, ImageLoader.getInstance().displayImage(app.iconUrlLarge, appIcon,
displayImageOptions); displayImageOptions);
tabHost.setup(); tabHost.setup();
@ -106,9 +106,9 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
scrollView = null; scrollView = null;
okCanInstall = false; okCanInstall = false;
int msg = 0; int msg = 0;
AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.mPkgInfo); AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.pkgInfo);
if (appDiff.mInstalledAppInfo != null) { if (appDiff.installedAppInfo != null) {
msg = (appDiff.mInstalledAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_update_system ? R.string.install_confirm_update_system
: R.string.install_confirm_update; : R.string.install_confirm_update;
scrollView = new CaffeinatedScrollView(this); scrollView = new CaffeinatedScrollView(this);
@ -144,10 +144,10 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
} }
if (!permVisible) { if (!permVisible) {
if (appDiff.mInstalledAppInfo != null) { if (appDiff.installedAppInfo != null) {
// This is an update to an application, but there are no // This is an update to an application, but there are no
// permissions at all. // permissions at all.
msg = (appDiff.mInstalledAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_update_system_no_perms ? R.string.install_confirm_update_system_no_perms
: R.string.install_confirm_update_no_perms; : R.string.install_confirm_update_no_perms;
} else { } else {
@ -192,18 +192,17 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
intent = getIntent(); intent = getIntent();
Uri uri = intent.getData(); Uri uri = intent.getData();
Apk apk = ApkProvider.Helper.find(this, uri, ApkProvider.DataColumns.ALL); Apk apk = ApkProvider.Helper.find(this, uri, ApkProvider.DataColumns.ALL);
mApp = AppProvider.Helper.findByPackageName(getContentResolver(), apk.packageName); app = AppProvider.Helper.findByPackageName(getContentResolver(), apk.packageName);
appDiff = new AppDiff(getPackageManager(), apk); appDiff = new AppDiff(getPackageManager(), apk);
if (appDiff.mPkgInfo == null) { if (appDiff.pkgInfo == null) {
setResult(RESULT_CANNOT_PARSE, intent); setResult(RESULT_CANNOT_PARSE, intent);
finish(); finish();
} }
setContentView(R.layout.install_start); setContentView(R.layout.install_start);
// increase dialog to full width for now // increase dialog to full width
// TODO: create a better design and minimum width for tablets
getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT); ViewGroup.LayoutParams.WRAP_CONTENT);

View File

@ -43,23 +43,23 @@ import java.util.List;
*/ */
class TabsAdapter extends PagerAdapter class TabsAdapter extends PagerAdapter
implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener { implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
private final Context mContext; private final Context context;
private final TabHost mTabHost; private final TabHost tabHost;
private final ViewPager mViewPager; private final ViewPager viewPager;
private final List<View> mTabs = new ArrayList<>(); private final List<View> tabs = new ArrayList<>();
private final Rect mTempRect = new Rect(); private final Rect tempRect = new Rect();
private TabHost.OnTabChangeListener mOnTabChangeListener; private TabHost.OnTabChangeListener onTabChangeListener;
static class DummyTabFactory implements TabHost.TabContentFactory { static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context mContext; private final Context context;
DummyTabFactory(Context context) { DummyTabFactory(Context context) {
mContext = context; this.context = context;
} }
@Override @Override
public View createTabContent(String tag) { public View createTabContent(String tag) {
View v = new View(mContext); View v = new View(context);
v.setMinimumWidth(0); v.setMinimumWidth(0);
v.setMinimumHeight(0); v.setMinimumHeight(0);
return v; return v;
@ -67,29 +67,29 @@ class TabsAdapter extends PagerAdapter
} }
TabsAdapter(Activity activity, TabHost tabHost, ViewPager pager) { TabsAdapter(Activity activity, TabHost tabHost, ViewPager pager) {
mContext = activity; context = activity;
mTabHost = tabHost; this.tabHost = tabHost;
mViewPager = pager; viewPager = pager;
mTabHost.setOnTabChangedListener(this); this.tabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this); viewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this); viewPager.setOnPageChangeListener(this);
} }
public void addTab(TabHost.TabSpec tabSpec, View view) { public void addTab(TabHost.TabSpec tabSpec, View view) {
tabSpec.setContent(new DummyTabFactory(mContext)); tabSpec.setContent(new DummyTabFactory(context));
mTabs.add(view); tabs.add(view);
mTabHost.addTab(tabSpec); tabHost.addTab(tabSpec);
notifyDataSetChanged(); notifyDataSetChanged();
} }
@Override @Override
public int getCount() { public int getCount() {
return mTabs.size(); return tabs.size();
} }
@Override @Override
public Object instantiateItem(ViewGroup container, int position) { public Object instantiateItem(ViewGroup container, int position) {
View view = mTabs.get(position); View view = tabs.get(position);
container.addView(view); container.addView(view);
return view; return view;
} }
@ -105,15 +105,15 @@ class TabsAdapter extends PagerAdapter
} }
public void setOnTabChangedListener(TabHost.OnTabChangeListener listener) { public void setOnTabChangedListener(TabHost.OnTabChangeListener listener) {
mOnTabChangeListener = listener; onTabChangeListener = listener;
} }
@Override @Override
public void onTabChanged(String tabId) { public void onTabChanged(String tabId) {
int position = mTabHost.getCurrentTab(); int position = tabHost.getCurrentTab();
mViewPager.setCurrentItem(position); viewPager.setCurrentItem(position);
if (mOnTabChangeListener != null) { if (onTabChangeListener != null) {
mOnTabChangeListener.onTabChanged(tabId); onTabChangeListener.onTabChanged(tabId);
} }
} }
@ -128,19 +128,19 @@ class TabsAdapter extends PagerAdapter
// The jerk. // The jerk.
// This hack tries to prevent this from pulling focus out of our // This hack tries to prevent this from pulling focus out of our
// ViewPager. // ViewPager.
TabWidget widget = mTabHost.getTabWidget(); TabWidget widget = tabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability(); int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position); tabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability); widget.setDescendantFocusability(oldFocusability);
// Scroll the current tab into visibility if needed. // Scroll the current tab into visibility if needed.
View tab = widget.getChildTabViewAt(position); View tab = widget.getChildTabViewAt(position);
mTempRect.set(tab.getLeft(), tab.getTop(), tab.getRight(), tab.getBottom()); tempRect.set(tab.getLeft(), tab.getTop(), tab.getRight(), tab.getBottom());
widget.requestRectangleOnScreen(mTempRect, false); widget.requestRectangleOnScreen(tempRect, false);
// Make sure the scrollbars are visible for a moment after selection // Make sure the scrollbars are visible for a moment after selection
final View contentView = mTabs.get(position); final View contentView = tabs.get(position);
if (contentView instanceof CaffeinatedScrollView) { if (contentView instanceof CaffeinatedScrollView) {
((CaffeinatedScrollView) contentView).awakenScrollBars(); ((CaffeinatedScrollView) contentView).awakenScrollBars();
} }

View File

@ -225,28 +225,17 @@ Changelog" />
android:drawableRight="@drawable/ic_expand_more_grey600" android:drawableRight="@drawable/ic_expand_more_grey600"
android:drawableEnd="@drawable/ic_expand_more_grey600" /> android:drawableEnd="@drawable/ic_expand_more_grey600" />
<TextView <LinearLayout
android:id="@+id/permissions_list" android:id="@+id/permission_list"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="false" android:orientation="vertical"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
android:textSize="14sp" android:textSize="14sp"
android:visibility="gone" android:visibility="gone"
android:layout_marginLeft="@dimen/layout_horizontal_margin" android:paddingEnd="4dp"
android:layout_marginStart="@dimen/layout_horizontal_margin" android:paddingRight="4dp"
tools:text=" * Full network access tools:ignore="RtlSymmetry" />
* View network connections
* View Wi-Fi connections
* Connect and disconnect from Wi-Fi
* Pair with Bluetooth devices
* Run at startup
* Modify or delete the contents of your USB storage
* Control Near Field Communication
* Directly install apps
* Delete apps
* Full permission to all device features and storage
* Test access to protected storage" />
</LinearLayout> </LinearLayout>