blob: 117e5073de4c0d192d61e1e5dfa257c31356ae79 [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
Adam Powell212db7d2010-04-08 16:24:46 -070019import com.android.internal.util.CharSequences;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.content.res.TypedArray;
25import android.os.Bundle;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.text.TextUtils;
29import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.view.AbsSavedState;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.widget.ListView;
35import android.widget.TextView;
36
Adam Powell212db7d2010-04-08 16:24:46 -070037import java.util.ArrayList;
38import java.util.List;
39import java.util.Set;
40
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041/**
42 * Represents the basic Preference UI building
43 * block displayed by a {@link PreferenceActivity} in the form of a
44 * {@link ListView}. This class provides the {@link View} to be displayed in
45 * the activity and associates with a {@link SharedPreferences} to
46 * store/retrieve the preference data.
47 * <p>
48 * When specifying a preference hierarchy in XML, each element can point to a
49 * subclass of {@link Preference}, similar to the view hierarchy and layouts.
50 * <p>
51 * This class contains a {@code key} that will be used as the key into the
52 * {@link SharedPreferences}. It is up to the subclass to decide how to store
53 * the value.
54 *
55 * @attr ref android.R.styleable#Preference_key
56 * @attr ref android.R.styleable#Preference_title
57 * @attr ref android.R.styleable#Preference_summary
58 * @attr ref android.R.styleable#Preference_order
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -070059 * @attr ref android.R.styleable#Preference_fragment
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 * @attr ref android.R.styleable#Preference_layout
61 * @attr ref android.R.styleable#Preference_widgetLayout
62 * @attr ref android.R.styleable#Preference_enabled
63 * @attr ref android.R.styleable#Preference_selectable
64 * @attr ref android.R.styleable#Preference_dependency
65 * @attr ref android.R.styleable#Preference_persistent
66 * @attr ref android.R.styleable#Preference_defaultValue
67 * @attr ref android.R.styleable#Preference_shouldDisableView
68 */
69public class Preference implements Comparable<Preference>, OnDependencyChangeListener {
70 /**
71 * Specify for {@link #setOrder(int)} if a specific order is not required.
72 */
73 public static final int DEFAULT_ORDER = Integer.MAX_VALUE;
74
75 private Context mContext;
76 private PreferenceManager mPreferenceManager;
77
78 /**
79 * Set when added to hierarchy since we need a unique ID within that
80 * hierarchy.
81 */
82 private long mId;
83
84 private OnPreferenceChangeListener mOnChangeListener;
85 private OnPreferenceClickListener mOnClickListener;
86
87 private int mOrder = DEFAULT_ORDER;
88 private CharSequence mTitle;
89 private CharSequence mSummary;
90 private String mKey;
91 private Intent mIntent;
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -070092 private String mFragment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 private boolean mEnabled = true;
94 private boolean mSelectable = true;
95 private boolean mRequiresKey;
96 private boolean mPersistent = true;
97 private String mDependencyKey;
98 private Object mDefaultValue;
Michael Chanda53eca2009-03-27 17:46:36 -070099 private boolean mDependencyMet = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
101 /**
102 * @see #setShouldDisableView(boolean)
103 */
104 private boolean mShouldDisableView = true;
105
106 private int mLayoutResId = com.android.internal.R.layout.preference;
107 private int mWidgetLayoutResId;
108 private boolean mHasSpecifiedLayout = false;
109
110 private OnPreferenceChangeInternalListener mListener;
111
112 private List<Preference> mDependents;
113
114 private boolean mBaseMethodCalled;
115
116 /**
117 * Interface definition for a callback to be invoked when the value of this
118 * {@link Preference} has been changed by the user and is
119 * about to be set and/or persisted. This gives the client a chance
120 * to prevent setting and/or persisting the value.
121 */
122 public interface OnPreferenceChangeListener {
123 /**
124 * Called when a Preference has been changed by the user. This is
125 * called before the state of the Preference is about to be updated and
126 * before the state is persisted.
127 *
128 * @param preference The changed Preference.
129 * @param newValue The new value of the Preference.
130 * @return True to update the state of the Preference with the new value.
131 */
132 boolean onPreferenceChange(Preference preference, Object newValue);
133 }
134
135 /**
136 * Interface definition for a callback to be invoked when a {@link Preference} is
137 * clicked.
138 */
139 public interface OnPreferenceClickListener {
140 /**
141 * Called when a Preference has been clicked.
142 *
143 * @param preference The Preference that was clicked.
144 * @return True if the click was handled.
145 */
146 boolean onPreferenceClick(Preference preference);
147 }
148
149 /**
150 * Interface definition for a callback to be invoked when this
151 * {@link Preference} is changed or, if this is a group, there is an
152 * addition/removal of {@link Preference}(s). This is used internally.
153 */
154 interface OnPreferenceChangeInternalListener {
155 /**
156 * Called when this Preference has changed.
157 *
158 * @param preference This preference.
159 */
160 void onPreferenceChange(Preference preference);
161
162 /**
163 * Called when this group has added/removed {@link Preference}(s).
164 *
165 * @param preference This Preference.
166 */
167 void onPreferenceHierarchyChange(Preference preference);
168 }
169
170 /**
171 * Perform inflation from XML and apply a class-specific base style. This
172 * constructor of Preference allows subclasses to use their own base
173 * style when they are inflating. For example, a {@link CheckBoxPreference}
174 * constructor calls this version of the super class constructor and
175 * supplies {@code android.R.attr.checkBoxPreferenceStyle} for <var>defStyle</var>.
176 * This allows the theme's checkbox preference style to modify all of the base
177 * preference attributes as well as the {@link CheckBoxPreference} class's
178 * attributes.
179 *
180 * @param context The Context this is associated with, through which it can
181 * access the current theme, resources, {@link SharedPreferences},
182 * etc.
183 * @param attrs The attributes of the XML tag that is inflating the preference.
184 * @param defStyle The default style to apply to this preference. If 0, no style
185 * will be applied (beyond what is included in the theme). This
186 * may either be an attribute resource, whose value will be
187 * retrieved from the current theme, or an explicit style
188 * resource.
189 * @see #Preference(Context, AttributeSet)
190 */
191 public Preference(Context context, AttributeSet attrs, int defStyle) {
192 mContext = context;
193
194 TypedArray a = context.obtainStyledAttributes(attrs,
Amith Yamasania98129b2009-10-05 15:31:47 -0700195 com.android.internal.R.styleable.Preference, defStyle, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 for (int i = a.getIndexCount(); i >= 0; i--) {
197 int attr = a.getIndex(i);
198 switch (attr) {
199 case com.android.internal.R.styleable.Preference_key:
200 mKey = a.getString(attr);
201 break;
202
203 case com.android.internal.R.styleable.Preference_title:
204 mTitle = a.getString(attr);
205 break;
206
207 case com.android.internal.R.styleable.Preference_summary:
208 mSummary = a.getString(attr);
209 break;
210
211 case com.android.internal.R.styleable.Preference_order:
212 mOrder = a.getInt(attr, mOrder);
213 break;
214
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700215 case com.android.internal.R.styleable.Preference_fragment:
216 mFragment = a.getString(attr);
217 break;
218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 case com.android.internal.R.styleable.Preference_layout:
220 mLayoutResId = a.getResourceId(attr, mLayoutResId);
221 break;
222
223 case com.android.internal.R.styleable.Preference_widgetLayout:
224 mWidgetLayoutResId = a.getResourceId(attr, mWidgetLayoutResId);
225 break;
226
227 case com.android.internal.R.styleable.Preference_enabled:
228 mEnabled = a.getBoolean(attr, true);
229 break;
230
231 case com.android.internal.R.styleable.Preference_selectable:
232 mSelectable = a.getBoolean(attr, true);
233 break;
234
235 case com.android.internal.R.styleable.Preference_persistent:
236 mPersistent = a.getBoolean(attr, mPersistent);
237 break;
238
239 case com.android.internal.R.styleable.Preference_dependency:
240 mDependencyKey = a.getString(attr);
241 break;
242
243 case com.android.internal.R.styleable.Preference_defaultValue:
244 mDefaultValue = onGetDefaultValue(a, attr);
245 break;
246
247 case com.android.internal.R.styleable.Preference_shouldDisableView:
248 mShouldDisableView = a.getBoolean(attr, mShouldDisableView);
249 break;
250 }
251 }
252 a.recycle();
Amith Yamasania98129b2009-10-05 15:31:47 -0700253
254 if (!getClass().getName().startsWith("android.preference")) {
255 // For subclasses not in this package, assume the worst and don't cache views
256 mHasSpecifiedLayout = true;
257 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 }
259
260 /**
261 * Constructor that is called when inflating a Preference from XML. This is
262 * called when a Preference is being constructed from an XML file, supplying
263 * attributes that were specified in the XML file. This version uses a
264 * default style of 0, so the only attribute values applied are those in the
265 * Context's Theme and the given AttributeSet.
266 *
267 * @param context The Context this is associated with, through which it can
268 * access the current theme, resources, {@link SharedPreferences},
269 * etc.
270 * @param attrs The attributes of the XML tag that is inflating the
271 * preference.
272 * @see #Preference(Context, AttributeSet, int)
273 */
274 public Preference(Context context, AttributeSet attrs) {
275 this(context, attrs, 0);
276 }
277
278 /**
279 * Constructor to create a Preference.
280 *
281 * @param context The Context in which to store Preference values.
282 */
283 public Preference(Context context) {
284 this(context, null);
285 }
286
287 /**
288 * Called when a Preference is being inflated and the default value
289 * attribute needs to be read. Since different Preference types have
290 * different value types, the subclass should get and return the default
291 * value which will be its value type.
292 * <p>
293 * For example, if the value type is String, the body of the method would
294 * proxy to {@link TypedArray#getString(int)}.
295 *
296 * @param a The set of attributes.
297 * @param index The index of the default value attribute.
298 * @return The default value of this preference type.
299 */
300 protected Object onGetDefaultValue(TypedArray a, int index) {
301 return null;
302 }
303
304 /**
305 * Sets an {@link Intent} to be used for
306 * {@link Context#startActivity(Intent)} when this Preference is clicked.
307 *
308 * @param intent The intent associated with this Preference.
309 */
310 public void setIntent(Intent intent) {
311 mIntent = intent;
312 }
313
314 /**
315 * Return the {@link Intent} associated with this Preference.
316 *
317 * @return The {@link Intent} last set via {@link #setIntent(Intent)} or XML.
318 */
319 public Intent getIntent() {
320 return mIntent;
321 }
322
323 /**
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700324 * Sets the class name of a fragment to be shown when this Preference is clicked.
325 *
326 * @param fragment The class name of the fragment associated with this Preference.
327 */
328 public void setFragment(String fragment) {
329 mFragment = fragment;
330 }
331
332 /**
333 * Return the fragment class name associated with this Preference.
334 *
335 * @return The fragment class name last set via {@link #setFragment} or XML.
336 */
337 public String getFragment() {
338 return mFragment;
339 }
340
341 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 * Sets the layout resource that is inflated as the {@link View} to be shown
343 * for this Preference. In most cases, the default layout is sufficient for
344 * custom Preference objects and only the widget layout needs to be changed.
345 * <p>
346 * This layout should contain a {@link ViewGroup} with ID
347 * {@link android.R.id#widget_frame} to be the parent of the specific widget
348 * for this Preference. It should similarly contain
349 * {@link android.R.id#title} and {@link android.R.id#summary}.
350 *
351 * @param layoutResId The layout resource ID to be inflated and returned as
352 * a {@link View}.
353 * @see #setWidgetLayoutResource(int)
354 */
355 public void setLayoutResource(int layoutResId) {
Amith Yamasania98129b2009-10-05 15:31:47 -0700356 if (layoutResId != mLayoutResId) {
357 // Layout changed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 mHasSpecifiedLayout = true;
359 }
Amith Yamasania98129b2009-10-05 15:31:47 -0700360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 mLayoutResId = layoutResId;
362 }
363
364 /**
365 * Gets the layout resource that will be shown as the {@link View} for this Preference.
366 *
367 * @return The layout resource ID.
368 */
369 public int getLayoutResource() {
370 return mLayoutResId;
371 }
372
373 /**
374 * Sets The layout for the controllable widget portion of this Preference. This
375 * is inflated into the main layout. For example, a {@link CheckBoxPreference}
376 * would specify a custom layout (consisting of just the CheckBox) here,
377 * instead of creating its own main layout.
378 *
379 * @param widgetLayoutResId The layout resource ID to be inflated into the
380 * main layout.
381 * @see #setLayoutResource(int)
382 */
383 public void setWidgetLayoutResource(int widgetLayoutResId) {
Amith Yamasania98129b2009-10-05 15:31:47 -0700384 if (widgetLayoutResId != mWidgetLayoutResId) {
385 // Layout changed
386 mHasSpecifiedLayout = true;
387 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 mWidgetLayoutResId = widgetLayoutResId;
389 }
390
391 /**
392 * Gets the layout resource for the controllable widget portion of this Preference.
393 *
394 * @return The layout resource ID.
395 */
396 public int getWidgetLayoutResource() {
397 return mWidgetLayoutResId;
398 }
399
400 /**
401 * Gets the View that will be shown in the {@link PreferenceActivity}.
402 *
403 * @param convertView The old View to reuse, if possible. Note: You should
404 * check that this View is non-null and of an appropriate type
405 * before using. If it is not possible to convert this View to
406 * display the correct data, this method can create a new View.
407 * @param parent The parent that this View will eventually be attached to.
408 * @return Returns the same Preference object, for chaining multiple calls
409 * into a single statement.
410 * @see #onCreateView(ViewGroup)
411 * @see #onBindView(View)
412 */
413 public View getView(View convertView, ViewGroup parent) {
414 if (convertView == null) {
Amith Yamasanibae6fc22009-09-30 14:08:07 -0700415 convertView = onCreateView(parent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 }
417 onBindView(convertView);
418 return convertView;
419 }
420
421 /**
422 * Creates the View to be shown for this Preference in the
423 * {@link PreferenceActivity}. The default behavior is to inflate the main
424 * layout of this Preference (see {@link #setLayoutResource(int)}. If
425 * changing this behavior, please specify a {@link ViewGroup} with ID
426 * {@link android.R.id#widget_frame}.
427 * <p>
428 * Make sure to call through to the superclass's implementation.
429 *
430 * @param parent The parent that this View will eventually be attached to.
431 * @return The View that displays this Preference.
432 * @see #onBindView(View)
433 */
434 protected View onCreateView(ViewGroup parent) {
435 final LayoutInflater layoutInflater =
436 (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
437
438 final View layout = layoutInflater.inflate(mLayoutResId, parent, false);
439
440 if (mWidgetLayoutResId != 0) {
441 final ViewGroup widgetFrame = (ViewGroup)layout.findViewById(com.android.internal.R.id.widget_frame);
442 layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);
443 }
444
445 return layout;
446 }
447
448 /**
449 * Binds the created View to the data for this Preference.
450 * <p>
451 * This is a good place to grab references to custom Views in the layout and
452 * set properties on them.
453 * <p>
454 * Make sure to call through to the superclass's implementation.
455 *
456 * @param view The View that shows this Preference.
457 * @see #onCreateView(ViewGroup)
458 */
459 protected void onBindView(View view) {
460 TextView textView = (TextView) view.findViewById(com.android.internal.R.id.title);
461 if (textView != null) {
462 textView.setText(getTitle());
463 }
464
465 textView = (TextView) view.findViewById(com.android.internal.R.id.summary);
466 if (textView != null) {
467 final CharSequence summary = getSummary();
468 if (!TextUtils.isEmpty(summary)) {
469 if (textView.getVisibility() != View.VISIBLE) {
470 textView.setVisibility(View.VISIBLE);
471 }
472
473 textView.setText(getSummary());
474 } else {
475 if (textView.getVisibility() != View.GONE) {
476 textView.setVisibility(View.GONE);
477 }
478 }
479 }
480
481 if (mShouldDisableView) {
482 setEnabledStateOnViews(view, isEnabled());
483 }
484 }
485
486 /**
487 * Makes sure the view (and any children) get the enabled state changed.
488 */
489 private void setEnabledStateOnViews(View v, boolean enabled) {
490 v.setEnabled(enabled);
491
492 if (v instanceof ViewGroup) {
493 final ViewGroup vg = (ViewGroup) v;
494 for (int i = vg.getChildCount() - 1; i >= 0; i--) {
495 setEnabledStateOnViews(vg.getChildAt(i), enabled);
496 }
497 }
498 }
499
500 /**
501 * Sets the order of this Preference with respect to other
502 * Preference objects on the same level. If this is not specified, the
503 * default behavior is to sort alphabetically. The
504 * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order
505 * Preference objects based on the order they appear in the XML.
506 *
507 * @param order The order for this Preference. A lower value will be shown
508 * first. Use {@link #DEFAULT_ORDER} to sort alphabetically or
509 * allow ordering from XML.
510 * @see PreferenceGroup#setOrderingAsAdded(boolean)
511 * @see #DEFAULT_ORDER
512 */
513 public void setOrder(int order) {
514 if (order != mOrder) {
515 mOrder = order;
516
517 // Reorder the list
518 notifyHierarchyChanged();
519 }
520 }
521
522 /**
523 * Gets the order of this Preference with respect to other Preference objects
524 * on the same level.
525 *
526 * @return The order of this Preference.
527 * @see #setOrder(int)
528 */
529 public int getOrder() {
530 return mOrder;
531 }
532
533 /**
534 * Sets the title for this Preference with a CharSequence.
535 * This title will be placed into the ID
536 * {@link android.R.id#title} within the View created by
537 * {@link #onCreateView(ViewGroup)}.
538 *
539 * @param title The title for this Preference.
540 */
541 public void setTitle(CharSequence title) {
542 if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
543 mTitle = title;
544 notifyChanged();
545 }
546 }
547
548 /**
549 * Sets the title for this Preference with a resource ID.
550 *
551 * @see #setTitle(CharSequence)
552 * @param titleResId The title as a resource ID.
553 */
554 public void setTitle(int titleResId) {
555 setTitle(mContext.getString(titleResId));
556 }
557
558 /**
559 * Returns the title of this Preference.
560 *
561 * @return The title.
562 * @see #setTitle(CharSequence)
563 */
564 public CharSequence getTitle() {
565 return mTitle;
566 }
567
568 /**
569 * Returns the summary of this Preference.
570 *
571 * @return The summary.
572 * @see #setSummary(CharSequence)
573 */
574 public CharSequence getSummary() {
575 return mSummary;
576 }
577
578 /**
579 * Sets the summary for this Preference with a CharSequence.
580 *
581 * @param summary The summary for the preference.
582 */
583 public void setSummary(CharSequence summary) {
584 if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) {
585 mSummary = summary;
586 notifyChanged();
587 }
588 }
589
590 /**
591 * Sets the summary for this Preference with a resource ID.
592 *
593 * @see #setSummary(CharSequence)
594 * @param summaryResId The summary as a resource.
595 */
596 public void setSummary(int summaryResId) {
597 setSummary(mContext.getString(summaryResId));
598 }
599
600 /**
601 * Sets whether this Preference is enabled. If disabled, it will
602 * not handle clicks.
603 *
604 * @param enabled Set true to enable it.
605 */
606 public void setEnabled(boolean enabled) {
607 if (mEnabled != enabled) {
608 mEnabled = enabled;
609
610 // Enabled state can change dependent preferences' states, so notify
611 notifyDependencyChange(shouldDisableDependents());
612
613 notifyChanged();
614 }
615 }
616
617 /**
618 * Checks whether this Preference should be enabled in the list.
619 *
620 * @return True if this Preference is enabled, false otherwise.
621 */
622 public boolean isEnabled() {
Michael Chanda53eca2009-03-27 17:46:36 -0700623 return mEnabled && mDependencyMet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625
626 /**
627 * Sets whether this Preference is selectable.
628 *
629 * @param selectable Set true to make it selectable.
630 */
631 public void setSelectable(boolean selectable) {
632 if (mSelectable != selectable) {
633 mSelectable = selectable;
634 notifyChanged();
635 }
636 }
637
638 /**
639 * Checks whether this Preference should be selectable in the list.
640 *
641 * @return True if it is selectable, false otherwise.
642 */
643 public boolean isSelectable() {
644 return mSelectable;
645 }
646
647 /**
648 * Sets whether this Preference should disable its view when it gets
649 * disabled.
650 * <p>
651 * For example, set this and {@link #setEnabled(boolean)} to false for
652 * preferences that are only displaying information and 1) should not be
653 * clickable 2) should not have the view set to the disabled state.
654 *
655 * @param shouldDisableView Set true if this preference should disable its view
656 * when the preference is disabled.
657 */
658 public void setShouldDisableView(boolean shouldDisableView) {
659 mShouldDisableView = shouldDisableView;
660 notifyChanged();
661 }
662
663 /**
664 * Checks whether this Preference should disable its view when it's action is disabled.
665 * @see #setShouldDisableView(boolean)
666 * @return True if it should disable the view.
667 */
668 public boolean getShouldDisableView() {
669 return mShouldDisableView;
670 }
671
672 /**
673 * Returns a unique ID for this Preference. This ID should be unique across all
674 * Preference objects in a hierarchy.
675 *
676 * @return A unique ID for this Preference.
677 */
678 long getId() {
679 return mId;
680 }
681
682 /**
683 * Processes a click on the preference. This includes saving the value to
684 * the {@link SharedPreferences}. However, the overridden method should
685 * call {@link #callChangeListener(Object)} to make sure the client wants to
686 * update the preference's state with the new value.
687 */
688 protected void onClick() {
689 }
690
691 /**
692 * Sets the key for this Preference, which is used as a key to the
693 * {@link SharedPreferences}. This should be unique for the package.
694 *
695 * @param key The key for the preference.
696 */
697 public void setKey(String key) {
698 mKey = key;
699
700 if (mRequiresKey && !hasKey()) {
701 requireKey();
702 }
703 }
704
705 /**
706 * Gets the key for this Preference, which is also the key used for storing
707 * values into SharedPreferences.
708 *
709 * @return The key.
710 */
711 public String getKey() {
712 return mKey;
713 }
714
715 /**
716 * Checks whether the key is present, and if it isn't throws an
717 * exception. This should be called by subclasses that store preferences in
718 * the {@link SharedPreferences}.
719 *
720 * @throws IllegalStateException If there is no key assigned.
721 */
722 void requireKey() {
723 if (mKey == null) {
724 throw new IllegalStateException("Preference does not have a key assigned.");
725 }
726
727 mRequiresKey = true;
728 }
729
730 /**
731 * Checks whether this Preference has a valid key.
732 *
733 * @return True if the key exists and is not a blank string, false otherwise.
734 */
735 public boolean hasKey() {
736 return !TextUtils.isEmpty(mKey);
737 }
738
739 /**
740 * Checks whether this Preference is persistent. If it is, it stores its value(s) into
741 * the persistent {@link SharedPreferences} storage.
742 *
743 * @return True if it is persistent.
744 */
745 public boolean isPersistent() {
746 return mPersistent;
747 }
748
749 /**
750 * Checks whether, at the given time this method is called,
751 * this Preference should store/restore its value(s) into the
752 * {@link SharedPreferences}. This, at minimum, checks whether this
753 * Preference is persistent and it currently has a key. Before you
754 * save/restore from the {@link SharedPreferences}, check this first.
755 *
756 * @return True if it should persist the value.
757 */
758 protected boolean shouldPersist() {
759 return mPreferenceManager != null && isPersistent() && hasKey();
760 }
761
762 /**
763 * Sets whether this Preference is persistent. When persistent,
764 * it stores its value(s) into the persistent {@link SharedPreferences}
765 * storage.
766 *
767 * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}.
768 */
769 public void setPersistent(boolean persistent) {
770 mPersistent = persistent;
771 }
772
773 /**
774 * Call this method after the user changes the preference, but before the
775 * internal state is set. This allows the client to ignore the user value.
776 *
777 * @param newValue The new value of this Preference.
778 * @return True if the user value should be set as the preference
779 * value (and persisted).
780 */
781 protected boolean callChangeListener(Object newValue) {
782 return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue);
783 }
784
785 /**
786 * Sets the callback to be invoked when this Preference is changed by the
787 * user (but before the internal state has been updated).
788 *
789 * @param onPreferenceChangeListener The callback to be invoked.
790 */
791 public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) {
792 mOnChangeListener = onPreferenceChangeListener;
793 }
794
795 /**
796 * Returns the callback to be invoked when this Preference is changed by the
797 * user (but before the internal state has been updated).
798 *
799 * @return The callback to be invoked.
800 */
801 public OnPreferenceChangeListener getOnPreferenceChangeListener() {
802 return mOnChangeListener;
803 }
804
805 /**
806 * Sets the callback to be invoked when this Preference is clicked.
807 *
808 * @param onPreferenceClickListener The callback to be invoked.
809 */
810 public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
811 mOnClickListener = onPreferenceClickListener;
812 }
813
814 /**
815 * Returns the callback to be invoked when this Preference is clicked.
816 *
817 * @return The callback to be invoked.
818 */
819 public OnPreferenceClickListener getOnPreferenceClickListener() {
820 return mOnClickListener;
821 }
822
823 /**
824 * Called when a click should be performed.
825 *
826 * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
827 * listener should be called in the proper order (between other
828 * processing). May be null.
829 */
830 void performClick(PreferenceScreen preferenceScreen) {
831
832 if (!isEnabled()) {
833 return;
834 }
835
836 onClick();
837
838 if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
839 return;
840 }
841
842 PreferenceManager preferenceManager = getPreferenceManager();
843 if (preferenceManager != null) {
844 PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
845 .getOnPreferenceTreeClickListener();
846 if (preferenceScreen != null && listener != null
847 && listener.onPreferenceTreeClick(preferenceScreen, this)) {
848 return;
849 }
850 }
851
852 if (mIntent != null) {
853 Context context = getContext();
854 context.startActivity(mIntent);
855 }
856 }
857
858 /**
859 * Returns the {@link android.content.Context} of this Preference.
860 * Each Preference in a Preference hierarchy can be
861 * from different Context (for example, if multiple activities provide preferences into a single
862 * {@link PreferenceActivity}). This Context will be used to save the Preference values.
863 *
864 * @return The Context of this Preference.
865 */
866 public Context getContext() {
867 return mContext;
868 }
869
870 /**
871 * Returns the {@link SharedPreferences} where this Preference can read its
872 * value(s). Usually, it's easier to use one of the helper read methods:
873 * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
874 * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
875 * {@link #getPersistedString(String)}. To save values, see
876 * {@link #getEditor()}.
877 * <p>
878 * In some cases, writes to the {@link #getEditor()} will not be committed
879 * right away and hence not show up in the returned
880 * {@link SharedPreferences}, this is intended behavior to improve
881 * performance.
882 *
883 * @return The {@link SharedPreferences} where this Preference reads its
884 * value(s), or null if it isn't attached to a Preference hierarchy.
885 * @see #getEditor()
886 */
887 public SharedPreferences getSharedPreferences() {
888 if (mPreferenceManager == null) {
889 return null;
890 }
891
892 return mPreferenceManager.getSharedPreferences();
893 }
894
895 /**
896 * Returns an {@link SharedPreferences.Editor} where this Preference can
897 * save its value(s). Usually it's easier to use one of the helper save
898 * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)},
899 * {@link #persistInt(int)}, {@link #persistLong(long)},
900 * {@link #persistString(String)}. To read values, see
901 * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns
902 * true, it is this Preference's responsibility to commit.
903 * <p>
904 * In some cases, writes to this will not be committed right away and hence
905 * not show up in the SharedPreferences, this is intended behavior to
906 * improve performance.
907 *
908 * @return A {@link SharedPreferences.Editor} where this preference saves
909 * its value(s), or null if it isn't attached to a Preference
910 * hierarchy.
911 * @see #shouldCommit()
912 * @see #getSharedPreferences()
913 */
914 public SharedPreferences.Editor getEditor() {
915 if (mPreferenceManager == null) {
916 return null;
917 }
918
919 return mPreferenceManager.getEditor();
920 }
921
922 /**
923 * Returns whether the {@link Preference} should commit its saved value(s) in
924 * {@link #getEditor()}. This may return false in situations where batch
925 * committing is being done (by the manager) to improve performance.
926 *
927 * @return Whether the Preference should commit its saved value(s).
928 * @see #getEditor()
929 */
930 public boolean shouldCommit() {
931 if (mPreferenceManager == null) {
932 return false;
933 }
934
935 return mPreferenceManager.shouldCommit();
936 }
937
938 /**
939 * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
940 *
941 * @param another The Preference to compare to this one.
942 * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
943 * greater than 0 if this Preference sorts after <var>another</var>.
944 */
945 public int compareTo(Preference another) {
946 if (mOrder != DEFAULT_ORDER
947 || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) {
948 // Do order comparison
949 return mOrder - another.mOrder;
950 } else if (mTitle == null) {
951 return 1;
952 } else if (another.mTitle == null) {
953 return -1;
954 } else {
955 // Do name comparison
956 return CharSequences.compareToIgnoreCase(mTitle, another.mTitle);
957 }
958 }
959
960 /**
961 * Sets the internal change listener.
962 *
963 * @param listener The listener.
964 * @see #notifyChanged()
965 */
966 final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) {
967 mListener = listener;
968 }
969
970 /**
971 * Should be called when the data of this {@link Preference} has changed.
972 */
973 protected void notifyChanged() {
974 if (mListener != null) {
975 mListener.onPreferenceChange(this);
976 }
977 }
978
979 /**
980 * Should be called when a Preference has been
981 * added/removed from this group, or the ordering should be
982 * re-evaluated.
983 */
984 protected void notifyHierarchyChanged() {
985 if (mListener != null) {
986 mListener.onPreferenceHierarchyChange(this);
987 }
988 }
989
990 /**
991 * Gets the {@link PreferenceManager} that manages this Preference object's tree.
992 *
993 * @return The {@link PreferenceManager}.
994 */
995 public PreferenceManager getPreferenceManager() {
996 return mPreferenceManager;
997 }
998
999 /**
1000 * Called when this Preference has been attached to a Preference hierarchy.
1001 * Make sure to call the super implementation.
1002 *
1003 * @param preferenceManager The PreferenceManager of the hierarchy.
1004 */
1005 protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
1006 mPreferenceManager = preferenceManager;
1007
1008 mId = preferenceManager.getNextId();
1009
1010 dispatchSetInitialValue();
1011 }
1012
1013 /**
1014 * Called when the Preference hierarchy has been attached to the
1015 * {@link PreferenceActivity}. This can also be called when this
1016 * Preference has been attached to a group that was already attached
1017 * to the {@link PreferenceActivity}.
1018 */
1019 protected void onAttachedToActivity() {
1020 // At this point, the hierarchy that this preference is in is connected
1021 // with all other preferences.
1022 registerDependency();
1023 }
1024
1025 private void registerDependency() {
1026
1027 if (TextUtils.isEmpty(mDependencyKey)) return;
1028
1029 Preference preference = findPreferenceInHierarchy(mDependencyKey);
1030 if (preference != null) {
1031 preference.registerDependent(this);
1032 } else {
1033 throw new IllegalStateException("Dependency \"" + mDependencyKey
1034 + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\"");
1035 }
1036 }
1037
1038 private void unregisterDependency() {
1039 if (mDependencyKey != null) {
1040 final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey);
1041 if (oldDependency != null) {
1042 oldDependency.unregisterDependent(this);
1043 }
1044 }
1045 }
1046
1047 /**
1048 * Finds a Preference in this hierarchy (the whole thing,
1049 * even above/below your {@link PreferenceScreen} screen break) with the given
1050 * key.
1051 * <p>
1052 * This only functions after we have been attached to a hierarchy.
1053 *
1054 * @param key The key of the Preference to find.
1055 * @return The Preference that uses the given key.
1056 */
1057 protected Preference findPreferenceInHierarchy(String key) {
1058 if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
1059 return null;
1060 }
1061
1062 return mPreferenceManager.findPreference(key);
1063 }
1064
1065 /**
1066 * Adds a dependent Preference on this Preference so we can notify it.
1067 * Usually, the dependent Preference registers itself (it's good for it to
1068 * know it depends on something), so please use
1069 * {@link Preference#setDependency(String)} on the dependent Preference.
1070 *
1071 * @param dependent The dependent Preference that will be enabled/disabled
1072 * according to the state of this Preference.
1073 */
1074 private void registerDependent(Preference dependent) {
1075 if (mDependents == null) {
1076 mDependents = new ArrayList<Preference>();
1077 }
1078
1079 mDependents.add(dependent);
1080
1081 dependent.onDependencyChanged(this, shouldDisableDependents());
1082 }
1083
1084 /**
1085 * Removes a dependent Preference on this Preference.
1086 *
1087 * @param dependent The dependent Preference that will be enabled/disabled
1088 * according to the state of this Preference.
1089 * @return Returns the same Preference object, for chaining multiple calls
1090 * into a single statement.
1091 */
1092 private void unregisterDependent(Preference dependent) {
1093 if (mDependents != null) {
1094 mDependents.remove(dependent);
1095 }
1096 }
1097
1098 /**
1099 * Notifies any listening dependents of a change that affects the
1100 * dependency.
1101 *
1102 * @param disableDependents Whether this Preference should disable
1103 * its dependents.
1104 */
1105 public void notifyDependencyChange(boolean disableDependents) {
1106 final List<Preference> dependents = mDependents;
1107
1108 if (dependents == null) {
1109 return;
1110 }
1111
1112 final int dependentsCount = dependents.size();
1113 for (int i = 0; i < dependentsCount; i++) {
1114 dependents.get(i).onDependencyChanged(this, disableDependents);
1115 }
1116 }
1117
1118 /**
1119 * Called when the dependency changes.
1120 *
1121 * @param dependency The Preference that this Preference depends on.
1122 * @param disableDependent Set true to disable this Preference.
1123 */
1124 public void onDependencyChanged(Preference dependency, boolean disableDependent) {
Michael Chanda53eca2009-03-27 17:46:36 -07001125 if (mDependencyMet == disableDependent) {
1126 mDependencyMet = !disableDependent;
1127
1128 // Enabled state can change dependent preferences' states, so notify
1129 notifyDependencyChange(shouldDisableDependents());
1130
1131 notifyChanged();
1132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 }
1134
1135 /**
1136 * Checks whether this preference's dependents should currently be
1137 * disabled.
1138 *
1139 * @return True if the dependents should be disabled, otherwise false.
1140 */
1141 public boolean shouldDisableDependents() {
1142 return !isEnabled();
1143 }
1144
1145 /**
1146 * Sets the key of a Preference that this Preference will depend on. If that
1147 * Preference is not set or is off, this Preference will be disabled.
1148 *
1149 * @param dependencyKey The key of the Preference that this depends on.
1150 */
1151 public void setDependency(String dependencyKey) {
1152 // Unregister the old dependency, if we had one
1153 unregisterDependency();
1154
1155 // Register the new
1156 mDependencyKey = dependencyKey;
1157 registerDependency();
1158 }
1159
1160 /**
1161 * Returns the key of the dependency on this Preference.
1162 *
1163 * @return The key of the dependency.
1164 * @see #setDependency(String)
1165 */
1166 public String getDependency() {
1167 return mDependencyKey;
1168 }
1169
1170 /**
1171 * Called when this Preference is being removed from the hierarchy. You
1172 * should remove any references to this Preference that you know about. Make
1173 * sure to call through to the superclass implementation.
1174 */
1175 protected void onPrepareForRemoval() {
1176 unregisterDependency();
1177 }
1178
1179 /**
1180 * Sets the default value for this Preference, which will be set either if
1181 * persistence is off or persistence is on and the preference is not found
1182 * in the persistent storage.
1183 *
1184 * @param defaultValue The default value.
1185 */
1186 public void setDefaultValue(Object defaultValue) {
1187 mDefaultValue = defaultValue;
1188 }
1189
1190 private void dispatchSetInitialValue() {
1191 // By now, we know if we are persistent.
1192 final boolean shouldPersist = shouldPersist();
1193 if (!shouldPersist || !getSharedPreferences().contains(mKey)) {
1194 if (mDefaultValue != null) {
1195 onSetInitialValue(false, mDefaultValue);
1196 }
1197 } else {
1198 onSetInitialValue(true, null);
1199 }
1200 }
1201
1202 /**
1203 * Implement this to set the initial value of the Preference.
1204 * <p>
1205 * If <var>restorePersistedValue</var> is true, you should restore the
1206 * Preference value from the {@link android.content.SharedPreferences}. If
1207 * <var>restorePersistedValue</var> is false, you should set the Preference
1208 * value to defaultValue that is given (and possibly store to SharedPreferences
1209 * if {@link #shouldPersist()} is true).
1210 * <p>
1211 * This may not always be called. One example is if it should not persist
1212 * but there is no default value given.
1213 *
1214 * @param restorePersistedValue True to restore the persisted value;
1215 * false to use the given <var>defaultValue</var>.
1216 * @param defaultValue The default value for this Preference. Only use this
1217 * if <var>restorePersistedValue</var> is false.
1218 */
1219 protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
1220 }
1221
1222 private void tryCommit(SharedPreferences.Editor editor) {
1223 if (mPreferenceManager.shouldCommit()) {
1224 editor.commit();
1225 }
1226 }
1227
1228 /**
1229 * Attempts to persist a String to the {@link android.content.SharedPreferences}.
1230 * <p>
1231 * This will check if this Preference is persistent, get an editor from
1232 * the {@link PreferenceManager}, put in the string, and check if we should commit (and
1233 * commit if so).
1234 *
1235 * @param value The value to persist.
1236 * @return True if the Preference is persistent. (This is not whether the
1237 * value was persisted, since we may not necessarily commit if there
1238 * will be a batch commit later.)
1239 * @see #getPersistedString(String)
1240 */
1241 protected boolean persistString(String value) {
1242 if (shouldPersist()) {
1243 // Shouldn't store null
1244 if (value == getPersistedString(null)) {
1245 // It's already there, so the same as persisting
1246 return true;
1247 }
1248
1249 SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1250 editor.putString(mKey, value);
1251 tryCommit(editor);
1252 return true;
1253 }
1254 return false;
1255 }
1256
1257 /**
1258 * Attempts to get a persisted String from the {@link android.content.SharedPreferences}.
1259 * <p>
1260 * This will check if this Preference is persistent, get the SharedPreferences
1261 * from the {@link PreferenceManager}, and get the value.
1262 *
1263 * @param defaultReturnValue The default value to return if either the
1264 * Preference is not persistent or the Preference is not in the
1265 * shared preferences.
1266 * @return The value from the SharedPreferences or the default return
1267 * value.
1268 * @see #persistString(String)
1269 */
1270 protected String getPersistedString(String defaultReturnValue) {
1271 if (!shouldPersist()) {
1272 return defaultReturnValue;
1273 }
1274
1275 return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
1276 }
1277
1278 /**
Adam Powell212db7d2010-04-08 16:24:46 -07001279 * Attempts to persist a set of Strings to the {@link android.content.SharedPreferences}.
1280 * <p>
1281 * This will check if this Preference is persistent, get an editor from
1282 * the {@link PreferenceManager}, put in the strings, and check if we should commit (and
1283 * commit if so).
1284 *
1285 * @param values The values to persist.
1286 * @return True if the Preference is persistent. (This is not whether the
1287 * value was persisted, since we may not necessarily commit if there
1288 * will be a batch commit later.)
1289 * @see #getPersistedString(Set)
1290 *
1291 * @hide Pending API approval
1292 */
1293 protected boolean persistStringSet(Set<String> values) {
1294 if (shouldPersist()) {
1295 // Shouldn't store null
1296 if (values.equals(getPersistedStringSet(null))) {
1297 // It's already there, so the same as persisting
1298 return true;
1299 }
1300
1301 SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1302 editor.putStringSet(mKey, values);
1303 tryCommit(editor);
1304 return true;
1305 }
1306 return false;
1307 }
1308
1309 /**
1310 * Attempts to get a persisted set of Strings from the
1311 * {@link android.content.SharedPreferences}.
1312 * <p>
1313 * This will check if this Preference is persistent, get the SharedPreferences
1314 * from the {@link PreferenceManager}, and get the value.
1315 *
1316 * @param defaultReturnValue The default value to return if either the
1317 * Preference is not persistent or the Preference is not in the
1318 * shared preferences.
1319 * @return The value from the SharedPreferences or the default return
1320 * value.
1321 * @see #persistStringSet(Set)
1322 *
1323 * @hide Pending API approval
1324 */
1325 protected Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
1326 if (!shouldPersist()) {
1327 return defaultReturnValue;
1328 }
1329
1330 return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
1331 }
1332
1333 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 * Attempts to persist an int to the {@link android.content.SharedPreferences}.
1335 *
1336 * @param value The value to persist.
1337 * @return True if the Preference is persistent. (This is not whether the
1338 * value was persisted, since we may not necessarily commit if there
1339 * will be a batch commit later.)
1340 * @see #persistString(String)
1341 * @see #getPersistedInt(int)
1342 */
1343 protected boolean persistInt(int value) {
1344 if (shouldPersist()) {
1345 if (value == getPersistedInt(~value)) {
1346 // It's already there, so the same as persisting
1347 return true;
1348 }
1349
1350 SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1351 editor.putInt(mKey, value);
1352 tryCommit(editor);
1353 return true;
1354 }
1355 return false;
1356 }
1357
1358 /**
1359 * Attempts to get a persisted int from the {@link android.content.SharedPreferences}.
1360 *
1361 * @param defaultReturnValue The default value to return if either this
1362 * Preference is not persistent or this Preference is not in the
1363 * SharedPreferences.
1364 * @return The value from the SharedPreferences or the default return
1365 * value.
1366 * @see #getPersistedString(String)
1367 * @see #persistInt(int)
1368 */
1369 protected int getPersistedInt(int defaultReturnValue) {
1370 if (!shouldPersist()) {
1371 return defaultReturnValue;
1372 }
1373
1374 return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
1375 }
1376
1377 /**
1378 * Attempts to persist a float to the {@link android.content.SharedPreferences}.
1379 *
1380 * @param value The value to persist.
1381 * @return True if this Preference is persistent. (This is not whether the
1382 * value was persisted, since we may not necessarily commit if there
1383 * will be a batch commit later.)
1384 * @see #persistString(String)
1385 * @see #getPersistedFloat(float)
1386 */
1387 protected boolean persistFloat(float value) {
1388 if (shouldPersist()) {
1389 if (value == getPersistedFloat(Float.NaN)) {
1390 // It's already there, so the same as persisting
1391 return true;
1392 }
1393
1394 SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1395 editor.putFloat(mKey, value);
1396 tryCommit(editor);
1397 return true;
1398 }
1399 return false;
1400 }
1401
1402 /**
1403 * Attempts to get a persisted float from the {@link android.content.SharedPreferences}.
1404 *
1405 * @param defaultReturnValue The default value to return if either this
1406 * Preference is not persistent or this Preference is not in the
1407 * SharedPreferences.
1408 * @return The value from the SharedPreferences or the default return
1409 * value.
1410 * @see #getPersistedString(String)
1411 * @see #persistFloat(float)
1412 */
1413 protected float getPersistedFloat(float defaultReturnValue) {
1414 if (!shouldPersist()) {
1415 return defaultReturnValue;
1416 }
1417
1418 return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
1419 }
1420
1421 /**
1422 * Attempts to persist a long to the {@link android.content.SharedPreferences}.
1423 *
1424 * @param value The value to persist.
1425 * @return True if this Preference is persistent. (This is not whether the
1426 * value was persisted, since we may not necessarily commit if there
1427 * will be a batch commit later.)
1428 * @see #persistString(String)
1429 * @see #getPersistedLong(long)
1430 */
1431 protected boolean persistLong(long value) {
1432 if (shouldPersist()) {
1433 if (value == getPersistedLong(~value)) {
1434 // It's already there, so the same as persisting
1435 return true;
1436 }
1437
1438 SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1439 editor.putLong(mKey, value);
1440 tryCommit(editor);
1441 return true;
1442 }
1443 return false;
1444 }
1445
1446 /**
1447 * Attempts to get a persisted long from the {@link android.content.SharedPreferences}.
1448 *
1449 * @param defaultReturnValue The default value to return if either this
1450 * Preference is not persistent or this Preference is not in the
1451 * SharedPreferences.
1452 * @return The value from the SharedPreferences or the default return
1453 * value.
1454 * @see #getPersistedString(String)
1455 * @see #persistLong(long)
1456 */
1457 protected long getPersistedLong(long defaultReturnValue) {
1458 if (!shouldPersist()) {
1459 return defaultReturnValue;
1460 }
1461
1462 return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
1463 }
1464
1465 /**
1466 * Attempts to persist a boolean to the {@link android.content.SharedPreferences}.
1467 *
1468 * @param value The value to persist.
1469 * @return True if this Preference is persistent. (This is not whether the
1470 * value was persisted, since we may not necessarily commit if there
1471 * will be a batch commit later.)
1472 * @see #persistString(String)
1473 * @see #getPersistedBoolean(boolean)
1474 */
1475 protected boolean persistBoolean(boolean value) {
1476 if (shouldPersist()) {
1477 if (value == getPersistedBoolean(!value)) {
1478 // It's already there, so the same as persisting
1479 return true;
1480 }
1481
1482 SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1483 editor.putBoolean(mKey, value);
1484 tryCommit(editor);
1485 return true;
1486 }
1487 return false;
1488 }
1489
1490 /**
1491 * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}.
1492 *
1493 * @param defaultReturnValue The default value to return if either this
1494 * Preference is not persistent or this Preference is not in the
1495 * SharedPreferences.
1496 * @return The value from the SharedPreferences or the default return
1497 * value.
1498 * @see #getPersistedString(String)
1499 * @see #persistBoolean(boolean)
1500 */
1501 protected boolean getPersistedBoolean(boolean defaultReturnValue) {
1502 if (!shouldPersist()) {
1503 return defaultReturnValue;
1504 }
1505
1506 return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
1507 }
1508
1509 boolean hasSpecifiedLayout() {
1510 return mHasSpecifiedLayout;
1511 }
1512
1513 @Override
1514 public String toString() {
1515 return getFilterableStringBuilder().toString();
1516 }
1517
1518 /**
1519 * Returns the text that will be used to filter this Preference depending on
1520 * user input.
1521 * <p>
1522 * If overridding and calling through to the superclass, make sure to prepend
1523 * your additions with a space.
1524 *
1525 * @return Text as a {@link StringBuilder} that will be used to filter this
1526 * preference. By default, this is the title and summary
1527 * (concatenated with a space).
1528 */
1529 StringBuilder getFilterableStringBuilder() {
1530 StringBuilder sb = new StringBuilder();
1531 CharSequence title = getTitle();
1532 if (!TextUtils.isEmpty(title)) {
1533 sb.append(title).append(' ');
1534 }
1535 CharSequence summary = getSummary();
1536 if (!TextUtils.isEmpty(summary)) {
1537 sb.append(summary).append(' ');
1538 }
Tammo Spalink0bb99602009-09-08 18:30:33 +08001539 if (sb.length() > 0) {
1540 // Drop the last space
1541 sb.setLength(sb.length() - 1);
1542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 return sb;
1544 }
1545
1546 /**
1547 * Store this Preference hierarchy's frozen state into the given container.
1548 *
1549 * @param container The Bundle in which to save the instance of this Preference.
1550 *
1551 * @see #restoreHierarchyState
1552 * @see #onSaveInstanceState
1553 */
1554 public void saveHierarchyState(Bundle container) {
1555 dispatchSaveInstanceState(container);
1556 }
1557
1558 /**
1559 * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children.
1560 * May be overridden to modify how the save happens for children. For example, some
1561 * Preference objects may want to not store an instance for their children.
1562 *
1563 * @param container The Bundle in which to save the instance of this Preference.
1564 *
1565 * @see #saveHierarchyState
1566 * @see #onSaveInstanceState
1567 */
1568 void dispatchSaveInstanceState(Bundle container) {
1569 if (hasKey()) {
1570 mBaseMethodCalled = false;
1571 Parcelable state = onSaveInstanceState();
1572 if (!mBaseMethodCalled) {
1573 throw new IllegalStateException(
1574 "Derived class did not call super.onSaveInstanceState()");
1575 }
1576 if (state != null) {
1577 container.putParcelable(mKey, state);
1578 }
1579 }
1580 }
1581
1582 /**
1583 * Hook allowing a Preference to generate a representation of its internal
1584 * state that can later be used to create a new instance with that same
1585 * state. This state should only contain information that is not persistent
1586 * or can be reconstructed later.
1587 *
1588 * @return A Parcelable object containing the current dynamic state of
1589 * this Preference, or null if there is nothing interesting to save.
1590 * The default implementation returns null.
1591 * @see #onRestoreInstanceState
1592 * @see #saveHierarchyState
1593 */
1594 protected Parcelable onSaveInstanceState() {
1595 mBaseMethodCalled = true;
1596 return BaseSavedState.EMPTY_STATE;
1597 }
1598
1599 /**
1600 * Restore this Preference hierarchy's previously saved state from the given container.
1601 *
1602 * @param container The Bundle that holds the previously saved state.
1603 *
1604 * @see #saveHierarchyState
1605 * @see #onRestoreInstanceState
1606 */
1607 public void restoreHierarchyState(Bundle container) {
1608 dispatchRestoreInstanceState(container);
1609 }
1610
1611 /**
1612 * Called by {@link #restoreHierarchyState} to retrieve the saved state for this
1613 * Preference and its children. May be overridden to modify how restoring
1614 * happens to the children of a Preference. For example, some Preference objects may
1615 * not want to save state for their children.
1616 *
1617 * @param container The Bundle that holds the previously saved state.
1618 * @see #restoreHierarchyState
1619 * @see #onRestoreInstanceState
1620 */
1621 void dispatchRestoreInstanceState(Bundle container) {
1622 if (hasKey()) {
1623 Parcelable state = container.getParcelable(mKey);
1624 if (state != null) {
1625 mBaseMethodCalled = false;
1626 onRestoreInstanceState(state);
1627 if (!mBaseMethodCalled) {
1628 throw new IllegalStateException(
1629 "Derived class did not call super.onRestoreInstanceState()");
1630 }
1631 }
1632 }
1633 }
1634
1635 /**
1636 * Hook allowing a Preference to re-apply a representation of its internal
1637 * state that had previously been generated by {@link #onSaveInstanceState}.
1638 * This function will never be called with a null state.
1639 *
1640 * @param state The saved state that had previously been returned by
1641 * {@link #onSaveInstanceState}.
1642 * @see #onSaveInstanceState
1643 * @see #restoreHierarchyState
1644 */
1645 protected void onRestoreInstanceState(Parcelable state) {
1646 mBaseMethodCalled = true;
1647 if (state != BaseSavedState.EMPTY_STATE && state != null) {
1648 throw new IllegalArgumentException("Wrong state class -- expecting Preference State");
1649 }
1650 }
1651
1652 /**
1653 * A base class for managing the instance state of a {@link Preference}.
1654 */
1655 public static class BaseSavedState extends AbsSavedState {
1656 public BaseSavedState(Parcel source) {
1657 super(source);
1658 }
1659
1660 public BaseSavedState(Parcelable superState) {
1661 super(superState);
1662 }
1663
1664 public static final Parcelable.Creator<BaseSavedState> CREATOR =
1665 new Parcelable.Creator<BaseSavedState>() {
1666 public BaseSavedState createFromParcel(Parcel in) {
1667 return new BaseSavedState(in);
1668 }
1669
1670 public BaseSavedState[] newArray(int size) {
1671 return new BaseSavedState[size];
1672 }
1673 };
1674 }
1675
1676}