summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml3
-rw-r--r--res/drawable/header_background.xml21
-rw-r--r--res/drawable/ic_info.xml27
-rw-r--r--res/layout/header.xml62
-rwxr-xr-xres/values/colors.xml1
-rw-r--r--res/values/dimens.xml25
-rw-r--r--res/values/strings.xml3
-rwxr-xr-xres/values/styles.xml3
-rw-r--r--src/com/android/packageinstaller/permission/AppPermissions.java4
-rw-r--r--src/com/android/packageinstaller/permission/ManagePermissionsActivity.java25
-rw-r--r--src/com/android/packageinstaller/permission/ManagePermissionsFragment.java82
-rw-r--r--src/com/android/packageinstaller/permission/PermissionApps.java260
-rw-r--r--src/com/android/packageinstaller/permission/PermissionManagementFragment.java112
-rw-r--r--src/com/android/packageinstaller/permission/SettingsWithHeader.java82
14 files changed, 675 insertions, 35 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3273ddef..8b7ba2f9 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -75,9 +75,12 @@
<activity android:name=".permission.ManagePermissionsActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
+ android:label="@string/app_permissions"
+ android:theme="@android:style/Theme.Material.Settings"
android:permission="android.permission.GRANT_REVOKE_PERMISSIONS">
<intent-filter>
<action android:name="android.intent.action.MANAGE_APP_PERMISSIONS" />
+ <action android:name="android.intent.action.MANAGE_PERMISSION_APPS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/res/drawable/header_background.xml b/res/drawable/header_background.xml
new file mode 100644
index 00000000..77db9e03
--- /dev/null
+++ b/res/drawable/header_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:drawable="@color/header_background_color" />
+</ripple>
+
diff --git a/res/drawable/ic_info.xml b/res/drawable/ic_info.xml
new file mode 100644
index 00000000..f80a41cb
--- /dev/null
+++ b/res/drawable/ic_info.xml
@@ -0,0 +1,27 @@
+<!--
+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="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M0 0h24v24H0z"
+ android:fillColor="#00000000"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.0,17.0l2.0,0.0l0.0,-6.0l-2.0,0.0l0.0,6.0zm1.0,-15.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zm0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0zM11.0,9.0l2.0,0.0L13.0,7.0l-2.0,0.0l0.0,2.0z"/>
+</vector>
diff --git a/res/layout/header.xml b/res/layout/header.xml
new file mode 100644
index 00000000..f9b27af6
--- /dev/null
+++ b/res/layout/header.xml
@@ -0,0 +1,62 @@
+<!--
+ 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="@drawable/header_background"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.Header.Settings" >
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/header_subsettings_margin_start"
+ android:layout_height="40dp"
+ android:gravity="end"
+ android:layout_centerVertical="true" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_toStartOf="@+id/app_settings"
+ android:layout_marginStart="@dimen/header_subsettings_margin_start"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_centerVertical="true"
+ android:textAppearance="@android:style/TextAppearance.Material.Medium"
+ android:textColor="@android:color/white"
+ android:textAlignment="viewStart" />
+
+ <ImageView
+ android:id="@+id/info"
+ android:layout_width="56dp"
+ android:layout_height="56dp"
+ android:layout_alignParentEnd="true"
+ android:layout_marginEnd="@dimen/header_subsettings_margin_end"
+ android:layout_centerVertical="true"
+ android:minHeight="0dp"
+ android:minWidth="0dp"
+ android:scaleType="center"
+ android:src="@drawable/ic_info"
+ style="?android:attr/borderlessButtonStyle" />
+
+ <View
+ android:id="@+id/row_divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/listDivider" />
+
+</RelativeLayout>
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 6a18b2eb..5d92d6fc 100755
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -17,5 +17,6 @@
<resources>
<color name="shadow">#cc222222</color>
<color name="transparent">#00000000</color>
+ <color name="header_background_color">#ff37474f</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 00000000..028def55
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <!-- Header sub settings margin start / end -->
+ <dimen name="header_subsettings_margin_start">72dp</dimen>
+ <dimen name="header_subsettings_margin_end">16dp</dimen>
+
+ <!-- Header margin start / end -->
+ <dimen name="header_margin_start">16dp</dimen>
+ <dimen name="header_margin_end">16dp</dimen>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e807392f..a8254f49 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -197,4 +197,7 @@
<string name="permdesc_togglePermissions" translatable="false">Allows the holder to launch
the UI for toggling app permissions. Should never be needed for normal apps.</string>
+ <!-- Title for page of managing application permissions -->
+ <string name="app_permissions">App permissions</string>
+
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index b5d8d480..c67f043a 100755
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -23,4 +23,7 @@
<style name="Theme.AlertDialogActivity" parent="@android:style/Theme.DeviceDefault.Light.Panel">
<item name="android:backgroundDimEnabled">true</item>
</style>
+
+ <style name="Theme.Header.Settings" parent="@android:style/Theme.Material.Settings">
+ </style>
</resources>
diff --git a/src/com/android/packageinstaller/permission/AppPermissions.java b/src/com/android/packageinstaller/permission/AppPermissions.java
index 658accc1..899c888d 100644
--- a/src/com/android/packageinstaller/permission/AppPermissions.java
+++ b/src/com/android/packageinstaller/permission/AppPermissions.java
@@ -289,5 +289,9 @@ public final class AppPermissions {
public boolean isGranted() {
return mGranted;
}
+
+ public void setGranted(boolean granted) {
+ mGranted = granted;
+ }
}
}
diff --git a/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java b/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java
index 598f5e02..c9b9daf2 100644
--- a/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java
+++ b/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java
@@ -17,6 +17,7 @@
package com.android.packageinstaller.permission;
import android.app.Activity;
+import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -28,14 +29,30 @@ public final class ManagePermissionsActivity extends Activity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
- if (packageName == null) {
- Log.i(LOG_TAG, "Missing mandatory argument ARG_PACKAGE_NAME");
+ Fragment fragment = null;
+ String action = getIntent().getAction();
+ if (Intent.ACTION_MANAGE_APP_PERMISSIONS.equals(action)) {
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ if (packageName == null) {
+ Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PACKAGE_NAME");
+ finish();
+ return;
+ }
+ fragment = ManagePermissionsFragment.newInstance(packageName);
+ } else if (Intent.ACTION_MANAGE_PERMISSION_APPS.equals(action)) {
+ String permissionName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_NAME);
+ if (permissionName == null) {
+ Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PERMISSION_NAME");
+ finish();
+ return;
+ }
+ fragment = PermissionManagementFragment.newInstance(permissionName);
+ } else {
+ Log.w(LOG_TAG, "Unrecognized action " + action);
finish();
return;
}
- ManagePermissionsFragment fragment = ManagePermissionsFragment.newInstance(packageName);
getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit();
}
}
diff --git a/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java b/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java
index 75e30aef..a9cef839 100644
--- a/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java
+++ b/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java
@@ -16,17 +16,24 @@
package com.android.packageinstaller.permission;
+import android.app.Activity;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.Preference;
-import android.preference.PreferenceFragment;
+import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Toast;
-public final class ManagePermissionsFragment extends PreferenceFragment {
+import com.android.packageinstaller.R;
+
+public final class ManagePermissionsFragment extends SettingsWithHeader
+ implements OnPreferenceChangeListener {
private static final String LOG_TAG = "ManagePermissionsFragment";
private AppPermissions mAppPermissions;
@@ -42,6 +49,8 @@ public final class ManagePermissionsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
bindUi();
}
@@ -51,45 +60,38 @@ public final class ManagePermissionsFragment extends PreferenceFragment {
updateUi();
}
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getActivity().finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
private void bindUi() {
String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
- final PackageInfo packageInfo = getPackageInfo(packageName);
+ final Activity activity = getActivity();
+ PackageInfo packageInfo = getPackageInfo(packageName);
if (packageInfo == null) {
- getActivity().finish();
+ Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG)
+ .show();
+ activity.finish();
return;
}
+ final PackageManager pm = activity.getPackageManager();
+ ApplicationInfo appInfo = packageInfo.applicationInfo;
+ setHeader(appInfo.loadIcon(pm), appInfo.loadLabel(pm), null);
- PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getActivity());
- mAppPermissions = new AppPermissions(getActivity(),
- packageInfo, null);
-
- Preference.OnPreferenceChangeListener changeListener =
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- String groupName = preference.getKey();
- AppPermissions.PermissionGroup group = mAppPermissions
- .getPermissionGroup(groupName);
-
- if (group == null) {
- return false;
- }
-
- if (newValue == Boolean.TRUE) {
- group.grantRuntimePermissions();
- } else {
- group.revokeRuntimePermissions();
- }
-
- return true;
- }
- };
+ PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(activity);
+ mAppPermissions = new AppPermissions(activity, packageInfo, null);
for (AppPermissions.PermissionGroup group : mAppPermissions.getPermissionGroups()) {
if (group.hasRuntimePermissions()) {
- SwitchPreference preference = new SwitchPreference(getActivity());
- preference.setOnPreferenceChangeListener(changeListener);
+ SwitchPreference preference = new SwitchPreference(activity);
+ preference.setOnPreferenceChangeListener(this);
preference.setKey(group.getName());
preference.setIcon(group.getIconResId());
preference.setTitle(group.getLabel());
@@ -101,6 +103,24 @@ public final class ManagePermissionsFragment extends PreferenceFragment {
setPreferenceScreen(screen);
}
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ String groupName = preference.getKey();
+ AppPermissions.PermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
+
+ if (group == null) {
+ return false;
+ }
+
+ if (newValue == Boolean.TRUE) {
+ group.grantRuntimePermissions();
+ } else {
+ group.revokeRuntimePermissions();
+ }
+
+ return true;
+ }
+
private void updateUi() {
mAppPermissions.refresh();
diff --git a/src/com/android/packageinstaller/permission/PermissionApps.java b/src/com/android/packageinstaller/permission/PermissionApps.java
new file mode 100644
index 00000000..b441d613
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/PermissionApps.java
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ */
+package com.android.packageinstaller.permission;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.graphics.LightingColorFilter;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.packageinstaller.permission.AppPermissions.Permission;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class PermissionApps {
+
+ private static final String LOG_TAG = "PermissionGroup";
+
+ private final Context mContext;
+ private final String mGroupName;
+ private final PackageManager mPm;
+ private final Callback mCallback;
+
+ private CharSequence mLabel;
+ private Drawable mIcon;
+ private ArrayList<PermissionApp> mAppPerms;
+ // Map (pkg|uid) -> AppPermission
+ private ArrayMap<String, PermissionApp> mAppLookup;
+
+ public PermissionApps(Context context, String groupName, Callback callback) {
+ mContext = context;
+ mPm = mContext.getPackageManager();
+ mGroupName = groupName;
+ mCallback = callback;
+ loadGroupInfo();
+ new PermissionAppsLoader().execute();
+ }
+
+ public void refresh() {
+ new PermissionAppsLoader().execute();
+ }
+
+ public Collection<PermissionApp> getApps() {
+ return mAppPerms;
+ }
+
+ public PermissionApp getApp(String key) {
+ return mAppLookup.get(key);
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ private void loadGroupInfo() {
+ PackageItemInfo info = null;
+ try {
+ info = mPm.getPermissionGroupInfo(mGroupName, 0);
+ } catch (NameNotFoundException e) {
+ try {
+ PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0);
+ if (permInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
+ Log.w(LOG_TAG, mGroupName + " is not a runtime permission");
+ return;
+ }
+ info = permInfo;
+ } catch (NameNotFoundException reallyNotFound) {
+ Log.w(LOG_TAG, "Can't find permission: " + mGroupName, reallyNotFound);
+ return;
+ }
+ }
+ mLabel = info.loadLabel(mPm);
+ mIcon = info.loadUnbadgedIcon(mPm);
+ LightingColorFilter filter = new LightingColorFilter(0, 0xffffffff);
+ mIcon.setColorFilter(filter);
+ }
+
+ public static class PermissionApp implements Comparable<PermissionApp> {
+ private final Context mContext;
+ private final String mLabel;
+ private final Drawable mIcon;
+ private final String mPkg;
+ private final int mUid;
+ private final List<Permission> mPermissions = new ArrayList<>();
+ private boolean mHasPermission;
+
+ public PermissionApp(Context context, String pkg, int uid, String label,
+ Drawable icon) {
+ mContext = context;
+ mPkg = pkg;
+ mUid = uid;
+ mLabel = label;
+ mIcon = icon;
+ }
+
+ public String getKey() {
+ return Integer.toString(mUid);
+ }
+
+ public String getLabel() {
+ return mLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public boolean hasRuntimePermissions() {
+ return mHasPermission;
+ }
+
+ /**
+ * Note: This class only expects to have runtime permissions added.
+ */
+ public void addPermission(Permission permission) {
+ mPermissions.add(permission);
+ if (permission.isGranted()) {
+ mHasPermission = true;
+ }
+ }
+
+ public boolean grantRuntimePermissions() {
+ for (Permission permission : mPermissions) {
+ if (!permission.isGranted()) {
+ mContext.getPackageManager().grantPermission(mPkg,
+ permission.getName(), new UserHandle(UserHandle.getUserId(mUid)));
+ permission.setGranted(true);
+ }
+ }
+ return true;
+ }
+
+ public boolean revokeRuntimePermissions() {
+ for (Permission permission : mPermissions) {
+ if (permission.isGranted()) {
+ mContext.getPackageManager().revokePermission(mPkg,
+ permission.getName(), new UserHandle(UserHandle.getUserId(mUid)));
+ permission.setGranted(false);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int compareTo(PermissionApp another) {
+ int result = mLabel.compareTo(another.mLabel);
+ if (result == 0) {
+ // Unbadged before badged.
+ return mUid - another.mUid;
+ }
+ return result;
+ }
+
+ }
+
+ private class PermissionAppsLoader extends AsyncTask<Void, Void, ArrayList<PermissionApp>> {
+
+ @Override
+ protected ArrayList<PermissionApp> doInBackground(Void... params) {
+ ArrayList<String> permStrs = new ArrayList<>();
+ try {
+ List<PermissionInfo> permissions = mPm.queryPermissionsByGroup(mGroupName, 0);
+ for (PermissionInfo info : permissions) {
+ if (info.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
+ continue;
+ }
+ permStrs.add(info.name);
+ }
+ } catch (NameNotFoundException e) {
+ permStrs.add(mGroupName);
+ }
+ ArrayList<PermissionApp> appPerms = new ArrayList<>();
+ for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
+ List<PackageInfo> apps = mPm.getInstalledPackages(
+ PackageManager.GET_PERMISSIONS, user.getIdentifier());
+ final int N = apps.size();
+ for (int i = 0; i < N; i++) {
+ PackageInfo app = apps.get(i);
+ if (app.applicationInfo.targetSdkVersion
+ <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ // Only care about apps that support runtime permissions here.
+ continue;
+ }
+ if (app.requestedPermissions == null) {
+ continue;
+ }
+ PermissionApp appPermission = new PermissionApp(mContext,
+ app.packageName, app.applicationInfo.uid,
+ app.applicationInfo.loadLabel(mPm).toString(),
+ getBadgedIcon(app.applicationInfo));
+ for (int j = 0; j < app.requestedPermissions.length; j++) {
+ if (permStrs.contains(app.requestedPermissions[j])) {
+ boolean granted = (app.requestedPermissionsFlags[j]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+ appPermission.addPermission(new Permission(
+ app.requestedPermissions[j], true, granted));
+ }
+ }
+ if (appPermission.mPermissions.size() != 0) {
+ appPerms.add(appPermission);
+ }
+ }
+ }
+ Collections.sort(appPerms);
+ return appPerms;
+ }
+
+ private Drawable getBadgedIcon(ApplicationInfo appInfo) {
+ Drawable unbadged = appInfo.loadUnbadgedIcon(mPm);
+ return mPm.getUserBadgedIcon(unbadged,
+ new UserHandle(UserHandle.getUserId(appInfo.uid)));
+ }
+
+ @Override
+ protected void onPostExecute(ArrayList<PermissionApp> result) {
+ mAppLookup = new ArrayMap<>();
+ for (PermissionApp app : result) {
+ mAppLookup.put(app.getKey(), app);
+ }
+ mAppPerms = result;
+ mCallback.onPermissionsLoaded();
+ }
+ }
+
+ public interface Callback {
+ void onPermissionsLoaded();
+ }
+
+}
diff --git a/src/com/android/packageinstaller/permission/PermissionManagementFragment.java b/src/com/android/packageinstaller/permission/PermissionManagementFragment.java
new file mode 100644
index 00000000..ab1f1919
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/PermissionManagementFragment.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+package com.android.packageinstaller.permission;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.view.MenuItem;
+
+import com.android.packageinstaller.permission.PermissionApps.PermissionApp;
+import com.android.packageinstaller.permission.PermissionApps.Callback;
+
+public class PermissionManagementFragment extends SettingsWithHeader implements Callback, OnPreferenceChangeListener {
+
+ public static PermissionManagementFragment newInstance(String permissionName) {
+ PermissionManagementFragment instance = new PermissionManagementFragment();
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
+ instance.setArguments(arguments);
+ return instance;
+ }
+
+ private PermissionApps mPermissionGroup;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
+ bindUi();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mPermissionGroup.refresh();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getActivity().finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void bindUi() {
+ String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+ mPermissionGroup = new PermissionApps(getActivity(), groupName, this);
+ setHeader(mPermissionGroup.getIcon(), mPermissionGroup.getLabel(), null);
+ }
+
+ @Override
+ public void onPermissionsLoaded() {
+ Context context = getActivity();
+ PreferenceScreen preferences = getPreferenceScreen();
+ if (preferences == null) {
+ preferences = getPreferenceManager().createPreferenceScreen(getActivity());
+ setPreferenceScreen(preferences);
+ }
+ preferences.removeAll();
+ for (PermissionApp app : mPermissionGroup.getApps()) {
+ SwitchPreference pref = (SwitchPreference) findPreference(app.getKey());
+ if (pref == null) {
+ pref = new SwitchPreference(context);
+ pref.setOnPreferenceChangeListener(this);
+ pref.setKey(app.getKey());
+ pref.setIcon(app.getIcon());
+ pref.setTitle(app.getLabel());
+ pref.setPersistent(false);
+ preferences.addPreference(pref);
+ }
+ pref.setChecked(app.hasRuntimePermissions());
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ String pkg = preference.getKey();
+ PermissionApp app = mPermissionGroup.getApp(pkg);
+
+ if (app == null) {
+ return false;
+ }
+ if (newValue == Boolean.TRUE) {
+ app.grantRuntimePermissions();
+ } else {
+ app.revokeRuntimePermissions();
+ }
+ return true;
+ }
+
+}
diff --git a/src/com/android/packageinstaller/permission/SettingsWithHeader.java b/src/com/android/packageinstaller/permission/SettingsWithHeader.java
new file mode 100644
index 00000000..c074f6ca
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/SettingsWithHeader.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.packageinstaller.permission;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+
+public abstract class SettingsWithHeader extends PreferenceFragment implements OnClickListener {
+
+ private View mHeader;
+ private Intent mInfoIntent;
+ private Drawable mIcon;
+ private CharSequence mLabel;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ LinearLayout contentParent =
+ (LinearLayout) super.onCreateView(inflater, container, savedInstanceState);
+ mHeader = inflater.inflate(R.layout.header, contentParent, false);
+ contentParent.addView(mHeader, 0);
+ updateHeader();
+
+ return contentParent;
+ }
+
+ public void setHeader(Drawable icon, CharSequence label, Intent infoIntent) {
+ mIcon = icon;
+ mLabel = label;
+ mInfoIntent = infoIntent;
+ updateHeader();
+ }
+
+ private void updateHeader() {
+ if (mHeader != null) {
+ final ImageView appIcon = (ImageView) mHeader.findViewById(R.id.icon);
+ appIcon.setImageDrawable(mIcon);
+
+ final TextView appName = (TextView) mHeader.findViewById(R.id.name);
+ appName.setText(mLabel);
+
+ final View info = mHeader.findViewById(R.id.info);
+ if (mInfoIntent == null) {
+ info.setVisibility(View.GONE);
+ } else {
+ info.setClickable(true);
+ info.setOnClickListener(this);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ getActivity().startActivity(mInfoIntent);
+ }
+
+}