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