Home | History | Annotate | Download | only in handheld
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.packageinstaller.permission.ui.handheld;
     18 
     19 import android.app.Activity;
     20 import android.app.DialogFragment;
     21 import android.content.Intent;
     22 import android.content.IntentSender;
     23 import android.content.pm.PackageInfo;
     24 import android.graphics.drawable.Drawable;
     25 import android.os.Bundle;
     26 import android.os.RemoteCallback;
     27 import android.preference.Preference;
     28 import android.preference.PreferenceCategory;
     29 import android.preference.PreferenceFragment;
     30 import android.preference.PreferenceGroup;
     31 import android.preference.PreferenceScreen;
     32 import android.preference.SwitchPreference;
     33 import android.preference.TwoStatePreference;
     34 import android.text.Html;
     35 import android.text.Spanned;
     36 import android.view.View;
     37 import android.widget.Button;
     38 import android.widget.ImageView;
     39 import android.widget.TextView;
     40 import com.android.packageinstaller.R;
     41 import com.android.packageinstaller.permission.model.AppPermissionGroup;
     42 import com.android.packageinstaller.permission.model.AppPermissions;
     43 import com.android.packageinstaller.permission.model.Permission;
     44 import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment;
     45 import com.android.packageinstaller.permission.ui.ManagePermissionsActivity;
     46 import com.android.packageinstaller.permission.utils.ArrayUtils;
     47 import com.android.packageinstaller.permission.utils.Utils;
     48 
     49 import java.util.List;
     50 
     51 public final class ReviewPermissionsFragment extends PreferenceFragment
     52         implements View.OnClickListener, Preference.OnPreferenceChangeListener,
     53         ConfirmActionDialogFragment.OnActionConfirmedListener {
     54 
     55     public static final String EXTRA_PACKAGE_INFO =
     56             "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO";
     57 
     58     private AppPermissions mAppPermissions;
     59 
     60     private Button mContinueButton;
     61     private Button mCancelButton;
     62     private Button mMoreInfoButton;
     63 
     64     private PreferenceCategory mNewPermissionsCategory;
     65 
     66     private boolean mHasConfirmedRevoke;
     67 
     68     public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) {
     69         Bundle arguments = new Bundle();
     70         arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo);
     71         ReviewPermissionsFragment instance = new ReviewPermissionsFragment();
     72         instance.setArguments(arguments);
     73         instance.setRetainInstance(true);
     74         return instance;
     75     }
     76 
     77     @Override
     78     public void onCreate(Bundle savedInstanceState) {
     79         super.onCreate(savedInstanceState);
     80 
     81         Activity activity = getActivity();
     82         if (activity == null) {
     83             return;
     84         }
     85 
     86         PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO);
     87         if (packageInfo == null) {
     88             activity.finish();
     89             return;
     90         }
     91 
     92         mAppPermissions = new AppPermissions(activity, packageInfo, null, false,
     93                 new Runnable() {
     94                     @Override
     95                     public void run() {
     96                         getActivity().finish();
     97                     }
     98                 });
     99 
    100         if (mAppPermissions.getPermissionGroups().isEmpty()) {
    101             activity.finish();
    102             return;
    103         }
    104 
    105         boolean reviewRequired = false;
    106         for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
    107             if (group.isReviewRequired()) {
    108                 reviewRequired = true;
    109                 break;
    110             }
    111         }
    112 
    113         if (!reviewRequired) {
    114             activity.finish();
    115         }
    116     }
    117 
    118     @Override
    119     public void onViewCreated(View view, Bundle savedInstanceState) {
    120         super.onViewCreated(view, savedInstanceState);
    121         bindUi();
    122     }
    123 
    124     @Override
    125     public void onResume() {
    126         super.onResume();
    127         mAppPermissions.refresh();
    128         loadPreferences();
    129     }
    130 
    131     @Override
    132     public void onClick(View view) {
    133         Activity activity = getActivity();
    134         if (activity == null) {
    135             return;
    136         }
    137         if (view == mContinueButton) {
    138             confirmPermissionsReview();
    139             executeCallback(true);
    140         } else if (view == mCancelButton) {
    141             executeCallback(false);
    142             activity.setResult(Activity.RESULT_CANCELED);
    143         } else if (view == mMoreInfoButton) {
    144             Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
    145             intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
    146                     mAppPermissions.getPackageInfo().packageName);
    147             intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
    148             getActivity().startActivity(intent);
    149         }
    150         activity.finish();
    151     }
    152 
    153     @Override
    154     public boolean onPreferenceChange(Preference preference, Object newValue) {
    155         if (mHasConfirmedRevoke) {
    156             return true;
    157         }
    158         if (preference instanceof SwitchPreference) {
    159             SwitchPreference switchPreference = (SwitchPreference) preference;
    160             if (switchPreference.isChecked()) {
    161                 showWarnRevokeDialog(switchPreference.getKey());
    162             } else {
    163                 return true;
    164             }
    165         }
    166         return false;
    167     }
    168 
    169     @Override
    170     public void onActionConfirmed(String action) {
    171         Preference preference = getPreferenceManager().findPreference(action);
    172         if (preference instanceof SwitchPreference) {
    173             SwitchPreference switchPreference = (SwitchPreference) preference;
    174             switchPreference.setChecked(false);
    175             mHasConfirmedRevoke = true;
    176         }
    177     }
    178 
    179     private void showWarnRevokeDialog(final String groupName) {
    180         DialogFragment fragment = ConfirmActionDialogFragment.newInstance(
    181                 getString(R.string.old_sdk_deny_warning), groupName);
    182         fragment.show(getFragmentManager(), fragment.getClass().getName());
    183     }
    184 
    185     private void confirmPermissionsReview() {
    186         PreferenceGroup preferenceGroup = mNewPermissionsCategory != null
    187                 ? mNewPermissionsCategory : getPreferenceScreen();
    188 
    189         final int preferenceCount = preferenceGroup.getPreferenceCount();
    190         for (int i = 0; i < preferenceCount; i++) {
    191             Preference preference = preferenceGroup.getPreference(i);
    192             if (preference instanceof TwoStatePreference) {
    193                 TwoStatePreference twoStatePreference = (TwoStatePreference) preference;
    194                 String groupName = preference.getKey();
    195                 AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
    196                 if (twoStatePreference.isChecked()) {
    197                     String[] permissionsToGrant = null;
    198                     final int permissionCount = group.getPermissions().size();
    199                     for (int j = 0; j < permissionCount; j++) {
    200                         final Permission permission = group.getPermissions().get(j);
    201                         if (permission.isReviewRequired()) {
    202                             permissionsToGrant = ArrayUtils.appendString(
    203                                     permissionsToGrant, permission.getName());
    204                         }
    205                     }
    206                     if (permissionsToGrant != null) {
    207                         group.grantRuntimePermissions(false, permissionsToGrant);
    208                     }
    209                 } else {
    210                     group.revokeRuntimePermissions(false);
    211                 }
    212                 group.resetReviewRequired();
    213             }
    214         }
    215     }
    216 
    217     private void bindUi() {
    218         Activity activity = getActivity();
    219         if (activity == null) {
    220             return;
    221         }
    222 
    223         // Set icon
    224         Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon(
    225                 activity.getPackageManager());
    226         ImageView iconView = (ImageView) activity.findViewById(R.id.app_icon);
    227         iconView.setImageDrawable(icon);
    228 
    229         // Set message
    230         final int labelTemplateResId = isPackageUpdated()
    231                 ? R.string.permission_review_title_template_update
    232                 : R.string.permission_review_title_template_install;
    233         Spanned message = Html.fromHtml(getString(labelTemplateResId,
    234                 mAppPermissions.getAppLabel()), 0);
    235 
    236         // Set the permission message as the title so it can be announced.
    237         activity.setTitle(message.toString());
    238 
    239         // Color the app name.
    240         TextView permissionsMessageView = (TextView) activity.findViewById(
    241                 R.id.permissions_message);
    242         permissionsMessageView.setText(message);
    243 
    244         mContinueButton = (Button) getActivity().findViewById(R.id.continue_button);
    245         mContinueButton.setOnClickListener(this);
    246 
    247         mCancelButton = (Button) getActivity().findViewById(R.id.cancel_button);
    248         mCancelButton.setOnClickListener(this);
    249 
    250         mMoreInfoButton = (Button) getActivity().findViewById(
    251                 R.id.permission_more_info_button);
    252         mMoreInfoButton.setOnClickListener(this);
    253     }
    254 
    255     private void loadPreferences() {
    256         Activity activity = getActivity();
    257         if (activity == null) {
    258             return;
    259         }
    260 
    261         PreferenceScreen screen = getPreferenceScreen();
    262         if (screen == null) {
    263             screen = getPreferenceManager().createPreferenceScreen(getActivity());
    264             setPreferenceScreen(screen);
    265         } else {
    266             screen.removeAll();
    267         }
    268 
    269         PreferenceGroup currentPermissionsCategory = null;
    270         PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory;
    271         mNewPermissionsCategory = null;
    272 
    273         final boolean isPackageUpdated = isPackageUpdated();
    274 
    275         for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
    276             if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)
    277                     || !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
    278                 continue;
    279             }
    280 
    281             final SwitchPreference preference;
    282             Preference cachedPreference = oldNewPermissionsCategory != null
    283                     ? oldNewPermissionsCategory.findPreference(group.getName()) : null;
    284             if (cachedPreference instanceof SwitchPreference) {
    285                 preference = (SwitchPreference) cachedPreference;
    286             } else {
    287                 preference = new SwitchPreference(getActivity());
    288 
    289                 preference.setKey(group.getName());
    290                 Drawable icon = Utils.loadDrawable(activity.getPackageManager(),
    291                         group.getIconPkg(), group.getIconResId());
    292                 preference.setIcon(Utils.applyTint(getContext(), icon,
    293                         android.R.attr.colorControlNormal));
    294                 preference.setTitle(group.getLabel());
    295                 preference.setSummary(group.getDescription());
    296                 preference.setPersistent(false);
    297 
    298                 preference.setOnPreferenceChangeListener(this);
    299             }
    300 
    301             preference.setChecked(group.areRuntimePermissionsGranted()
    302                     || group.isReviewRequired());
    303 
    304             // Mutable state
    305             if (group.isPolicyFixed()) {
    306                 preference.setEnabled(false);
    307                 preference.setSummary(getString(
    308                         R.string.permission_summary_enforced_by_policy));
    309             } else {
    310                 preference.setEnabled(true);
    311             }
    312 
    313             if (group.isReviewRequired()) {
    314                 if (!isPackageUpdated) {
    315                     screen.addPreference(preference);
    316                 } else {
    317                     if (mNewPermissionsCategory == null) {
    318                         mNewPermissionsCategory = new PreferenceCategory(activity);
    319                         mNewPermissionsCategory.setTitle(R.string.new_permissions_category);
    320                         mNewPermissionsCategory.setOrder(1);
    321                         screen.addPreference(mNewPermissionsCategory);
    322                     }
    323                     mNewPermissionsCategory.addPreference(preference);
    324                 }
    325             } else {
    326                 if (currentPermissionsCategory == null) {
    327                     currentPermissionsCategory = new PreferenceCategory(activity);
    328                     currentPermissionsCategory.setTitle(R.string.current_permissions_category);
    329                     currentPermissionsCategory.setOrder(2);
    330                     screen.addPreference(currentPermissionsCategory);
    331                 }
    332                 currentPermissionsCategory.addPreference(preference);
    333             }
    334         }
    335     }
    336 
    337     private boolean isPackageUpdated() {
    338         List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups();
    339         final int groupCount = groups.size();
    340         for (int i = 0; i < groupCount; i++) {
    341             AppPermissionGroup group = groups.get(i);
    342             if (!group.isReviewRequired()) {
    343                 return true;
    344             }
    345         }
    346         return false;
    347     }
    348 
    349     private void executeCallback(boolean success) {
    350         Activity activity = getActivity();
    351         if (activity == null) {
    352             return;
    353         }
    354         if (success) {
    355             IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
    356             if (intent != null) {
    357                 try {
    358                     int flagMask = 0;
    359                     int flagValues = 0;
    360                     if (activity.getIntent().getBooleanExtra(
    361                             Intent.EXTRA_RESULT_NEEDED, false)) {
    362                         flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
    363                         flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
    364                     }
    365                     activity.startIntentSenderForResult(intent, -1, null,
    366                             flagMask, flagValues, 0);
    367                 } catch (IntentSender.SendIntentException e) {
    368                         /* ignore */
    369                 }
    370                 return;
    371             }
    372         }
    373         RemoteCallback callback = activity.getIntent().getParcelableExtra(
    374                 Intent.EXTRA_REMOTE_CALLBACK);
    375         if (callback != null) {
    376             Bundle result = new Bundle();
    377             result.putBoolean(Intent.EXTRA_RETURN_RESULT, success);
    378             callback.sendResult(result);
    379         }
    380     }
    381 }
    382