blob: 73174e310e113d35eb76dcdb218629bb8ebe26b4 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.preference;
18
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -070019import android.annotation.SystemApi;
Tor Norbye7b9c9122013-05-30 16:48:33 -070020import android.annotation.XmlRes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.app.Activity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
23import android.content.DialogInterface;
24import android.content.Intent;
25import android.content.SharedPreferences;
26import android.content.pm.ActivityInfo;
27import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -070029import android.content.pm.ResolveInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.res.XmlResourceParser;
31import android.os.Bundle;
32import android.util.Log;
33
Amith Yamasani82e7bc12010-09-23 15:07:58 -070034import java.util.ArrayList;
35import java.util.HashSet;
36import java.util.List;
37
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038/**
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 */
48public 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 /**
Amith Yamasani82e7bc12010-09-23 15:07:58 -070065 * Fragment that owns this instance.
66 */
67 private PreferenceFragment mFragment;
68
69 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 * 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;
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700114
115 private static final int STORAGE_DEFAULT = 0;
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600116 private static final int STORAGE_DEVICE_PROTECTED = 1;
117 private static final int STORAGE_CREDENTIAL_PROTECTED = 2;
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700118
119 private int mStorage = STORAGE_DEFAULT;
120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 /**
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
Justin Koh37ae5582012-10-25 15:26:36 -0700149 /**
150 * @hide
151 */
152 public PreferenceManager(Activity activity, int firstRequestCode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 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 */
Deepanshu Gupta10bb1372014-10-05 14:41:17 -0700167 /*package*/ PreferenceManager(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 init(context);
169 }
170
171 private void init(Context context) {
172 mContext = context;
173
174 setSharedPreferencesName(getDefaultSharedPreferencesName(context));
175 }
Amith Yamasani82e7bc12010-09-23 15:07:58 -0700176
177 /**
178 * Sets the owning preference fragment
179 */
180 void setFragment(PreferenceFragment fragment) {
181 mFragment = fragment;
182 }
183
184 /**
185 * Returns the owning preference fragment, if any.
186 */
187 PreferenceFragment getFragment() {
188 return mFragment;
189 }
190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 /**
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 */
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 */
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).
Owen Linf9b702e2009-08-27 18:30:05 +0800272 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700274 public PreferenceScreen inflateFromResource(Context context, @XmlRes int resId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 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
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 */
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 */
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 */
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 */
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 */
347 public void setSharedPreferencesMode(int sharedPreferencesMode) {
348 mSharedPreferencesMode = sharedPreferencesMode;
349 mSharedPreferences = null;
350 }
351
352 /**
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700353 * Sets the storage location used internally by this class to be the default
354 * provided by the hosting {@link Context}.
355 */
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
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600363 * device-protected storage.
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700364 * <p>
Jeff Sharkeycf3f0a12016-03-17 19:57:58 -0600365 * On devices with direct boot, data stored in this location is encrypted
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600366 * 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.
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700375 *
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600376 * @see Context#createDeviceProtectedStorageContext()
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700377 */
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600378 public void setStorageDeviceProtected() {
379 mStorage = STORAGE_DEVICE_PROTECTED;
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700380 mSharedPreferences = null;
381 }
382
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600383 /** @removed */
384 @Deprecated
385 public void setStorageDeviceEncrypted() {
386 setStorageDeviceProtected();
387 }
388
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700389 /**
390 * Explicitly set the storage location used internally by this class to be
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600391 * credential-protected storage. This is the default storage area for apps
392 * unless {@code forceDeviceProtectedStorage} was requested.
393 * <p>
Jeff Sharkeycf3f0a12016-03-17 19:57:58 -0600394 * On devices with direct boot, data stored in this location is encrypted
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600395 * 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).
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700398 *
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600399 * @see Context#createCredentialProtectedStorageContext()
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700400 * @hide
401 */
402 @SystemApi
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600403 public void setStorageCredentialProtected() {
404 mStorage = STORAGE_CREDENTIAL_PROTECTED;
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700405 mSharedPreferences = null;
406 }
407
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600408 /** @removed */
409 @Deprecated
410 public void setStorageCredentialEncrypted() {
411 setStorageCredentialProtected();
412 }
413
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700414 /**
Jeff Sharkeyc9a40cd2016-03-27 12:10:38 -0600415 * 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 */
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 */
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
445 public boolean isStorageCredentialProtected() {
446 return mStorage == STORAGE_CREDENTIAL_PROTECTED;
447 }
448
449 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 * 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 */
456 public SharedPreferences getSharedPreferences() {
457 if (mSharedPreferences == null) {
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700458 final Context storageContext;
459 switch (mStorage) {
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600460 case STORAGE_DEVICE_PROTECTED:
461 storageContext = mContext.createDeviceProtectedStorageContext();
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700462 break;
Jeff Sharkey8a372a02016-03-16 16:25:45 -0600463 case STORAGE_CREDENTIAL_PROTECTED:
464 storageContext = mContext.createCredentialProtectedStorageContext();
Jeff Sharkeyfd37abe2016-01-28 11:53:48 -0700465 break;
466 default:
467 storageContext = mContext;
468 break;
469 }
470
471 mSharedPreferences = storageContext.getSharedPreferences(mSharedPreferencesName,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 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 */
486 public static SharedPreferences getDefaultSharedPreferences(Context context) {
487 return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
488 getDefaultSharedPreferencesMode());
489 }
490
Jeff Sharkey6a6cdaf2015-12-07 19:25:19 -0700491 /**
492 * Returns the name used for storing default shared preferences.
493 *
494 * @see #getDefaultSharedPreferences(Context)
495 * @see Context#getSharedPreferencesPath(String)
496 */
497 public static String getDefaultSharedPreferencesName(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 return context.getPackageName() + "_preferences";
499 }
500
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 */
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 */
520 boolean setPreferences(PreferenceScreen preferenceScreen) {
521 if (preferenceScreen != mPreferenceScreen) {
Jason Monke18dc502015-08-11 16:40:44 -0400522 if (mPreferenceScreen != null) {
523 mPreferenceScreen.onDetachedFromActivity();
524 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 mPreferenceScreen = preferenceScreen;
526 return true;
527 }
528
529 return false;
530 }
531
532 /**
533 * Finds a {@link Preference} based on its key.
534 *
535 * @param key The key of the preference to retrieve.
536 * @return The {@link Preference} with the key, or null.
537 * @see PreferenceGroup#findPreference(CharSequence)
538 */
539 public Preference findPreference(CharSequence key) {
540 if (mPreferenceScreen == null) {
541 return null;
542 }
543
544 return mPreferenceScreen.findPreference(key);
545 }
546
547 /**
Scott Mainbbb3f412012-03-09 19:10:40 -0800548 * Sets the default values from an XML preference file by reading the values defined
549 * by each {@link Preference} item's {@code android:defaultValue} attribute. This should
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 * be called by the application's main activity.
551 * <p>
Scott Mainbbb3f412012-03-09 19:10:40 -0800552 *
553 * @param context The context of the shared preferences.
554 * @param resId The resource ID of the preference XML file.
555 * @param readAgain Whether to re-read the default values.
556 * If false, this method sets the default values only if this
557 * method has never been called in the past (or if the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
559 * preferences file is false). To attempt to set the default values again
560 * bypassing this check, set {@code readAgain} to true.
Scott Mainbbb3f412012-03-09 19:10:40 -0800561 * <p class="note">
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 * Note: this will NOT reset preferences back to their default
563 * values. For that functionality, use
564 * {@link PreferenceManager#getDefaultSharedPreferences(Context)}
565 * and clear it followed by a call to this method with this
566 * parameter set to true.
567 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700568 public static void setDefaultValues(Context context, @XmlRes int resId, boolean readAgain) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569
570 // Use the default shared preferences name and mode
571 setDefaultValues(context, getDefaultSharedPreferencesName(context),
572 getDefaultSharedPreferencesMode(), resId, readAgain);
573 }
574
575 /**
576 * Similar to {@link #setDefaultValues(Context, int, boolean)} but allows
577 * the client to provide the filename and mode of the shared preferences
578 * file.
Scott Mainbbb3f412012-03-09 19:10:40 -0800579 *
580 * @param context The context of the shared preferences.
581 * @param sharedPreferencesName A custom name for the shared preferences file.
582 * @param sharedPreferencesMode The file creation mode for the shared preferences file, such
583 * as {@link android.content.Context#MODE_PRIVATE} or {@link
584 * android.content.Context#MODE_PRIVATE}
585 * @param resId The resource ID of the preference XML file.
586 * @param readAgain Whether to re-read the default values.
587 * If false, this method will set the default values only if this
588 * method has never been called in the past (or if the
589 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
590 * preferences file is false). To attempt to set the default values again
591 * bypassing this check, set {@code readAgain} to true.
592 * <p class="note">
593 * Note: this will NOT reset preferences back to their default
594 * values. For that functionality, use
595 * {@link PreferenceManager#getDefaultSharedPreferences(Context)}
596 * and clear it followed by a call to this method with this
597 * parameter set to true.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 *
599 * @see #setDefaultValues(Context, int, boolean)
600 * @see #setSharedPreferencesName(String)
601 * @see #setSharedPreferencesMode(int)
602 */
603 public static void setDefaultValues(Context context, String sharedPreferencesName,
604 int sharedPreferencesMode, int resId, boolean readAgain) {
605 final SharedPreferences defaultValueSp = context.getSharedPreferences(
606 KEY_HAS_SET_DEFAULT_VALUES, Context.MODE_PRIVATE);
607
608 if (readAgain || !defaultValueSp.getBoolean(KEY_HAS_SET_DEFAULT_VALUES, false)) {
609 final PreferenceManager pm = new PreferenceManager(context);
610 pm.setSharedPreferencesName(sharedPreferencesName);
611 pm.setSharedPreferencesMode(sharedPreferencesMode);
612 pm.inflateFromResource(context, resId, null);
613
Brad Fitzpatrickdd644c12010-10-10 10:58:47 -0700614 SharedPreferences.Editor editor =
615 defaultValueSp.edit().putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true);
616 try {
617 editor.apply();
618 } catch (AbstractMethodError unused) {
619 // The app injected its own pre-Gingerbread
620 // SharedPreferences.Editor implementation without
621 // an apply method.
622 editor.commit();
623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625 }
626
627 /**
628 * Returns an editor to use when modifying the shared preferences.
629 * <p>
630 * Do NOT commit unless {@link #shouldCommit()} returns true.
631 *
632 * @return An editor to use to write to shared preferences.
633 * @see #shouldCommit()
634 */
635 SharedPreferences.Editor getEditor() {
636
637 if (mNoCommit) {
638 if (mEditor == null) {
639 mEditor = getSharedPreferences().edit();
640 }
641
642 return mEditor;
643 } else {
644 return getSharedPreferences().edit();
645 }
646 }
647
648 /**
649 * Whether it is the client's responsibility to commit on the
650 * {@link #getEditor()}. This will return false in cases where the writes
651 * should be batched, for example when inflating preferences from XML.
652 *
653 * @return Whether the client should commit.
654 */
655 boolean shouldCommit() {
656 return !mNoCommit;
657 }
Brad Fitzpatrickdd644c12010-10-10 10:58:47 -0700658
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 private void setNoCommit(boolean noCommit) {
660 if (!noCommit && mEditor != null) {
Brad Fitzpatrickdd644c12010-10-10 10:58:47 -0700661 try {
662 mEditor.apply();
663 } catch (AbstractMethodError unused) {
664 // The app injected its own pre-Gingerbread
665 // SharedPreferences.Editor implementation without
666 // an apply method.
667 mEditor.commit();
668 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 mNoCommit = noCommit;
671 }
Brad Fitzpatrickdd644c12010-10-10 10:58:47 -0700672
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 /**
674 * Returns the activity that shows the preferences. This is useful for doing
675 * managed queries, but in most cases the use of {@link #getContext()} is
676 * preferred.
677 * <p>
678 * This will return null if this class was instantiated with a Context
679 * instead of Activity. For example, when setting the default values.
680 *
681 * @return The activity that shows the preferences.
682 * @see #mContext
683 */
684 Activity getActivity() {
685 return mActivity;
686 }
687
688 /**
689 * Returns the context. This is preferred over {@link #getActivity()} when
690 * possible.
691 *
692 * @return The context.
693 */
694 Context getContext() {
695 return mContext;
696 }
697
698 /**
699 * Registers a listener.
700 *
701 * @see OnActivityResultListener
702 */
703 void registerOnActivityResultListener(OnActivityResultListener listener) {
704 synchronized (this) {
705 if (mActivityResultListeners == null) {
706 mActivityResultListeners = new ArrayList<OnActivityResultListener>();
707 }
708
709 if (!mActivityResultListeners.contains(listener)) {
710 mActivityResultListeners.add(listener);
711 }
712 }
713 }
714
715 /**
716 * Unregisters a listener.
717 *
718 * @see OnActivityResultListener
719 */
720 void unregisterOnActivityResultListener(OnActivityResultListener listener) {
721 synchronized (this) {
722 if (mActivityResultListeners != null) {
723 mActivityResultListeners.remove(listener);
724 }
725 }
726 }
727
728 /**
729 * Called by the {@link PreferenceManager} to dispatch a subactivity result.
730 */
731 void dispatchActivityResult(int requestCode, int resultCode, Intent data) {
732 List<OnActivityResultListener> list;
733
734 synchronized (this) {
735 if (mActivityResultListeners == null) return;
736 list = new ArrayList<OnActivityResultListener>(mActivityResultListeners);
737 }
738
739 final int N = list.size();
740 for (int i = 0; i < N; i++) {
741 if (list.get(i).onActivityResult(requestCode, resultCode, data)) {
742 break;
743 }
744 }
745 }
746
747 /**
748 * Registers a listener.
749 *
750 * @see OnActivityStopListener
John Spurlock74a2e062014-05-16 21:03:29 -0400751 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 */
John Spurlock74a2e062014-05-16 21:03:29 -0400753 public void registerOnActivityStopListener(OnActivityStopListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 synchronized (this) {
755 if (mActivityStopListeners == null) {
756 mActivityStopListeners = new ArrayList<OnActivityStopListener>();
757 }
758
759 if (!mActivityStopListeners.contains(listener)) {
760 mActivityStopListeners.add(listener);
761 }
762 }
763 }
764
765 /**
766 * Unregisters a listener.
767 *
768 * @see OnActivityStopListener
John Spurlock74a2e062014-05-16 21:03:29 -0400769 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 */
John Spurlock74a2e062014-05-16 21:03:29 -0400771 public void unregisterOnActivityStopListener(OnActivityStopListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 synchronized (this) {
773 if (mActivityStopListeners != null) {
774 mActivityStopListeners.remove(listener);
775 }
776 }
777 }
778
779 /**
780 * Called by the {@link PreferenceManager} to dispatch the activity stop
781 * event.
782 */
783 void dispatchActivityStop() {
784 List<OnActivityStopListener> list;
785
786 synchronized (this) {
787 if (mActivityStopListeners == null) return;
788 list = new ArrayList<OnActivityStopListener>(mActivityStopListeners);
789 }
790
791 final int N = list.size();
792 for (int i = 0; i < N; i++) {
793 list.get(i).onActivityStop();
794 }
795 }
796
797 /**
798 * Registers a listener.
799 *
800 * @see OnActivityDestroyListener
801 */
802 void registerOnActivityDestroyListener(OnActivityDestroyListener listener) {
803 synchronized (this) {
804 if (mActivityDestroyListeners == null) {
805 mActivityDestroyListeners = new ArrayList<OnActivityDestroyListener>();
806 }
807
808 if (!mActivityDestroyListeners.contains(listener)) {
809 mActivityDestroyListeners.add(listener);
810 }
811 }
812 }
813
814 /**
815 * Unregisters a listener.
816 *
817 * @see OnActivityDestroyListener
818 */
819 void unregisterOnActivityDestroyListener(OnActivityDestroyListener listener) {
820 synchronized (this) {
821 if (mActivityDestroyListeners != null) {
822 mActivityDestroyListeners.remove(listener);
823 }
824 }
825 }
826
827 /**
828 * Called by the {@link PreferenceManager} to dispatch the activity destroy
829 * event.
830 */
831 void dispatchActivityDestroy() {
832 List<OnActivityDestroyListener> list = null;
Jason Monke18dc502015-08-11 16:40:44 -0400833
834 if (mPreferenceScreen != null) {
835 mPreferenceScreen.onDetachedFromActivity();
836 mPreferenceScreen = null;
837 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 synchronized (this) {
839 if (mActivityDestroyListeners != null) {
840 list = new ArrayList<OnActivityDestroyListener>(mActivityDestroyListeners);
841 }
842 }
843
844 if (list != null) {
845 final int N = list.size();
846 for (int i = 0; i < N; i++) {
847 list.get(i).onActivityDestroy();
848 }
849 }
850
851 // Dismiss any PreferenceScreens still showing
852 dismissAllScreens();
853 }
854
855 /**
856 * Returns a request code that is unique for the activity. Each subsequent
857 * call to this method should return another unique request code.
858 *
859 * @return A unique request code that will never be used by anyone other
860 * than the caller of this method.
861 */
862 int getNextRequestCode() {
863 synchronized (this) {
864 return mNextRequestCode++;
865 }
866 }
867
868 void addPreferencesScreen(DialogInterface screen) {
869 synchronized (this) {
870
871 if (mPreferencesScreens == null) {
872 mPreferencesScreens = new ArrayList<DialogInterface>();
873 }
874
875 mPreferencesScreens.add(screen);
876 }
877 }
878
879 void removePreferencesScreen(DialogInterface screen) {
880 synchronized (this) {
881
882 if (mPreferencesScreens == null) {
883 return;
884 }
885
886 mPreferencesScreens.remove(screen);
887 }
888 }
889
890 /**
891 * Called by {@link PreferenceActivity} to dispatch the new Intent event.
892 *
893 * @param intent The new Intent.
894 */
895 void dispatchNewIntent(Intent intent) {
896 dismissAllScreens();
897 }
898
899 private void dismissAllScreens() {
900 // Remove any of the previously shown preferences screens
901 ArrayList<DialogInterface> screensToDismiss;
902
903 synchronized (this) {
904
905 if (mPreferencesScreens == null) {
906 return;
907 }
908
909 screensToDismiss = new ArrayList<DialogInterface>(mPreferencesScreens);
910 mPreferencesScreens.clear();
911 }
912
913 for (int i = screensToDismiss.size() - 1; i >= 0; i--) {
914 screensToDismiss.get(i).dismiss();
915 }
916 }
917
918 /**
919 * Sets the callback to be invoked when a {@link Preference} in the
920 * hierarchy rooted at this {@link PreferenceManager} is clicked.
921 *
922 * @param listener The callback to be invoked.
923 */
924 void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) {
925 mOnPreferenceTreeClickListener = listener;
926 }
927
928 OnPreferenceTreeClickListener getOnPreferenceTreeClickListener() {
929 return mOnPreferenceTreeClickListener;
930 }
931
932 /**
933 * Interface definition for a callback to be invoked when a
934 * {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is
935 * clicked.
Fabrice Di Meglioad2fcfe2014-01-20 11:10:52 -0800936 *
937 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 */
Fabrice Di Meglioad2fcfe2014-01-20 11:10:52 -0800939 public interface OnPreferenceTreeClickListener {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 /**
941 * Called when a preference in the tree rooted at this
942 * {@link PreferenceScreen} has been clicked.
943 *
944 * @param preferenceScreen The {@link PreferenceScreen} that the
945 * preference is located in.
946 * @param preference The preference that was clicked.
947 * @return Whether the click was handled.
948 */
949 boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference);
950 }
951
952 /**
953 * Interface definition for a class that will be called when the container's activity
954 * receives an activity result.
955 */
956 public interface OnActivityResultListener {
957
958 /**
959 * See Activity's onActivityResult.
960 *
961 * @return Whether the request code was handled (in which case
962 * subsequent listeners will not be called.
963 */
964 boolean onActivityResult(int requestCode, int resultCode, Intent data);
965 }
966
967 /**
968 * Interface definition for a class that will be called when the container's activity
969 * is stopped.
970 */
971 public interface OnActivityStopListener {
972
973 /**
974 * See Activity's onStop.
975 */
976 void onActivityStop();
977 }
978
979 /**
980 * Interface definition for a class that will be called when the container's activity
981 * is destroyed.
982 */
983 public interface OnActivityDestroyListener {
984
985 /**
986 * See Activity's onDestroy.
987 */
988 void onActivityDestroy();
989 }
990
991}