Merge branch 'installer-ui-updates' into 'master'

Installer UI updates and lint fixes

This updates UI classes of the installer to reflect the changes of AOSP's installer and fixes some lint errors.
* No more differentiation between personal and device permissions (didn't even notice that there was a differentiation here...)
* Icon for "other" permission group

I carefully merged only the changes from AOSP that are related to us and decided against maintaining different versions of the AppSecurityPermissions.

There will be more changes and discussions to the installer UI coming in the next days.

See merge request !277
This commit is contained in:
Hans-Christoph Steiner 2016-05-06 07:55:56 +00:00
commit 7e53baf4c1
7 changed files with 84 additions and 103 deletions

View File

@ -17,6 +17,7 @@
package org.fdroid.fdroid.privileged.views;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@ -60,14 +61,22 @@ import java.util.Set;
* extended information consisting of all groups and permissions.
* To use this view define a LinearLayout or any ViewGroup and add this
* view by instantiating AppSecurityPermissions and invoking getPermissionsView.
* <p/>
* NOTES:
* Based on AOSP core/java/android/widget/AppSecurityPermissions
* latest included commit: a3f68ef2f6811cf72f1282214c0883db5a30901d
* <p/>
* To update this file:
* - Open https://github.com/android/platform_frameworks_base/commits/master/core/java/android/widget/AppSecurityPermissions.java
* - Start from latest included commit and include changes until the newest commit with care
*/
@TargetApi(Build.VERSION_CODES.M)
public class AppSecurityPermissions {
private static final String TAG = "AppSecurityPermissions";
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 Context mContext;
private final LayoutInflater mInflater;
@ -78,12 +87,12 @@ public class AppSecurityPermissions {
private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
private final CharSequence mNewPermPrefix;
// PermissionGroupInfo implements Parcelable but its Parcel constructor is private and thus cannot be extended.
@SuppressLint("ParcelCreator")
static class MyPermissionGroupInfo extends PermissionGroupInfo {
CharSequence mLabel;
final List<MyPermissionInfo> mNewPermissions = new ArrayList<>();
final List<MyPermissionInfo> mPersonalPermissions = new ArrayList<>();
final List<MyPermissionInfo> mDevicePermissions = new ArrayList<>();
final List<MyPermissionInfo> mAllPermissions = new ArrayList<>();
MyPermissionGroupInfo(PermissionInfo perm) {
@ -95,17 +104,11 @@ public class AppSecurityPermissions {
super(info);
}
public Drawable loadGroupIcon(PackageManager pm) {
public Drawable loadGroupIcon(Context context, PackageManager pm) {
if (icon != 0) {
return loadIcon(pm);
return (Build.VERSION.SDK_INT < 22) ? loadIcon(pm) : loadUnbadgedIcon(pm);
}
try {
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
return appInfo.loadIcon(pm);
} catch (NameNotFoundException e) {
// ignore
}
return null;
return context.getDrawable(R.drawable.ic_perm_device_info);
}
public int flags() {
@ -119,6 +122,8 @@ public class AppSecurityPermissions {
}
}
// PermissionInfo implements Parcelable but its Parcel constructor is private and thus cannot be extended.
@SuppressLint("ParcelCreator")
private static class MyPermissionInfo extends PermissionInfo {
CharSequence mLabel;
@ -159,7 +164,7 @@ public class AppSecurityPermissions {
PackageManager pm = getContext().getPackageManager();
Drawable icon = null;
if (first) {
icon = grp.loadGroupIcon(pm);
icon = grp.loadGroupIcon(getContext(), pm);
}
CharSequence label = perm.mLabel;
if (perm.mNew && newPermPrefix != null) {
@ -203,7 +208,7 @@ public class AppSecurityPermissions {
R.string.perms_description_app, appName) + "\n\n" + mPerm.name);
}
builder.setCancelable(true);
builder.setIcon(mGroup.loadGroupIcon(pm));
builder.setIcon(mGroup.loadGroupIcon(getContext(), pm));
mDialog = builder.show();
mDialog.setCanceledOnTouchOutside(true);
}
@ -240,14 +245,13 @@ public class AppSecurityPermissions {
installedPkgInfo = mPm.getPackageInfo(info.packageName,
PackageManager.GET_PERMISSIONS);
} catch (NameNotFoundException e) {
// ignore
throw new RuntimeException("NameNotFoundException during GET_PERMISSIONS!");
}
extractPerms(info, permSet, installedPkgInfo);
}
setPermissions(new ArrayList<>(permSet));
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private int[] getRequestedPermissionFlags(PackageInfo info) {
if (Build.VERSION.SDK_INT < 16) {
return new int[info.requestedPermissions.length];
@ -255,7 +259,6 @@ public class AppSecurityPermissions {
return info.requestedPermissionsFlags;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet,
PackageInfo installedPkgInfo) {
@ -263,15 +266,8 @@ public class AppSecurityPermissions {
if (strList == null || strList.length == 0) {
return;
}
final int[] flagsList = getRequestedPermissionFlags(info);
for (int i = 0; i < strList.length; i++) {
String permName = strList[i];
// If we are only looking at an existing app, then we only
// care about permissions that have actually been granted to it.
if (installedPkgInfo != null && info == installedPkgInfo && (flagsList[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
continue;
}
for (String permName : strList) {
try {
PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0);
if (tmpPermInfo == null) {
@ -341,10 +337,6 @@ public class AppSecurityPermissions {
switch (which) {
case WHICH_NEW:
return grp.mNewPermissions;
case WHICH_PERSONAL:
return grp.mPersonalPermissions;
case WHICH_DEVICE:
return grp.mDevicePermissions;
default:
return grp.mAllPermissions;
}
@ -415,11 +407,18 @@ public class AppSecurityPermissions {
return permView;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private boolean isDisplayablePermission(PermissionInfo pInfo, int existingReqFlags) {
final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
final boolean isNormal = base == PermissionInfo.PROTECTION_NORMAL;
final boolean isDangerous = base == PermissionInfo.PROTECTION_DANGEROUS;
// 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
|| ((pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0);
// Dangerous and normal permissions are always shown to the user
if (isNormal || isDangerous) {
@ -439,16 +438,7 @@ public class AppSecurityPermissions {
private final Collator sCollator = Collator.getInstance();
PermissionGroupInfoComparator() {
}
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);
}
}
@ -491,11 +481,6 @@ public class AppSecurityPermissions {
if (pInfo.mNew) {
addPermToList(group.mNewPermissions, pInfo);
}
if ((group.flags() & PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) {
addPermToList(group.mPersonalPermissions, pInfo);
} else {
addPermToList(group.mDevicePermissions, pInfo);
}
}
}
}

View File

@ -41,6 +41,11 @@ import android.widget.TextView;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
/**
* NOTES:
* Parts are based on AOSP src/com/android/packageinstaller/PackageInstallerActivity.java
* latest included commit: c23d802958158d522e7350321ad9ac6d43013883
*/
public class InstallConfirmActivity extends Activity implements OnCancelListener, OnClickListener {
public static final int RESULT_CANNOT_PARSE = RESULT_FIRST_USER + 1;
@ -107,9 +112,8 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
findViewById(R.id.divider).setVisibility(View.VISIBLE);
}
final int np = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
final int nd = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
if (np > 0 || nd > 0) {
final int n = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
if (n > 0) {
permVisible = true;
LayoutInflater inflater = (LayoutInflater) getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
@ -117,18 +121,8 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
if (mScrollView == null) {
mScrollView = (CaffeinatedScrollView) root.findViewById(R.id.scrollview);
}
final ViewGroup privacyList = (ViewGroup) root.findViewById(R.id.privacylist);
if (np > 0) {
privacyList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
} else {
privacyList.setVisibility(View.GONE);
}
final ViewGroup deviceList = (ViewGroup) root.findViewById(R.id.devicelist);
if (nd > 0) {
deviceList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
} else {
root.findViewById(R.id.devicelist).setVisibility(View.GONE);
}
final ViewGroup permList = (ViewGroup) root.findViewById(R.id.permission_list);
permList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
getText(R.string.allPerms)), root);
}

View File

@ -0,0 +1,21 @@
<!--
Copyright (C) 2015 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
android:fillColor="#FF000000"
android:pathData="M26.0,14.0l-4.0,0.0l0.0,4.0l4.0,0.0l0.0,-4.0zm0.0,8.0l-4.0,0.0l0.0,12.0l4.0,0.0L26.0,22.0zm8.0,-19.98L14.0,2.0c-2.21,0.0 -4.0,1.79 -4.0,4.0l0.0,36.0c0.0,2.21 1.79,4.0 4.0,4.0l20.0,0.0c2.21,0.0 4.0,-1.79 4.0,-4.0L38.0,6.0c0.0,-2.21 -1.79,-3.98 -4.0,-3.98zM34.0,38.0L14.0,38.0L14.0,10.0l20.0,0.0l0.0,28.0z"/>
</vector>

View File

@ -33,7 +33,8 @@
android:layout_marginStart="16dp"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:scaleType="fitCenter" />
android:scaleType="fitCenter"
android:tint="@android:color/black" />
<ImageView
android:layout_width="wrap_content"

View File

@ -32,7 +32,8 @@
android:layout_marginStart="16dp"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:scaleType="fitCenter" />
android:scaleType="fitCenter"
android:tint="@android:color/black" />
<ImageView
android:layout_width="wrap_content"

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2012 The Android Open Source Project
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2012 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.
@ -17,37 +16,19 @@
<!--
This is the structure for the list of all permissions.
-->
<org.fdroid.fdroid.privileged.views.CaffeinatedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<org.fdroid.fdroid.privileged.views.CaffeinatedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fillViewport="true">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout android:id="@+id/privacylist"
android:orientation="vertical"
android:id="@+id/permission_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp">
<TextView
style="?android:attr/listSeparatorTextViewStyle"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/privacyPerms" />
</LinearLayout>
<LinearLayout android:id="@+id/devicelist"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp">
<TextView
style="?android:attr/listSeparatorTextViewStyle"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/devicePerms" />
</LinearLayout>
</LinearLayout>
android:paddingEnd="4dp"
android:paddingRight="4dp"
tools:ignore="RtlSymmetry" />
</org.fdroid.fdroid.privileged.views.CaffeinatedScrollView>

View File

@ -361,8 +361,6 @@
be lost. It does not require any special access.</string>
<string name="newPerms">New</string>
<string name="allPerms">All</string>
<string name="privacyPerms">Privacy</string>
<string name="devicePerms">Device Access</string>
<string name="perm_costs_money">This may cost you money</string>
<string name="uninstall_update_confirm">Do you want to replace this app with the factory version?</string>
<string name="uninstall_confirm">Do you want to uninstall this app?</string>