1 /* 2 * Copyright 2018 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 androidx.preference; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21 import android.content.Context; 22 import android.content.SharedPreferences; 23 import android.graphics.drawable.Drawable; 24 import android.os.Build; 25 import android.text.TextUtils; 26 27 import androidx.annotation.Nullable; 28 import androidx.annotation.RestrictTo; 29 import androidx.core.content.ContextCompat; 30 31 /** 32 * Used to help create {@link Preference} hierarchies 33 * from activities or XML. 34 * <p> 35 * In most cases, clients should use 36 * {@link androidx.preference.PreferenceFragment#addPreferencesFromResource(int)}, or 37 * {@link PreferenceFragmentCompat#addPreferencesFromResource(int)}. 38 * 39 * @see androidx.preference.PreferenceFragment 40 * @see PreferenceFragmentCompat 41 */ 42 public class PreferenceManager { 43 44 public static final String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values"; 45 46 /** 47 * The context to use. This should always be set. 48 */ 49 private Context mContext; 50 51 /** 52 * The counter for unique IDs. 53 */ 54 private long mNextId = 0; 55 56 /** 57 * Cached shared preferences. 58 */ 59 @Nullable 60 private SharedPreferences mSharedPreferences; 61 62 /** 63 * Data store to be used by the Preferences or null if {@link android.content.SharedPreferences} 64 * should be used. 65 */ 66 @Nullable 67 private PreferenceDataStore mPreferenceDataStore; 68 69 /** 70 * If in no-commit mode, the shared editor to give out (which will be 71 * committed when exiting no-commit mode). 72 */ 73 @Nullable 74 private SharedPreferences.Editor mEditor; 75 76 /** 77 * Blocks commits from happening on the shared editor. This is used when 78 * inflating the hierarchy. Do not set this directly, use {@link #setNoCommit(boolean)} 79 */ 80 private boolean mNoCommit; 81 82 /** 83 * The SharedPreferences name that will be used for all {@link Preference}s 84 * managed by this instance. 85 */ 86 private String mSharedPreferencesName; 87 88 /** 89 * The SharedPreferences mode that will be used for all {@link Preference}s 90 * managed by this instance. 91 */ 92 private int mSharedPreferencesMode; 93 94 private static final int STORAGE_DEFAULT = 0; 95 private static final int STORAGE_DEVICE_PROTECTED = 1; 96 97 private int mStorage = STORAGE_DEFAULT; 98 99 /** 100 * The {@link PreferenceScreen} at the root of the preference hierarchy. 101 */ 102 private PreferenceScreen mPreferenceScreen; 103 104 private PreferenceComparisonCallback mPreferenceComparisonCallback; 105 private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener; 106 private OnDisplayPreferenceDialogListener mOnDisplayPreferenceDialogListener; 107 private OnNavigateToScreenListener mOnNavigateToScreenListener; 108 109 /** 110 * @hide 111 */ 112 @RestrictTo(LIBRARY_GROUP) 113 public PreferenceManager(Context context) { 114 mContext = context; 115 116 setSharedPreferencesName(getDefaultSharedPreferencesName(context)); 117 } 118 119 /** 120 * Inflates a preference hierarchy from XML. If a preference hierarchy is 121 * given, the new preference hierarchies will be merged in. 122 * 123 * @param context The context of the resource. 124 * @param resId The resource ID of the XML to inflate. 125 * @param rootPreferences Optional existing hierarchy to merge the new 126 * hierarchies into. 127 * @return The root hierarchy (if one was not provided, the new hierarchy's 128 * root). 129 * @hide 130 */ 131 @RestrictTo(LIBRARY_GROUP) 132 public PreferenceScreen inflateFromResource(Context context, int resId, 133 PreferenceScreen rootPreferences) { 134 // Block commits 135 setNoCommit(true); 136 137 final PreferenceInflater inflater = new PreferenceInflater(context, this); 138 rootPreferences = (PreferenceScreen) inflater.inflate(resId, rootPreferences); 139 rootPreferences.onAttachedToHierarchy(this); 140 141 // Unblock commits 142 setNoCommit(false); 143 144 return rootPreferences; 145 } 146 147 public PreferenceScreen createPreferenceScreen(Context context) { 148 final PreferenceScreen preferenceScreen = new PreferenceScreen(context, null); 149 preferenceScreen.onAttachedToHierarchy(this); 150 return preferenceScreen; 151 } 152 153 /** 154 * Called by a preference to get a unique ID in its hierarchy. 155 * 156 * @return A unique ID. 157 */ 158 long getNextId() { 159 synchronized (this) { 160 return mNextId++; 161 } 162 } 163 164 /** 165 * Returns the current name of the {@link SharedPreferences} file that preferences managed by 166 * this will use. 167 * 168 * @return The name that can be passed to {@link Context#getSharedPreferences(String, int)}. 169 * @see Context#getSharedPreferences(String, int) 170 */ 171 public String getSharedPreferencesName() { 172 return mSharedPreferencesName; 173 } 174 175 /** 176 * Sets the name of the {@link SharedPreferences} file that preferences managed by this 177 * will use. 178 * 179 * <p>If custom {@link PreferenceDataStore} is set, this won't override its usage. 180 * 181 * @param sharedPreferencesName The name of the SharedPreferences file. 182 * @see Context#getSharedPreferences(String, int) 183 * @see #setPreferenceDataStore(PreferenceDataStore) 184 */ 185 public void setSharedPreferencesName(String sharedPreferencesName) { 186 mSharedPreferencesName = sharedPreferencesName; 187 mSharedPreferences = null; 188 } 189 190 /** 191 * Returns the current mode of the SharedPreferences file that preferences managed by 192 * this will use. 193 * 194 * @return The mode that can be passed to {@link Context#getSharedPreferences(String, int)}. 195 * @see Context#getSharedPreferences(String, int) 196 */ 197 public int getSharedPreferencesMode() { 198 return mSharedPreferencesMode; 199 } 200 201 /** 202 * Sets the mode of the SharedPreferences file that preferences managed by this 203 * will use. 204 * 205 * @param sharedPreferencesMode The mode of the SharedPreferences file. 206 * @see Context#getSharedPreferences(String, int) 207 */ 208 public void setSharedPreferencesMode(int sharedPreferencesMode) { 209 mSharedPreferencesMode = sharedPreferencesMode; 210 mSharedPreferences = null; 211 } 212 213 /** 214 * Sets the storage location used internally by this class to be the default 215 * provided by the hosting {@link Context}. 216 */ 217 public void setStorageDefault() { 218 if (Build.VERSION.SDK_INT >= 24) { 219 mStorage = STORAGE_DEFAULT; 220 mSharedPreferences = null; 221 } 222 } 223 224 /** 225 * Explicitly set the storage location used internally by this class to be 226 * device-protected storage. 227 * <p> 228 * On devices with direct boot, data stored in this location is encrypted 229 * with a key tied to the physical device, and it can be accessed 230 * immediately after the device has booted successfully, both 231 * <em>before and after</em> the user has authenticated with their 232 * credentials (such as a lock pattern or PIN). 233 * <p> 234 * Because device-protected data is available without user authentication, 235 * you should carefully limit the data you store using this Context. For 236 * example, storing sensitive authentication tokens or passwords in the 237 * device-protected area is strongly discouraged. 238 * <p> 239 * Prior to API 24 this method has no effect, 240 * since device-protected storage is not available. 241 * 242 * @see Context#createDeviceProtectedStorageContext() 243 */ 244 public void setStorageDeviceProtected() { 245 if (Build.VERSION.SDK_INT >= 24) { 246 mStorage = STORAGE_DEVICE_PROTECTED; 247 mSharedPreferences = null; 248 } 249 } 250 251 /** 252 * Indicates if the storage location used internally by this class is the 253 * default provided by the hosting {@link Context}. 254 * 255 * @see #setStorageDefault() 256 * @see #setStorageDeviceProtected() 257 */ 258 public boolean isStorageDefault() { 259 if (Build.VERSION.SDK_INT >= 24) { 260 return mStorage == STORAGE_DEFAULT; 261 } else { 262 return true; 263 } 264 } 265 266 /** 267 * Indicates if the storage location used internally by this class is backed 268 * by device-protected storage. 269 * 270 * @see #setStorageDefault() 271 * @see #setStorageDeviceProtected() 272 */ 273 public boolean isStorageDeviceProtected() { 274 if (Build.VERSION.SDK_INT >= 24) { 275 return mStorage == STORAGE_DEVICE_PROTECTED; 276 } else { 277 return false; 278 } 279 } 280 281 /** 282 * Sets a {@link PreferenceDataStore} to be used by all Preferences associated with this manager 283 * that don't have a custom {@link PreferenceDataStore} assigned via 284 * {@link Preference#setPreferenceDataStore(PreferenceDataStore)}. Also if the data store is 285 * set, the child preferences won't use {@link android.content.SharedPreferences} as long as 286 * they are assigned to this manager. 287 * 288 * @param dataStore the {@link PreferenceDataStore} to be used by this manager 289 * @see Preference#setPreferenceDataStore(PreferenceDataStore) 290 */ 291 public void setPreferenceDataStore(PreferenceDataStore dataStore) { 292 mPreferenceDataStore = dataStore; 293 } 294 295 /** 296 * Returns the {@link PreferenceDataStore} associated with this manager or {@code null} if 297 * the default {@link android.content.SharedPreferences} are used instead. 298 * 299 * @return The {@link PreferenceDataStore} associated with this manager or {@code null} if none. 300 * @see #setPreferenceDataStore(PreferenceDataStore) 301 */ 302 @Nullable 303 public PreferenceDataStore getPreferenceDataStore() { 304 return mPreferenceDataStore; 305 } 306 307 /** 308 * Gets a {@link SharedPreferences} instance that preferences managed by this will 309 * use. 310 * 311 * @return a {@link SharedPreferences} instance pointing to the file that contain the values of 312 * preferences that are managed by this PreferenceManager. If 313 * a {@link PreferenceDataStore} has been set, this method returns {@code null}. 314 */ 315 public SharedPreferences getSharedPreferences() { 316 if (getPreferenceDataStore() != null) { 317 return null; 318 } 319 320 if (mSharedPreferences == null) { 321 final Context storageContext; 322 switch (mStorage) { 323 case STORAGE_DEVICE_PROTECTED: 324 storageContext = ContextCompat.createDeviceProtectedStorageContext(mContext); 325 break; 326 default: 327 storageContext = mContext; 328 break; 329 } 330 331 mSharedPreferences = storageContext.getSharedPreferences(mSharedPreferencesName, 332 mSharedPreferencesMode); 333 } 334 335 return mSharedPreferences; 336 } 337 338 /** 339 * Gets a SharedPreferences instance that points to the default file that is 340 * used by the preference framework in the given context. 341 * 342 * @param context The context of the preferences whose values are wanted. 343 * @return A SharedPreferences instance that can be used to retrieve and 344 * listen to values of the preferences. 345 */ 346 public static SharedPreferences getDefaultSharedPreferences(Context context) { 347 return context.getSharedPreferences(getDefaultSharedPreferencesName(context), 348 getDefaultSharedPreferencesMode()); 349 } 350 351 private static String getDefaultSharedPreferencesName(Context context) { 352 return context.getPackageName() + "_preferences"; 353 } 354 355 private static int getDefaultSharedPreferencesMode() { 356 return Context.MODE_PRIVATE; 357 } 358 359 /** 360 * Returns the root of the preference hierarchy managed by this class. 361 * 362 * @return The {@link PreferenceScreen} object that is at the root of the hierarchy. 363 */ 364 public PreferenceScreen getPreferenceScreen() { 365 return mPreferenceScreen; 366 } 367 368 /** 369 * Sets the root of the preference hierarchy. 370 * 371 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. 372 * @return Whether the {@link PreferenceScreen} given is different than the previous. 373 */ 374 public boolean setPreferences(PreferenceScreen preferenceScreen) { 375 if (preferenceScreen != mPreferenceScreen) { 376 if (mPreferenceScreen != null) { 377 mPreferenceScreen.onDetached(); 378 } 379 mPreferenceScreen = preferenceScreen; 380 return true; 381 } 382 383 return false; 384 } 385 386 /** 387 * Finds a {@link Preference} based on its key. 388 * 389 * @param key The key of the preference to retrieve. 390 * @return The {@link Preference} with the key, or null. 391 * @see PreferenceGroup#findPreference(CharSequence) 392 */ 393 public Preference findPreference(CharSequence key) { 394 if (mPreferenceScreen == null) { 395 return null; 396 } 397 398 return mPreferenceScreen.findPreference(key); 399 } 400 401 /** 402 * Sets the default values from an XML preference file by reading the values defined 403 * by each {@link Preference} item's {@code android:defaultValue} attribute. This should 404 * be called by the application's main activity. 405 * <p> 406 * 407 * @param context The context of the shared preferences. 408 * @param resId The resource ID of the preference XML file. 409 * @param readAgain Whether to re-read the default values. 410 * If false, this method sets the default values only if this 411 * method has never been called in the past (or if the 412 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared 413 * preferences file is false). To attempt to set the default values again 414 * bypassing this check, set {@code readAgain} to true. 415 * <p class="note"> 416 * Note: this will NOT reset preferences back to their default 417 * values. For that functionality, use 418 * {@link PreferenceManager#getDefaultSharedPreferences(Context)} 419 * and clear it followed by a call to this method with this 420 * parameter set to true. 421 */ 422 public static void setDefaultValues(Context context, int resId, boolean readAgain) { 423 // Use the default shared preferences name and mode 424 setDefaultValues(context, getDefaultSharedPreferencesName(context), 425 getDefaultSharedPreferencesMode(), resId, readAgain); 426 } 427 428 /** 429 * Similar to {@link #setDefaultValues(Context, int, boolean)} but allows 430 * the client to provide the filename and mode of the shared preferences 431 * file. 432 * 433 * @param context The context of the shared preferences. 434 * @param sharedPreferencesName A custom name for the shared preferences file. 435 * @param sharedPreferencesMode The file creation mode for the shared preferences file, such 436 * as {@link android.content.Context#MODE_PRIVATE} or {@link 437 * android.content.Context#MODE_PRIVATE} 438 * @param resId The resource ID of the preference XML file. 439 * @param readAgain Whether to re-read the default values. 440 * If false, this method will set the default values only if this 441 * method has never been called in the past (or if the 442 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared 443 * preferences file is false). To attempt to set the default values again 444 * bypassing this check, set {@code readAgain} to true. 445 * <p class="note"> 446 * Note: this will NOT reset preferences back to their default 447 * values. For that functionality, use 448 * {@link PreferenceManager#getDefaultSharedPreferences(Context)} 449 * and clear it followed by a call to this method with this 450 * parameter set to true. 451 * 452 * @see #setDefaultValues(Context, int, boolean) 453 * @see #setSharedPreferencesName(String) 454 * @see #setSharedPreferencesMode(int) 455 */ 456 public static void setDefaultValues(Context context, String sharedPreferencesName, 457 int sharedPreferencesMode, int resId, boolean readAgain) { 458 final SharedPreferences defaultValueSp = context.getSharedPreferences( 459 KEY_HAS_SET_DEFAULT_VALUES, Context.MODE_PRIVATE); 460 461 if (readAgain || !defaultValueSp.getBoolean(KEY_HAS_SET_DEFAULT_VALUES, false)) { 462 final PreferenceManager pm = new PreferenceManager(context); 463 pm.setSharedPreferencesName(sharedPreferencesName); 464 pm.setSharedPreferencesMode(sharedPreferencesMode); 465 pm.inflateFromResource(context, resId, null); 466 467 defaultValueSp.edit() 468 .putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true) 469 .apply(); 470 } 471 } 472 473 /** 474 * Returns an editor to use when modifying the shared preferences. 475 * 476 * <p>Do NOT commit unless {@link #shouldCommit()} returns true. 477 * 478 * @return an editor to use to write to shared preferences. If a {@link PreferenceDataStore} has 479 * been set, this method returns {@code null}. 480 * @see #shouldCommit() 481 */ 482 SharedPreferences.Editor getEditor() { 483 if (mPreferenceDataStore != null) { 484 return null; 485 } 486 487 if (mNoCommit) { 488 if (mEditor == null) { 489 mEditor = getSharedPreferences().edit(); 490 } 491 492 return mEditor; 493 } else { 494 return getSharedPreferences().edit(); 495 } 496 } 497 498 /** 499 * Whether it is the client's responsibility to commit on the 500 * {@link #getEditor()}. This will return false in cases where the writes 501 * should be batched, for example when inflating preferences from XML. 502 * 503 * <p>If preferences are using {@link PreferenceDataStore} this value is irrelevant. 504 * 505 * @return Whether the client should commit. 506 */ 507 boolean shouldCommit() { 508 return !mNoCommit; 509 } 510 511 private void setNoCommit(boolean noCommit) { 512 if (!noCommit && mEditor != null) { 513 mEditor.apply(); 514 } 515 mNoCommit = noCommit; 516 } 517 518 /** 519 * Returns the context. 520 * 521 * @return The context. 522 */ 523 public Context getContext() { 524 return mContext; 525 } 526 527 public PreferenceComparisonCallback getPreferenceComparisonCallback() { 528 return mPreferenceComparisonCallback; 529 } 530 531 public void setPreferenceComparisonCallback( 532 PreferenceComparisonCallback preferenceComparisonCallback) { 533 mPreferenceComparisonCallback = preferenceComparisonCallback; 534 } 535 536 public OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener() { 537 return mOnDisplayPreferenceDialogListener; 538 } 539 540 public void setOnDisplayPreferenceDialogListener( 541 OnDisplayPreferenceDialogListener onDisplayPreferenceDialogListener) { 542 mOnDisplayPreferenceDialogListener = onDisplayPreferenceDialogListener; 543 } 544 545 /** 546 * Called when a preference requests that a dialog be shown to complete a user interaction. 547 * 548 * @param preference The preference requesting the dialog. 549 */ 550 public void showDialog(Preference preference) { 551 if (mOnDisplayPreferenceDialogListener != null) { 552 mOnDisplayPreferenceDialogListener.onDisplayPreferenceDialog(preference); 553 } 554 } 555 556 /** 557 * Sets the callback to be invoked when a {@link Preference} in the 558 * hierarchy rooted at this {@link PreferenceManager} is clicked. 559 * 560 * @param listener The callback to be invoked. 561 */ 562 public void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) { 563 mOnPreferenceTreeClickListener = listener; 564 } 565 566 public OnPreferenceTreeClickListener getOnPreferenceTreeClickListener() { 567 return mOnPreferenceTreeClickListener; 568 } 569 570 /** 571 * Sets the callback to be invoked when a {@link PreferenceScreen} in the hierarchy rooted at 572 * this {@link PreferenceManager} is clicked. 573 * 574 * @param listener The callback to be invoked. 575 */ 576 public void setOnNavigateToScreenListener(OnNavigateToScreenListener listener) { 577 mOnNavigateToScreenListener = listener; 578 } 579 580 /** 581 * Returns the {@link PreferenceManager.OnNavigateToScreenListener}, if one has been set. 582 */ 583 public OnNavigateToScreenListener getOnNavigateToScreenListener() { 584 return mOnNavigateToScreenListener; 585 } 586 587 /** 588 * Callback class to be used by the {@link androidx.recyclerview.widget.RecyclerView.Adapter} 589 * associated with the {@link PreferenceScreen}, used to determine when two {@link Preference} 590 * objects are semantically and visually the same. 591 */ 592 public static abstract class PreferenceComparisonCallback { 593 /** 594 * Called to determine if two {@link Preference} objects represent the same item 595 * 596 * @param p1 {@link Preference} object to compare 597 * @param p2 {@link Preference} object to compare 598 * @return {@code true} if the objects represent the same item 599 */ 600 public abstract boolean arePreferenceItemsTheSame(Preference p1, Preference p2); 601 602 /** 603 * Called to determine if two {@link Preference} objects will display the same data 604 * 605 * @param p1 {@link Preference} object to compare 606 * @param p2 {@link Preference} object to compare 607 * @return {@code true} if the objects are visually identical 608 */ 609 public abstract boolean arePreferenceContentsTheSame(Preference p1, Preference p2); 610 } 611 612 /** 613 * A basic implementation of {@link PreferenceComparisonCallback} suitable for use with the 614 * default {@link Preference} classes. If the {@link PreferenceScreen} contains custom 615 * {@link Preference} subclasses, you must override 616 * {@link #arePreferenceContentsTheSame(Preference, Preference)} 617 */ 618 public static class SimplePreferenceComparisonCallback extends PreferenceComparisonCallback { 619 /** 620 * {@inheritDoc} 621 * 622 * <p>This method will not be able to track replaced {@link Preference} objects if they 623 * do not have a unique key.</p> 624 * 625 * @see Preference#setKey(String) 626 */ 627 @Override 628 public boolean arePreferenceItemsTheSame(Preference p1, Preference p2) { 629 return p1.getId() == p2.getId(); 630 } 631 632 /** 633 * {@inheritDoc} 634 * 635 * <p>The result of this method is only valid for the default {@link Preference} objects, 636 * and custom subclasses which do not override 637 * {@link Preference#onBindViewHolder(PreferenceViewHolder)}. This method also assumes 638 * that if a preference object is being replaced by a new instance, the old instance was 639 * not modified after being removed from its containing {@link PreferenceGroup}.</p> 640 */ 641 @Override 642 public boolean arePreferenceContentsTheSame(Preference p1, Preference p2) { 643 if (p1.getClass() != p2.getClass()) { 644 return false; 645 } 646 if (p1 == p2 && p1.wasDetached()) { 647 // Defensively handle the case where a preference was removed, updated and re-added. 648 // Hopefully this is rare. 649 return false; 650 } 651 if (!TextUtils.equals(p1.getTitle(), p2.getTitle())) { 652 return false; 653 } 654 if (!TextUtils.equals(p1.getSummary(), p2.getSummary())) { 655 return false; 656 } 657 final Drawable p1Icon = p1.getIcon(); 658 final Drawable p2Icon = p2.getIcon(); 659 if (p1Icon != p2Icon && (p1Icon == null || !p1Icon.equals(p2Icon))) { 660 return false; 661 } 662 if (p1.isEnabled() != p2.isEnabled()) { 663 return false; 664 } 665 if (p1.isSelectable() != p2.isSelectable()) { 666 return false; 667 } 668 if (p1 instanceof TwoStatePreference) { 669 if (((TwoStatePreference) p1).isChecked() 670 != ((TwoStatePreference) p2).isChecked()) { 671 return false; 672 } 673 } 674 if (p1 instanceof DropDownPreference && p1 != p2) { 675 // Different object, must re-bind spinner adapter 676 return false; 677 } 678 679 return true; 680 } 681 } 682 683 /** 684 * Interface definition for a callback to be invoked when a 685 * {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is 686 * clicked. 687 */ 688 public interface OnPreferenceTreeClickListener { 689 /** 690 * Called when a preference in the tree rooted at this 691 * {@link PreferenceScreen} has been clicked. 692 * 693 * @param preference The preference that was clicked. 694 * @return Whether the click was handled. 695 */ 696 boolean onPreferenceTreeClick(Preference preference); 697 } 698 699 /** 700 * Interface definition for a class that will be called when a 701 * {@link androidx.preference.Preference} requests to display a dialog. 702 */ 703 public interface OnDisplayPreferenceDialogListener { 704 705 /** 706 * Called when a preference in the tree requests to display a dialog. 707 * 708 * @param preference The Preference object requesting the dialog. 709 */ 710 void onDisplayPreferenceDialog(Preference preference); 711 } 712 713 /** 714 * Interface definition for a class that will be called when a 715 * {@link androidx.preference.PreferenceScreen} requests navigation. 716 */ 717 public interface OnNavigateToScreenListener { 718 719 /** 720 * Called when a PreferenceScreen in the tree requests to navigate to its contents. 721 * 722 * @param preferenceScreen The PreferenceScreen requesting navigation. 723 */ 724 void onNavigateToScreen(PreferenceScreen preferenceScreen); 725 } 726 727 } 728