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.example.android.apprestrictionenforcer; 18 19 import android.app.Activity; 20 import android.app.admin.DevicePolicyManager; 21 import android.content.Context; 22 import android.content.RestrictionEntry; 23 import android.content.RestrictionsManager; 24 import android.content.SharedPreferences; 25 import android.os.Bundle; 26 import android.support.annotation.Nullable; 27 import android.support.v4.app.Fragment; 28 import android.text.Editable; 29 import android.text.TextUtils; 30 import android.text.TextWatcher; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.widget.AdapterView; 35 import android.widget.ArrayAdapter; 36 import android.widget.CompoundButton; 37 import android.widget.EditText; 38 import android.widget.LinearLayout; 39 import android.widget.Spinner; 40 import android.widget.Switch; 41 import android.widget.Toast; 42 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.List; 46 47 /** 48 * This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema 49 * sample. 50 */ 51 public class AppRestrictionEnforcerFragment extends Fragment implements 52 CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener { 53 54 /** 55 * Key for {@link SharedPreferences} 56 */ 57 private static final String PREFS_KEY = "AppRestrictionEnforcerFragment"; 58 59 /** 60 * Key for the boolean restriction in AppRestrictionSchema. 61 */ 62 private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello"; 63 64 /** 65 * Key for the string restriction in AppRestrictionSchema. 66 */ 67 private static final String RESTRICTION_KEY_MESSAGE = "message"; 68 69 /** 70 * Key for the integer restriction in AppRestrictionSchema. 71 */ 72 private static final String RESTRICTION_KEY_NUMBER = "number"; 73 74 /** 75 * Key for the choice restriction in AppRestrictionSchema. 76 */ 77 private static final String RESTRICTION_KEY_RANK = "rank"; 78 79 /** 80 * Key for the multi-select restriction in AppRestrictionSchema. 81 */ 82 private static final String RESTRICTION_KEY_APPROVALS = "approvals"; 83 84 private static final String DELIMETER = ","; 85 86 /** 87 * Current status of the restrictions. 88 */ 89 private Bundle mCurrentRestrictions = new Bundle(); 90 91 // UI Components 92 private Switch mSwitchSayHello; 93 private EditText mEditMessage; 94 private EditText mEditNumber; 95 private Spinner mSpinnerRank; 96 private LinearLayout mLayoutApprovals; 97 98 @Override 99 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 100 @Nullable Bundle savedInstanceState) { 101 return inflater.inflate(R.layout.fragment_app_restriction_enforcer, container, false); 102 } 103 104 @Override 105 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 106 // Retain references for the UI elements 107 mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello); 108 mEditMessage = (EditText) view.findViewById(R.id.message); 109 mEditNumber = (EditText) view.findViewById(R.id.number); 110 mSpinnerRank = (Spinner) view.findViewById(R.id.rank); 111 mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals); 112 } 113 114 @Override 115 public void onResume() { 116 super.onResume(); 117 loadRestrictions(getActivity()); 118 } 119 120 @Override 121 public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 122 switch (compoundButton.getId()) { 123 case R.id.say_hello: { 124 saveCanSayHello(getActivity(), checked); 125 break; 126 } 127 case R.id.approval: { 128 if (checked) { 129 addApproval(getActivity(), (String) compoundButton.getTag()); 130 } else { 131 removeApproval(getActivity(), (String) compoundButton.getTag()); 132 } 133 break; 134 } 135 } 136 } 137 138 private TextWatcher mWatcherMessage = new EasyTextWatcher() { 139 @Override 140 public void afterTextChanged(Editable s) { 141 saveMessage(getActivity(), s.toString()); 142 } 143 }; 144 145 private TextWatcher mWatcherNumber = new EasyTextWatcher() { 146 @Override 147 public void afterTextChanged(Editable s) { 148 try { 149 String string = s.toString(); 150 if (!TextUtils.isEmpty(string)) { 151 saveNumber(getActivity(), Integer.parseInt(string)); 152 } 153 } catch (NumberFormatException e) { 154 Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show(); 155 } 156 } 157 }; 158 159 @Override 160 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 161 switch (parent.getId()) { 162 case R.id.rank: { 163 saveRank(getActivity(), (String) parent.getAdapter().getItem(position)); 164 break; 165 } 166 } 167 } 168 169 @Override 170 public void onNothingSelected(AdapterView<?> parent) { 171 // Nothing to do 172 } 173 174 /** 175 * Loads the restrictions for the AppRestrictionSchema sample. In this implementation, we just 176 * read the default value for the "can_say_hello" restriction. 177 * 178 * @param activity The activity 179 */ 180 private void loadRestrictions(Activity activity) { 181 RestrictionsManager manager = 182 (RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE); 183 List<RestrictionEntry> restrictions = 184 manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA); 185 SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE); 186 for (RestrictionEntry restriction : restrictions) { 187 String key = restriction.getKey(); 188 if (RESTRICTION_KEY_SAY_HELLO.equals(key)) { 189 updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO, 190 restriction.getSelectedState())); 191 } else if (RESTRICTION_KEY_MESSAGE.equals(key)) { 192 updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE, 193 restriction.getSelectedString())); 194 } else if (RESTRICTION_KEY_NUMBER.equals(key)) { 195 updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER, 196 restriction.getIntValue())); 197 } else if (RESTRICTION_KEY_RANK.equals(key)) { 198 updateRank(activity, restriction.getChoiceValues(), 199 prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString())); 200 } else if (RESTRICTION_KEY_APPROVALS.equals(key)) { 201 updateApprovals(activity, restriction.getChoiceValues(), 202 TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS, 203 TextUtils.join(DELIMETER, 204 restriction.getAllSelectedStrings())), 205 DELIMETER)); 206 } 207 } 208 } 209 210 private void updateCanSayHello(boolean canSayHello) { 211 mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello); 212 mSwitchSayHello.setOnCheckedChangeListener(null); 213 mSwitchSayHello.setChecked(canSayHello); 214 mSwitchSayHello.setOnCheckedChangeListener(this); 215 } 216 217 private void updateMessage(String message) { 218 mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message); 219 mEditMessage.removeTextChangedListener(mWatcherMessage); 220 mEditMessage.setText(message); 221 mEditMessage.addTextChangedListener(mWatcherMessage); 222 } 223 224 private void updateNumber(int number) { 225 mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number); 226 mEditNumber.removeTextChangedListener(mWatcherNumber); 227 mEditNumber.setText(String.valueOf(number)); 228 mEditNumber.addTextChangedListener(mWatcherNumber); 229 } 230 231 private void updateRank(Context context, String[] ranks, String selectedRank) { 232 mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank); 233 mSpinnerRank.setAdapter(new ArrayAdapter<>(context, 234 android.R.layout.simple_spinner_dropdown_item, ranks)); 235 mSpinnerRank.setSelection(search(ranks, selectedRank)); 236 mSpinnerRank.setOnItemSelectedListener(this); 237 } 238 239 private void updateApprovals(Context context, String[] approvals, 240 String[] selectedApprovals) { 241 mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals); 242 mLayoutApprovals.removeAllViews(); 243 for (String approval : approvals) { 244 Switch sw = new Switch(context); 245 sw.setText(approval); 246 sw.setTag(approval); 247 sw.setChecked(Arrays.asList(selectedApprovals).contains(approval)); 248 sw.setOnCheckedChangeListener(this); 249 sw.setId(R.id.approval); 250 mLayoutApprovals.addView(sw); 251 } 252 } 253 254 /** 255 * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema. 256 * 257 * @param activity The activity 258 * @param allow The value to be set for the restriction. 259 */ 260 private void saveCanSayHello(Activity activity, boolean allow) { 261 mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow); 262 saveRestrictions(activity); 263 // Note that the owner app needs to remember the restrictions on its own. 264 editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply(); 265 } 266 267 /** 268 * Saves the value for the "message" restriction of AppRestrictionSchema. 269 * 270 * @param activity The activity 271 * @param message The value to be set for the restriction. 272 */ 273 private void saveMessage(Activity activity, String message) { 274 mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message); 275 saveRestrictions(activity); 276 editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply(); 277 } 278 279 /** 280 * Saves the value for the "number" restriction of AppRestrictionSchema. 281 * 282 * @param activity The activity 283 * @param number The value to be set for the restriction. 284 */ 285 private void saveNumber(Activity activity, int number) { 286 mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number); 287 saveRestrictions(activity); 288 editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply(); 289 } 290 291 /** 292 * Saves the value for the "rank" restriction of AppRestrictionSchema. 293 * 294 * @param activity The activity 295 * @param rank The value to be set for the restriction. 296 */ 297 private void saveRank(Activity activity, String rank) { 298 mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank); 299 saveRestrictions(activity); 300 editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply(); 301 } 302 303 private void addApproval(Activity activity, String approval) { 304 List<String> approvals = new ArrayList<>(Arrays.asList( 305 mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS))); 306 if (approvals.contains(approval)) { 307 return; 308 } 309 approvals.add(approval); 310 saveApprovals(activity, approvals.toArray(new String[approvals.size()])); 311 } 312 313 private void removeApproval(Activity activity, String approval) { 314 List<String> approvals = new ArrayList<>(Arrays.asList( 315 mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS))); 316 if (!approval.contains(approval)) { 317 return; 318 } 319 approvals.remove(approval); 320 saveApprovals(activity, approvals.toArray(new String[approvals.size()])); 321 } 322 323 /** 324 * Saves the value for the "approvals" restriction of AppRestrictionSchema. 325 * 326 * @param activity The activity 327 * @param approvals The value to be set for the restriction. 328 */ 329 private void saveApprovals(Activity activity, String[] approvals) { 330 mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals); 331 saveRestrictions(activity); 332 editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS, 333 TextUtils.join(DELIMETER, approvals)).apply(); 334 } 335 336 private void saveRestrictions(Activity activity) { 337 DevicePolicyManager devicePolicyManager 338 = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 339 devicePolicyManager.setApplicationRestrictions( 340 EnforcerDeviceAdminReceiver.getComponentName(activity), 341 Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, mCurrentRestrictions); 342 } 343 344 private SharedPreferences.Editor editPreferences(Activity activity) { 345 return activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE).edit(); 346 } 347 348 /** 349 * Sequential search 350 * 351 * @param array The string array 352 * @param s The string to search for 353 * @return Index if found. -1 if not found. 354 */ 355 private int search(String[] array, String s) { 356 for (int i = 0; i < array.length; ++i) { 357 if (s.equals(array[i])) { 358 return i; 359 } 360 } 361 return -1; 362 } 363 364 } 365