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.Build; 26 import android.os.Bundle; 27 import android.os.Parcelable; 28 import android.support.annotation.NonNull; 29 import android.support.annotation.Nullable; 30 import android.support.v4.app.Fragment; 31 import android.text.Editable; 32 import android.text.TextUtils; 33 import android.text.TextWatcher; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.widget.AdapterView; 38 import android.widget.ArrayAdapter; 39 import android.widget.Button; 40 import android.widget.CompoundButton; 41 import android.widget.EditText; 42 import android.widget.LinearLayout; 43 import android.widget.Spinner; 44 import android.widget.Switch; 45 import android.widget.TextView; 46 import android.widget.Toast; 47 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.HashMap; 51 import java.util.List; 52 import java.util.Map; 53 54 /** 55 * This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema 56 * sample. 57 */ 58 public class AppRestrictionEnforcerFragment extends Fragment implements 59 CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener, 60 View.OnClickListener, ItemAddFragment.OnItemAddedListener { 61 62 /** 63 * Key for {@link SharedPreferences} 64 */ 65 private static final String PREFS_KEY = "AppRestrictionEnforcerFragment"; 66 67 /** 68 * Key for the boolean restriction in AppRestrictionSchema. 69 */ 70 private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello"; 71 72 /** 73 * Key for the string restriction in AppRestrictionSchema. 74 */ 75 private static final String RESTRICTION_KEY_MESSAGE = "message"; 76 77 /** 78 * Key for the integer restriction in AppRestrictionSchema. 79 */ 80 private static final String RESTRICTION_KEY_NUMBER = "number"; 81 82 /** 83 * Key for the choice restriction in AppRestrictionSchema. 84 */ 85 private static final String RESTRICTION_KEY_RANK = "rank"; 86 87 /** 88 * Key for the multi-select restriction in AppRestrictionSchema. 89 */ 90 private static final String RESTRICTION_KEY_APPROVALS = "approvals"; 91 92 /** 93 * Key for the bundle restriction in AppRestrictionSchema. 94 */ 95 private static final String RESTRICTION_KEY_PROFILE = "profile"; 96 private static final String RESTRICTION_KEY_PROFILE_NAME = "name"; 97 private static final String RESTRICTION_KEY_PROFILE_AGE = "age"; 98 99 /** 100 * Key for the bundle array restriction in AppRestrictionSchema. 101 */ 102 private static final String RESTRICTION_KEY_ITEMS = "items"; 103 private static final String RESTRICTION_KEY_ITEM_KEY = "key"; 104 private static final String RESTRICTION_KEY_ITEM_VALUE = "value"; 105 106 private static final String DELIMETER = ","; 107 private static final String SEPARATOR = ":"; 108 109 private static final boolean BUNDLE_SUPPORTED = Build.VERSION.SDK_INT >= 23; 110 111 /** 112 * Current status of the restrictions. 113 */ 114 private Bundle mCurrentRestrictions = new Bundle(); 115 116 // UI Components 117 private Switch mSwitchSayHello; 118 private EditText mEditMessage; 119 private EditText mEditNumber; 120 private Spinner mSpinnerRank; 121 private LinearLayout mLayoutApprovals; 122 private EditText mEditProfileName; 123 private EditText mEditProfileAge; 124 private LinearLayout mLayoutItems; 125 126 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)127 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 128 @Nullable Bundle savedInstanceState) { 129 return inflater.inflate(R.layout.fragment_app_restriction_enforcer, container, false); 130 } 131 132 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)133 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 134 // Retain references for the UI elements 135 mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello); 136 mEditMessage = (EditText) view.findViewById(R.id.message); 137 mEditNumber = (EditText) view.findViewById(R.id.number); 138 mSpinnerRank = (Spinner) view.findViewById(R.id.rank); 139 mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals); 140 mEditProfileName = (EditText) view.findViewById(R.id.profile_name); 141 mEditProfileAge = (EditText) view.findViewById(R.id.profile_age); 142 mLayoutItems = (LinearLayout) view.findViewById(R.id.items); 143 view.findViewById(R.id.item_add).setOnClickListener(this); 144 View bundleLayout = view.findViewById(R.id.bundle_layout); 145 View bundleArrayLayout = view.findViewById(R.id.bundle_array_layout); 146 if (BUNDLE_SUPPORTED) { 147 bundleLayout.setVisibility(View.VISIBLE); 148 bundleArrayLayout.setVisibility(View.VISIBLE); 149 } else { 150 bundleLayout.setVisibility(View.GONE); 151 bundleArrayLayout.setVisibility(View.GONE); 152 } 153 } 154 155 @Override onResume()156 public void onResume() { 157 super.onResume(); 158 loadRestrictions(getActivity()); 159 } 160 161 @Override onCheckedChanged(CompoundButton compoundButton, boolean checked)162 public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 163 switch (compoundButton.getId()) { 164 case R.id.say_hello: { 165 saveCanSayHello(getActivity(), checked); 166 break; 167 } 168 case R.id.approval: { 169 if (checked) { 170 addApproval(getActivity(), (String) compoundButton.getTag()); 171 } else { 172 removeApproval(getActivity(), (String) compoundButton.getTag()); 173 } 174 break; 175 } 176 } 177 } 178 179 private TextWatcher mWatcherMessage = new EasyTextWatcher() { 180 @Override 181 public void afterTextChanged(Editable s) { 182 saveMessage(getActivity(), s.toString()); 183 } 184 }; 185 186 private TextWatcher mWatcherNumber = new EasyTextWatcher() { 187 @Override 188 public void afterTextChanged(Editable s) { 189 try { 190 String string = s.toString(); 191 if (!TextUtils.isEmpty(string)) { 192 saveNumber(getActivity(), Integer.parseInt(string)); 193 } 194 } catch (NumberFormatException e) { 195 Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show(); 196 } 197 } 198 }; 199 200 private TextWatcher mWatcherProfile = new EasyTextWatcher() { 201 @Override 202 public void afterTextChanged(Editable s) { 203 try { 204 String name = mEditProfileName.getText().toString(); 205 String ageString = mEditProfileAge.getText().toString(); 206 if (!TextUtils.isEmpty(ageString)) { 207 saveProfile(getActivity(), name, Integer.parseInt(ageString)); 208 } 209 } catch (NumberFormatException e) { 210 Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show(); 211 } 212 } 213 }; 214 215 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)216 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 217 switch (parent.getId()) { 218 case R.id.rank: { 219 saveRank(getActivity(), (String) parent.getAdapter().getItem(position)); 220 break; 221 } 222 } 223 } 224 225 @Override onNothingSelected(AdapterView<?> parent)226 public void onNothingSelected(AdapterView<?> parent) { 227 // Nothing to do 228 } 229 230 @Override onClick(View v)231 public void onClick(View v) { 232 switch (v.getId()) { 233 case R.id.item_add: 234 new ItemAddFragment().show(getChildFragmentManager(), "dialog"); 235 break; 236 case R.id.item_remove: 237 String key = (String) v.getTag(); 238 removeItem(key); 239 mLayoutItems.removeView((View) v.getParent()); 240 break; 241 } 242 } 243 244 @Override onItemAdded(String key, String value)245 public void onItemAdded(String key, String value) { 246 key = TextUtils.replace(key, 247 new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString(); 248 value = TextUtils.replace(value, 249 new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString(); 250 Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS); 251 Map<String, String> items = new HashMap<>(); 252 if (parcelables != null) { 253 for (Parcelable parcelable : parcelables) { 254 Bundle bundle = (Bundle) parcelable; 255 items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY), 256 bundle.getString(RESTRICTION_KEY_ITEM_VALUE)); 257 } 258 } 259 items.put(key, value); 260 insertItemRow(LayoutInflater.from(getActivity()), key, value); 261 saveItems(getActivity(), items); 262 } 263 264 /** 265 * Loads the restrictions for the AppRestrictionSchema sample. 266 * 267 * @param activity The activity 268 */ loadRestrictions(Activity activity)269 private void loadRestrictions(Activity activity) { 270 RestrictionsManager manager = 271 (RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE); 272 List<RestrictionEntry> restrictions = 273 manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA); 274 SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE); 275 for (RestrictionEntry restriction : restrictions) { 276 String key = restriction.getKey(); 277 if (RESTRICTION_KEY_SAY_HELLO.equals(key)) { 278 updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO, 279 restriction.getSelectedState())); 280 } else if (RESTRICTION_KEY_MESSAGE.equals(key)) { 281 updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE, 282 restriction.getSelectedString())); 283 } else if (RESTRICTION_KEY_NUMBER.equals(key)) { 284 updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER, 285 restriction.getIntValue())); 286 } else if (RESTRICTION_KEY_RANK.equals(key)) { 287 updateRank(activity, restriction.getChoiceValues(), 288 prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString())); 289 } else if (RESTRICTION_KEY_APPROVALS.equals(key)) { 290 updateApprovals(activity, restriction.getChoiceValues(), 291 TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS, 292 TextUtils.join(DELIMETER, 293 restriction.getAllSelectedStrings())), 294 DELIMETER)); 295 } else if (BUNDLE_SUPPORTED && RESTRICTION_KEY_PROFILE.equals(key)) { 296 String name = null; 297 int age = 0; 298 for (RestrictionEntry entry : restriction.getRestrictions()) { 299 String profileKey = entry.getKey(); 300 if (RESTRICTION_KEY_PROFILE_NAME.equals(profileKey)) { 301 name = entry.getSelectedString(); 302 } else if (RESTRICTION_KEY_PROFILE_AGE.equals(profileKey)) { 303 age = entry.getIntValue(); 304 } 305 } 306 name = prefs.getString(RESTRICTION_KEY_PROFILE_NAME, name); 307 age = prefs.getInt(RESTRICTION_KEY_PROFILE_AGE, age); 308 updateProfile(name, age); 309 } else if (BUNDLE_SUPPORTED && RESTRICTION_KEY_ITEMS.equals(key)) { 310 String itemsString = prefs.getString(RESTRICTION_KEY_ITEMS, ""); 311 HashMap<String, String> items = new HashMap<>(); 312 for (String itemString : TextUtils.split(itemsString, DELIMETER)) { 313 String[] strings = itemString.split(SEPARATOR, 2); 314 items.put(strings[0], strings[1]); 315 } 316 updateItems(activity, items); 317 } 318 } 319 } 320 updateCanSayHello(boolean canSayHello)321 private void updateCanSayHello(boolean canSayHello) { 322 mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello); 323 mSwitchSayHello.setOnCheckedChangeListener(null); 324 mSwitchSayHello.setChecked(canSayHello); 325 mSwitchSayHello.setOnCheckedChangeListener(this); 326 } 327 updateMessage(String message)328 private void updateMessage(String message) { 329 mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message); 330 mEditMessage.removeTextChangedListener(mWatcherMessage); 331 mEditMessage.setText(message); 332 mEditMessage.addTextChangedListener(mWatcherMessage); 333 } 334 updateNumber(int number)335 private void updateNumber(int number) { 336 mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number); 337 mEditNumber.removeTextChangedListener(mWatcherNumber); 338 mEditNumber.setText(String.valueOf(number)); 339 mEditNumber.addTextChangedListener(mWatcherNumber); 340 } 341 updateRank(Context context, String[] ranks, String selectedRank)342 private void updateRank(Context context, String[] ranks, String selectedRank) { 343 mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank); 344 mSpinnerRank.setAdapter(new ArrayAdapter<>(context, 345 android.R.layout.simple_spinner_dropdown_item, ranks)); 346 mSpinnerRank.setSelection(search(ranks, selectedRank)); 347 mSpinnerRank.setOnItemSelectedListener(this); 348 } 349 updateApprovals(Context context, String[] approvals, String[] selectedApprovals)350 private void updateApprovals(Context context, String[] approvals, 351 String[] selectedApprovals) { 352 mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals); 353 mLayoutApprovals.removeAllViews(); 354 for (String approval : approvals) { 355 Switch sw = new Switch(context); 356 sw.setText(approval); 357 sw.setTag(approval); 358 sw.setChecked(Arrays.asList(selectedApprovals).contains(approval)); 359 sw.setOnCheckedChangeListener(this); 360 sw.setId(R.id.approval); 361 mLayoutApprovals.addView(sw); 362 } 363 } 364 updateProfile(String name, int age)365 private void updateProfile(String name, int age) { 366 if (!BUNDLE_SUPPORTED) { 367 return; 368 } 369 Bundle profile = new Bundle(); 370 profile.putString(RESTRICTION_KEY_PROFILE_NAME, name); 371 profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age); 372 mCurrentRestrictions.putBundle(RESTRICTION_KEY_PROFILE, profile); 373 mEditProfileName.removeTextChangedListener(mWatcherProfile); 374 mEditProfileName.setText(name); 375 mEditProfileName.addTextChangedListener(mWatcherProfile); 376 mEditProfileAge.removeTextChangedListener(mWatcherProfile); 377 mEditProfileAge.setText(String.valueOf(age)); 378 mEditProfileAge.addTextChangedListener((mWatcherProfile)); 379 } 380 updateItems(Context context, Map<String, String> items)381 private void updateItems(Context context, Map<String, String> items) { 382 if (!BUNDLE_SUPPORTED) { 383 return; 384 } 385 mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items)); 386 LayoutInflater inflater = LayoutInflater.from(context); 387 mLayoutItems.removeAllViews(); 388 for (String key : items.keySet()) { 389 insertItemRow(inflater, key, items.get(key)); 390 } 391 } 392 insertItemRow(LayoutInflater inflater, String key, String value)393 private void insertItemRow(LayoutInflater inflater, String key, String value) { 394 View view = inflater.inflate(R.layout.item, mLayoutItems, false); 395 TextView textView = (TextView) view.findViewById(R.id.item_text); 396 textView.setText(getString(R.string.item, key, value)); 397 Button remove = (Button) view.findViewById(R.id.item_remove); 398 remove.setTag(key); 399 remove.setOnClickListener(this); 400 mLayoutItems.addView(view); 401 } 402 403 @NonNull convertToBundles(Map<String, String> items)404 private Bundle[] convertToBundles(Map<String, String> items) { 405 Bundle[] bundles = new Bundle[items.size()]; 406 int i = 0; 407 for (String key : items.keySet()) { 408 Bundle bundle = new Bundle(); 409 bundle.putString(RESTRICTION_KEY_ITEM_KEY, key); 410 bundle.putString(RESTRICTION_KEY_ITEM_VALUE, items.get(key)); 411 bundles[i++] = bundle; 412 } 413 return bundles; 414 } 415 removeItem(String key)416 private void removeItem(String key) { 417 Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS); 418 if (parcelables != null) { 419 Map<String, String> items = new HashMap<>(); 420 for (Parcelable parcelable : parcelables) { 421 Bundle bundle = (Bundle) parcelable; 422 if (!key.equals(bundle.getString(RESTRICTION_KEY_ITEM_KEY))) { 423 items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY), 424 bundle.getString(RESTRICTION_KEY_ITEM_VALUE)); 425 } 426 } 427 saveItems(getActivity(), items); 428 } 429 } 430 431 /** 432 * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema. 433 * 434 * @param activity The activity 435 * @param allow The value to be set for the restriction. 436 */ saveCanSayHello(Activity activity, boolean allow)437 private void saveCanSayHello(Activity activity, boolean allow) { 438 mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow); 439 saveRestrictions(activity); 440 // Note that the owner app needs to remember the restrictions on its own. 441 editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply(); 442 } 443 444 /** 445 * Saves the value for the "message" restriction of AppRestrictionSchema. 446 * 447 * @param activity The activity 448 * @param message The value to be set for the restriction. 449 */ saveMessage(Activity activity, String message)450 private void saveMessage(Activity activity, String message) { 451 mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message); 452 saveRestrictions(activity); 453 editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply(); 454 } 455 456 /** 457 * Saves the value for the "number" restriction of AppRestrictionSchema. 458 * 459 * @param activity The activity 460 * @param number The value to be set for the restriction. 461 */ saveNumber(Activity activity, int number)462 private void saveNumber(Activity activity, int number) { 463 mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number); 464 saveRestrictions(activity); 465 editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply(); 466 } 467 468 /** 469 * Saves the value for the "rank" restriction of AppRestrictionSchema. 470 * 471 * @param activity The activity 472 * @param rank The value to be set for the restriction. 473 */ saveRank(Activity activity, String rank)474 private void saveRank(Activity activity, String rank) { 475 mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank); 476 saveRestrictions(activity); 477 editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply(); 478 } 479 addApproval(Activity activity, String approval)480 private void addApproval(Activity activity, String approval) { 481 List<String> approvals = new ArrayList<>(Arrays.asList( 482 mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS))); 483 if (approvals.contains(approval)) { 484 return; 485 } 486 approvals.add(approval); 487 saveApprovals(activity, approvals.toArray(new String[approvals.size()])); 488 } 489 removeApproval(Activity activity, String approval)490 private void removeApproval(Activity activity, String approval) { 491 List<String> approvals = new ArrayList<>(Arrays.asList( 492 mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS))); 493 if (!approval.contains(approval)) { 494 return; 495 } 496 approvals.remove(approval); 497 saveApprovals(activity, approvals.toArray(new String[approvals.size()])); 498 } 499 500 /** 501 * Saves the value for the "approvals" restriction of AppRestrictionSchema. 502 * 503 * @param activity The activity 504 * @param approvals The value to be set for the restriction. 505 */ saveApprovals(Activity activity, String[] approvals)506 private void saveApprovals(Activity activity, String[] approvals) { 507 mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals); 508 saveRestrictions(activity); 509 editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS, 510 TextUtils.join(DELIMETER, approvals)).apply(); 511 } 512 513 /** 514 * Saves the value for the "profile" restriction of AppRestrictionSchema. 515 * 516 * @param activity The activity 517 * @param name The value to be set for the "name" field. 518 * @param age The value to be set for the "age" field. 519 */ saveProfile(Activity activity, String name, int age)520 private void saveProfile(Activity activity, String name, int age) { 521 if (!BUNDLE_SUPPORTED) { 522 return; 523 } 524 Bundle profile = new Bundle(); 525 profile.putString(RESTRICTION_KEY_PROFILE_NAME, name); 526 profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age); 527 mCurrentRestrictions.putBundle(RESTRICTION_KEY_PROFILE, profile); 528 saveRestrictions(activity); 529 editPreferences(activity).putString(RESTRICTION_KEY_PROFILE_NAME, name).apply(); 530 } 531 532 /** 533 * Saves the value for the "items" restriction of AppRestrictionSchema. 534 * 535 * @param activity The activity. 536 * @param items The values. 537 */ saveItems(Activity activity, Map<String, String> items)538 private void saveItems(Activity activity, Map<String, String> items) { 539 if (!BUNDLE_SUPPORTED) { 540 return; 541 } 542 mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items)); 543 saveRestrictions(activity); 544 StringBuilder builder = new StringBuilder(); 545 boolean first = true; 546 for (String key : items.keySet()) { 547 if (first) { 548 first = false; 549 } else { 550 builder.append(DELIMETER); 551 } 552 builder.append(key); 553 builder.append(SEPARATOR); 554 builder.append(items.get(key)); 555 } 556 editPreferences(activity).putString(RESTRICTION_KEY_ITEMS, builder.toString()).apply(); 557 } 558 559 /** 560 * Saves all the restrictions. 561 * 562 * @param activity The activity. 563 */ saveRestrictions(Activity activity)564 private void saveRestrictions(Activity activity) { 565 DevicePolicyManager devicePolicyManager 566 = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 567 devicePolicyManager.setApplicationRestrictions( 568 EnforcerDeviceAdminReceiver.getComponentName(activity), 569 Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, mCurrentRestrictions); 570 } 571 editPreferences(Activity activity)572 private SharedPreferences.Editor editPreferences(Activity activity) { 573 return activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE).edit(); 574 } 575 576 /** 577 * Sequential search 578 * 579 * @param array The string array 580 * @param s The string to search for 581 * @return Index if found. -1 if not found. 582 */ search(String[] array, String s)583 private int search(String[] array, String s) { 584 for (int i = 0; i < array.length; ++i) { 585 if (s.equals(array[i])) { 586 return i; 587 } 588 } 589 return -1; 590 } 591 592 } 593