Home | History | Annotate | Download | only in preference
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.preference;
     18 
     19 import android.annotation.Nullable;
     20 import android.annotation.SystemApi;
     21 import android.annotation.UnsupportedAppUsage;
     22 import android.annotation.XmlRes;
     23 import android.app.Activity;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.Intent;
     27 import android.content.SharedPreferences;
     28 import android.content.pm.ActivityInfo;
     29 import android.content.pm.PackageManager;
     30 import android.content.pm.PackageManager.NameNotFoundException;
     31 import android.content.pm.ResolveInfo;
     32 import android.content.res.XmlResourceParser;
     33 import android.os.Build;
     34 import android.os.Bundle;
     35 import android.util.Log;
     36 
     37 import java.util.ArrayList;
     38 import java.util.HashSet;
     39 import java.util.List;
     40 
     41 /**
     42  * Used to help create {@link Preference} hierarchies
     43  * from activities or XML.
     44  * <p>
     45  * In most cases, clients should use
     46  * {@link PreferenceActivity#addPreferencesFromIntent} or
     47  * {@link PreferenceActivity#addPreferencesFromResource(int)}.
     48  *
     49  * @see PreferenceActivity
     50  *
     51  * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
     52  *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
     53  *      Preference Library</a> for consistent behavior across all devices. For more information on
     54  *      using the AndroidX Preference Library see
     55  *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
     56  */
     57 @Deprecated
     58 public class PreferenceManager {
     59 
     60     private static final String TAG = "PreferenceManager";
     61 
     62     /**
     63      * The Activity meta-data key for its XML preference hierarchy.
     64      */
     65     public static final String METADATA_KEY_PREFERENCES = "android.preference";
     66 
     67     public static final String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
     68 
     69     /**
     70      * @see #getActivity()
     71      */
     72     @Nullable
     73     private Activity mActivity;
     74 
     75     /**
     76      * Fragment that owns this instance.
     77      */
     78     @Nullable
     79     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     80     private PreferenceFragment mFragment;
     81 
     82     /**
     83      * The context to use. This should always be set.
     84      *
     85      * @see #mActivity
     86      */
     87     private Context mContext;
     88 
     89     /**
     90      * The counter for unique IDs.
     91      */
     92     private long mNextId = 0;
     93 
     94     /**
     95      * The counter for unique request codes.
     96      */
     97     private int mNextRequestCode;
     98 
     99     /**
    100      * Cached shared preferences.
    101      */
    102     @Nullable
    103     @UnsupportedAppUsage
    104     private SharedPreferences mSharedPreferences;
    105 
    106     /**
    107      * Data store to be used by the Preferences or {@code null} if
    108      * {@link android.content.SharedPreferences} should be used.
    109      */
    110     @Nullable
    111     private PreferenceDataStore mPreferenceDataStore;
    112 
    113     /**
    114      * If in no-commit mode, the shared editor to give out (which will be
    115      * committed when exiting no-commit mode).
    116      */
    117     @Nullable
    118     private SharedPreferences.Editor mEditor;
    119 
    120     /**
    121      * Blocks commits from happening on the shared editor. This is used when
    122      * inflating the hierarchy. Do not set this directly, use {@link #setNoCommit(boolean)}
    123      */
    124     private boolean mNoCommit;
    125 
    126     /**
    127      * The SharedPreferences name that will be used for all {@link Preference}s
    128      * managed by this instance.
    129      */
    130     private String mSharedPreferencesName;
    131 
    132     /**
    133      * The SharedPreferences mode that will be used for all {@link Preference}s
    134      * managed by this instance.
    135      */
    136     private int mSharedPreferencesMode;
    137 
    138     private static final int STORAGE_DEFAULT = 0;
    139     private static final int STORAGE_DEVICE_PROTECTED = 1;
    140     private static final int STORAGE_CREDENTIAL_PROTECTED = 2;
    141 
    142     private int mStorage = STORAGE_DEFAULT;
    143 
    144     /**
    145      * The {@link PreferenceScreen} at the root of the preference hierarchy.
    146      */
    147     @Nullable
    148     private PreferenceScreen mPreferenceScreen;
    149 
    150     /**
    151      * List of activity result listeners.
    152      */
    153     @Nullable
    154     private List<OnActivityResultListener> mActivityResultListeners;
    155 
    156     /**
    157      * List of activity stop listeners.
    158      */
    159     @Nullable
    160     private List<OnActivityStopListener> mActivityStopListeners;
    161 
    162     /**
    163      * List of activity destroy listeners.
    164      */
    165     @Nullable
    166     @UnsupportedAppUsage
    167     private List<OnActivityDestroyListener> mActivityDestroyListeners;
    168 
    169     /**
    170      * List of dialogs that should be dismissed when we receive onNewIntent in
    171      * our PreferenceActivity.
    172      */
    173     @Nullable
    174     private List<DialogInterface> mPreferencesScreens;
    175 
    176     @UnsupportedAppUsage
    177     private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener;
    178 
    179     /**
    180      * @hide
    181      */
    182     @UnsupportedAppUsage
    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
    199     /*package*/ PreferenceManager(Context context) {
    200         init(context);
    201     }
    202 
    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)
    213     void setFragment(PreferenceFragment fragment) {
    214         mFragment = fragment;
    215     }
    216 
    217     /**
    218      * Returns the owning preference fragment, if any.
    219      */
    220     @Nullable
    221     @UnsupportedAppUsage
    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      */
    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
    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      */
    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
    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
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    554     public static String getDefaultSharedPreferencesName(Context context) {
    555         return context.getPackageName() + "_preferences";
    556     }
    557 
    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
    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
    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
    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      */
    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      */
    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
    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
    721     boolean shouldCommit() {
    722         return !mNoCommit;
    723     }
    724 
    725     @UnsupportedAppUsage
    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
    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      */
    763     Context getContext() {
    764         return mContext;
    765     }
    766 
    767     /**
    768      * Registers a listener.
    769      *
    770      * @see OnActivityResultListener
    771      */
    772     @UnsupportedAppUsage
    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
    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
    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
    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
    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
    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
    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
    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
    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
    937     int getNextRequestCode() {
    938         synchronized (this) {
    939             return mNextRequestCode++;
    940         }
    941     }
    942 
    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 
    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      */
    970     void dispatchNewIntent(Intent intent) {
    971         dismissAllScreens();
    972     }
    973 
    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      */
    999     void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) {
   1000         mOnPreferenceTreeClickListener = listener;
   1001     }
   1002 
   1003     @Nullable
   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          */
   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          */
   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          */
   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          */
   1092         void onActivityDestroy();
   1093     }
   1094 
   1095 }
   1096