Home | History | Annotate | Download | only in users
      1 /*
      2  * Copyright (C) 2014 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.tv.settings.users;
     18 
     19 import com.android.tv.settings.R;
     20 import com.android.tv.settings.dialog.DialogFragment;
     21 import com.android.tv.settings.dialog.DialogFragment.Action;
     22 
     23 import android.app.Activity;
     24 import android.app.Fragment;
     25 import android.app.FragmentManager;
     26 import android.content.BroadcastReceiver;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.RestrictionEntry;
     30 import android.content.pm.IPackageManager;
     31 import android.os.Bundle;
     32 import android.os.ServiceManager;
     33 import android.os.UserHandle;
     34 import android.os.UserManager;
     35 import android.text.TextUtils;
     36 import android.util.Log;
     37 
     38 import java.util.ArrayList;
     39 import java.util.Arrays;
     40 import java.util.HashSet;
     41 import java.util.List;
     42 
     43 /**
     44  * Handles all aspects of DialogFragment Actions for setting individual app restrictions.
     45  */
     46 class AppRestrictionsManager implements Action.Listener {
     47 
     48     interface Listener {
     49         void onRestrictionActionsLoaded(String packageName, ArrayList<Action> actions);
     50     }
     51 
     52     private static final boolean DEBUG = false;
     53     private static final String TAG = "RestrictedProfile";
     54 
     55     private static final String EXTRA_PACKAGE_NAME = "AppRestrictionsManager.package_name";
     56     private static final String EXTRA_RESTRICTIONS = "AppRestrictionsManager.restrictions";
     57     private static final String EXTRA_USER_HANDLE = "user_handle";
     58     private static final String EXTRA_ENABLED = "enabled";
     59     private static final String EXTRA_RESTRICTION_KEY = "restriction_key";
     60     private static final String EXTRA_CHOICE_VALUE = "choice_value";
     61     private static final String ACTION_CUSTOM_CONFIGURATION = "action_custom_configuration";
     62     private static final String ACTION_TRUE = "action_true";
     63     private static final String ACTION_FALSE = "action_false";
     64     private static final String ACTION_CHANGE_RESTRICTION = "action_change_restriction";
     65     private static final String ACTION_CHOICE = "action_choice";
     66     private static final String ACTION_MULTI_CHOICE = "action_multi_choice";
     67     private static final int MUTUALLY_EXCLUSIVE_CHOICE_CHECK_SET_ID = 1;
     68 
     69     private final Fragment mFragment;
     70     private final UserManager mUserManager;
     71     private final UserHandle mUserHandle;
     72     private final IPackageManager mIPm;
     73     private final String mPackageName;
     74     private final Listener mListener;
     75     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
     76         private static final String CUSTOM_RESTRICTIONS_INTENT = Intent.EXTRA_RESTRICTIONS_INTENT;
     77 
     78         @Override
     79         public void onReceive(Context context, Intent intent) {
     80             Bundle results = getResultExtras(true);
     81             mRestrictions = results.getParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST);
     82             mRestrictionsIntent = (Intent) results.getParcelable(CUSTOM_RESTRICTIONS_INTENT);
     83 
     84             if (mRestrictions != null && mRestrictionsIntent == null) {
     85                 saveRestrictions();
     86             } else if (mRestrictionsIntent != null) {
     87                 getRestrictionActions();
     88             }
     89         }
     90     };
     91 
     92     private ArrayList<RestrictionEntry> mRestrictions;
     93     private Intent mRestrictionsIntent;
     94     private DialogFragment mCurrentDialogFragment;
     95 
     96     /**
     97      * @param fragment THe fragment for which DialogFragment actions are being managed.
     98      * @param userHandle the user whose app restrictions are being set.
     99      * @param userManager the user manager for setting app restrictions.
    100      * @param packageName settings' package name (for special case restrictions).
    101      * @param listener a listener for when restriciton acitons are ready to be displayed.
    102      */
    103     AppRestrictionsManager(Fragment fragment, UserHandle userHandle, UserManager userManager,
    104             String packageName, Listener listener) {
    105         mFragment = fragment;
    106         mUserHandle = userHandle;
    107         mUserManager = userManager;
    108         mPackageName = packageName;
    109         mListener = listener;
    110         mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    111     }
    112 
    113     void loadRestrictionActions() {
    114         if (mPackageName.equals(mFragment.getActivity().getPackageName())) {
    115             // Settings, fake it by using user restrictions
    116             mRestrictions = RestrictionUtils.getRestrictions(mFragment.getActivity(), mUserHandle);
    117             mRestrictionsIntent = null;
    118             getRestrictionActions();
    119         } else {
    120             requestRestrictionsForApp();
    121         }
    122     }
    123 
    124     @Override
    125     public void onActionClicked(Action action) {
    126         String restrictionEntryKey = action.getIntent().getStringExtra(EXTRA_RESTRICTION_KEY);
    127         RestrictionEntry entry = restrictionEntryKey != null ? findRestriction(restrictionEntryKey)
    128                 : null;
    129 
    130         if (ACTION_CUSTOM_CONFIGURATION.equals(action.getKey())) {
    131             mFragment.startActivityForResult(action.getIntent(), 1);
    132         } else if (ACTION_CHANGE_RESTRICTION.equals(action.getKey())) {
    133             ArrayList<Action> actions = new ArrayList<Action>();
    134             switch (entry.getType()) {
    135                 case RestrictionEntry.TYPE_BOOLEAN:
    136                     actions.add(new Action.Builder()
    137                             .key(ACTION_TRUE)
    138                             .title(Boolean.toString(true))
    139                             .checked(entry.getSelectedState())
    140                             .checkSetId(MUTUALLY_EXCLUSIVE_CHOICE_CHECK_SET_ID)
    141                             .intent(action.getIntent())
    142                             .build());
    143                     actions.add(new Action.Builder()
    144                             .key(ACTION_FALSE)
    145                             .title(Boolean.toString(false))
    146                             .checked(!entry.getSelectedState())
    147                             .checkSetId(MUTUALLY_EXCLUSIVE_CHOICE_CHECK_SET_ID)
    148                             .intent(action.getIntent())
    149                             .build());
    150                     break;
    151                 case RestrictionEntry.TYPE_CHOICE:
    152                 case RestrictionEntry.TYPE_CHOICE_LEVEL:
    153                 {
    154                     String value = entry.getSelectedString();
    155                     if (value == null) {
    156                         value = entry.getDescription();
    157                     }
    158                     String[] choiceEntries = entry.getChoiceEntries();
    159                     String[] choiceValues = entry.getChoiceValues();
    160                     boolean useValue = (choiceEntries == null
    161                             || choiceEntries.length != choiceValues.length);
    162                     for (int i = 0; i < choiceValues.length; i++) {
    163                         String choiceValue = choiceValues[i];
    164                         String title = useValue ? choiceValue : choiceEntries[i];
    165                         Intent intent = new Intent(action.getIntent());
    166                         intent.putExtra(EXTRA_CHOICE_VALUE, choiceValue);
    167                         actions.add(new Action.Builder()
    168                                 .key(ACTION_CHOICE)
    169                                 .title(title)
    170                                 .checked(choiceValue.equals(value))
    171                                 .checkSetId(MUTUALLY_EXCLUSIVE_CHOICE_CHECK_SET_ID)
    172                                 .intent(intent)
    173                                 .build());
    174                     }
    175                 }
    176                     break;
    177                 case RestrictionEntry.TYPE_MULTI_SELECT:
    178                 {
    179                     List<String> selectedChoiceValues = Arrays.asList(
    180                             entry.getAllSelectedStrings());
    181                     String[] choiceEntries = entry.getChoiceEntries();
    182                     String[] choiceValues = entry.getChoiceValues();
    183                     boolean useValues = (choiceEntries == null
    184                             || choiceEntries.length != choiceValues.length);
    185                     for (int i = 0; i < choiceValues.length; i++) {
    186                         String choiceValue = choiceValues[i];
    187                         String title = useValues ? choiceValue : choiceEntries[i];
    188                         Intent intent = new Intent(action.getIntent());
    189                         intent.putExtra(EXTRA_CHOICE_VALUE, choiceValue);
    190                         actions.add(new Action.Builder()
    191                                 .key(ACTION_MULTI_CHOICE)
    192                                 .title(title)
    193                                 .checked(selectedChoiceValues.contains(choiceValue))
    194                                 .intent(intent)
    195                                 .build());
    196                     }
    197                 }
    198                     break;
    199                 case RestrictionEntry.TYPE_NULL:
    200                 default:
    201             }
    202 
    203             if (!actions.isEmpty()) {
    204                 mCurrentDialogFragment = new DialogFragment.Builder()
    205                         .title(entry.getTitle())
    206                         .description(entry.getDescription())
    207                         .iconResourceId(RestrictedProfileActivity.getIconResource())
    208                         .iconBackgroundColor(
    209                                 mFragment.getActivity().getResources()
    210                                         .getColor(R.color.icon_background))
    211                         .actions(actions).build();
    212                 mCurrentDialogFragment.setListener(this);
    213                 DialogFragment.add(mFragment.getFragmentManager(), mCurrentDialogFragment);
    214 
    215             }
    216         } else if (ACTION_TRUE.equals(action.getKey())) {
    217             entry.setSelectedState(true);
    218             saveRestrictions();
    219         } else if (ACTION_FALSE.equals(action.getKey())) {
    220             entry.setSelectedState(false);
    221             saveRestrictions();
    222         } else if (ACTION_CHOICE.equals(action.getKey())) {
    223             entry.setSelectedString(getChoiceValue(action));
    224             saveRestrictions();
    225         } else if (ACTION_MULTI_CHOICE.equals(action.getKey())) {
    226 
    227             action.setChecked(!action.isChecked());
    228             if (mCurrentDialogFragment != null) {
    229                 mCurrentDialogFragment.setActions(mCurrentDialogFragment.getActions());
    230             }
    231             HashSet<String> selectedChoiceValues = new HashSet<String>();
    232             selectedChoiceValues.addAll(Arrays.asList(entry.getAllSelectedStrings()));
    233 
    234             if (action.isChecked()) {
    235                 selectedChoiceValues.add(getChoiceValue(action));
    236             } else {
    237                 selectedChoiceValues.remove(getChoiceValue(action));
    238             }
    239             entry.setAllSelectedStrings(
    240                     selectedChoiceValues.toArray(new String[selectedChoiceValues.size()]));
    241             saveRestrictions();
    242         }
    243     }
    244 
    245     void onActivityResult(int requestCode, int resultCode, Intent data) {
    246         if (resultCode == Activity.RESULT_OK) {
    247             ArrayList<RestrictionEntry> list =
    248                     data.getParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST);
    249             Bundle bundle = data.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE);
    250             if (list != null) {
    251                 // If there's a valid result, persist it to the user manager.
    252                 mUserManager.setApplicationRestrictions(mPackageName,
    253                         RestrictionUtils.restrictionsToBundle(list), mUserHandle);
    254             } else if (bundle != null) {
    255                 // If there's a valid result, persist it to the user manager.
    256                 mUserManager.setApplicationRestrictions(mPackageName, bundle, mUserHandle);
    257             }
    258         }
    259     }
    260 
    261     private RestrictionEntry findRestriction(String key) {
    262         for (RestrictionEntry entry : mRestrictions) {
    263             if (entry.getKey().equals(key)) {
    264                 return entry;
    265             }
    266         }
    267         Log.wtf(TAG, "Couldn't find the restriction to set!");
    268         return null;
    269     }
    270 
    271     private String getChoiceValue(Action action) {
    272         return action.getIntent().getStringExtra(EXTRA_CHOICE_VALUE);
    273     }
    274 
    275     /**
    276      * Send a broadcast to the app to query its restrictions
    277      *
    278      * @param packageName package name of the app with restrictions
    279      */
    280     private void requestRestrictionsForApp() {
    281         Bundle oldEntries = mUserManager.getApplicationRestrictions(mPackageName, mUserHandle);
    282         Intent intent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES);
    283         intent.setPackage(mPackageName);
    284         intent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, oldEntries);
    285         intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
    286         mFragment.getActivity().sendOrderedBroadcast(intent, null, mBroadcastReceiver, null,
    287                 Activity.RESULT_OK, null, null);
    288     }
    289 
    290     private void getRestrictionActions() {
    291         ArrayList<Action> actions = new ArrayList<Action>();
    292 
    293         if (mRestrictionsIntent != null) {
    294             actions.add(new Action.Builder()
    295                     .key(ACTION_CUSTOM_CONFIGURATION)
    296                     .title(mFragment.getActivity().getString(
    297                             R.string.restricted_profile_customize_restrictions))
    298                     .intent(mRestrictionsIntent)
    299                     .hasNext(true)
    300                     .build());
    301         }
    302 
    303         if (mRestrictions != null) {
    304             for (RestrictionEntry entry : mRestrictions) {
    305                 Intent data = new Intent().putExtra(EXTRA_RESTRICTION_KEY, entry.getKey());
    306                 switch (entry.getType()) {
    307                     case RestrictionEntry.TYPE_BOOLEAN:
    308                         actions.add(new Action.Builder()
    309                                 .key(ACTION_CHANGE_RESTRICTION)
    310                                 .title(entry.getTitle())
    311                                 .description(mFragment.getActivity().getString(
    312                                         R.string.restriction_description, entry.getDescription(),
    313                                         Boolean.toString(entry.getSelectedState())))
    314                                 .hasNext(mRestrictionsIntent == null)
    315                                 .infoOnly(mRestrictionsIntent != null)
    316                                 .intent(data)
    317                                 .multilineDescription(true)
    318                                 .build());
    319                         break;
    320                     case RestrictionEntry.TYPE_CHOICE:
    321                     case RestrictionEntry.TYPE_CHOICE_LEVEL:
    322                         String value = entry.getSelectedString();
    323                         if (value == null) {
    324                             value = entry.getDescription();
    325                         }
    326                         actions.add(new Action.Builder()
    327                                 .key(ACTION_CHANGE_RESTRICTION)
    328                                 .title(entry.getTitle())
    329                                 .description(mFragment.getActivity().getString(
    330                                         R.string.restriction_description,
    331                                         entry.getDescription(), findInArray(
    332                                                 entry.getChoiceEntries(), entry.getChoiceValues(),
    333                                                 value)))
    334                                 .hasNext(mRestrictionsIntent == null)
    335                                 .infoOnly(mRestrictionsIntent != null)
    336                                 .intent(data)
    337                                 .multilineDescription(true)
    338                                 .build());
    339                         break;
    340                     case RestrictionEntry.TYPE_MULTI_SELECT:
    341                         actions.add(new Action.Builder()
    342                                 .key(ACTION_CHANGE_RESTRICTION)
    343                                 .title(entry.getTitle())
    344                                 .description(mFragment.getActivity().getString(
    345                                         R.string.restriction_description,
    346                                         entry.getDescription(), TextUtils.join(
    347                                                 ", ", findSelectedStrings(entry.getChoiceEntries(),
    348                                                         entry.getChoiceValues(),
    349                                                         entry.getAllSelectedStrings()))))
    350                                 .hasNext(mRestrictionsIntent == null)
    351                                 .infoOnly(mRestrictionsIntent != null)
    352                                 .intent(data)
    353                                 .multilineDescription(true)
    354                                 .build());
    355                         break;
    356                     case RestrictionEntry.TYPE_NULL:
    357                     default:
    358                 }
    359             }
    360         }
    361 
    362         mListener.onRestrictionActionsLoaded(mPackageName, actions);
    363     }
    364 
    365     private String findInArray(String[] choiceEntries, String[] choiceValues,
    366             String selectedString) {
    367         if (choiceEntries == null || choiceValues.length != choiceEntries.length) {
    368             return selectedString;
    369         }
    370         for (int i = 0; i < choiceValues.length; i++) {
    371             if (choiceValues[i].equals(selectedString)) {
    372                 return choiceEntries[i];
    373             }
    374         }
    375         return selectedString;
    376     }
    377 
    378     private String[] findSelectedStrings(String[] choiceEntries, String[] choiceValues,
    379             String[] selectedStrings) {
    380         if (choiceEntries == null || choiceValues.length != choiceEntries.length) {
    381             return selectedStrings;
    382         }
    383         String[] selectedStringsMapped = new String[selectedStrings.length];
    384         for (int i = 0; i < selectedStrings.length; i++) {
    385             selectedStringsMapped[i] = selectedStrings[i];
    386             for (int j = 0; j < choiceValues.length; j++) {
    387                 if (choiceValues[j].equals(selectedStrings[i])) {
    388                     selectedStringsMapped[i] = choiceEntries[j];
    389                 }
    390             }
    391         }
    392         return selectedStringsMapped;
    393     }
    394 
    395     private void saveRestrictions() {
    396         getRestrictionActions();
    397         if (mPackageName.equals(mFragment.getActivity().getPackageName())) {
    398             RestrictionUtils.setRestrictions(mFragment.getActivity(), mRestrictions, mUserHandle);
    399         } else {
    400             Bundle bundle = RestrictionUtils.restrictionsToBundle(mRestrictions);
    401             mUserManager.setApplicationRestrictions(mPackageName, bundle, mUserHandle);
    402         }
    403     }
    404 }
    405