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:  See merge request !332
This commit is contained in:
commit
6b18ab4204
@ -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) {
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user