From bf7316751f0c73b435b693fd20b0c9b2fa973e85 Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Sun, 27 Sep 2015 08:10:39 -0700 Subject: Add optional permission review for legacy apps - installer Change-Id: Ifc88b2fa259d2d22bea6b5500cded2714ad4da85 --- .../permission/ui/ConfirmActionDialogFragment.java | 63 ++++ .../permission/ui/ReviewPermissionsActivity.java | 397 +++++++++++++++++++++ .../ui/television/AllAppPermissionsFragment.java | 213 +++++++---- .../ui/television/AppPermissionsFragment.java | 9 + .../ui/television/PermissionAppsFragment.java | 15 +- 5 files changed, 632 insertions(+), 65 deletions(-) create mode 100644 src/com/android/packageinstaller/permission/ui/ConfirmActionDialogFragment.java create mode 100644 src/com/android/packageinstaller/permission/ui/ReviewPermissionsActivity.java (limited to 'src/com/android/packageinstaller/permission/ui') diff --git a/src/com/android/packageinstaller/permission/ui/ConfirmActionDialogFragment.java b/src/com/android/packageinstaller/permission/ui/ConfirmActionDialogFragment.java new file mode 100644 index 00000000..fb562e2a --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/ConfirmActionDialogFragment.java @@ -0,0 +1,63 @@ +/* + * 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.ui; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import com.android.packageinstaller.R; + +public final class ConfirmActionDialogFragment extends DialogFragment { + public static final String ARG_MESSAGE = "MESSAGE"; + public static final String ARG_ACTION = "ACTION"; + + public static interface OnActionConfirmedListener { + public void onActionConfirmed(String action); + } + + public static ConfirmActionDialogFragment newInstance(CharSequence message, String action) { + Bundle arguments = new Bundle(); + arguments.putCharSequence(ARG_MESSAGE, message); + arguments.putString(ARG_ACTION, action); + ConfirmActionDialogFragment fragment = new ConfirmActionDialogFragment(); + fragment.setArguments(arguments); + return fragment; + } + + @Override + public Dialog onCreateDialog(Bundle bundle) { + return new AlertDialog.Builder(getContext()) + .setMessage(getArguments().getString(ARG_MESSAGE)) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.grant_dialog_button_deny, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Activity activity = getActivity(); + if (activity instanceof OnActionConfirmedListener) { + String groupName = getArguments().getString(ARG_ACTION); + ((OnActionConfirmedListener) activity) + .onActionConfirmed(groupName); + } + } + }) + .create(); + } +} diff --git a/src/com/android/packageinstaller/permission/ui/ReviewPermissionsActivity.java b/src/com/android/packageinstaller/permission/ui/ReviewPermissionsActivity.java new file mode 100644 index 00000000..b872983d --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/ReviewPermissionsActivity.java @@ -0,0 +1,397 @@ +/* + * 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.ui; + +import android.annotation.Nullable; +import android.app.Activity; + +import android.app.DialogFragment; +import android.app.Fragment; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.RemoteCallback; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceGroup; +import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; +import android.preference.TwoStatePreference; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; +import android.util.TypedValue; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.packageinstaller.R; +import com.android.packageinstaller.permission.model.AppPermissionGroup; +import com.android.packageinstaller.permission.model.AppPermissions; +import com.android.packageinstaller.permission.utils.Utils; +import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment.OnActionConfirmedListener; + +import java.util.List; + +public final class ReviewPermissionsActivity extends Activity + implements OnActionConfirmedListener { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + PackageInfo packageInfo = getTargetPackageInfo(); + if (packageInfo == null) { + finish(); + return; + } + + setContentView(R.layout.review_permissions); + if (getFragmentManager().findFragmentById(R.id.preferences_frame) == null) { + getFragmentManager().beginTransaction().add(R.id.preferences_frame, + ReviewPermissionsFragment.newInstance(packageInfo)).commit(); + } + } + + @Override + public void onActionConfirmed(String action) { + Fragment fragment = getFragmentManager().findFragmentById(R.id.preferences_frame); + if (fragment instanceof OnActionConfirmedListener) { + ((OnActionConfirmedListener) fragment).onActionConfirmed(action); + } + } + + private PackageInfo getTargetPackageInfo() { + String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); + if (TextUtils.isEmpty(packageName)) { + return null; + } + try { + return getPackageManager().getPackageInfo(packageName, + PackageManager.GET_PERMISSIONS); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + public static final class ReviewPermissionsFragment extends PreferenceFragment + implements View.OnClickListener, Preference.OnPreferenceChangeListener, + ConfirmActionDialogFragment.OnActionConfirmedListener { + public static final String EXTRA_PACKAGE_INFO = + "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO"; + + private AppPermissions mAppPermissions; + + private Button mContinueButton; + private Button mCancelButton; + + private PreferenceCategory mNewPermissionsCategory; + + private boolean mHasConfirmedRevoke; + + public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) { + Bundle arguments = new Bundle(); + arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo); + ReviewPermissionsFragment instance = new ReviewPermissionsFragment(); + instance.setArguments(arguments); + instance.setRetainInstance(true); + return instance; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO); + if (packageInfo == null) { + getActivity().finish(); + return; + } + + mAppPermissions = new AppPermissions(getActivity(), packageInfo, null, false, + new Runnable() { + @Override + public void run() { + getActivity().finish(); + } + }); + + if (mAppPermissions.getPermissionGroups().isEmpty()) { + getActivity().finish(); + return; + } + + boolean reviewRequired= false; + for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { + if (group.isReviewRequired()) { + reviewRequired = true; + break; + } + } + + if (!reviewRequired) { + getActivity().finish(); + } + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + bindUi(); + } + + @Override + public void onResume() { + super.onResume(); + loadPreferences(); + } + + @Override + public void onClick(View view) { + if (view == mContinueButton) { + confirmPermissionsReview(); + executeCallback(true); + } else if (view == mCancelButton) { + executeCallback(false); + getActivity().setResult(Activity.RESULT_CANCELED); + } + getActivity().finish(); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (mHasConfirmedRevoke) { + return true; + } + if (preference instanceof SwitchPreference) { + SwitchPreference switchPreference = (SwitchPreference) preference; + if (switchPreference.isChecked()) { + showWarnRevokeDialog(switchPreference.getKey()); + } + } + return false; + } + + @Override + public void onActionConfirmed(String action) { + Preference preference = getPreferenceManager().findPreference(action); + if (preference instanceof SwitchPreference) { + SwitchPreference switchPreference = (SwitchPreference) preference; + switchPreference.setChecked(false); + mHasConfirmedRevoke = true; + } + } + + private void showWarnRevokeDialog(final String groupName) { + DialogFragment fragment = ConfirmActionDialogFragment.newInstance( + getString(R.string.old_sdk_deny_warning), groupName); + fragment.show(getFragmentManager(), fragment.getClass().getName()); + } + + private void confirmPermissionsReview() { + PreferenceGroup preferenceGroup = mNewPermissionsCategory != null + ? mNewPermissionsCategory : getPreferenceScreen(); + + final int preferenceCount = preferenceGroup.getPreferenceCount(); + for (int i = 0; i < preferenceCount; i++) { + Preference preference = preferenceGroup.getPreference(i); + if (preference instanceof TwoStatePreference) { + TwoStatePreference twoStatePreference = (TwoStatePreference) preference; + String groupName = preference.getKey(); + AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName); + if (twoStatePreference.isChecked()) { + group.grantRuntimePermissions(false); + } else { + group.revokeRuntimePermissions(false); + } + group.resetReviewRequired(); + } + } + } + + private void bindUi() { + Activity activity = getActivity(); + + // Set icon + Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon( + activity.getPackageManager()); + ImageView iconView = (ImageView) activity.findViewById(R.id.app_icon); + iconView.setImageDrawable(icon); + + // Set message + String appLabel = mAppPermissions.getAppLabel().toString(); + final int labelTemplateResId = isPackageUpdated() + ? R.string.permission_review_title_template_update + : R.string.permission_review_title_template_install; + SpannableString message = new SpannableString(getString(labelTemplateResId, appLabel)); + // Set the permission message as the title so it can be announced. + activity.setTitle(message); + + // Color the app name. + final int appLabelStart = message.toString().indexOf(appLabel, 0); + final int appLabelLength = appLabel.length(); + + TypedValue typedValue = new TypedValue(); + activity.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true); + final int color = activity.getColor(typedValue.resourceId); + + message.setSpan(new ForegroundColorSpan(color), appLabelStart, + appLabelStart + appLabelLength, 0); + TextView permissionsMessageView = (TextView) activity.findViewById( + R.id.permissions_message); + permissionsMessageView.setText(message); + + + mContinueButton = (Button) getActivity().findViewById(R.id.continue_button); + mContinueButton.setOnClickListener(this); + + mCancelButton = (Button) getActivity().findViewById(R.id.cancel_button); + mCancelButton.setOnClickListener(this); + } + + private void loadPreferences() { + PreferenceScreen screen = getPreferenceScreen(); + if (screen == null) { + screen = getPreferenceManager().createPreferenceScreen(getActivity()); + setPreferenceScreen(screen); + } else { + screen.removeAll(); + } + + PreferenceGroup currentPermissionsCategory = null; + PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory; + mNewPermissionsCategory = null; + + final boolean isPackageUpdated = isPackageUpdated(); + + for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { + if (!Utils.shouldShowPermission(group, + mAppPermissions.getPackageInfo().packageName)) { + continue; + } + + // TODO: Sort permissions - platform first then third-party ones + + final SwitchPreference preference; + Preference cachedPreference = oldNewPermissionsCategory != null + ? oldNewPermissionsCategory.findPreference(group.getName()) : null; + if (cachedPreference instanceof SwitchPreference) { + preference = (SwitchPreference) cachedPreference; + } else { + preference = new SwitchPreference(getActivity()); + + // We update permission grants based on the final preference states + if (group.isReviewRequired()) { + // If review is required use granted as default + preference.setChecked(true); + } else { + // If review not required use the current grant state as default + preference.setChecked(group.areRuntimePermissionsGranted()); + } + + preference.setKey(group.getName()); + Drawable icon = Utils.loadDrawable(getActivity().getPackageManager(), + group.getIconPkg(), group.getIconResId()); + preference.setIcon(Utils.applyTint(getContext(), icon, + android.R.attr.colorControlNormal)); + preference.setTitle(group.getLabel()); + preference.setSummary(group.getDescription()); + preference.setPersistent(false); + + preference.setOnPreferenceChangeListener(this); + } + + // Mutable state + if (group.isPolicyFixed()) { + preference.setEnabled(false); + preference.setSummary(getString( + R.string.permission_summary_enforced_by_policy)); + } else { + preference.setEnabled(true); + } + + if (group.isReviewRequired()) { + if (!isPackageUpdated) { + screen.addPreference(preference); + } else { + if (mNewPermissionsCategory == null) { + mNewPermissionsCategory = new PreferenceCategory(getActivity()); + mNewPermissionsCategory.setTitle(R.string.new_permissions_category); + mNewPermissionsCategory.setOrder(1); + screen.addPreference(mNewPermissionsCategory); + } + mNewPermissionsCategory.addPreference(preference); + } + } else { + if (currentPermissionsCategory == null) { + currentPermissionsCategory = new PreferenceCategory(getActivity()); + currentPermissionsCategory.setTitle(R.string.current_permissions_category); + currentPermissionsCategory.setOrder(2); + screen.addPreference(currentPermissionsCategory); + } + currentPermissionsCategory.addPreference(preference); + } + } + } + + private boolean isPackageUpdated() { + List groups = mAppPermissions.getPermissionGroups(); + final int groupCount = groups.size(); + for (int i = 0; i < groupCount; i++) { + AppPermissionGroup group = groups.get(i); + if (!group.isReviewRequired()) { + return true; + } + } + return false; + } + + private void executeCallback(boolean success) { + Activity activity = getActivity(); + if (success) { + IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT); + if (intent != null) { + try { + int flagMask = 0; + int flagValues = 0; + if (activity.getIntent().getBooleanExtra( + Intent.EXTRA_RESULT_NEEDED, false)) { + flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT; + flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT; + } + activity.startIntentSenderForResult(intent, -1, null, + flagMask, flagValues, 0); + } catch (IntentSender.SendIntentException e) { + /* ignore */ + } + return; + } + } + RemoteCallback callback = activity.getIntent().getParcelableExtra( + Intent.EXTRA_REMOTE_CALLBACK); + if (callback != null) { + Bundle result = new Bundle(); + result.putBoolean(Intent.EXTRA_RETURN_RESULT, success); + callback.sendResult(result); + } + } + } +} diff --git a/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java b/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java index d4910128..2c0a123b 100644 --- a/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java +++ b/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java @@ -16,8 +16,10 @@ package com.android.packageinstaller.permission.ui.television; +import android.Manifest; import android.app.ActionBar; import android.app.AlertDialog; +import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -28,9 +30,12 @@ import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.provider.Settings; +import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.Preference.OnPreferenceClickListener; import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceGroup; @@ -38,6 +43,8 @@ import android.util.Log; import android.view.MenuItem; import com.android.packageinstaller.R; +import com.android.packageinstaller.permission.model.AppPermissionGroup; +import com.android.packageinstaller.permission.model.AppPermissions; import com.android.packageinstaller.permission.utils.Utils; import java.util.ArrayList; @@ -50,6 +57,10 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { private static final String KEY_OTHER = "other_perms"; + private PackageInfo mPackageInfo; + + private AppPermissions mAppPermissions; + public static AllAppPermissionsFragment newInstance(String packageName) { AllAppPermissionsFragment instance = new AllAppPermissionsFragment(); Bundle arguments = new Bundle(); @@ -67,6 +78,22 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { ab.setTitle(R.string.all_permissions); ab.setDisplayHomeAsUpEnabled(true); } + + String pkg = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); + try { + mPackageInfo = getActivity().getPackageManager().getPackageInfo(pkg, + PackageManager.GET_PERMISSIONS); + } catch (NameNotFoundException e) { + getActivity().finish(); + } + + mAppPermissions = new AppPermissions(getActivity(), mPackageInfo, null, false, + new Runnable() { + @Override + public void run() { + getActivity().finish(); + } + }); } @Override @@ -86,62 +113,64 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { return super.onOptionsItemSelected(item); } - private void updateUi() { - if (getPreferenceScreen() != null) { - getPreferenceScreen().removeAll(); - } - addPreferencesFromResource(R.xml.all_permissions); + private PreferenceGroup getOtherGroup() { PreferenceGroup otherGroup = (PreferenceGroup) findPreference(KEY_OTHER); + if (otherGroup == null) { + otherGroup = new PreferenceCategory(getPreferenceManager().getContext()); + otherGroup.setKey(KEY_OTHER); + otherGroup.setTitle(getString(R.string.other_permissions)); + getPreferenceScreen().addPreference(otherGroup); + } + return otherGroup; + } + + private void updateUi() { + getPreferenceScreen().removeAll(); + ArrayList prefs = new ArrayList<>(); // Used for sorting. - prefs.add(otherGroup); - String pkg = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); - otherGroup.removeAll(); - PackageManager pm = getContext().getPackageManager(); + PackageManager pm = getActivity().getPackageManager(); - try { - PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS); - - ApplicationInfo appInfo = info.applicationInfo; - final Drawable icon = appInfo.loadIcon(pm); - final CharSequence label = appInfo.loadLabel(pm); - Intent infoIntent = null; - if (!getActivity().getIntent().getBooleanExtra( - AppPermissionsFragment.EXTRA_HIDE_INFO_BUTTON, false)) { - infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) - .setData(Uri.fromParts("package", pkg, null)); - } - setHeader(icon, label, infoIntent); - - if (info.requestedPermissions != null) { - for (int i = 0; i < info.requestedPermissions.length; i++) { - PermissionInfo perm; - try { - perm = pm.getPermissionInfo(info.requestedPermissions[i], 0); - } catch (NameNotFoundException e) { - Log.e(LOG_TAG, - "Can't get permission info for " + info.requestedPermissions[i], e); - continue; - } + ApplicationInfo appInfo = mPackageInfo.applicationInfo; + final Drawable icon = appInfo.loadIcon(pm); + final CharSequence label = appInfo.loadLabel(pm); + Intent infoIntent = null; + if (!getActivity().getIntent().getBooleanExtra( + AppPermissionsFragment.EXTRA_HIDE_INFO_BUTTON, false)) { + infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + .setData(Uri.fromParts("package", mPackageInfo.packageName, null)); + } + setHeader(icon, label, infoIntent); - if ((perm.flags & PermissionInfo.FLAG_INSTALLED) == 0 - || (perm.flags & PermissionInfo.FLAG_HIDDEN) != 0) { - continue; - } + if (mPackageInfo.requestedPermissions != null) { + for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) { + PermissionInfo perm; + try { + perm = pm.getPermissionInfo(mPackageInfo.requestedPermissions[i], 0); + } catch (NameNotFoundException e) { + Log.e(LOG_TAG, "Can't get permission info for " + + mPackageInfo.requestedPermissions[i], e); + continue; + } + + if ((perm.flags & PermissionInfo.FLAG_INSTALLED) == 0 + || (perm.flags & PermissionInfo.FLAG_HIDDEN) != 0) { + continue; + } - if (perm.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { - PermissionGroupInfo group = getGroup(perm.group, pm); - PreferenceGroup pref = - findOrCreate(group != null ? group : perm, pm, prefs); - pref.addPreference(getPreference(perm, group, pm)); - } else if (perm.protectionLevel == PermissionInfo.PROTECTION_NORMAL) { - PermissionGroupInfo group = getGroup(perm.group, pm); - otherGroup.addPreference(getPreference(perm, group, pm)); + PermissionGroupInfo group = getGroup(perm.group, pm); + if (perm.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { + PreferenceGroup pref = findOrCreate(group != null ? group : perm, pm, prefs); + pref.addPreference(getPreference(perm, group)); + } else if (perm.protectionLevel == PermissionInfo.PROTECTION_NORMAL) { + PreferenceGroup otherGroup = getOtherGroup(); + if (prefs.indexOf(otherGroup) < 0) { + prefs.add(otherGroup); } + getOtherGroup().addPreference(getPreference(perm, group)); } } - } catch (NameNotFoundException e) { - Log.e(LOG_TAG, "Problem getting package info for " + pkg, e); } + // Sort an ArrayList of the groups and then set the order from the sorting. Collections.sort(prefs, new Comparator() { @Override @@ -176,7 +205,7 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { ArrayList prefs) { PreferenceGroup pref = (PreferenceGroup) findPreference(group.name); if (pref == null) { - pref = new PreferenceCategory(getContext()); + pref = new PreferenceCategory(getActivity()); pref.setKey(group.name); pref.setLayoutResource(R.layout.preference_category_material); pref.setTitle(group.loadLabel(pm)); @@ -186,26 +215,57 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { return pref; } - private Preference getPreference(PermissionInfo perm, PermissionGroupInfo group, - PackageManager pm) { - Preference pref = new Preference(getContext()); - pref.setLayoutResource(R.layout.preference_permissions); - Drawable icon = null; - if (perm.icon != 0) { - icon = perm.loadIcon(pm); - } else if (group != null && group.icon != 0) { - icon = group.loadIcon(pm); + private Preference getPreference(final PermissionInfo perm, final PermissionGroupInfo group) { + if (isMutableGranularPermission(perm.name)) { + return getMutablePreference(perm, group); } else { - icon = getContext().getDrawable(R.drawable.ic_perm_device_info); + return getImmutablePreference(perm, group); } - pref.setIcon(Utils.applyTint(getContext(), icon, android.R.attr.colorControlNormal)); + } + + private Preference getMutablePreference(final PermissionInfo perm, PermissionGroupInfo group) { + final AppPermissionGroup permGroup = mAppPermissions.getPermissionGroup(group.name); + final String[] filterPermissions = new String[]{perm.name}; + + // TODO: No hardcoded layouts + SwitchPreference pref = new SwitchPreference(getPreferenceManager().getContext()); + pref.setLayoutResource(R.layout.preference_permissions); + pref.setChecked(permGroup.areRuntimePermissionsGranted(filterPermissions)); + pref.setIcon(getTintedPermissionIcon(getActivity(), perm, group)); + pref.setTitle(perm.loadLabel(getActivity().getPackageManager())); + pref.setPersistent(false); + + pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + if (value == Boolean.TRUE) { + permGroup.grantRuntimePermissions(false, filterPermissions); + } else { + permGroup.revokeRuntimePermissions(false, filterPermissions); + } + return true; + } + }); + + return pref; + } + + private Preference getImmutablePreference(final PermissionInfo perm, + PermissionGroupInfo group) { + final PackageManager pm = getActivity().getPackageManager(); + + // TODO: No hardcoded layouts + Preference pref = new Preference(getActivity()); + pref.setLayoutResource(R.layout.preference_permissions); + pref.setIcon(getTintedPermissionIcon(getActivity(), perm, group)); pref.setTitle(perm.loadLabel(pm)); - final CharSequence desc = perm.loadDescription(pm); + pref.setPersistent(false); + pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - new AlertDialog.Builder(getContext()) - .setMessage(desc) + new AlertDialog.Builder(getActivity()) + .setMessage(perm.loadDescription(pm)) .setPositiveButton(android.R.string.ok, null) .show(); return true; @@ -214,4 +274,33 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { return pref; } + + private static Drawable getTintedPermissionIcon(Context context, PermissionInfo perm, + PermissionGroupInfo group) { + final Drawable icon; + if (perm.icon != 0) { + icon = perm.loadIcon(context.getPackageManager()); + } else if (group != null && group.icon != 0) { + icon = group.loadIcon(context.getPackageManager()); + } else { + icon = context.getDrawable(R.drawable.ic_perm_device_info); + } + return Utils.applyTint(context, icon, android.R.attr.colorControlNormal); + } + + private static boolean isMutableGranularPermission(String name) { + if (!Build.PERMISSIONS_REVIEW_REQUIRED) { + return false; + } + switch (name) { + case Manifest.permission.READ_CONTACTS: + case Manifest.permission.WRITE_CONTACTS: + case Manifest.permission.READ_SMS: + case Manifest.permission.READ_CALL_LOG: + case Manifest.permission.CALL_PHONE: { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java b/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java index 42a2661c..52a6cbec 100644 --- a/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java +++ b/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java @@ -109,6 +109,15 @@ public final class AppPermissionsFragment extends SettingsWithHeader getActivity().finish(); } }); + + if (mAppPermissions.isReviewRequired()) { + Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + startActivity(intent); + getActivity().finish(); + return; + } + loadPreferences(); } diff --git a/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java b/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java index 0f240bef..f286aba0 100644 --- a/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java +++ b/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java @@ -271,7 +271,8 @@ public final class PermissionAppsFragment extends PermissionsFrameFragment imple @Override public boolean onPreferenceClick(Preference preference) { SystemAppsFragment frag = new SystemAppsFragment(); - setPermissionName(frag, getArguments().getString(Intent.EXTRA_PERMISSION_NAME)); + setPermissionName(frag, getArguments().getString( + Intent.EXTRA_PERMISSION_NAME)); frag.setTargetFragment(PermissionAppsFragment.this, 0); getFragmentManager().beginTransaction() .replace(android.R.id.content, frag) @@ -327,13 +328,21 @@ public final class PermissionAppsFragment extends PermissionsFrameFragment imple return false; } - addToggledGroup(app.getPackageName(), app.getPermissionGroup()); - if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(), app.getPackageName())) { LocationUtils.showLocationDialog(getContext(), app.getLabel()); return false; } + + addToggledGroup(app.getPackageName(), app.getPermissionGroup()); + + if (app.isReviewRequired()) { + Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, app.getPackageName()); + startActivity(intent); + return false; + } + if (newValue == Boolean.TRUE) { app.grantRuntimePermissions(); } else { -- cgit v1.2.3