1 /* 2 * Copyright (C) 2007 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 android.preference; 18 19 import android.annotation.SystemApi; 20 import android.annotation.XmlRes; 21 import android.app.Activity; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.SharedPreferences; 26 import android.content.pm.ActivityInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.PackageManager.NameNotFoundException; 29 import android.content.pm.ResolveInfo; 30 import android.content.res.XmlResourceParser; 31 import android.os.Bundle; 32 import android.util.Log; 33 34 import java.util.ArrayList; 35 import java.util.HashSet; 36 import java.util.List; 37 38 /** 39 * Used to help create {@link Preference} hierarchies 40 * from activities or XML. 41 * <p> 42 * In most cases, clients should use 43 * {@link PreferenceActivity#addPreferencesFromIntent} or 44 * {@link PreferenceActivity#addPreferencesFromResource(int)}. 45 * 46 * @see PreferenceActivity 47 */ 48 public class PreferenceManager { 49 50 private static final String TAG = "PreferenceManager"; 51 52 /** 53 * The Activity meta-data key for its XML preference hierarchy. 54 */ 55 public static final String METADATA_KEY_PREFERENCES = "android.preference"; 56 57 public static final String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values"; 58 59 /** 60 * @see #getActivity() 61 */ 62 private Activity mActivity; 63 64 /** 65 * Fragment that owns this instance. 66 */ 67 private PreferenceFragment mFragment; 68 69 /** 70 * The context to use. This should always be set. 71 * 72 * @see #mActivity 73 */ 74 private Context mContext; 75 76 /** 77 * The counter for unique IDs. 78 */ 79 private long mNextId = 0; 80 81 /** 82 * The counter for unique request codes. 83 */ 84 private int mNextRequestCode; 85 86 /** 87 * Cached shared preferences. 88 */ 89 private SharedPreferences mSharedPreferences; 90 91 /** 92 * If in no-commit mode, the shared editor to give out (which will be 93 * committed when exiting no-commit mode). 94 */ 95 private SharedPreferences.Editor mEditor; 96 97 /** 98 * Blocks commits from happening on the shared editor. This is used when 99 * inflating the hierarchy. Do not set this directly, use {@link #setNoCommit(boolean)} 100 */ 101 private boolean mNoCommit; 102 103 /** 104 * The SharedPreferences name that will be used for all {@link Preference}s 105 * managed by this instance. 106 */ 107 private String mSharedPreferencesName; 108 109 /** 110 * The SharedPreferences mode that will be used for all {@link Preference}s 111 * managed by this instance. 112 */ 113 private int mSharedPreferencesMode; 114 115 private static final int STORAGE_DEFAULT = 0; 116 private static final int STORAGE_DEVICE_PROTECTED = 1; 117 private static final int STORAGE_CREDENTIAL_PROTECTED = 2; 118 119 private int mStorage = STORAGE_DEFAULT; 120 121 /** 122 * The {@link PreferenceScreen} at the root of the preference hierarchy. 123 */ 124 private PreferenceScreen mPreferenceScreen; 125 126 /** 127 * List of activity result listeners. 128 */ 129 private List<OnActivityResultListener> mActivityResultListeners; 130 131 /** 132 * List of activity stop listeners. 133 */ 134 private List<OnActivityStopListener> mActivityStopListeners; 135 136 /** 137 * List of activity destroy listeners. 138 */ 139 private List<OnActivityDestroyListener> mActivityDestroyListeners; 140 141 /** 142 * List of dialogs that should be dismissed when we receive onNewIntent in 143 * our PreferenceActivity. 144 */ 145 private List<DialogInterface> mPreferencesScreens; 146 147 private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener; 148 149 /** 150 * @hide 151 */ PreferenceManager(Activity activity, int firstRequestCode)152 public PreferenceManager(Activity activity, int firstRequestCode) { 153 mActivity = activity; 154 mNextRequestCode = firstRequestCode; 155 156 init(activity); 157 } 158 159 /** 160 * This constructor should ONLY be used when getting default values from 161 * an XML preference hierarchy. 162 * <p> 163 * The {@link PreferenceManager#PreferenceManager(Activity)} 164 * should be used ANY time a preference will be displayed, since some preference 165 * types need an Activity for managed queries. 166 */ PreferenceManager(Context context)167 /*package*/ PreferenceManager(Context context) { 168 init(context); 169 } 170 init(Context context)171 private void init(Context context) { 172 mContext = context; 173 174 setSharedPreferencesName(getDefaultSharedPreferencesName(context)); 175 } 176 177 /** 178 * Sets the owning preference fragment 179 */ setFragment(PreferenceFragment fragment)180 void setFragment(PreferenceFragment fragment) { 181 mFragment = fragment; 182 } 183 184 /** 185 * Returns the owning preference fragment, if any. 186 */ getFragment()187 PreferenceFragment getFragment() { 188 return mFragment; 189 } 190 191 /** 192 * Returns a list of {@link Activity} (indirectly) that match a given 193 * {@link Intent}. 194 * 195 * @param queryIntent The Intent to match. 196 * @return The list of {@link ResolveInfo} that point to the matched 197 * activities. 198 */ queryIntentActivities(Intent queryIntent)199 private List<ResolveInfo> queryIntentActivities(Intent queryIntent) { 200 return mContext.getPackageManager().queryIntentActivities(queryIntent, 201 PackageManager.GET_META_DATA); 202 } 203 204 /** 205 * Inflates a preference hierarchy from the preference hierarchies of 206 * {@link Activity Activities} that match the given {@link Intent}. An 207 * {@link Activity} defines its preference hierarchy with meta-data using 208 * the {@link #METADATA_KEY_PREFERENCES} key. 209 * <p> 210 * If a preference hierarchy is given, the new preference hierarchies will 211 * be merged in. 212 * 213 * @param queryIntent The intent to match activities. 214 * @param rootPreferences Optional existing hierarchy to merge the new 215 * hierarchies into. 216 * @return The root hierarchy (if one was not provided, the new hierarchy's 217 * root). 218 */ inflateFromIntent(Intent queryIntent, PreferenceScreen rootPreferences)219 PreferenceScreen inflateFromIntent(Intent queryIntent, PreferenceScreen rootPreferences) { 220 final List<ResolveInfo> activities = queryIntentActivities(queryIntent); 221 final HashSet<String> inflatedRes = new HashSet<String>(); 222 223 for (int i = activities.size() - 1; i >= 0; i--) { 224 final ActivityInfo activityInfo = activities.get(i).activityInfo; 225 final Bundle metaData = activityInfo.metaData; 226 227 if ((metaData == null) || !metaData.containsKey(METADATA_KEY_PREFERENCES)) { 228 continue; 229 } 230 231 // Need to concat the package with res ID since the same res ID 232 // can be re-used across contexts 233 final String uniqueResId = activityInfo.packageName + ":" 234 + activityInfo.metaData.getInt(METADATA_KEY_PREFERENCES); 235 236 if (!inflatedRes.contains(uniqueResId)) { 237 inflatedRes.add(uniqueResId); 238 239 final Context context; 240 try { 241 context = mContext.createPackageContext(activityInfo.packageName, 0); 242 } catch (NameNotFoundException e) { 243 Log.w(TAG, "Could not create context for " + activityInfo.packageName + ": " 244 + Log.getStackTraceString(e)); 245 continue; 246 } 247 248 final PreferenceInflater inflater = new PreferenceInflater(context, this); 249 final XmlResourceParser parser = activityInfo.loadXmlMetaData(context 250 .getPackageManager(), METADATA_KEY_PREFERENCES); 251 rootPreferences = (PreferenceScreen) inflater 252 .inflate(parser, rootPreferences, true); 253 parser.close(); 254 } 255 } 256 257 rootPreferences.onAttachedToHierarchy(this); 258 259 return rootPreferences; 260 } 261 262 /** 263 * Inflates a preference hierarchy from XML. If a preference hierarchy is 264 * given, the new preference hierarchies will be merged in. 265 * 266 * @param context The context of the resource. 267 * @param resId The resource ID of the XML to inflate. 268 * @param rootPreferences Optional existing hierarchy to merge the new 269 * hierarchies into. 270 * @return The root hierarchy (if one was not provided, the new hierarchy's 271 * root). 272 * @hide 273 */ inflateFromResource(Context context, @XmlRes int resId, PreferenceScreen rootPreferences)274 public PreferenceScreen inflateFromResource(Context context, @XmlRes int resId, 275 PreferenceScreen rootPreferences) { 276 // Block commits 277 setNoCommit(true); 278 279 final PreferenceInflater inflater = new PreferenceInflater(context, this); 280 rootPreferences = (PreferenceScreen) inflater.inflate(resId, rootPreferences, true); 281 rootPreferences.onAttachedToHierarchy(this); 282 283 // Unblock commits 284 setNoCommit(false); 285 286 return rootPreferences; 287 } 288 createPreferenceScreen(Context context)289 public PreferenceScreen createPreferenceScreen(Context context) { 290 final PreferenceScreen preferenceScreen = new PreferenceScreen(context, null); 291 preferenceScreen.onAttachedToHierarchy(this); 292 return preferenceScreen; 293 } 294 295 /** 296 * Called by a preference to get a unique ID in its hierarchy. 297 * 298 * @return A unique ID. 299 */ getNextId()300 long getNextId() { 301 synchronized (this) { 302 return mNextId++; 303 } 304 } 305 306 /** 307 * Returns the current name of the SharedPreferences file that preferences managed by 308 * this will use. 309 * 310 * @return The name that can be passed to {@link Context#getSharedPreferences(String, int)}. 311 * @see Context#getSharedPreferences(String, int) 312 */ getSharedPreferencesName()313 public String getSharedPreferencesName() { 314 return mSharedPreferencesName; 315 } 316 317 /** 318 * Sets the name of the SharedPreferences file that preferences managed by this 319 * will use. 320 * 321 * @param sharedPreferencesName The name of the SharedPreferences file. 322 * @see Context#getSharedPreferences(String, int) 323 */ setSharedPreferencesName(String sharedPreferencesName)324 public void setSharedPreferencesName(String sharedPreferencesName) { 325 mSharedPreferencesName = sharedPreferencesName; 326 mSharedPreferences = null; 327 } 328 329 /** 330 * Returns the current mode of the SharedPreferences file that preferences managed by 331 * this will use. 332 * 333 * @return The mode that can be passed to {@link Context#getSharedPreferences(String, int)}. 334 * @see Context#getSharedPreferences(String, int) 335 */ getSharedPreferencesMode()336 public int getSharedPreferencesMode() { 337 return mSharedPreferencesMode; 338 } 339 340 /** 341 * Sets the mode of the SharedPreferences file that preferences managed by this 342 * will use. 343 * 344 * @param sharedPreferencesMode The mode of the SharedPreferences file. 345 * @see Context#getSharedPreferences(String, int) 346 */ setSharedPreferencesMode(int sharedPreferencesMode)347 public void setSharedPreferencesMode(int sharedPreferencesMode) { 348 mSharedPreferencesMode = sharedPreferencesMode; 349 mSharedPreferences = null; 350 } 351 352 /** 353 * Sets the storage location used internally by this class to be the default 354 * provided by the hosting {@link Context}. 355 */ setStorageDefault()356 public void setStorageDefault() { 357 mStorage = STORAGE_DEFAULT; 358 mSharedPreferences = null; 359 } 360 361 /** 362 * Explicitly set the storage location used internally by this class to be 363 * device-protected storage. 364 * <p> 365 * On devices with direct boot, data stored in this location is encrypted 366 * with a key tied to the physical device, and it can be accessed 367 * immediately after the device has booted successfully, both 368 * <em>before and after</em> the user has authenticated with their 369 * credentials (such as a lock pattern or PIN). 370 * <p> 371 * Because device-protected data is available without user authentication, 372 * you should carefully limit the data you store using this Context. For 373 * example, storing sensitive authentication tokens or passwords in the 374 * device-protected area is strongly discouraged. 375 * 376 * @see Context#createDeviceProtectedStorageContext() 377 */ setStorageDeviceProtected()378 public void setStorageDeviceProtected() { 379 mStorage = STORAGE_DEVICE_PROTECTED; 380 mSharedPreferences = null; 381 } 382 383 /** @removed */ 384 @Deprecated setStorageDeviceEncrypted()385 public void setStorageDeviceEncrypted() { 386 setStorageDeviceProtected(); 387 } 388 389 /** 390 * Explicitly set the storage location used internally by this class to be 391 * credential-protected storage. This is the default storage area for apps 392 * unless {@code forceDeviceProtectedStorage} was requested. 393 * <p> 394 * On devices with direct boot, data stored in this location is encrypted 395 * with a key tied to user credentials, which can be accessed 396 * <em>only after</em> the user has entered their credentials (such as a 397 * lock pattern or PIN). 398 * 399 * @see Context#createCredentialProtectedStorageContext() 400 * @hide 401 */ 402 @SystemApi setStorageCredentialProtected()403 public void setStorageCredentialProtected() { 404 mStorage = STORAGE_CREDENTIAL_PROTECTED; 405 mSharedPreferences = null; 406 } 407 408 /** @removed */ 409 @Deprecated setStorageCredentialEncrypted()410 public void setStorageCredentialEncrypted() { 411 setStorageCredentialProtected(); 412 } 413 414 /** 415 * Indicates if the storage location used internally by this class is the 416 * default provided by the hosting {@link Context}. 417 * 418 * @see #setStorageDefault() 419 * @see #setStorageDeviceProtected() 420 */ isStorageDefault()421 public boolean isStorageDefault() { 422 return mStorage == STORAGE_DEFAULT; 423 } 424 425 /** 426 * Indicates if the storage location used internally by this class is backed 427 * by device-protected storage. 428 * 429 * @see #setStorageDefault() 430 * @see #setStorageDeviceProtected() 431 */ isStorageDeviceProtected()432 public boolean isStorageDeviceProtected() { 433 return mStorage == STORAGE_DEVICE_PROTECTED; 434 } 435 436 /** 437 * Indicates if the storage location used internally by this class is backed 438 * by credential-protected storage. 439 * 440 * @see #setStorageDefault() 441 * @see #setStorageDeviceProtected() 442 * @hide 443 */ 444 @SystemApi isStorageCredentialProtected()445 public boolean isStorageCredentialProtected() { 446 return mStorage == STORAGE_CREDENTIAL_PROTECTED; 447 } 448 449 /** 450 * Gets a SharedPreferences instance that preferences managed by this will 451 * use. 452 * 453 * @return A SharedPreferences instance pointing to the file that contains 454 * the values of preferences that are managed by this. 455 */ getSharedPreferences()456 public SharedPreferences getSharedPreferences() { 457 if (mSharedPreferences == null) { 458 final Context storageContext; 459 switch (mStorage) { 460 case STORAGE_DEVICE_PROTECTED: 461 storageContext = mContext.createDeviceProtectedStorageContext(); 462 break; 463 case STORAGE_CREDENTIAL_PROTECTED: 464 storageContext = mContext.createCredentialProtectedStorageContext(); 465 break; 466 default: 467 storageContext = mContext; 468 break; 469 } 470 471 mSharedPreferences = storageContext.getSharedPreferences(mSharedPreferencesName, 472 mSharedPreferencesMode); 473 } 474 475 return mSharedPreferences; 476 } 477 478 /** 479 * Gets a SharedPreferences instance that points to the default file that is 480 * used by the preference framework in the given context. 481 * 482 * @param context The context of the preferences whose values are wanted. 483 * @return A SharedPreferences instance that can be used to retrieve and 484 * listen to values of the preferences. 485 */ getDefaultSharedPreferences(Context context)486 public static SharedPreferences getDefaultSharedPreferences(Context context) { 487 return context.getSharedPreferences(getDefaultSharedPreferencesName(context), 488 getDefaultSharedPreferencesMode()); 489 } 490 491 /** 492 * Returns the name used for storing default shared preferences. 493 * 494 * @see #getDefaultSharedPreferences(Context) 495 * @see Context#getSharedPreferencesPath(String) 496 */ getDefaultSharedPreferencesName(Context context)497 public static String getDefaultSharedPreferencesName(Context context) { 498 return context.getPackageName() + "_preferences"; 499 } 500 getDefaultSharedPreferencesMode()501 private static int getDefaultSharedPreferencesMode() { 502 return Context.MODE_PRIVATE; 503 } 504 505 /** 506 * Returns the root of the preference hierarchy managed by this class. 507 * 508 * @return The {@link PreferenceScreen} object that is at the root of the hierarchy. 509 */ getPreferenceScreen()510 PreferenceScreen getPreferenceScreen() { 511 return mPreferenceScreen; 512 } 513 514 /** 515 * Sets the root of the preference hierarchy. 516 * 517 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. 518 * @return Whether the {@link PreferenceScreen} given is different than the previous. 519 */ setPreferences(PreferenceScreen preferenceScreen)520 boolean setPreferences(PreferenceScreen preferenceScreen) { 521 if (preferenceScreen != mPreferenceScreen) { 522 mPreferenceScreen = preferenceScreen; 523 return true; 524 } 525 526 return false; 527 } 528 529 /** 530 * Finds a {@link Preference} based on its key. 531 * 532 * @param key The key of the preference to retrieve. 533 * @return The {@link Preference} with the key, or null. 534 * @see PreferenceGroup#findPreference(CharSequence) 535 */ findPreference(CharSequence key)536 public Preference findPreference(CharSequence key) { 537 if (mPreferenceScreen == null) { 538 return null; 539 } 540 541 return mPreferenceScreen.findPreference(key); 542 } 543 544 /** 545 * Sets the default values from an XML preference file by reading the values defined 546 * by each {@link Preference} item's {@code android:defaultValue} attribute. This should 547 * be called by the application's main activity. 548 * <p> 549 * 550 * @param context The context of the shared preferences. 551 * @param resId The resource ID of the preference XML file. 552 * @param readAgain Whether to re-read the default values. 553 * If false, this method sets the default values only if this 554 * method has never been called in the past (or if the 555 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared 556 * preferences file is false). To attempt to set the default values again 557 * bypassing this check, set {@code readAgain} to true. 558 * <p class="note"> 559 * Note: this will NOT reset preferences back to their default 560 * values. For that functionality, use 561 * {@link PreferenceManager#getDefaultSharedPreferences(Context)} 562 * and clear it followed by a call to this method with this 563 * parameter set to true. 564 */ setDefaultValues(Context context, @XmlRes int resId, boolean readAgain)565 public static void setDefaultValues(Context context, @XmlRes int resId, boolean readAgain) { 566 567 // Use the default shared preferences name and mode 568 setDefaultValues(context, getDefaultSharedPreferencesName(context), 569 getDefaultSharedPreferencesMode(), resId, readAgain); 570 } 571 572 /** 573 * Similar to {@link #setDefaultValues(Context, int, boolean)} but allows 574 * the client to provide the filename and mode of the shared preferences 575 * file. 576 * 577 * @param context The context of the shared preferences. 578 * @param sharedPreferencesName A custom name for the shared preferences file. 579 * @param sharedPreferencesMode The file creation mode for the shared preferences file, such 580 * as {@link android.content.Context#MODE_PRIVATE} or {@link 581 * android.content.Context#MODE_PRIVATE} 582 * @param resId The resource ID of the preference XML file. 583 * @param readAgain Whether to re-read the default values. 584 * If false, this method will set the default values only if this 585 * method has never been called in the past (or if the 586 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared 587 * preferences file is false). To attempt to set the default values again 588 * bypassing this check, set {@code readAgain} to true. 589 * <p class="note"> 590 * Note: this will NOT reset preferences back to their default 591 * values. For that functionality, use 592 * {@link PreferenceManager#getDefaultSharedPreferences(Context)} 593 * and clear it followed by a call to this method with this 594 * parameter set to true. 595 * 596 * @see #setDefaultValues(Context, int, boolean) 597 * @see #setSharedPreferencesName(String) 598 * @see #setSharedPreferencesMode(int) 599 */ setDefaultValues(Context context, String sharedPreferencesName, int sharedPreferencesMode, int resId, boolean readAgain)600 public static void setDefaultValues(Context context, String sharedPreferencesName, 601 int sharedPreferencesMode, int resId, boolean readAgain) { 602 final SharedPreferences defaultValueSp = context.getSharedPreferences( 603 KEY_HAS_SET_DEFAULT_VALUES, Context.MODE_PRIVATE); 604 605 if (readAgain || !defaultValueSp.getBoolean(KEY_HAS_SET_DEFAULT_VALUES, false)) { 606 final PreferenceManager pm = new PreferenceManager(context); 607 pm.setSharedPreferencesName(sharedPreferencesName); 608 pm.setSharedPreferencesMode(sharedPreferencesMode); 609 pm.inflateFromResource(context, resId, null); 610 611 SharedPreferences.Editor editor = 612 defaultValueSp.edit().putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true); 613 try { 614 editor.apply(); 615 } catch (AbstractMethodError unused) { 616 // The app injected its own pre-Gingerbread 617 // SharedPreferences.Editor implementation without 618 // an apply method. 619 editor.commit(); 620 } 621 } 622 } 623 624 /** 625 * Returns an editor to use when modifying the shared preferences. 626 * <p> 627 * Do NOT commit unless {@link #shouldCommit()} returns true. 628 * 629 * @return An editor to use to write to shared preferences. 630 * @see #shouldCommit() 631 */ getEditor()632 SharedPreferences.Editor getEditor() { 633 634 if (mNoCommit) { 635 if (mEditor == null) { 636 mEditor = getSharedPreferences().edit(); 637 } 638 639 return mEditor; 640 } else { 641 return getSharedPreferences().edit(); 642 } 643 } 644 645 /** 646 * Whether it is the client's responsibility to commit on the 647 * {@link #getEditor()}. This will return false in cases where the writes 648 * should be batched, for example when inflating preferences from XML. 649 * 650 * @return Whether the client should commit. 651 */ shouldCommit()652 boolean shouldCommit() { 653 return !mNoCommit; 654 } 655 setNoCommit(boolean noCommit)656 private void setNoCommit(boolean noCommit) { 657 if (!noCommit && mEditor != null) { 658 try { 659 mEditor.apply(); 660 } catch (AbstractMethodError unused) { 661 // The app injected its own pre-Gingerbread 662 // SharedPreferences.Editor implementation without 663 // an apply method. 664 mEditor.commit(); 665 } 666 } 667 mNoCommit = noCommit; 668 } 669 670 /** 671 * Returns the activity that shows the preferences. This is useful for doing 672 * managed queries, but in most cases the use of {@link #getContext()} is 673 * preferred. 674 * <p> 675 * This will return null if this class was instantiated with a Context 676 * instead of Activity. For example, when setting the default values. 677 * 678 * @return The activity that shows the preferences. 679 * @see #mContext 680 */ getActivity()681 Activity getActivity() { 682 return mActivity; 683 } 684 685 /** 686 * Returns the context. This is preferred over {@link #getActivity()} when 687 * possible. 688 * 689 * @return The context. 690 */ getContext()691 Context getContext() { 692 return mContext; 693 } 694 695 /** 696 * Registers a listener. 697 * 698 * @see OnActivityResultListener 699 */ registerOnActivityResultListener(OnActivityResultListener listener)700 void registerOnActivityResultListener(OnActivityResultListener listener) { 701 synchronized (this) { 702 if (mActivityResultListeners == null) { 703 mActivityResultListeners = new ArrayList<OnActivityResultListener>(); 704 } 705 706 if (!mActivityResultListeners.contains(listener)) { 707 mActivityResultListeners.add(listener); 708 } 709 } 710 } 711 712 /** 713 * Unregisters a listener. 714 * 715 * @see OnActivityResultListener 716 */ unregisterOnActivityResultListener(OnActivityResultListener listener)717 void unregisterOnActivityResultListener(OnActivityResultListener listener) { 718 synchronized (this) { 719 if (mActivityResultListeners != null) { 720 mActivityResultListeners.remove(listener); 721 } 722 } 723 } 724 725 /** 726 * Called by the {@link PreferenceManager} to dispatch a subactivity result. 727 */ dispatchActivityResult(int requestCode, int resultCode, Intent data)728 void dispatchActivityResult(int requestCode, int resultCode, Intent data) { 729 List<OnActivityResultListener> list; 730 731 synchronized (this) { 732 if (mActivityResultListeners == null) return; 733 list = new ArrayList<OnActivityResultListener>(mActivityResultListeners); 734 } 735 736 final int N = list.size(); 737 for (int i = 0; i < N; i++) { 738 if (list.get(i).onActivityResult(requestCode, resultCode, data)) { 739 break; 740 } 741 } 742 } 743 744 /** 745 * Registers a listener. 746 * 747 * @see OnActivityStopListener 748 * @hide 749 */ registerOnActivityStopListener(OnActivityStopListener listener)750 public void registerOnActivityStopListener(OnActivityStopListener listener) { 751 synchronized (this) { 752 if (mActivityStopListeners == null) { 753 mActivityStopListeners = new ArrayList<OnActivityStopListener>(); 754 } 755 756 if (!mActivityStopListeners.contains(listener)) { 757 mActivityStopListeners.add(listener); 758 } 759 } 760 } 761 762 /** 763 * Unregisters a listener. 764 * 765 * @see OnActivityStopListener 766 * @hide 767 */ unregisterOnActivityStopListener(OnActivityStopListener listener)768 public void unregisterOnActivityStopListener(OnActivityStopListener listener) { 769 synchronized (this) { 770 if (mActivityStopListeners != null) { 771 mActivityStopListeners.remove(listener); 772 } 773 } 774 } 775 776 /** 777 * Called by the {@link PreferenceManager} to dispatch the activity stop 778 * event. 779 */ dispatchActivityStop()780 void dispatchActivityStop() { 781 List<OnActivityStopListener> list; 782 783 synchronized (this) { 784 if (mActivityStopListeners == null) return; 785 list = new ArrayList<OnActivityStopListener>(mActivityStopListeners); 786 } 787 788 final int N = list.size(); 789 for (int i = 0; i < N; i++) { 790 list.get(i).onActivityStop(); 791 } 792 } 793 794 /** 795 * Registers a listener. 796 * 797 * @see OnActivityDestroyListener 798 */ registerOnActivityDestroyListener(OnActivityDestroyListener listener)799 void registerOnActivityDestroyListener(OnActivityDestroyListener listener) { 800 synchronized (this) { 801 if (mActivityDestroyListeners == null) { 802 mActivityDestroyListeners = new ArrayList<OnActivityDestroyListener>(); 803 } 804 805 if (!mActivityDestroyListeners.contains(listener)) { 806 mActivityDestroyListeners.add(listener); 807 } 808 } 809 } 810 811 /** 812 * Unregisters a listener. 813 * 814 * @see OnActivityDestroyListener 815 */ unregisterOnActivityDestroyListener(OnActivityDestroyListener listener)816 void unregisterOnActivityDestroyListener(OnActivityDestroyListener listener) { 817 synchronized (this) { 818 if (mActivityDestroyListeners != null) { 819 mActivityDestroyListeners.remove(listener); 820 } 821 } 822 } 823 824 /** 825 * Called by the {@link PreferenceManager} to dispatch the activity destroy 826 * event. 827 */ dispatchActivityDestroy()828 void dispatchActivityDestroy() { 829 List<OnActivityDestroyListener> list = null; 830 831 synchronized (this) { 832 if (mActivityDestroyListeners != null) { 833 list = new ArrayList<OnActivityDestroyListener>(mActivityDestroyListeners); 834 } 835 } 836 837 if (list != null) { 838 final int N = list.size(); 839 for (int i = 0; i < N; i++) { 840 list.get(i).onActivityDestroy(); 841 } 842 } 843 844 // Dismiss any PreferenceScreens still showing 845 dismissAllScreens(); 846 } 847 848 /** 849 * Returns a request code that is unique for the activity. Each subsequent 850 * call to this method should return another unique request code. 851 * 852 * @return A unique request code that will never be used by anyone other 853 * than the caller of this method. 854 */ getNextRequestCode()855 int getNextRequestCode() { 856 synchronized (this) { 857 return mNextRequestCode++; 858 } 859 } 860 addPreferencesScreen(DialogInterface screen)861 void addPreferencesScreen(DialogInterface screen) { 862 synchronized (this) { 863 864 if (mPreferencesScreens == null) { 865 mPreferencesScreens = new ArrayList<DialogInterface>(); 866 } 867 868 mPreferencesScreens.add(screen); 869 } 870 } 871 removePreferencesScreen(DialogInterface screen)872 void removePreferencesScreen(DialogInterface screen) { 873 synchronized (this) { 874 875 if (mPreferencesScreens == null) { 876 return; 877 } 878 879 mPreferencesScreens.remove(screen); 880 } 881 } 882 883 /** 884 * Called by {@link PreferenceActivity} to dispatch the new Intent event. 885 * 886 * @param intent The new Intent. 887 */ dispatchNewIntent(Intent intent)888 void dispatchNewIntent(Intent intent) { 889 dismissAllScreens(); 890 } 891 dismissAllScreens()892 private void dismissAllScreens() { 893 // Remove any of the previously shown preferences screens 894 ArrayList<DialogInterface> screensToDismiss; 895 896 synchronized (this) { 897 898 if (mPreferencesScreens == null) { 899 return; 900 } 901 902 screensToDismiss = new ArrayList<DialogInterface>(mPreferencesScreens); 903 mPreferencesScreens.clear(); 904 } 905 906 for (int i = screensToDismiss.size() - 1; i >= 0; i--) { 907 screensToDismiss.get(i).dismiss(); 908 } 909 } 910 911 /** 912 * Sets the callback to be invoked when a {@link Preference} in the 913 * hierarchy rooted at this {@link PreferenceManager} is clicked. 914 * 915 * @param listener The callback to be invoked. 916 */ setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener)917 void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) { 918 mOnPreferenceTreeClickListener = listener; 919 } 920 getOnPreferenceTreeClickListener()921 OnPreferenceTreeClickListener getOnPreferenceTreeClickListener() { 922 return mOnPreferenceTreeClickListener; 923 } 924 925 /** 926 * Interface definition for a callback to be invoked when a 927 * {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is 928 * clicked. 929 * 930 * @hide 931 */ 932 public interface OnPreferenceTreeClickListener { 933 /** 934 * Called when a preference in the tree rooted at this 935 * {@link PreferenceScreen} has been clicked. 936 * 937 * @param preferenceScreen The {@link PreferenceScreen} that the 938 * preference is located in. 939 * @param preference The preference that was clicked. 940 * @return Whether the click was handled. 941 */ onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)942 boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference); 943 } 944 945 /** 946 * Interface definition for a class that will be called when the container's activity 947 * receives an activity result. 948 */ 949 public interface OnActivityResultListener { 950 951 /** 952 * See Activity's onActivityResult. 953 * 954 * @return Whether the request code was handled (in which case 955 * subsequent listeners will not be called. 956 */ onActivityResult(int requestCode, int resultCode, Intent data)957 boolean onActivityResult(int requestCode, int resultCode, Intent data); 958 } 959 960 /** 961 * Interface definition for a class that will be called when the container's activity 962 * is stopped. 963 */ 964 public interface OnActivityStopListener { 965 966 /** 967 * See Activity's onStop. 968 */ onActivityStop()969 void onActivityStop(); 970 } 971 972 /** 973 * Interface definition for a class that will be called when the container's activity 974 * is destroyed. 975 */ 976 public interface OnActivityDestroyListener { 977 978 /** 979 * See Activity's onDestroy. 980 */ onActivityDestroy()981 void onActivityDestroy(); 982 } 983 984 } 985