Home | History | Annotate | Download | only in television
      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 package com.android.packageinstaller.permission.ui.television;
     17 
     18 import android.app.ActionBar;
     19 import android.app.AlertDialog;
     20 import android.app.Fragment;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.DialogInterface.OnClickListener;
     24 import android.content.Intent;
     25 import android.graphics.drawable.Drawable;
     26 import android.os.Bundle;
     27 import android.support.v14.preference.SwitchPreference;
     28 import android.support.v4.util.ArrayMap;
     29 import android.support.v7.preference.Preference;
     30 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
     31 import android.support.v7.preference.Preference.OnPreferenceClickListener;
     32 import android.support.v7.preference.PreferenceScreen;
     33 import android.util.ArraySet;
     34 import android.view.Menu;
     35 import android.view.MenuInflater;
     36 import android.view.MenuItem;
     37 import android.view.View;
     38 import android.widget.TextView;
     39 
     40 import com.android.packageinstaller.DeviceUtils;
     41 import com.android.packageinstaller.R;
     42 import com.android.packageinstaller.permission.model.AppPermissionGroup;
     43 import com.android.packageinstaller.permission.model.PermissionApps;
     44 import com.android.packageinstaller.permission.model.PermissionApps.Callback;
     45 import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
     46 import com.android.packageinstaller.permission.ui.ReviewPermissionsActivity;
     47 import com.android.packageinstaller.permission.utils.LocationUtils;
     48 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
     49 import com.android.packageinstaller.permission.utils.Utils;
     50 
     51 import java.util.ArrayList;
     52 import java.util.List;
     53 
     54 public final class PermissionAppsFragment extends SettingsWithHeader implements Callback,
     55         OnPreferenceChangeListener {
     56 
     57     private static final int MENU_SHOW_SYSTEM = Menu.FIRST;
     58     private static final int MENU_HIDE_SYSTEM = Menu.FIRST + 1;
     59     private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
     60 
     61     public static PermissionAppsFragment newInstance(String permissionName) {
     62         return setPermissionName(new PermissionAppsFragment(), permissionName);
     63     }
     64 
     65     private static <T extends Fragment> T setPermissionName(T fragment, String permissionName) {
     66         Bundle arguments = new Bundle();
     67         arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
     68         fragment.setArguments(arguments);
     69         return fragment;
     70     }
     71 
     72     private PermissionApps mPermissionApps;
     73 
     74     private PreferenceScreen mExtraScreen;
     75 
     76     private ArrayMap<String, AppPermissionGroup> mToggledGroups;
     77     private ArraySet<String> mLauncherPkgs;
     78     private boolean mHasConfirmedRevoke;
     79 
     80     private boolean mShowSystem;
     81     private boolean mHasSystemApps;
     82     private MenuItem mShowSystemMenu;
     83     private MenuItem mHideSystemMenu;
     84 
     85     private Callback mOnPermissionsLoadedListener;
     86 
     87     @Override
     88     public void onCreate(Bundle savedInstanceState) {
     89         super.onCreate(savedInstanceState);
     90         setLoading(true /* loading */, false /* animate */);
     91         setHasOptionsMenu(true);
     92         final ActionBar ab = getActivity().getActionBar();
     93         if (ab != null) {
     94             ab.setDisplayHomeAsUpEnabled(true);
     95         }
     96         mLauncherPkgs = Utils.getLauncherPackages(getContext());
     97 
     98         String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
     99         mPermissionApps = new PermissionApps(getActivity(), groupName, this);
    100         mPermissionApps.refresh(true);
    101     }
    102 
    103     @Override
    104     public void onResume() {
    105         super.onResume();
    106         mPermissionApps.refresh(true);
    107     }
    108 
    109     @Override
    110     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    111         if (mHasSystemApps) {
    112             mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
    113                     R.string.menu_show_system);
    114             mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE,
    115                     R.string.menu_hide_system);
    116             updateMenu();
    117         }
    118     }
    119 
    120     @Override
    121     public boolean onOptionsItemSelected(MenuItem item) {
    122         switch (item.getItemId()) {
    123             case android.R.id.home:
    124                 getActivity().finish();
    125                 return true;
    126             case MENU_SHOW_SYSTEM:
    127             case MENU_HIDE_SYSTEM:
    128                 mShowSystem = item.getItemId() == MENU_SHOW_SYSTEM;
    129                 if (mPermissionApps.getApps() != null) {
    130                     onPermissionsLoaded(mPermissionApps);
    131                 }
    132                 updateMenu();
    133                 break;
    134         }
    135         return super.onOptionsItemSelected(item);
    136     }
    137 
    138     private void updateMenu() {
    139         mShowSystemMenu.setVisible(!mShowSystem);
    140         mHideSystemMenu.setVisible(mShowSystem);
    141     }
    142 
    143     @Override
    144     protected void onSetEmptyText(TextView textView) {
    145         textView.setText(R.string.no_apps);
    146     }
    147 
    148     @Override
    149     public void onViewCreated(View view, Bundle savedInstanceState) {
    150         super.onViewCreated(view, savedInstanceState);
    151         bindUi(this, mPermissionApps);
    152     }
    153 
    154     private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) {
    155         final Drawable icon = permissionApps.getIcon();
    156         final CharSequence label = permissionApps.getLabel();
    157 
    158         fragment.setHeader(null, null, null,
    159                 fragment.getString(R.string.permission_apps_decor_title, label));
    160     }
    161 
    162     private void setOnPermissionsLoadedListener(Callback callback) {
    163         mOnPermissionsLoadedListener = callback;
    164     }
    165 
    166     @Override
    167     public void onPermissionsLoaded(PermissionApps permissionApps) {
    168         Context context = getPreferenceManager().getContext();
    169 
    170         if (context == null) {
    171             return;
    172         }
    173 
    174         boolean isTelevision = DeviceUtils.isTelevision(context);
    175         PreferenceScreen screen = getPreferenceScreen();
    176 
    177         ArraySet<String> preferencesToRemove = new ArraySet<>();
    178         for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
    179             preferencesToRemove.add(screen.getPreference(i).getKey());
    180         }
    181         if (mExtraScreen != null) {
    182             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
    183                 preferencesToRemove.add(mExtraScreen.getPreference(i).getKey());
    184             }
    185         }
    186 
    187         mHasSystemApps = false;
    188         boolean menuOptionsInvalided = false;
    189 
    190         for (PermissionApp app : permissionApps.getApps()) {
    191             if (!Utils.shouldShowPermission(app)) {
    192                 continue;
    193             }
    194 
    195             String key = app.getKey();
    196             preferencesToRemove.remove(key);
    197             Preference existingPref = screen.findPreference(key);
    198             if (existingPref == null && mExtraScreen != null) {
    199                 existingPref = mExtraScreen.findPreference(key);
    200             }
    201 
    202             boolean isSystemApp = Utils.isSystem(app, mLauncherPkgs);
    203 
    204             if (isSystemApp && !menuOptionsInvalided) {
    205                 mHasSystemApps = true;
    206                 getActivity().invalidateOptionsMenu();
    207                 menuOptionsInvalided = true;
    208             }
    209 
    210             if (isSystemApp && !isTelevision && !mShowSystem) {
    211                 if (existingPref != null) {
    212                     screen.removePreference(existingPref);
    213                 }
    214                 continue;
    215             }
    216 
    217             if (existingPref != null) {
    218                 // If existing preference - only update its state.
    219                 if (app.isPolicyFixed()) {
    220                     existingPref.setSummary(getString(
    221                             R.string.permission_summary_enforced_by_policy));
    222                 }
    223                 existingPref.setPersistent(false);
    224                 existingPref.setEnabled(!app.isPolicyFixed());
    225                 if (existingPref instanceof SwitchPreference) {
    226                     ((SwitchPreference) existingPref)
    227                             .setChecked(app.areRuntimePermissionsGranted());
    228                 }
    229                 continue;
    230             }
    231 
    232             SwitchPreference pref = new SwitchPreference(context);
    233             pref.setOnPreferenceChangeListener(this);
    234             pref.setKey(app.getKey());
    235             pref.setIcon(app.getIcon());
    236             pref.setTitle(app.getLabel());
    237             if (app.isPolicyFixed()) {
    238                 pref.setSummary(getString(R.string.permission_summary_enforced_by_policy));
    239             }
    240             pref.setPersistent(false);
    241             pref.setEnabled(!app.isPolicyFixed());
    242             pref.setChecked(app.areRuntimePermissionsGranted());
    243 
    244             if (isSystemApp && isTelevision) {
    245                 if (mExtraScreen == null) {
    246                     mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
    247                 }
    248                 mExtraScreen.addPreference(pref);
    249             } else {
    250                 screen.addPreference(pref);
    251             }
    252         }
    253 
    254         if (mExtraScreen != null) {
    255             preferencesToRemove.remove(KEY_SHOW_SYSTEM_PREFS);
    256             Preference pref = screen.findPreference(KEY_SHOW_SYSTEM_PREFS);
    257 
    258             if (pref == null) {
    259                 pref = new Preference(context);
    260                 pref.setKey(KEY_SHOW_SYSTEM_PREFS);
    261                 pref.setIcon(Utils.applyTint(context, R.drawable.ic_toc,
    262                         android.R.attr.colorControlNormal));
    263                 pref.setTitle(R.string.preference_show_system_apps);
    264                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    265                     @Override
    266                     public boolean onPreferenceClick(Preference preference) {
    267                         SystemAppsFragment frag = new SystemAppsFragment();
    268                         setPermissionName(frag, getArguments().getString(
    269                                 Intent.EXTRA_PERMISSION_NAME));
    270                         frag.setTargetFragment(PermissionAppsFragment.this, 0);
    271                         getFragmentManager().beginTransaction()
    272                             .replace(android.R.id.content, frag)
    273                             .addToBackStack("SystemApps")
    274                             .commit();
    275                         return true;
    276                     }
    277                 });
    278                 screen.addPreference(pref);
    279             }
    280 
    281             int grantedCount = 0;
    282             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
    283                 if (((SwitchPreference) mExtraScreen.getPreference(i)).isChecked()) {
    284                     grantedCount++;
    285                 }
    286             }
    287             pref.setSummary(getString(R.string.app_permissions_group_summary,
    288                     grantedCount, mExtraScreen.getPreferenceCount()));
    289         }
    290 
    291         for (String key : preferencesToRemove) {
    292             Preference pref = screen.findPreference(key);
    293             if (pref != null) {
    294                 screen.removePreference(pref);
    295             } else if (mExtraScreen != null) {
    296                 pref = mExtraScreen.findPreference(key);
    297                 if (pref != null) {
    298                     mExtraScreen.removePreference(pref);
    299                 }
    300             }
    301         }
    302 
    303         setLoading(false /* loading */, true /* animate */);
    304 
    305         if (mOnPermissionsLoadedListener != null) {
    306             mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps);
    307         }
    308     }
    309 
    310     @Override
    311     public boolean onPreferenceChange(final Preference preference, Object newValue) {
    312         String pkg = preference.getKey();
    313         final PermissionApp app = mPermissionApps.getApp(pkg);
    314 
    315         if (app == null) {
    316             return false;
    317         }
    318 
    319         if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(),
    320                 app.getPackageName())) {
    321             LocationUtils.showLocationDialog(getContext(), app.getLabel());
    322             return false;
    323         }
    324 
    325         addToggledGroup(app.getPackageName(), app.getPermissionGroup());
    326 
    327         if (app.isReviewRequired()) {
    328             Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class);
    329             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, app.getPackageName());
    330             startActivity(intent);
    331             return false;
    332         }
    333 
    334         if (newValue == Boolean.TRUE) {
    335             app.grantRuntimePermissions();
    336         } else {
    337             final boolean grantedByDefault = app.hasGrantedByDefaultPermissions();
    338             if (grantedByDefault || (!app.doesSupportRuntimePermissions()
    339                     && !mHasConfirmedRevoke)) {
    340                 new AlertDialog.Builder(getContext())
    341                         .setMessage(grantedByDefault ? R.string.system_warning
    342                                 : R.string.old_sdk_deny_warning)
    343                         .setNegativeButton(R.string.cancel, null)
    344                         .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
    345                                 new OnClickListener() {
    346                             @Override
    347                             public void onClick(DialogInterface dialog, int which) {
    348                                 ((SwitchPreference) preference).setChecked(false);
    349                                 app.revokeRuntimePermissions();
    350                                 if (!grantedByDefault) {
    351                                     mHasConfirmedRevoke = true;
    352                                 }
    353                             }
    354                         })
    355                         .show();
    356                 return false;
    357             } else {
    358                 app.revokeRuntimePermissions();
    359             }
    360         }
    361         return true;
    362     }
    363 
    364     @Override
    365     public void onPause() {
    366         super.onPause();
    367         logToggledGroups();
    368     }
    369 
    370     private void addToggledGroup(String packageName, AppPermissionGroup group) {
    371         if (mToggledGroups == null) {
    372             mToggledGroups = new ArrayMap<>();
    373         }
    374         // Double toggle is back to initial state.
    375         if (mToggledGroups.containsKey(packageName)) {
    376             mToggledGroups.remove(packageName);
    377         } else {
    378             mToggledGroups.put(packageName, group);
    379         }
    380     }
    381 
    382     private void logToggledGroups() {
    383         if (mToggledGroups != null) {
    384             final int groupCount = mToggledGroups.size();
    385             for (int i = 0; i < groupCount; i++) {
    386                 String packageName = mToggledGroups.keyAt(i);
    387                 List<AppPermissionGroup> groups = new ArrayList<>();
    388                 groups.add(mToggledGroups.valueAt(i));
    389                 SafetyNetLogger.logPermissionsToggled(packageName, groups);
    390             }
    391             mToggledGroups = null;
    392         }
    393     }
    394 
    395     public static class SystemAppsFragment extends SettingsWithHeader implements Callback {
    396         PermissionAppsFragment mOuterFragment;
    397 
    398         @Override
    399         public void onCreate(Bundle savedInstanceState) {
    400             mOuterFragment = (PermissionAppsFragment) getTargetFragment();
    401             setLoading(true /* loading */, false /* animate */);
    402             super.onCreate(savedInstanceState);
    403         }
    404 
    405         @Override
    406         public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    407             if (mOuterFragment.mExtraScreen != null) {
    408                 setPreferenceScreen();
    409             } else {
    410                 mOuterFragment.setOnPermissionsLoadedListener(this);
    411             }
    412         }
    413 
    414         @Override
    415         public void onViewCreated(View view, Bundle savedInstanceState) {
    416             super.onViewCreated(view, savedInstanceState);
    417             String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
    418             PermissionApps permissionApps = new PermissionApps(getActivity(), groupName, null);
    419             bindUi(this, permissionApps);
    420         }
    421 
    422         @Override
    423         public void onResume() {
    424             super.onResume();
    425             mOuterFragment.mPermissionApps.refresh(true);
    426         }
    427 
    428         @Override
    429         public void onDestroy() {
    430             super.onDestroy();
    431             mOuterFragment.setOnPermissionsLoadedListener(null);
    432         }
    433 
    434 
    435         private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) {
    436             final CharSequence label = permissionApps.getLabel();
    437             fragment.setHeader(null, null, null,
    438                     fragment.getString(R.string.system_apps_decor_title, label));
    439         }
    440 
    441         @Override
    442         public void onPermissionsLoaded(PermissionApps permissionApps) {
    443             setPreferenceScreen();
    444         }
    445 
    446         private void setPreferenceScreen() {
    447             setPreferenceScreen(mOuterFragment.mExtraScreen);
    448             setLoading(false /* loading */, true /* animate */);
    449         }
    450     }
    451 }
    452