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