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