• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
PreferenceManager(Context context)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)
inflateFromResource(Context context, int resId, PreferenceScreen rootPreferences)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 
createPreferenceScreen(Context context)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      */
getNextId()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      */
getSharedPreferencesName()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      */
setSharedPreferencesName(String sharedPreferencesName)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      */
getSharedPreferencesMode()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      */
setSharedPreferencesMode(int sharedPreferencesMode)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      */
setStorageDefault()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      */
setStorageDeviceProtected()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      */
isStorageDefault()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      */
isStorageDeviceProtected()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      */
setPreferenceDataStore(PreferenceDataStore dataStore)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
getPreferenceDataStore()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      */
getSharedPreferences()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      */
getDefaultSharedPreferences(Context context)346     public static SharedPreferences getDefaultSharedPreferences(Context context) {
347         return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
348                 getDefaultSharedPreferencesMode());
349     }
350 
getDefaultSharedPreferencesName(Context context)351     private static String getDefaultSharedPreferencesName(Context context) {
352         return context.getPackageName() + "_preferences";
353     }
354 
getDefaultSharedPreferencesMode()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      */
getPreferenceScreen()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      */
setPreferences(PreferenceScreen preferenceScreen)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      */
findPreference(CharSequence key)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      */
setDefaultValues(Context context, int resId, boolean readAgain)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      */
setDefaultValues(Context context, String sharedPreferencesName, int sharedPreferencesMode, int resId, boolean readAgain)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      */
getEditor()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      */
shouldCommit()507     boolean shouldCommit() {
508         return !mNoCommit;
509     }
510 
setNoCommit(boolean noCommit)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      */
getContext()523     public Context getContext() {
524         return mContext;
525     }
526 
getPreferenceComparisonCallback()527     public PreferenceComparisonCallback getPreferenceComparisonCallback() {
528         return mPreferenceComparisonCallback;
529     }
530 
setPreferenceComparisonCallback( PreferenceComparisonCallback preferenceComparisonCallback)531     public void setPreferenceComparisonCallback(
532             PreferenceComparisonCallback preferenceComparisonCallback) {
533         mPreferenceComparisonCallback = preferenceComparisonCallback;
534     }
535 
getOnDisplayPreferenceDialogListener()536     public OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener() {
537         return mOnDisplayPreferenceDialogListener;
538     }
539 
setOnDisplayPreferenceDialogListener( OnDisplayPreferenceDialogListener onDisplayPreferenceDialogListener)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      */
showDialog(Preference preference)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      */
setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener)562     public void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) {
563         mOnPreferenceTreeClickListener = listener;
564     }
565 
getOnPreferenceTreeClickListener()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      */
setOnNavigateToScreenListener(OnNavigateToScreenListener listener)576     public void setOnNavigateToScreenListener(OnNavigateToScreenListener listener) {
577         mOnNavigateToScreenListener = listener;
578     }
579 
580     /**
581      * Returns the {@link PreferenceManager.OnNavigateToScreenListener}, if one has been set.
582      */
getOnNavigateToScreenListener()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          */
arePreferenceItemsTheSame(Preference p1, Preference p2)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          */
arePreferenceContentsTheSame(Preference p1, Preference p2)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
arePreferenceItemsTheSame(Preference p1, Preference p2)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
arePreferenceContentsTheSame(Preference p1, Preference p2)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          */
onPreferenceTreeClick(Preference preference)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          */
onDisplayPreferenceDialog(Preference preference)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          */
onNavigateToScreen(PreferenceScreen preferenceScreen)724         void onNavigateToScreen(PreferenceScreen preferenceScreen);
725     }
726 
727 }
728