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