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.XmlRes;
22 import android.app.Activity;
23 import android.compat.annotation.UnsupportedAppUsage;
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