Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2015 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;
     18 
     19 import android.app.Activity;
     20 
     21 import android.app.DialogFragment;
     22 import android.app.Fragment;
     23 import android.content.Intent;
     24 import android.content.IntentSender;
     25 import android.content.pm.PackageInfo;
     26 import android.content.pm.PackageManager;
     27 import android.graphics.drawable.Drawable;
     28 import android.os.Bundle;
     29 import android.os.RemoteCallback;
     30 import android.preference.Preference;
     31 import android.preference.PreferenceCategory;
     32 import android.preference.PreferenceFragment;
     33 import android.preference.PreferenceGroup;
     34 import android.preference.PreferenceScreen;
     35 import android.preference.SwitchPreference;
     36 import android.preference.TwoStatePreference;
     37 import android.text.SpannableString;
     38 import android.text.TextUtils;
     39 import android.text.style.ForegroundColorSpan;
     40 import android.util.TypedValue;
     41 import android.view.View;
     42 import android.widget.Button;
     43 import android.widget.ImageView;
     44 import android.widget.TextView;
     45 import com.android.packageinstaller.R;
     46 import com.android.packageinstaller.permission.model.AppPermissionGroup;
     47 import com.android.packageinstaller.permission.model.AppPermissions;
     48 import com.android.packageinstaller.permission.utils.Utils;
     49 import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment.OnActionConfirmedListener;
     50 
     51 import java.util.List;
     52 
     53 public final class ReviewPermissionsActivity extends Activity
     54         implements OnActionConfirmedListener {
     55 
     56     @Override
     57     protected void onCreate(Bundle savedInstanceState) {
     58         super.onCreate(savedInstanceState);
     59 
     60         PackageInfo packageInfo = getTargetPackageInfo();
     61         if (packageInfo == null) {
     62             finish();
     63             return;
     64         }
     65 
     66         setContentView(R.layout.review_permissions);
     67         if (getFragmentManager().findFragmentById(R.id.preferences_frame) == null) {
     68             getFragmentManager().beginTransaction().add(R.id.preferences_frame,
     69                     ReviewPermissionsFragment.newInstance(packageInfo)).commit();
     70         }
     71     }
     72 
     73     @Override
     74     public void onActionConfirmed(String action) {
     75         Fragment fragment = getFragmentManager().findFragmentById(R.id.preferences_frame);
     76         if (fragment instanceof OnActionConfirmedListener) {
     77             ((OnActionConfirmedListener) fragment).onActionConfirmed(action);
     78         }
     79     }
     80 
     81     private PackageInfo getTargetPackageInfo() {
     82         String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
     83         if (TextUtils.isEmpty(packageName)) {
     84             return null;
     85         }
     86         try {
     87             return getPackageManager().getPackageInfo(packageName,
     88                     PackageManager.GET_PERMISSIONS);
     89         } catch (PackageManager.NameNotFoundException e) {
     90             return null;
     91         }
     92     }
     93 
     94     public static final class ReviewPermissionsFragment extends PreferenceFragment
     95             implements View.OnClickListener, Preference.OnPreferenceChangeListener,
     96             ConfirmActionDialogFragment.OnActionConfirmedListener {
     97         public static final String EXTRA_PACKAGE_INFO =
     98                 "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO";
     99 
    100         private AppPermissions mAppPermissions;
    101 
    102         private Button mContinueButton;
    103         private Button mCancelButton;
    104 
    105         private PreferenceCategory mNewPermissionsCategory;
    106 
    107         private boolean mHasConfirmedRevoke;
    108 
    109         public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) {
    110             Bundle arguments = new Bundle();
    111             arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo);
    112             ReviewPermissionsFragment instance = new ReviewPermissionsFragment();
    113             instance.setArguments(arguments);
    114             instance.setRetainInstance(true);
    115             return instance;
    116         }
    117 
    118         @Override
    119         public void onCreate(Bundle savedInstanceState) {
    120             super.onCreate(savedInstanceState);
    121 
    122             Activity activity = getActivity();
    123             if (activity == null) {
    124                 return;
    125             }
    126 
    127             PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO);
    128             if (packageInfo == null) {
    129                 activity.finish();
    130                 return;
    131             }
    132 
    133             mAppPermissions = new AppPermissions(activity, packageInfo, null, false,
    134                     new Runnable() {
    135                         @Override
    136                         public void run() {
    137                             getActivity().finish();
    138                         }
    139                     });
    140 
    141             if (mAppPermissions.getPermissionGroups().isEmpty()) {
    142                 activity.finish();
    143                 return;
    144             }
    145 
    146             boolean reviewRequired = false;
    147             for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
    148                 if (group.isReviewRequired()) {
    149                     reviewRequired = true;
    150                     break;
    151                 }
    152             }
    153 
    154             if (!reviewRequired) {
    155                 activity.finish();
    156             }
    157         }
    158 
    159         @Override
    160         public void onViewCreated(View view, Bundle savedInstanceState) {
    161             super.onViewCreated(view, savedInstanceState);
    162             bindUi();
    163         }
    164 
    165         @Override
    166         public void onResume() {
    167             super.onResume();
    168             mAppPermissions.refresh();
    169             loadPreferences();
    170         }
    171 
    172         @Override
    173         public void onClick(View view) {
    174             Activity activity = getActivity();
    175             if (activity == null) {
    176                 return;
    177             }
    178             if (view == mContinueButton) {
    179                 confirmPermissionsReview();
    180                 executeCallback(true);
    181             } else if (view == mCancelButton) {
    182                 executeCallback(false);
    183                 activity.setResult(Activity.RESULT_CANCELED);
    184             }
    185             activity.finish();
    186         }
    187 
    188         @Override
    189         public boolean onPreferenceChange(Preference preference, Object newValue) {
    190             if (mHasConfirmedRevoke) {
    191                 return true;
    192             }
    193             if (preference instanceof SwitchPreference) {
    194                 SwitchPreference switchPreference = (SwitchPreference) preference;
    195                 if (switchPreference.isChecked()) {
    196                     showWarnRevokeDialog(switchPreference.getKey());
    197                 } else {
    198                     return true;
    199                 }
    200             }
    201             return false;
    202         }
    203 
    204         @Override
    205         public void onActionConfirmed(String action) {
    206             Preference preference = getPreferenceManager().findPreference(action);
    207             if (preference instanceof SwitchPreference) {
    208                 SwitchPreference switchPreference = (SwitchPreference) preference;
    209                 switchPreference.setChecked(false);
    210                 mHasConfirmedRevoke = true;
    211             }
    212         }
    213 
    214         private void showWarnRevokeDialog(final String groupName) {
    215             DialogFragment fragment = ConfirmActionDialogFragment.newInstance(
    216                     getString(R.string.old_sdk_deny_warning), groupName);
    217             fragment.show(getFragmentManager(), fragment.getClass().getName());
    218         }
    219 
    220         private void confirmPermissionsReview() {
    221             PreferenceGroup preferenceGroup = mNewPermissionsCategory != null
    222                 ? mNewPermissionsCategory : getPreferenceScreen();
    223 
    224             final int preferenceCount = preferenceGroup.getPreferenceCount();
    225             for (int i = 0; i < preferenceCount; i++) {
    226                 Preference preference = preferenceGroup.getPreference(i);
    227                 if (preference instanceof TwoStatePreference) {
    228                     TwoStatePreference twoStatePreference = (TwoStatePreference) preference;
    229                     String groupName = preference.getKey();
    230                     AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
    231                     if (twoStatePreference.isChecked()) {
    232                         group.grantRuntimePermissions(false);
    233                     } else {
    234                         group.revokeRuntimePermissions(false);
    235                     }
    236                     group.resetReviewRequired();
    237                 }
    238             }
    239         }
    240 
    241         private void bindUi() {
    242             Activity activity = getActivity();
    243             if (activity == null) {
    244                 return;
    245             }
    246 
    247             // Set icon
    248             Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon(
    249                     activity.getPackageManager());
    250             ImageView iconView = (ImageView) activity.findViewById(R.id.app_icon);
    251             iconView.setImageDrawable(icon);
    252 
    253             // Set message
    254             String appLabel = mAppPermissions.getAppLabel().toString();
    255             final int labelTemplateResId = isPackageUpdated()
    256                     ?  R.string.permission_review_title_template_update
    257                     :  R.string.permission_review_title_template_install;
    258             SpannableString message = new SpannableString(getString(labelTemplateResId, appLabel));
    259             // Set the permission message as the title so it can be announced.
    260             activity.setTitle(message);
    261 
    262             // Color the app name.
    263             final int appLabelStart = message.toString().indexOf(appLabel, 0);
    264             final int appLabelLength = appLabel.length();
    265 
    266             TypedValue typedValue = new TypedValue();
    267             activity.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
    268             final int color = activity.getColor(typedValue.resourceId);
    269 
    270             message.setSpan(new ForegroundColorSpan(color), appLabelStart,
    271                     appLabelStart + appLabelLength, 0);
    272             TextView permissionsMessageView = (TextView) activity.findViewById(
    273                     R.id.permissions_message);
    274             permissionsMessageView.setText(message);
    275 
    276 
    277             mContinueButton = (Button) getActivity().findViewById(R.id.continue_button);
    278             mContinueButton.setOnClickListener(this);
    279 
    280             mCancelButton = (Button) getActivity().findViewById(R.id.cancel_button);
    281             mCancelButton.setOnClickListener(this);
    282         }
    283 
    284         private void loadPreferences() {
    285             Activity activity = getActivity();
    286             if (activity == null) {
    287                 return;
    288             }
    289 
    290             PreferenceScreen screen = getPreferenceScreen();
    291             if (screen == null) {
    292                 screen = getPreferenceManager().createPreferenceScreen(getActivity());
    293                 setPreferenceScreen(screen);
    294             } else {
    295                 screen.removeAll();
    296             }
    297 
    298             PreferenceGroup currentPermissionsCategory = null;
    299             PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory;
    300             mNewPermissionsCategory = null;
    301 
    302             final boolean isPackageUpdated = isPackageUpdated();
    303 
    304             for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
    305                 if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)
    306                         || !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
    307                     continue;
    308                 }
    309 
    310                 // TODO: Sort permissions - platform first then third-party ones
    311 
    312                 final SwitchPreference preference;
    313                 Preference cachedPreference = oldNewPermissionsCategory != null
    314                         ? oldNewPermissionsCategory.findPreference(group.getName()) : null;
    315                 if (cachedPreference instanceof SwitchPreference) {
    316                     preference = (SwitchPreference) cachedPreference;
    317                 } else {
    318                     preference = new SwitchPreference(getActivity());
    319 
    320                     preference.setKey(group.getName());
    321                     Drawable icon = Utils.loadDrawable(activity.getPackageManager(),
    322                             group.getIconPkg(), group.getIconResId());
    323                     preference.setIcon(Utils.applyTint(getContext(), icon,
    324                             android.R.attr.colorControlNormal));
    325                     preference.setTitle(group.getLabel());
    326                     preference.setSummary(group.getDescription());
    327                     preference.setPersistent(false);
    328 
    329                     preference.setOnPreferenceChangeListener(this);
    330                 }
    331 
    332                 preference.setChecked(group.areRuntimePermissionsGranted());
    333 
    334                 // Mutable state
    335                 if (group.isPolicyFixed()) {
    336                     preference.setEnabled(false);
    337                     preference.setSummary(getString(
    338                             R.string.permission_summary_enforced_by_policy));
    339                 } else {
    340                     preference.setEnabled(true);
    341                 }
    342 
    343                 if (group.isReviewRequired()) {
    344                     if (!isPackageUpdated) {
    345                         screen.addPreference(preference);
    346                     } else {
    347                         if (mNewPermissionsCategory == null) {
    348                             mNewPermissionsCategory = new PreferenceCategory(activity);
    349                             mNewPermissionsCategory.setTitle(R.string.new_permissions_category);
    350                             mNewPermissionsCategory.setOrder(1);
    351                             screen.addPreference(mNewPermissionsCategory);
    352                         }
    353                         mNewPermissionsCategory.addPreference(preference);
    354                     }
    355                 } else {
    356                     if (currentPermissionsCategory == null) {
    357                         currentPermissionsCategory = new PreferenceCategory(activity);
    358                         currentPermissionsCategory.setTitle(R.string.current_permissions_category);
    359                         currentPermissionsCategory.setOrder(2);
    360                         screen.addPreference(currentPermissionsCategory);
    361                     }
    362                     currentPermissionsCategory.addPreference(preference);
    363                 }
    364             }
    365         }
    366 
    367         private boolean isPackageUpdated() {
    368             List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups();
    369             final int groupCount = groups.size();
    370             for (int i = 0; i < groupCount; i++) {
    371                 AppPermissionGroup group = groups.get(i);
    372                 if (!group.isReviewRequired()) {
    373                     return true;
    374                 }
    375             }
    376             return false;
    377         }
    378 
    379         private void executeCallback(boolean success) {
    380             Activity activity = getActivity();
    381             if (activity == null) {
    382                 return;
    383             }
    384             if (success) {
    385                 IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
    386                 if (intent != null) {
    387                     try {
    388                         int flagMask = 0;
    389                         int flagValues = 0;
    390                         if (activity.getIntent().getBooleanExtra(
    391                                 Intent.EXTRA_RESULT_NEEDED, false)) {
    392                             flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
    393                             flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
    394                         }
    395                         activity.startIntentSenderForResult(intent, -1, null,
    396                                 flagMask, flagValues, 0);
    397                     } catch (IntentSender.SendIntentException e) {
    398                         /* ignore */
    399                     }
    400                     return;
    401                 }
    402             }
    403             RemoteCallback callback = activity.getIntent().getParcelableExtra(
    404                     Intent.EXTRA_REMOTE_CALLBACK);
    405             if (callback != null) {
    406                 Bundle result = new Bundle();
    407                 result.putBoolean(Intent.EXTRA_RETURN_RESULT, success);
    408                 callback.sendResult(result);
    409             }
    410         }
    411     }
    412 }
    413