diff options
author | Jason Monk <jmonk@google.com> | 2015-03-26 11:05:38 -0400 |
---|---|---|
committer | Jason Monk <jmonk@google.com> | 2015-03-27 10:43:05 -0400 |
commit | 46c55104384a9138d51c18c24114885af842afde (patch) | |
tree | 24ad4d4dd7a9dfe989947893dbbcf71c8483138c | |
parent | 024f373dc0a90cbd4b513a1cf1dcf8a6c248dc49 (diff) | |
download | android_packages_apps_PackageInstaller-46c55104384a9138d51c18c24114885af842afde.tar.gz android_packages_apps_PackageInstaller-46c55104384a9138d51c18c24114885af842afde.tar.bz2 android_packages_apps_PackageInstaller-46c55104384a9138d51c18c24114885af842afde.zip |
Update permissions UI
- Update existing settings page to show action bar and app header.
- Add page to manage apps based on permission.
Change-Id: I2211044fbe4db4b4fbf981f1a7ad7a01666cd69d
-rw-r--r-- | AndroidManifest.xml | 3 | ||||
-rw-r--r-- | res/drawable/header_background.xml | 21 | ||||
-rw-r--r-- | res/drawable/ic_info.xml | 27 | ||||
-rw-r--r-- | res/layout/header.xml | 62 | ||||
-rwxr-xr-x | res/values/colors.xml | 1 | ||||
-rw-r--r-- | res/values/dimens.xml | 25 | ||||
-rw-r--r-- | res/values/strings.xml | 3 | ||||
-rwxr-xr-x | res/values/styles.xml | 3 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/AppPermissions.java | 4 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/ManagePermissionsActivity.java | 25 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/ManagePermissionsFragment.java | 82 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/PermissionApps.java | 260 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/PermissionManagementFragment.java | 112 | ||||
-rw-r--r-- | src/com/android/packageinstaller/permission/SettingsWithHeader.java | 82 |
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); + } + +} |