PreferenceManager.java revision f9799b58e543fbddf7a88262a73147b9b959d916
1/* 2 * Copyright (C) 2015 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.support.v7.preference; 18 19import android.content.Context; 20import android.content.SharedPreferences; 21import android.support.v4.content.SharedPreferencesCompat; 22 23/** 24 * Used to help create {@link Preference} hierarchies 25 * from activities or XML. 26 * <p> 27 * In most cases, clients should use 28 * {@link PreferenceFragment#addPreferencesFromResource(int)}, or 29 * {@link PreferenceFragmentCompat#addPreferencesFromResource(int)}. 30 * 31 * @see PreferenceFragment 32 * @see PreferenceFragmentCompat 33 */ 34public class PreferenceManager { 35 36 private static final String TAG = "PreferenceManager"; 37 38 public static final String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values"; 39 40 /** 41 * The context to use. This should always be set. 42 */ 43 private Context mContext; 44 45 /** 46 * The counter for unique IDs. 47 */ 48 private long mNextId = 0; 49 50 /** 51 * Cached shared preferences. 52 */ 53 private SharedPreferences mSharedPreferences; 54 55 /** 56 * If in no-commit mode, the shared editor to give out (which will be 57 * committed when exiting no-commit mode). 58 */ 59 private SharedPreferences.Editor mEditor; 60 61 /** 62 * Blocks commits from happening on the shared editor. This is used when 63 * inflating the hierarchy. Do not set this directly, use {@link #setNoCommit(boolean)} 64 */ 65 private boolean mNoCommit; 66 67 /** 68 * The SharedPreferences name that will be used for all {@link Preference}s 69 * managed by this instance. 70 */ 71 private String mSharedPreferencesName; 72 73 /** 74 * The SharedPreferences mode that will be used for all {@link Preference}s 75 * managed by this instance. 76 */ 77 private int mSharedPreferencesMode; 78 79 /** 80 * The {@link PreferenceScreen} at the root of the preference hierarchy. 81 */ 82 private PreferenceScreen mPreferenceScreen; 83 84 private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener; 85 private OnDisplayPreferenceDialogListener mOnDisplayPreferenceDialogListener; 86 private OnNavigateToScreenListener mOnNavigateToScreenListener; 87 88 /** 89 * @hide 90 */ 91 public PreferenceManager(Context context) { 92 mContext = context; 93 94 setSharedPreferencesName(getDefaultSharedPreferencesName(context)); 95 } 96 97 /** 98 * Inflates a preference hierarchy from XML. If a preference hierarchy is 99 * given, the new preference hierarchies will be merged in. 100 * 101 * @param context The context of the resource. 102 * @param resId The resource ID of the XML to inflate. 103 * @param rootPreferences Optional existing hierarchy to merge the new 104 * hierarchies into. 105 * @return The root hierarchy (if one was not provided, the new hierarchy's 106 * root). 107 * @hide 108 */ 109 public PreferenceScreen inflateFromResource(Context context, int resId, 110 PreferenceScreen rootPreferences) { 111 // Block commits 112 setNoCommit(true); 113 114 final PreferenceInflater inflater = new PreferenceInflater(context, this); 115 rootPreferences = (PreferenceScreen) inflater.inflate(resId, rootPreferences); 116 rootPreferences.onAttachedToHierarchy(this); 117 118 // Unblock commits 119 setNoCommit(false); 120 121 return rootPreferences; 122 } 123 124 public PreferenceScreen createPreferenceScreen(Context context) { 125 final PreferenceScreen preferenceScreen = new PreferenceScreen(context, null); 126 preferenceScreen.onAttachedToHierarchy(this); 127 return preferenceScreen; 128 } 129 130 /** 131 * Called by a preference to get a unique ID in its hierarchy. 132 * 133 * @return A unique ID. 134 */ 135 long getNextId() { 136 synchronized (this) { 137 return mNextId++; 138 } 139 } 140 141 /** 142 * Returns the current name of the SharedPreferences file that preferences managed by 143 * this will use. 144 * 145 * @return The name that can be passed to {@link Context#getSharedPreferences(String, int)}. 146 * @see Context#getSharedPreferences(String, int) 147 */ 148 public String getSharedPreferencesName() { 149 return mSharedPreferencesName; 150 } 151 152 /** 153 * Sets the name of the SharedPreferences file that preferences managed by this 154 * will use. 155 * 156 * @param sharedPreferencesName The name of the SharedPreferences file. 157 * @see Context#getSharedPreferences(String, int) 158 */ 159 public void setSharedPreferencesName(String sharedPreferencesName) { 160 mSharedPreferencesName = sharedPreferencesName; 161 mSharedPreferences = null; 162 } 163 164 /** 165 * Returns the current mode of the SharedPreferences file that preferences managed by 166 * this will use. 167 * 168 * @return The mode that can be passed to {@link Context#getSharedPreferences(String, int)}. 169 * @see Context#getSharedPreferences(String, int) 170 */ 171 public int getSharedPreferencesMode() { 172 return mSharedPreferencesMode; 173 } 174 175 /** 176 * Sets the mode of the SharedPreferences file that preferences managed by this 177 * will use. 178 * 179 * @param sharedPreferencesMode The mode of the SharedPreferences file. 180 * @see Context#getSharedPreferences(String, int) 181 */ 182 public void setSharedPreferencesMode(int sharedPreferencesMode) { 183 mSharedPreferencesMode = sharedPreferencesMode; 184 mSharedPreferences = null; 185 } 186 187 /** 188 * Gets a SharedPreferences instance that preferences managed by this will 189 * use. 190 * 191 * @return A SharedPreferences instance pointing to the file that contains 192 * the values of preferences that are managed by this. 193 */ 194 public SharedPreferences getSharedPreferences() { 195 if (mSharedPreferences == null) { 196 mSharedPreferences = mContext.getSharedPreferences(mSharedPreferencesName, 197 mSharedPreferencesMode); 198 } 199 200 return mSharedPreferences; 201 } 202 203 /** 204 * Gets a SharedPreferences instance that points to the default file that is 205 * used by the preference framework in the given context. 206 * 207 * @param context The context of the preferences whose values are wanted. 208 * @return A SharedPreferences instance that can be used to retrieve and 209 * listen to values of the preferences. 210 */ 211 public static SharedPreferences getDefaultSharedPreferences(Context context) { 212 return context.getSharedPreferences(getDefaultSharedPreferencesName(context), 213 getDefaultSharedPreferencesMode()); 214 } 215 216 private static String getDefaultSharedPreferencesName(Context context) { 217 return context.getPackageName() + "_preferences"; 218 } 219 220 private static int getDefaultSharedPreferencesMode() { 221 return Context.MODE_PRIVATE; 222 } 223 224 /** 225 * Returns the root of the preference hierarchy managed by this class. 226 * 227 * @return The {@link PreferenceScreen} object that is at the root of the hierarchy. 228 */ 229 public PreferenceScreen getPreferenceScreen() { 230 return mPreferenceScreen; 231 } 232 233 /** 234 * Sets the root of the preference hierarchy. 235 * 236 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. 237 * @return Whether the {@link PreferenceScreen} given is different than the previous. 238 */ 239 public boolean setPreferences(PreferenceScreen preferenceScreen) { 240 if (preferenceScreen != mPreferenceScreen) { 241 mPreferenceScreen = preferenceScreen; 242 return true; 243 } 244 245 return false; 246 } 247 248 /** 249 * Finds a {@link Preference} based on its key. 250 * 251 * @param key The key of the preference to retrieve. 252 * @return The {@link Preference} with the key, or null. 253 * @see PreferenceGroup#findPreference(CharSequence) 254 */ 255 public Preference findPreference(CharSequence key) { 256 if (mPreferenceScreen == null) { 257 return null; 258 } 259 260 return mPreferenceScreen.findPreference(key); 261 } 262 263 /** 264 * Sets the default values from an XML preference file by reading the values defined 265 * by each {@link Preference} item's {@code android:defaultValue} attribute. This should 266 * be called by the application's main activity. 267 * <p> 268 * 269 * @param context The context of the shared preferences. 270 * @param resId The resource ID of the preference XML file. 271 * @param readAgain Whether to re-read the default values. 272 * If false, this method sets the default values only if this 273 * method has never been called in the past (or if the 274 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared 275 * preferences file is false). To attempt to set the default values again 276 * bypassing this check, set {@code readAgain} to true. 277 * <p class="note"> 278 * Note: this will NOT reset preferences back to their default 279 * values. For that functionality, use 280 * {@link PreferenceManager#getDefaultSharedPreferences(Context)} 281 * and clear it followed by a call to this method with this 282 * parameter set to true. 283 */ 284 public static void setDefaultValues(Context context, int resId, boolean readAgain) { 285 286 // Use the default shared preferences name and mode 287 setDefaultValues(context, getDefaultSharedPreferencesName(context), 288 getDefaultSharedPreferencesMode(), resId, readAgain); 289 } 290 291 /** 292 * Similar to {@link #setDefaultValues(Context, int, boolean)} but allows 293 * the client to provide the filename and mode of the shared preferences 294 * file. 295 * 296 * @param context The context of the shared preferences. 297 * @param sharedPreferencesName A custom name for the shared preferences file. 298 * @param sharedPreferencesMode The file creation mode for the shared preferences file, such 299 * as {@link android.content.Context#MODE_PRIVATE} or {@link 300 * android.content.Context#MODE_PRIVATE} 301 * @param resId The resource ID of the preference XML file. 302 * @param readAgain Whether to re-read the default values. 303 * If false, this method will set the default values only if this 304 * method has never been called in the past (or if the 305 * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared 306 * preferences file is false). To attempt to set the default values again 307 * bypassing this check, set {@code readAgain} to true. 308 * <p class="note"> 309 * Note: this will NOT reset preferences back to their default 310 * values. For that functionality, use 311 * {@link PreferenceManager#getDefaultSharedPreferences(Context)} 312 * and clear it followed by a call to this method with this 313 * parameter set to true. 314 * 315 * @see #setDefaultValues(Context, int, boolean) 316 * @see #setSharedPreferencesName(String) 317 * @see #setSharedPreferencesMode(int) 318 */ 319 public static void setDefaultValues(Context context, String sharedPreferencesName, 320 int sharedPreferencesMode, int resId, boolean readAgain) { 321 final SharedPreferences defaultValueSp = context.getSharedPreferences( 322 KEY_HAS_SET_DEFAULT_VALUES, Context.MODE_PRIVATE); 323 324 if (readAgain || !defaultValueSp.getBoolean(KEY_HAS_SET_DEFAULT_VALUES, false)) { 325 final PreferenceManager pm = new PreferenceManager(context); 326 pm.setSharedPreferencesName(sharedPreferencesName); 327 pm.setSharedPreferencesMode(sharedPreferencesMode); 328 pm.inflateFromResource(context, resId, null); 329 330 SharedPreferences.Editor editor = 331 defaultValueSp.edit().putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true); 332 333 SharedPreferencesCompat.EditorCompat.getInstance().apply(editor); 334 } 335 } 336 337 /** 338 * Returns an editor to use when modifying the shared preferences. 339 * <p> 340 * Do NOT commit unless {@link #shouldCommit()} returns true. 341 * 342 * @return An editor to use to write to shared preferences. 343 * @see #shouldCommit() 344 */ 345 SharedPreferences.Editor getEditor() { 346 347 if (mNoCommit) { 348 if (mEditor == null) { 349 mEditor = getSharedPreferences().edit(); 350 } 351 352 return mEditor; 353 } else { 354 return getSharedPreferences().edit(); 355 } 356 } 357 358 /** 359 * Whether it is the client's responsibility to commit on the 360 * {@link #getEditor()}. This will return false in cases where the writes 361 * should be batched, for example when inflating preferences from XML. 362 * 363 * @return Whether the client should commit. 364 */ 365 boolean shouldCommit() { 366 return !mNoCommit; 367 } 368 369 private void setNoCommit(boolean noCommit) { 370 if (!noCommit && mEditor != null) { 371 SharedPreferencesCompat.EditorCompat.getInstance().apply(mEditor); 372 } 373 mNoCommit = noCommit; 374 } 375 376 /** 377 * Returns the context. 378 * 379 * @return The context. 380 */ 381 Context getContext() { 382 return mContext; 383 } 384 385 public OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener() { 386 return mOnDisplayPreferenceDialogListener; 387 } 388 389 public void setOnDisplayPreferenceDialogListener( 390 OnDisplayPreferenceDialogListener onDisplayPreferenceDialogListener) { 391 mOnDisplayPreferenceDialogListener = onDisplayPreferenceDialogListener; 392 } 393 394 /** 395 * Called when a preference requests that a dialog be shown to complete a user interaction. 396 * 397 * @param preference The preference requesting the dialog. 398 */ 399 public void showDialog(Preference preference) { 400 if (mOnDisplayPreferenceDialogListener != null) { 401 mOnDisplayPreferenceDialogListener.onDisplayPreferenceDialog(preference); 402 } 403 } 404 405 /** 406 * Sets the callback to be invoked when a {@link Preference} in the 407 * hierarchy rooted at this {@link PreferenceManager} is clicked. 408 * 409 * @param listener The callback to be invoked. 410 */ 411 public void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) { 412 mOnPreferenceTreeClickListener = listener; 413 } 414 415 public OnPreferenceTreeClickListener getOnPreferenceTreeClickListener() { 416 return mOnPreferenceTreeClickListener; 417 } 418 419 /** 420 * Sets the callback to be invoked when a {@link PreferenceScreen} in the hierarchy rooted at 421 * this {@link PreferenceManager} is clicked. 422 * 423 * @param listener The callback to be invoked. 424 */ 425 public void setOnNavigateToScreenListener(OnNavigateToScreenListener listener) { 426 mOnNavigateToScreenListener = listener; 427 } 428 429 /** 430 * Returns the {@link PreferenceManager.OnNavigateToScreenListener}, if one has been set. 431 */ 432 public OnNavigateToScreenListener getOnNavigateToScreenListener() { 433 return mOnNavigateToScreenListener; 434 } 435 436 /** 437 * Interface definition for a callback to be invoked when a 438 * {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is 439 * clicked. 440 */ 441 public interface OnPreferenceTreeClickListener { 442 /** 443 * Called when a preference in the tree rooted at this 444 * {@link PreferenceScreen} has been clicked. 445 * 446 * @param preference The preference that was clicked. 447 * @return Whether the click was handled. 448 */ 449 boolean onPreferenceTreeClick(Preference preference); 450 } 451 452 /** 453 * Interface definition for a class that will be called when a 454 * {@link android.support.v7.preference.Preference} requests to display a dialog. 455 */ 456 public interface OnDisplayPreferenceDialogListener { 457 458 /** 459 * Called when a preference in the tree requests to display a dialog. 460 * 461 * @param preference The Preference object requesting the dialog. 462 */ 463 void onDisplayPreferenceDialog(Preference preference); 464 } 465 466 /** 467 * Interface definition for a class that will be called when a 468 * {@link android.support.v7.preference.PreferenceScreen} requests navigation. 469 */ 470 public interface OnNavigateToScreenListener { 471 472 /** 473 * Called when a PreferenceScreen in the tree requests to navigate to its contents. 474 * 475 * @param preferenceScreen The PreferenceScreen requesting navigation. 476 */ 477 void onNavigateToScreen(PreferenceScreen preferenceScreen); 478 } 479 480} 481