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