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