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.annotation.Nullable;
     20 import android.app.ActionBar;
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.app.Fragment;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.DialogInterface.OnClickListener;
     27 import android.content.Intent;
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.pm.PackageInfo;
     30 import android.content.pm.PackageManager;
     31 import android.graphics.drawable.Drawable;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.provider.Settings;
     35 import android.support.v14.preference.SwitchPreference;
     36 import android.support.v7.preference.Preference;
     37 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
     38 import android.support.v7.preference.Preference.OnPreferenceClickListener;
     39 import android.support.v7.preference.PreferenceScreen;
     40 import android.util.Log;
     41 import android.view.Menu;
     42 import android.view.MenuInflater;
     43 import android.view.MenuItem;
     44 import android.view.View;
     45 import android.view.ViewGroup;
     46 import android.widget.ImageView;
     47 import android.widget.TextView;
     48 import android.widget.Toast;
     49 
     50 import com.android.packageinstaller.R;
     51 import com.android.packageinstaller.permission.model.AppPermissionGroup;
     52 import com.android.packageinstaller.permission.model.AppPermissions;
     53 import com.android.packageinstaller.permission.utils.LocationUtils;
     54 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
     55 import com.android.packageinstaller.permission.utils.Utils;
     56 
     57 import java.util.ArrayList;
     58 import java.util.List;
     59 
     60 public final class AppPermissionsFragment extends SettingsWithHeader
     61         implements OnPreferenceChangeListener {
     62 
     63     private static final String LOG_TAG = "ManagePermsFragment";
     64 
     65     static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
     66 
     67     private static final int MENU_ALL_PERMS = 0;
     68 
     69     private List<AppPermissionGroup> mToggledGroups;
     70     private AppPermissions mAppPermissions;
     71     private PreferenceScreen mExtraScreen;
     72 
     73     private boolean mHasConfirmedRevoke;
     74 
     75     public static AppPermissionsFragment newInstance(String packageName) {
     76         return setPackageName(new AppPermissionsFragment(), packageName);
     77     }
     78 
     79     private static <T extends Fragment> T setPackageName(T fragment, String packageName) {
     80         Bundle arguments = new Bundle();
     81         arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
     82         fragment.setArguments(arguments);
     83         return fragment;
     84     }
     85 
     86     @Override
     87     public void onCreate(Bundle savedInstanceState) {
     88         super.onCreate(savedInstanceState);
     89         setLoading(true /* loading */, false /* animate */);
     90         setHasOptionsMenu(true);
     91         final ActionBar ab = getActivity().getActionBar();
     92         if (ab != null) {
     93             ab.setDisplayHomeAsUpEnabled(true);
     94         }
     95 
     96         String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
     97         Activity activity = getActivity();
     98         PackageInfo packageInfo = getPackageInfo(activity, packageName);
     99         if (packageInfo == null) {
    100             Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
    101             activity.finish();
    102             return;
    103         }
    104 
    105         mAppPermissions = new AppPermissions(activity, packageInfo, null, true, new Runnable() {
    106             @Override
    107             public void run() {
    108                 getActivity().finish();
    109             }
    110         });
    111         loadPreferences();
    112     }
    113 
    114     @Override
    115     public void onResume() {
    116         super.onResume();
    117         mAppPermissions.refresh();
    118         setPreferencesCheckedState();
    119     }
    120 
    121     @Override
    122     public boolean onOptionsItemSelected(MenuItem item) {
    123         switch (item.getItemId()) {
    124             case android.R.id.home: {
    125                 getActivity().finish();
    126                 return true;
    127             }
    128 
    129             case MENU_ALL_PERMS: {
    130                 Fragment frag = AllAppPermissionsFragment.newInstance(
    131                         getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
    132                 getFragmentManager().beginTransaction()
    133                         .replace(android.R.id.content, frag)
    134                         .addToBackStack("AllPerms")
    135                         .commit();
    136                 return true;
    137             }
    138         }
    139         return super.onOptionsItemSelected(item);
    140     }
    141 
    142     @Override
    143     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    144         super.onViewCreated(view, savedInstanceState);
    145         if (mAppPermissions != null) {
    146             bindUi(this, mAppPermissions.getPackageInfo());
    147         }
    148     }
    149 
    150     @Override
    151     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    152         super.onCreateOptionsMenu(menu, inflater);
    153         menu.add(Menu.NONE, MENU_ALL_PERMS, Menu.NONE, R.string.all_permissions);
    154     }
    155 
    156     private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) {
    157         Activity activity = fragment.getActivity();
    158         PackageManager pm = activity.getPackageManager();
    159         ApplicationInfo appInfo = packageInfo.applicationInfo;
    160         Intent infoIntent = null;
    161         if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) {
    162             infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
    163                     .setData(Uri.fromParts("package", packageInfo.packageName, null));
    164         }
    165 
    166         Drawable icon = appInfo.loadIcon(pm);
    167         CharSequence label = appInfo.loadLabel(pm);
    168         fragment.setHeader(icon, label, infoIntent);
    169 
    170         ActionBar ab = activity.getActionBar();
    171         if (ab != null) {
    172             ab.setTitle(R.string.app_permissions);
    173         }
    174 
    175         ViewGroup rootView = (ViewGroup) fragment.getView();
    176         ImageView iconView = (ImageView) rootView.findViewById(R.id.lb_icon);
    177         if (iconView != null) {
    178             iconView.setImageDrawable(icon);
    179         }
    180         TextView titleView = (TextView) rootView.findViewById(R.id.lb_title);
    181         if (titleView != null) {
    182             titleView.setText(R.string.app_permissions);
    183         }
    184         TextView breadcrumbView = (TextView) rootView.findViewById(R.id.lb_breadcrumb);
    185         if (breadcrumbView != null) {
    186             breadcrumbView.setText(label);
    187         }
    188     }
    189 
    190     private void loadPreferences() {
    191         Context context = getPreferenceManager().getContext();
    192         if (context == null) {
    193             return;
    194         }
    195 
    196         PreferenceScreen screen = getPreferenceScreen();
    197         screen.removeAll();
    198 
    199         if (mExtraScreen != null) {
    200             mExtraScreen.removeAll();
    201         }
    202 
    203         final Preference extraPerms = new Preference(context);
    204         extraPerms.setIcon(R.drawable.ic_toc);
    205         extraPerms.setTitle(R.string.additional_permissions);
    206 
    207         for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
    208             if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)) {
    209                 continue;
    210             }
    211 
    212             boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
    213 
    214             SwitchPreference preference = new SwitchPreference(context);
    215             preference.setOnPreferenceChangeListener(this);
    216             preference.setKey(group.getName());
    217             Drawable icon = Utils.loadDrawable(context.getPackageManager(),
    218                     group.getIconPkg(), group.getIconResId());
    219             preference.setIcon(Utils.applyTint(getContext(), icon,
    220                     android.R.attr.colorControlNormal));
    221             preference.setTitle(group.getLabel());
    222             if (group.isPolicyFixed()) {
    223                 preference.setSummary(getString(R.string.permission_summary_enforced_by_policy));
    224             }
    225             preference.setPersistent(false);
    226             preference.setEnabled(!group.isPolicyFixed());
    227             preference.setChecked(group.areRuntimePermissionsGranted());
    228 
    229             if (isPlatform) {
    230                 screen.addPreference(preference);
    231             } else {
    232                 if (mExtraScreen == null) {
    233                     mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
    234                 }
    235                 mExtraScreen.addPreference(preference);
    236             }
    237         }
    238 
    239         if (mExtraScreen != null) {
    240             extraPerms.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    241                 @Override
    242                 public boolean onPreferenceClick(Preference preference) {
    243                     AdditionalPermissionsFragment frag = new AdditionalPermissionsFragment();
    244                     setPackageName(frag, getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
    245                     frag.setTargetFragment(AppPermissionsFragment.this, 0);
    246                     getFragmentManager().beginTransaction()
    247                             .replace(android.R.id.content, frag)
    248                             .addToBackStack(null)
    249                             .commit();
    250                     return true;
    251                 }
    252             });
    253             int count = mExtraScreen.getPreferenceCount();
    254             extraPerms.setSummary(getResources().getQuantityString(
    255                     R.plurals.additional_permissions_more, count, count));
    256             screen.addPreference(extraPerms);
    257         }
    258 
    259         setLoading(false /* loading */, true /* animate */);
    260     }
    261 
    262     @Override
    263     public boolean onPreferenceChange(final Preference preference, Object newValue) {
    264         String groupName = preference.getKey();
    265         final AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
    266 
    267         if (group == null) {
    268             return false;
    269         }
    270 
    271         OverlayTouchActivity activity = (OverlayTouchActivity) getActivity();
    272         if (activity.isObscuredTouch()) {
    273             activity.showOverlayDialog();
    274             return false;
    275         }
    276 
    277         addToggledGroup(group);
    278 
    279         if (LocationUtils.isLocationGroupAndProvider(group.getName(), group.getApp().packageName)) {
    280             LocationUtils.showLocationDialog(getContext(), mAppPermissions.getAppLabel());
    281             return false;
    282         }
    283         if (newValue == Boolean.TRUE) {
    284             group.grantRuntimePermissions(false);
    285         } else {
    286             final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
    287             if (grantedByDefault || (!group.hasRuntimePermission() && !mHasConfirmedRevoke)) {
    288                 new AlertDialog.Builder(getContext())
    289                         .setMessage(grantedByDefault ? R.string.system_warning
    290                                 : R.string.old_sdk_deny_warning)
    291                         .setNegativeButton(R.string.cancel, null)
    292                         .setPositiveButton(R.string.grant_dialog_button_deny,
    293                                 new OnClickListener() {
    294                             @Override
    295                             public void onClick(DialogInterface dialog, int which) {
    296                                 ((SwitchPreference) preference).setChecked(false);
    297                                 group.revokeRuntimePermissions(false);
    298                                 if (!grantedByDefault) {
    299                                     mHasConfirmedRevoke = true;
    300                                 }
    301                             }
    302                         })
    303                         .show();
    304                 return false;
    305             } else {
    306                 group.revokeRuntimePermissions(false);
    307             }
    308         }
    309 
    310         return true;
    311     }
    312 
    313     @Override
    314     public void onPause() {
    315         super.onPause();
    316         logToggledGroups();
    317     }
    318 
    319     private void addToggledGroup(AppPermissionGroup group) {
    320         if (mToggledGroups == null) {
    321             mToggledGroups = new ArrayList<>();
    322         }
    323         // Double toggle is back to initial state.
    324         if (mToggledGroups.contains(group)) {
    325             mToggledGroups.remove(group);
    326         } else {
    327             mToggledGroups.add(group);
    328         }
    329     }
    330 
    331     private void logToggledGroups() {
    332         if (mToggledGroups != null) {
    333             String packageName = mAppPermissions.getPackageInfo().packageName;
    334             SafetyNetLogger.logPermissionsToggled(packageName, mToggledGroups);
    335             mToggledGroups = null;
    336         }
    337     }
    338 
    339     private void setPreferencesCheckedState() {
    340         setPreferencesCheckedState(getPreferenceScreen());
    341         if (mExtraScreen != null) {
    342             setPreferencesCheckedState(mExtraScreen);
    343         }
    344     }
    345 
    346     private void setPreferencesCheckedState(PreferenceScreen screen) {
    347         int preferenceCount = screen.getPreferenceCount();
    348         for (int i = 0; i < preferenceCount; i++) {
    349             Preference preference = screen.getPreference(i);
    350             if (preference instanceof SwitchPreference) {
    351                 SwitchPreference switchPref = (SwitchPreference) preference;
    352                 AppPermissionGroup group = mAppPermissions.getPermissionGroup(switchPref.getKey());
    353                 if (group != null) {
    354                     switchPref.setChecked(group.areRuntimePermissionsGranted());
    355                 }
    356             }
    357         }
    358     }
    359 
    360     private static PackageInfo getPackageInfo(Activity activity, String packageName) {
    361         try {
    362             return activity.getPackageManager().getPackageInfo(
    363                     packageName, PackageManager.GET_PERMISSIONS);
    364         } catch (PackageManager.NameNotFoundException e) {
    365             Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
    366             return null;
    367         }
    368     }
    369 
    370     public static class AdditionalPermissionsFragment extends SettingsWithHeader {
    371         AppPermissionsFragment mOuterFragment;
    372 
    373         @Override
    374         public void onCreate(Bundle savedInstanceState) {
    375             mOuterFragment = (AppPermissionsFragment) getTargetFragment();
    376             super.onCreate(savedInstanceState);
    377             setHeader(mOuterFragment.mIcon, mOuterFragment.mLabel, mOuterFragment.mInfoIntent);
    378             setHasOptionsMenu(true);
    379         }
    380 
    381         @Override
    382         public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    383             setPreferenceScreen(mOuterFragment.mExtraScreen);
    384         }
    385 
    386         @Override
    387         public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    388             super.onViewCreated(view, savedInstanceState);
    389             String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
    390             bindUi(this, getPackageInfo(getActivity(), packageName));
    391         }
    392 
    393         @Override
    394         public boolean onOptionsItemSelected(MenuItem item) {
    395             switch (item.getItemId()) {
    396             case android.R.id.home:
    397                 getFragmentManager().popBackStack();
    398                 return true;
    399             }
    400             return super.onOptionsItemSelected(item);
    401         }
    402     }
    403 }
    404