blob: b6fd0c161c62b99c56fa19d79fdf272be4f3d463 [file] [log] [blame]
/*
* 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.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.ArrayList;
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 PreferenceCategory mCurrentPermissionsCategory;
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(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Activity activity = getActivity();
if (activity == null) {
return;
}
PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO);
if (packageInfo == null) {
activity.finish();
return;
}
mAppPermissions = new AppPermissions(activity, packageInfo, null, false,
new Runnable() {
@Override
public void run() {
getActivity().finish();
}
});
if (mAppPermissions.getPermissionGroups().isEmpty()) {
activity.finish();
return;
}
boolean reviewRequired = false;
for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
if (group.isReviewRequired()) {
reviewRequired = true;
break;
}
}
if (!reviewRequired) {
activity.finish();
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
bindUi();
}
@Override
public void onResume() {
super.onResume();
mAppPermissions.refresh();
loadPreferences();
}
@Override
public void onClick(View view) {
Activity activity = getActivity();
if (activity == null) {
return;
}
if (view == mContinueButton) {
confirmPermissionsReview();
executeCallback(true);
} else if (view == mCancelButton) {
executeCallback(false);
activity.setResult(Activity.RESULT_CANCELED);
}
activity.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());
} else {
return true;
}
}
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() {
final List<PreferenceGroup> preferenceGroups = new ArrayList<PreferenceGroup>();
if (mNewPermissionsCategory != null) {
preferenceGroups.add(mNewPermissionsCategory);
preferenceGroups.add(mCurrentPermissionsCategory);
} else {
preferenceGroups.add(getPreferenceScreen());
}
for (PreferenceGroup preferenceGroup : preferenceGroups) {
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();
if (activity == null) {
return;
}
// 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() {
Activity activity = getActivity();
if (activity == null) {
return;
}
PreferenceScreen screen = getPreferenceScreen();
if (screen == null) {
screen = getPreferenceManager().createPreferenceScreen(getActivity());
setPreferenceScreen(screen);
} else {
screen.removeAll();
}
mCurrentPermissionsCategory = null;
PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory;
mNewPermissionsCategory = null;
final boolean isPackageUpdated = isPackageUpdated();
for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)
|| !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
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());
preference.setKey(group.getName());
Drawable icon = Utils.loadDrawable(activity.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);
}
preference.setChecked(group.areRuntimePermissionsGranted());
// 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(activity);
mNewPermissionsCategory.setTitle(R.string.new_permissions_category);
mNewPermissionsCategory.setOrder(1);
screen.addPreference(mNewPermissionsCategory);
}
mNewPermissionsCategory.addPreference(preference);
}
} else {
if (mCurrentPermissionsCategory == null) {
mCurrentPermissionsCategory = new PreferenceCategory(activity);
mCurrentPermissionsCategory.setTitle(R.string.current_permissions_category);
mCurrentPermissionsCategory.setOrder(2);
screen.addPreference(mCurrentPermissionsCategory);
}
mCurrentPermissionsCategory.addPreference(preference);
}
}
}
private boolean isPackageUpdated() {
List<AppPermissionGroup> 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 (activity == null) {
return;
}
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);
}
}
}
}