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