blob: ec97efb0df28844f707bcf0ee59daa45d103d089 [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
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070019import android.app.Fragment;
Dianne Hackbornc6669ca2010-09-16 01:33:24 -070020import android.app.FragmentBreadCrumbs;
Dianne Hackborn3a57fb92010-11-15 17:58:52 -080021import android.app.FragmentManager;
Andrew Stadleraa904f42010-09-02 14:50:08 -070022import android.app.FragmentTransaction;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.app.ListActivity;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070024import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Intent;
Dianne Hackborn50ed8292010-12-03 12:30:21 -080026import android.content.res.Resources;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070027import android.content.res.TypedArray;
28import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.Bundle;
30import android.os.Handler;
31import android.os.Message;
Dianne Hackborna21e3da2010-09-12 19:27:46 -070032import android.os.Parcel;
33import android.os.Parcelable;
Freeman Ng19ea2e02010-03-25 15:09:00 -070034import android.text.TextUtils;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070035import android.util.AttributeSet;
Dianne Hackborn50ed8292010-12-03 12:30:21 -080036import android.util.TypedValue;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070037import android.util.Xml;
38import android.view.LayoutInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.view.View;
Amith Yamasani405c1af2011-05-26 13:08:25 -070040import android.view.View.OnClickListener;
Gilles Debunne39725ac2011-06-14 18:52:41 -070041import android.view.ViewGroup;
Dianne Hackborna21e3da2010-09-12 19:27:46 -070042import android.widget.AbsListView;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070043import android.widget.ArrayAdapter;
Gilles Debunne39725ac2011-06-14 18:52:41 -070044import android.widget.BaseAdapter;
Freeman Ng19ea2e02010-03-25 15:09:00 -070045import android.widget.Button;
Dianne Hackborn5c769a42010-08-26 17:08:08 -070046import android.widget.FrameLayout;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070047import android.widget.ImageView;
48import android.widget.ListView;
49import android.widget.TextView;
50
Gilles Debunne39725ac2011-06-14 18:52:41 -070051import com.android.internal.util.XmlUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
Amith Yamasani405c1af2011-05-26 13:08:25 -070053import org.xmlpull.v1.XmlPullParser;
54import org.xmlpull.v1.XmlPullParserException;
55
Gilles Debunne39725ac2011-06-14 18:52:41 -070056import java.io.IOException;
57import java.util.ArrayList;
58import java.util.List;
59
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060/**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070061 * This is the base class for an activity to show a hierarchy of preferences
62 * to the user. Prior to {@link android.os.Build.VERSION_CODES#HONEYCOMB}
63 * this class only allowed the display of a single set of preference; this
64 * functionality should now be found in the new {@link PreferenceFragment}
65 * class. If you are using PreferenceActivity in its old mode, the documentation
66 * there applies to the deprecated APIs here.
Freeman Ng19ea2e02010-03-25 15:09:00 -070067 *
Gilles Debunne39725ac2011-06-14 18:52:41 -070068 * <p>This activity shows one or more headers of preferences, each of which
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070069 * is associated with a {@link PreferenceFragment} to display the preferences
70 * of that header. The actual layout and display of these associations can
71 * however vary; currently there are two major approaches it may take:
72 *
73 * <ul>
74 * <li>On a small screen it may display only the headers as a single list
75 * when first launched. Selecting one of the header items will re-launch
76 * the activity with it only showing the PreferenceFragment of that header.
77 * <li>On a large screen in may display both the headers and current
78 * PreferenceFragment together as panes. Selecting a header item switches
79 * to showing the correct PreferenceFragment for that item.
80 * </ul>
81 *
82 * <p>Subclasses of PreferenceActivity should implement
83 * {@link #onBuildHeaders} to populate the header list with the desired
84 * items. Doing this implicitly switches the class into its new "headers
85 * + fragments" mode rather than the old style of just showing a single
86 * preferences list.
Scott Maincdd0c592012-07-26 17:03:51 -070087 *
88 * <div class="special reference">
89 * <h3>Developer Guides</h3>
90 * <p>For information about using {@code PreferenceActivity},
91 * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
92 * guide.</p>
93 * </div>
Dianne Hackbornb1ad5972010-08-02 17:30:33 -070094 *
95 * <a name="SampleCode"></a>
96 * <h3>Sample Code</h3>
97 *
98 * <p>The following sample code shows a simple preference activity that
99 * has two different sets of preferences. The implementation, consisting
100 * of the activity itself as well as its two preference fragments is:</p>
101 *
102 * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/PreferenceWithHeaders.java
103 * activity}
104 *
105 * <p>The preference_headers resource describes the headers to be displayed
106 * and the fragments associated with them. It is:
107 *
108 * {@sample development/samples/ApiDemos/res/xml/preference_headers.xml headers}
109 *
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700110 * <p>The first header is shown by Prefs1Fragment, which populates itself
111 * from the following XML resource:</p>
112 *
113 * {@sample development/samples/ApiDemos/res/xml/fragmented_preferences.xml preferences}
114 *
115 * <p>Note that this XML resource contains a preference screen holding another
116 * fragment, the Prefs1FragmentInner implemented here. This allows the user
117 * to traverse down a hierarchy of preferences; pressing back will pop each
118 * fragment off the stack to return to the previous preferences.
119 *
120 * <p>See {@link PreferenceFragment} for information on implementing the
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700121 * fragments themselves.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 */
123public abstract class PreferenceActivity extends ListActivity implements
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700124 PreferenceManager.OnPreferenceTreeClickListener,
125 PreferenceFragment.OnPreferenceStartFragmentCallback {
Freeman Ng19ea2e02010-03-25 15:09:00 -0700126
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700127 // Constants for state save/restore
128 private static final String HEADERS_TAG = ":android:headers";
129 private static final String CUR_HEADER_TAG = ":android:cur_header";
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700130 private static final String PREFERENCES_TAG = ":android:preferences";
Freeman Ng19ea2e02010-03-25 15:09:00 -0700131
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700132 /**
133 * When starting this activity, the invoking Intent can contain this extra
134 * string to specify which fragment should be initially displayed.
135 */
136 public static final String EXTRA_SHOW_FRAGMENT = ":android:show_fragment";
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700137
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700138 /**
139 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
Dianne Hackborne72f2372011-03-16 10:43:18 -0700140 * this extra can also be specified to supply a Bundle of arguments to pass
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700141 * to that fragment when it is instantiated during the initial creation
142 * of PreferenceActivity.
143 */
144 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":android:show_fragment_args";
145
146 /**
Dianne Hackborne72f2372011-03-16 10:43:18 -0700147 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
148 * this extra can also be specify to supply the title to be shown for
149 * that fragment.
150 */
151 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":android:show_fragment_title";
152
153 /**
154 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
155 * this extra can also be specify to supply the short title to be shown for
156 * that fragment.
157 */
158 public static final String EXTRA_SHOW_FRAGMENT_SHORT_TITLE
159 = ":android:show_fragment_short_title";
160
161 /**
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700162 * When starting this activity, the invoking Intent can contain this extra
163 * boolean that the header list should not be displayed. This is most often
164 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
165 * the activity to display a specific fragment that the user has navigated
166 * to.
167 */
168 public static final String EXTRA_NO_HEADERS = ":android:no_headers";
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700169
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700170 private static final String BACK_STACK_PREFS = ":android:prefs";
171
Freeman Ng19ea2e02010-03-25 15:09:00 -0700172 // extras that allow any preference activity to be launched as part of a wizard
173
174 // show Back and Next buttons? takes boolean parameter
175 // Back will then return RESULT_CANCELED and Next RESULT_OK
176 private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
177
Freeman Ng09dbf182010-08-11 15:45:01 -0700178 // add a Skip button?
179 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
180
Freeman Ng19ea2e02010-03-25 15:09:00 -0700181 // specify custom text for the Back or Next buttons, or cause a button to not appear
182 // at all by setting it to null
183 private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
184 private static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
185
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700186 // --- State for new mode when showing a list of headers + prefs fragment
187
188 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
189
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700190 private FrameLayout mListFooter;
191
Amith Yamasani3c9f5192010-12-08 16:48:31 -0800192 private ViewGroup mPrefsContainer;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700193
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700194 private FragmentBreadCrumbs mFragmentBreadCrumbs;
195
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700196 private boolean mSinglePane;
197
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700198 private Header mCurHeader;
199
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700200 // --- State for old mode when showing a single preference list
Freeman Ng19ea2e02010-03-25 15:09:00 -0700201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 private PreferenceManager mPreferenceManager;
Freeman Ng19ea2e02010-03-25 15:09:00 -0700203
Adam Powelle7fea452010-03-18 14:51:39 -0700204 private Bundle mSavedInstanceState;
205
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700206 // --- Common state
207
208 private Button mNextButton;
209
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 /**
211 * The starting request code given out to preference framework.
212 */
213 private static final int FIRST_REQUEST_CODE = 100;
Freeman Ng19ea2e02010-03-25 15:09:00 -0700214
Dianne Hackborn3e449ce2010-09-11 20:52:31 -0700215 private static final int MSG_BIND_PREFERENCES = 1;
216 private static final int MSG_BUILD_HEADERS = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 private Handler mHandler = new Handler() {
218 @Override
219 public void handleMessage(Message msg) {
220 switch (msg.what) {
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700221 case MSG_BIND_PREFERENCES: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 bindPreferences();
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700223 } break;
224 case MSG_BUILD_HEADERS: {
225 ArrayList<Header> oldHeaders = new ArrayList<Header>(mHeaders);
226 mHeaders.clear();
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700227 onBuildHeaders(mHeaders);
Gilles Debunne39725ac2011-06-14 18:52:41 -0700228 if (mAdapter instanceof BaseAdapter) {
229 ((BaseAdapter) mAdapter).notifyDataSetChanged();
Andrew Stadler83681eb2010-11-04 15:49:05 -0700230 }
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700231 Header header = onGetNewHeader();
232 if (header != null && header.fragment != null) {
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700233 Header mappedHeader = findBestMatchingHeader(header, oldHeaders);
234 if (mappedHeader == null || mCurHeader != mappedHeader) {
235 switchToHeader(header);
236 }
237 } else if (mCurHeader != null) {
238 Header mappedHeader = findBestMatchingHeader(mCurHeader, mHeaders);
239 if (mappedHeader != null) {
240 setSelectedHeader(mappedHeader);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700241 }
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700242 }
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700243 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
245 }
246 };
247
Andrew Stadler468c3232010-08-17 16:16:42 -0700248 private static class HeaderAdapter extends ArrayAdapter<Header> {
249 private static class HeaderViewHolder {
250 ImageView icon;
251 TextView title;
252 TextView summary;
253 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700254
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700255 private LayoutInflater mInflater;
256
257 public HeaderAdapter(Context context, List<Header> objects) {
258 super(context, 0, objects);
259 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
260 }
261
262 @Override
263 public View getView(int position, View convertView, ViewGroup parent) {
264 HeaderViewHolder holder;
265 View view;
266
267 if (convertView == null) {
Dianne Hackbornd0fa3712010-09-14 18:57:14 -0700268 view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700269 parent, false);
270 holder = new HeaderViewHolder();
Andrew Stadler468c3232010-08-17 16:16:42 -0700271 holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
272 holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
273 holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700274 view.setTag(holder);
275 } else {
276 view = convertView;
Andrew Stadler468c3232010-08-17 16:16:42 -0700277 holder = (HeaderViewHolder) view.getTag();
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700278 }
279
Andrew Stadler468c3232010-08-17 16:16:42 -0700280 // All view fields must be updated every time, because the view may be recycled
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700281 Header header = getItem(position);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700282 holder.icon.setImageResource(header.iconRes);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800283 holder.title.setText(header.getTitle(getContext().getResources()));
284 CharSequence summary = header.getSummary(getContext().getResources());
285 if (!TextUtils.isEmpty(summary)) {
Andrew Stadler468c3232010-08-17 16:16:42 -0700286 holder.summary.setVisibility(View.VISIBLE);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800287 holder.summary.setText(summary);
288 } else {
289 holder.summary.setVisibility(View.GONE);
Andrew Stadler468c3232010-08-17 16:16:42 -0700290 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700291
292 return view;
293 }
294 }
295
296 /**
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700297 * Default value for {@link Header#id Header.id} indicating that no
298 * identifier value is set. All other values (including those below -1)
299 * are valid.
300 */
301 public static final long HEADER_ID_UNDEFINED = -1;
302
303 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700304 * Description of a single Header item that the user can select.
305 */
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700306 public static final class Header implements Parcelable {
307 /**
308 * Identifier for this header, to correlate with a new list when
309 * it is updated. The default value is
310 * {@link PreferenceActivity#HEADER_ID_UNDEFINED}, meaning no id.
311 * @attr ref android.R.styleable#PreferenceHeader_id
312 */
313 public long id = HEADER_ID_UNDEFINED;
314
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700315 /**
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800316 * Resource ID of title of the header that is shown to the user.
317 * @attr ref android.R.styleable#PreferenceHeader_title
318 */
319 public int titleRes;
320
321 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700322 * Title of the header that is shown to the user.
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700323 * @attr ref android.R.styleable#PreferenceHeader_title
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700324 */
Andrew Stadler468c3232010-08-17 16:16:42 -0700325 public CharSequence title;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700326
327 /**
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800328 * Resource ID of optional summary describing what this header controls.
329 * @attr ref android.R.styleable#PreferenceHeader_summary
330 */
331 public int summaryRes;
332
333 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700334 * Optional summary describing what this header controls.
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700335 * @attr ref android.R.styleable#PreferenceHeader_summary
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700336 */
Andrew Stadler468c3232010-08-17 16:16:42 -0700337 public CharSequence summary;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700338
339 /**
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800340 * Resource ID of optional text to show as the title in the bread crumb.
341 * @attr ref android.R.styleable#PreferenceHeader_breadCrumbTitle
342 */
343 public int breadCrumbTitleRes;
344
345 /**
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700346 * Optional text to show as the title in the bread crumb.
347 * @attr ref android.R.styleable#PreferenceHeader_breadCrumbTitle
348 */
349 public CharSequence breadCrumbTitle;
350
351 /**
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800352 * Resource ID of optional text to show as the short title in the bread crumb.
353 * @attr ref android.R.styleable#PreferenceHeader_breadCrumbShortTitle
354 */
355 public int breadCrumbShortTitleRes;
356
357 /**
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700358 * Optional text to show as the short title in the bread crumb.
359 * @attr ref android.R.styleable#PreferenceHeader_breadCrumbShortTitle
360 */
361 public CharSequence breadCrumbShortTitle;
362
363 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700364 * Optional icon resource to show for this header.
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700365 * @attr ref android.R.styleable#PreferenceHeader_icon
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700366 */
Andrew Stadler468c3232010-08-17 16:16:42 -0700367 public int iconRes;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700368
369 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700370 * Full class name of the fragment to display when this header is
371 * selected.
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -0700372 * @attr ref android.R.styleable#PreferenceHeader_fragment
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700373 */
Andrew Stadler468c3232010-08-17 16:16:42 -0700374 public String fragment;
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700375
376 /**
377 * Optional arguments to supply to the fragment when it is
378 * instantiated.
379 */
Andrew Stadler468c3232010-08-17 16:16:42 -0700380 public Bundle fragmentArguments;
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700381
382 /**
383 * Intent to launch when the preference is selected.
384 */
385 public Intent intent;
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700386
387 /**
388 * Optional additional data for use by subclasses of PreferenceActivity.
389 */
390 public Bundle extras;
391
392 public Header() {
Gilles Debunne39725ac2011-06-14 18:52:41 -0700393 // Empty
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700394 }
395
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800396 /**
397 * Return the currently set title. If {@link #titleRes} is set,
398 * this resource is loaded from <var>res</var> and returned. Otherwise
399 * {@link #title} is returned.
400 */
401 public CharSequence getTitle(Resources res) {
402 if (titleRes != 0) {
403 return res.getText(titleRes);
404 }
405 return title;
406 }
407
408 /**
409 * Return the currently set summary. If {@link #summaryRes} is set,
410 * this resource is loaded from <var>res</var> and returned. Otherwise
411 * {@link #summary} is returned.
412 */
413 public CharSequence getSummary(Resources res) {
414 if (summaryRes != 0) {
415 return res.getText(summaryRes);
416 }
Dianne Hackborn9d071802010-12-08 14:49:15 -0800417 return summary;
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800418 }
419
420 /**
421 * Return the currently set bread crumb title. If {@link #breadCrumbTitleRes} is set,
422 * this resource is loaded from <var>res</var> and returned. Otherwise
423 * {@link #breadCrumbTitle} is returned.
424 */
425 public CharSequence getBreadCrumbTitle(Resources res) {
426 if (breadCrumbTitleRes != 0) {
427 return res.getText(breadCrumbTitleRes);
428 }
429 return breadCrumbTitle;
430 }
431
432 /**
433 * Return the currently set bread crumb short title. If
434 * {@link #breadCrumbShortTitleRes} is set,
435 * this resource is loaded from <var>res</var> and returned. Otherwise
436 * {@link #breadCrumbShortTitle} is returned.
437 */
438 public CharSequence getBreadCrumbShortTitle(Resources res) {
439 if (breadCrumbShortTitleRes != 0) {
440 return res.getText(breadCrumbShortTitleRes);
441 }
442 return breadCrumbShortTitle;
443 }
444
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700445 @Override
446 public int describeContents() {
447 return 0;
448 }
449
450 @Override
451 public void writeToParcel(Parcel dest, int flags) {
452 dest.writeLong(id);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800453 dest.writeInt(titleRes);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700454 TextUtils.writeToParcel(title, dest, flags);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800455 dest.writeInt(summaryRes);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700456 TextUtils.writeToParcel(summary, dest, flags);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800457 dest.writeInt(breadCrumbTitleRes);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700458 TextUtils.writeToParcel(breadCrumbTitle, dest, flags);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800459 dest.writeInt(breadCrumbShortTitleRes);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700460 TextUtils.writeToParcel(breadCrumbShortTitle, dest, flags);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700461 dest.writeInt(iconRes);
462 dest.writeString(fragment);
463 dest.writeBundle(fragmentArguments);
464 if (intent != null) {
465 dest.writeInt(1);
466 intent.writeToParcel(dest, flags);
467 } else {
468 dest.writeInt(0);
469 }
470 dest.writeBundle(extras);
471 }
472
473 public void readFromParcel(Parcel in) {
474 id = in.readLong();
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800475 titleRes = in.readInt();
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700476 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800477 summaryRes = in.readInt();
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700478 summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800479 breadCrumbTitleRes = in.readInt();
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700480 breadCrumbTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800481 breadCrumbShortTitleRes = in.readInt();
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700482 breadCrumbShortTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700483 iconRes = in.readInt();
484 fragment = in.readString();
485 fragmentArguments = in.readBundle();
486 if (in.readInt() != 0) {
487 intent = Intent.CREATOR.createFromParcel(in);
488 }
489 extras = in.readBundle();
490 }
491
492 Header(Parcel in) {
493 readFromParcel(in);
494 }
495
496 public static final Creator<Header> CREATOR = new Creator<Header>() {
497 public Header createFromParcel(Parcel source) {
498 return new Header(source);
499 }
500 public Header[] newArray(int size) {
501 return new Header[size];
502 }
503 };
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700504 }
505
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 @Override
507 protected void onCreate(Bundle savedInstanceState) {
508 super.onCreate(savedInstanceState);
509
Amith Yamasani405c1af2011-05-26 13:08:25 -0700510 setContentView(com.android.internal.R.layout.preference_list_content);
Freeman Ng19ea2e02010-03-25 15:09:00 -0700511
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700512 mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
Amith Yamasani3c9f5192010-12-08 16:48:31 -0800513 mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs_frame);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700514 boolean hidingHeaders = onIsHidingHeaders();
515 mSinglePane = hidingHeaders || !onIsMultiPane();
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700516 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
517 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Dianne Hackborne72f2372011-03-16 10:43:18 -0700518 int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
519 int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700520
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700521 if (savedInstanceState != null) {
522 // We are restarting from a previous saved state; used that to
523 // initialize, instead of starting fresh.
524 ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG);
525 if (headers != null) {
526 mHeaders.addAll(headers);
527 int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
Gilles Debunne39725ac2011-06-14 18:52:41 -0700528 (int) HEADER_ID_UNDEFINED);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700529 if (curHeader >= 0 && curHeader < mHeaders.size()) {
530 setSelectedHeader(mHeaders.get(curHeader));
531 }
532 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700533
534 } else {
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700535 if (initialFragment != null && mSinglePane) {
536 // If we are just showing a fragment, we want to run in
537 // new fragment mode, but don't need to compute and show
538 // the headers.
539 switchToHeader(initialFragment, initialArguments);
Dianne Hackborne72f2372011-03-16 10:43:18 -0700540 if (initialTitle != 0) {
541 CharSequence initialTitleStr = getText(initialTitle);
542 CharSequence initialShortTitleStr = initialShortTitle != 0
543 ? getText(initialShortTitle) : null;
544 showBreadCrumbs(initialTitleStr, initialShortTitleStr);
545 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700546
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700547 } else {
548 // We need to try to build the headers.
549 onBuildHeaders(mHeaders);
550
551 // If there are headers, then at this point we need to show
552 // them and, depending on the screen, we may also show in-line
553 // the currently selected preference fragment.
554 if (mHeaders.size() > 0) {
555 if (!mSinglePane) {
556 if (initialFragment == null) {
557 Header h = onGetInitialHeader();
558 switchToHeader(h);
559 } else {
560 switchToHeader(initialFragment, initialArguments);
561 }
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700562 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700563 }
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700564 }
565 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700566
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700567 // The default configuration is to only show the list view. Adjust
568 // visibility for other configurations.
569 if (initialFragment != null && mSinglePane) {
570 // Single pane, showing just a prefs fragment.
Amith Yamasani05fbc312010-09-26 13:29:01 -0700571 findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700572 mPrefsContainer.setVisibility(View.VISIBLE);
Dianne Hackborn34905a92011-07-21 17:30:07 -0700573 if (initialTitle != 0) {
574 CharSequence initialTitleStr = getText(initialTitle);
575 CharSequence initialShortTitleStr = initialShortTitle != 0
576 ? getText(initialShortTitle) : null;
577 showBreadCrumbs(initialTitleStr, initialShortTitleStr);
578 }
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700579 } else if (mHeaders.size() > 0) {
Gilles Debunne39725ac2011-06-14 18:52:41 -0700580 setListAdapter(new HeaderAdapter(this, mHeaders));
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700581 if (!mSinglePane) {
582 // Multi-pane.
583 getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
Dianne Hackbornd0fa3712010-09-14 18:57:14 -0700584 if (mCurHeader != null) {
585 setSelectedHeader(mCurHeader);
586 }
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700587 mPrefsContainer.setVisibility(View.VISIBLE);
588 }
589 } else {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700590 // If there are no headers, we are in the old "just show a screen
591 // of preferences" mode.
Amith Yamasani405c1af2011-05-26 13:08:25 -0700592 setContentView(com.android.internal.R.layout.preference_list_content_single);
Amith Yamasani8da35292010-11-05 09:15:51 -0700593 mListFooter = (FrameLayout) findViewById(com.android.internal.R.id.list_footer);
Amith Yamasani3c9f5192010-12-08 16:48:31 -0800594 mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs);
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700595 mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
596 mPreferenceManager.setOnPreferenceTreeClickListener(this);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700597 }
598
Freeman Ng19ea2e02010-03-25 15:09:00 -0700599 // see if we should show Back/Next buttons
600 Intent intent = getIntent();
601 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
602
603 findViewById(com.android.internal.R.id.button_bar).setVisibility(View.VISIBLE);
604
605 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
606 backButton.setOnClickListener(new OnClickListener() {
607 public void onClick(View v) {
608 setResult(RESULT_CANCELED);
609 finish();
610 }
611 });
Freeman Ng09dbf182010-08-11 15:45:01 -0700612 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
613 skipButton.setOnClickListener(new OnClickListener() {
614 public void onClick(View v) {
615 setResult(RESULT_OK);
616 finish();
617 }
618 });
Freeman Ng19ea2e02010-03-25 15:09:00 -0700619 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
620 mNextButton.setOnClickListener(new OnClickListener() {
621 public void onClick(View v) {
622 setResult(RESULT_OK);
623 finish();
624 }
625 });
626
627 // set our various button parameters
628 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
629 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
630 if (TextUtils.isEmpty(buttonText)) {
631 mNextButton.setVisibility(View.GONE);
632 }
633 else {
634 mNextButton.setText(buttonText);
635 }
636 }
637 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
638 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
639 if (TextUtils.isEmpty(buttonText)) {
640 backButton.setVisibility(View.GONE);
641 }
642 else {
643 backButton.setText(buttonText);
644 }
645 }
Freeman Ng09dbf182010-08-11 15:45:01 -0700646 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
647 skipButton.setVisibility(View.VISIBLE);
648 }
Freeman Ng19ea2e02010-03-25 15:09:00 -0700649 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700650 }
Freeman Ng19ea2e02010-03-25 15:09:00 -0700651
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700652 /**
Dianne Hackborn291905e2010-08-17 15:17:15 -0700653 * Returns true if this activity is currently showing the header list.
654 */
655 public boolean hasHeaders() {
656 return getListView().getVisibility() == View.VISIBLE
657 && mPreferenceManager == null;
658 }
659
660 /**
Amith Yamasani423d48b2012-06-20 13:54:53 -0700661 * Returns the Header list
662 * @hide
663 */
664 public List<Header> getHeaders() {
665 return mHeaders;
666 }
667
668 /**
Dianne Hackborn291905e2010-08-17 15:17:15 -0700669 * Returns true if this activity is showing multiple panes -- the headers
670 * and a preference fragment.
671 */
672 public boolean isMultiPane() {
673 return hasHeaders() && mPrefsContainer.getVisibility() == View.VISIBLE;
674 }
675
676 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700677 * Called to determine if the activity should run in multi-pane mode.
678 * The default implementation returns true if the screen is large
679 * enough.
680 */
681 public boolean onIsMultiPane() {
Amith Yamasani405c1af2011-05-26 13:08:25 -0700682 boolean preferMultiPane = getResources().getBoolean(
683 com.android.internal.R.bool.preferences_prefer_dual_pane);
684 return preferMultiPane;
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700685 }
686
687 /**
Dianne Hackborn291905e2010-08-17 15:17:15 -0700688 * Called to determine whether the header list should be hidden.
689 * The default implementation returns the
690 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
691 * This is set to false, for example, when the activity is being re-launched
692 * to show a particular preference activity.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700693 */
694 public boolean onIsHidingHeaders() {
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700695 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700696 }
697
698 /**
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700699 * Called to determine the initial header to be shown. The default
700 * implementation simply returns the fragment of the first header. Note
701 * that the returned Header object does not actually need to exist in
702 * your header list -- whatever its fragment is will simply be used to
703 * show for the initial UI.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700704 */
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700705 public Header onGetInitialHeader() {
Dianne Hackborn19470ec2013-02-12 11:42:51 -0800706 for (int i=0; i<mHeaders.size(); i++) {
707 Header h = mHeaders.get(i);
708 if (h.fragment != null) {
709 return h;
710 }
711 }
712 throw new IllegalStateException("Must have at least one header with a fragment");
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700713 }
714
715 /**
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700716 * Called after the header list has been updated ({@link #onBuildHeaders}
717 * has been called and returned due to {@link #invalidateHeaders()}) to
718 * specify the header that should now be selected. The default implementation
719 * returns null to keep whatever header is currently selected.
720 */
721 public Header onGetNewHeader() {
722 return null;
723 }
724
725 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700726 * Called when the activity needs its list of headers build. By
727 * implementing this and adding at least one item to the list, you
728 * will cause the activity to run in its modern fragment mode. Note
729 * that this function may not always be called; for example, if the
730 * activity has been asked to display a particular fragment without
731 * the header list, there is no need to build the headers.
732 *
733 * <p>Typical implementations will use {@link #loadHeadersFromResource}
734 * to fill in the list from a resource.
735 *
736 * @param target The list in which to place the headers.
737 */
738 public void onBuildHeaders(List<Header> target) {
Gilles Debunne39725ac2011-06-14 18:52:41 -0700739 // Should be overloaded by subclasses
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700740 }
741
742 /**
Dianne Hackbornb7a2e472010-08-12 16:20:42 -0700743 * Call when you need to change the headers being displayed. Will result
744 * in onBuildHeaders() later being called to retrieve the new list.
745 */
746 public void invalidateHeaders() {
747 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
748 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
749 }
750 }
751
752 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700753 * Parse the given XML file as a header description, adding each
754 * parsed Header into the target list.
755 *
756 * @param resid The XML resource to load and parse.
757 * @param target The list in which the parsed headers should be placed.
758 */
759 public void loadHeadersFromResource(int resid, List<Header> target) {
760 XmlResourceParser parser = null;
761 try {
762 parser = getResources().getXml(resid);
763 AttributeSet attrs = Xml.asAttributeSet(parser);
764
765 int type;
766 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
767 && type != XmlPullParser.START_TAG) {
Gilles Debunne39725ac2011-06-14 18:52:41 -0700768 // Parse next until start tag is found
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700769 }
770
771 String nodeName = parser.getName();
Dianne Hackborndef15372010-08-15 12:43:52 -0700772 if (!"preference-headers".equals(nodeName)) {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700773 throw new RuntimeException(
Dianne Hackborndef15372010-08-15 12:43:52 -0700774 "XML document must start with <preference-headers> tag; found"
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700775 + nodeName + " at " + parser.getPositionDescription());
776 }
777
Dianne Hackborndef15372010-08-15 12:43:52 -0700778 Bundle curBundle = null;
779
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700780 final int outerDepth = parser.getDepth();
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700781 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
782 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
783 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
784 continue;
785 }
786
787 nodeName = parser.getName();
Dianne Hackborndef15372010-08-15 12:43:52 -0700788 if ("header".equals(nodeName)) {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700789 Header header = new Header();
790
791 TypedArray sa = getResources().obtainAttributes(attrs,
792 com.android.internal.R.styleable.PreferenceHeader);
Amith Yamasanied13cde2010-09-17 16:56:47 -0700793 header.id = sa.getResourceId(
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700794 com.android.internal.R.styleable.PreferenceHeader_id,
795 (int)HEADER_ID_UNDEFINED);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800796 TypedValue tv = sa.peekValue(
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700797 com.android.internal.R.styleable.PreferenceHeader_title);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800798 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
799 if (tv.resourceId != 0) {
800 header.titleRes = tv.resourceId;
801 } else {
802 header.title = tv.string;
803 }
804 }
805 tv = sa.peekValue(
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700806 com.android.internal.R.styleable.PreferenceHeader_summary);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800807 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
808 if (tv.resourceId != 0) {
809 header.summaryRes = tv.resourceId;
810 } else {
811 header.summary = tv.string;
812 }
813 }
814 tv = sa.peekValue(
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700815 com.android.internal.R.styleable.PreferenceHeader_breadCrumbTitle);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800816 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
817 if (tv.resourceId != 0) {
818 header.breadCrumbTitleRes = tv.resourceId;
819 } else {
820 header.breadCrumbTitle = tv.string;
821 }
822 }
823 tv = sa.peekValue(
Dianne Hackbornc6669ca2010-09-16 01:33:24 -0700824 com.android.internal.R.styleable.PreferenceHeader_breadCrumbShortTitle);
Dianne Hackborn50ed8292010-12-03 12:30:21 -0800825 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
826 if (tv.resourceId != 0) {
827 header.breadCrumbShortTitleRes = tv.resourceId;
828 } else {
829 header.breadCrumbShortTitle = tv.string;
830 }
831 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700832 header.iconRes = sa.getResourceId(
833 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
834 header.fragment = sa.getString(
835 com.android.internal.R.styleable.PreferenceHeader_fragment);
836 sa.recycle();
837
Dianne Hackborndef15372010-08-15 12:43:52 -0700838 if (curBundle == null) {
839 curBundle = new Bundle();
840 }
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700841
842 final int innerDepth = parser.getDepth();
843 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
844 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
845 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
846 continue;
847 }
848
849 String innerNodeName = parser.getName();
850 if (innerNodeName.equals("extra")) {
851 getResources().parseBundleExtra("extra", attrs, curBundle);
852 XmlUtils.skipCurrentTag(parser);
853
854 } else if (innerNodeName.equals("intent")) {
855 header.intent = Intent.parseIntent(getResources(), parser, attrs);
856
857 } else {
858 XmlUtils.skipCurrentTag(parser);
859 }
860 }
861
Dianne Hackborndef15372010-08-15 12:43:52 -0700862 if (curBundle.size() > 0) {
863 header.fragmentArguments = curBundle;
864 curBundle = null;
865 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700866
Dianne Hackborndef15372010-08-15 12:43:52 -0700867 target.add(header);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700868 } else {
869 XmlUtils.skipCurrentTag(parser);
870 }
871 }
872
873 } catch (XmlPullParserException e) {
874 throw new RuntimeException("Error parsing headers", e);
875 } catch (IOException e) {
876 throw new RuntimeException("Error parsing headers", e);
877 } finally {
878 if (parser != null) parser.close();
879 }
880
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 }
882
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700883 /**
884 * Set a footer that should be shown at the bottom of the header list.
885 */
886 public void setListFooter(View view) {
887 mListFooter.removeAllViews();
888 mListFooter.addView(view, new FrameLayout.LayoutParams(
889 FrameLayout.LayoutParams.MATCH_PARENT,
890 FrameLayout.LayoutParams.WRAP_CONTENT));
891 }
892
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 @Override
894 protected void onStop() {
895 super.onStop();
Freeman Ng19ea2e02010-03-25 15:09:00 -0700896
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700897 if (mPreferenceManager != null) {
898 mPreferenceManager.dispatchActivityStop();
899 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 }
901
902 @Override
903 protected void onDestroy() {
Hiroaki Kuriyama1ebf13e2012-11-16 18:46:49 +0900904 mHandler.removeMessages(MSG_BIND_PREFERENCES);
905 mHandler.removeMessages(MSG_BUILD_HEADERS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 super.onDestroy();
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700907
908 if (mPreferenceManager != null) {
909 mPreferenceManager.dispatchActivityDestroy();
910 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 }
912
913 @Override
914 protected void onSaveInstanceState(Bundle outState) {
915 super.onSaveInstanceState(outState);
916
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700917 if (mHeaders.size() > 0) {
918 outState.putParcelableArrayList(HEADERS_TAG, mHeaders);
919 if (mCurHeader != null) {
920 int index = mHeaders.indexOf(mCurHeader);
921 if (index >= 0) {
922 outState.putInt(CUR_HEADER_TAG, index);
923 }
924 }
925 }
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700926
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700927 if (mPreferenceManager != null) {
928 final PreferenceScreen preferenceScreen = getPreferenceScreen();
929 if (preferenceScreen != null) {
930 Bundle container = new Bundle();
931 preferenceScreen.saveHierarchyState(container);
932 outState.putBundle(PREFERENCES_TAG, container);
933 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 }
935 }
936
937 @Override
938 protected void onRestoreInstanceState(Bundle state) {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700939 if (mPreferenceManager != null) {
940 Bundle container = state.getBundle(PREFERENCES_TAG);
941 if (container != null) {
942 final PreferenceScreen preferenceScreen = getPreferenceScreen();
943 if (preferenceScreen != null) {
944 preferenceScreen.restoreHierarchyState(container);
945 mSavedInstanceState = state;
946 return;
947 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 }
949 }
Adam Powelle7fea452010-03-18 14:51:39 -0700950
951 // Only call this if we didn't save the instance state for later.
952 // If we did save it, it will be restored when we bind the adapter.
953 super.onRestoreInstanceState(state);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 }
955
956 @Override
957 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
958 super.onActivityResult(requestCode, resultCode, data);
Freeman Ng19ea2e02010-03-25 15:09:00 -0700959
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700960 if (mPreferenceManager != null) {
961 mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
962 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 }
964
965 @Override
966 public void onContentChanged() {
967 super.onContentChanged();
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700968
969 if (mPreferenceManager != null) {
970 postBindPreferences();
971 }
972 }
973
974 @Override
975 protected void onListItemClick(ListView l, View v, int position, long id) {
Amith Yamasani49bdc162013-07-17 15:52:45 -0700976 if (!isResumed()) {
977 return;
978 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700979 super.onListItemClick(l, v, position, id);
980
981 if (mAdapter != null) {
Gilles Debunne39725ac2011-06-14 18:52:41 -0700982 Object item = mAdapter.getItem(position);
983 if (item instanceof Header) onHeaderClick((Header) item, position);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700984 }
985 }
986
987 /**
988 * Called when the user selects an item in the header list. The default
Dianne Hackborne72f2372011-03-16 10:43:18 -0700989 * implementation will call either
990 * {@link #startWithFragment(String, Bundle, Fragment, int, int, int)}
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700991 * or {@link #switchToHeader(Header)} as appropriate.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700992 *
993 * @param header The header that was selected.
994 * @param position The header's position in the list.
995 */
996 public void onHeaderClick(Header header, int position) {
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700997 if (header.fragment != null) {
998 if (mSinglePane) {
Dianne Hackborne72f2372011-03-16 10:43:18 -0700999 int titleRes = header.breadCrumbTitleRes;
1000 int shortTitleRes = header.breadCrumbShortTitleRes;
1001 if (titleRes == 0) {
1002 titleRes = header.titleRes;
1003 shortTitleRes = 0;
1004 }
1005 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
1006 titleRes, shortTitleRes);
Dianne Hackborn5c769a42010-08-26 17:08:08 -07001007 } else {
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001008 switchToHeader(header);
Dianne Hackborn5c769a42010-08-26 17:08:08 -07001009 }
1010 } else if (header.intent != null) {
1011 startActivity(header.intent);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001012 }
1013 }
1014
1015 /**
Dianne Hackborne72f2372011-03-16 10:43:18 -07001016 * Called by {@link #startWithFragment(String, Bundle, Fragment, int, int, int)} when
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001017 * in single-pane mode, to build an Intent to launch a new activity showing
1018 * the selected fragment. The default implementation constructs an Intent
1019 * that re-launches the current activity with the appropriate arguments to
1020 * display the fragment.
1021 *
1022 * @param fragmentName The name of the fragment to display.
1023 * @param args Optional arguments to supply to the fragment.
Dianne Hackborne72f2372011-03-16 10:43:18 -07001024 * @param titleRes Optional resource ID of title to show for this item.
Gilles Debunne39725ac2011-06-14 18:52:41 -07001025 * @param shortTitleRes Optional resource ID of short title to show for this item.
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001026 * @return Returns an Intent that can be launched to display the given
1027 * fragment.
1028 */
Dianne Hackborne72f2372011-03-16 10:43:18 -07001029 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
1030 int titleRes, int shortTitleRes) {
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001031 Intent intent = new Intent(Intent.ACTION_MAIN);
1032 intent.setClass(this, getClass());
1033 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
1034 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Dianne Hackborne72f2372011-03-16 10:43:18 -07001035 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, titleRes);
1036 intent.putExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, shortTitleRes);
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001037 intent.putExtra(EXTRA_NO_HEADERS, true);
1038 return intent;
1039 }
1040
1041 /**
Dianne Hackborne72f2372011-03-16 10:43:18 -07001042 * Like {@link #startWithFragment(String, Bundle, Fragment, int, int, int)}
1043 * but uses a 0 titleRes.
1044 */
1045 public void startWithFragment(String fragmentName, Bundle args,
1046 Fragment resultTo, int resultRequestCode) {
1047 startWithFragment(fragmentName, args, resultTo, resultRequestCode, 0, 0);
1048 }
1049
1050 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001051 * Start a new instance of this activity, showing only the given
1052 * preference fragment. When launched in this mode, the header list
1053 * will be hidden and the given preference fragment will be instantiated
1054 * and fill the entire activity.
1055 *
1056 * @param fragmentName The name of the fragment to display.
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001057 * @param args Optional arguments to supply to the fragment.
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001058 * @param resultTo Option fragment that should receive the result of
1059 * the activity launch.
1060 * @param resultRequestCode If resultTo is non-null, this is the request
1061 * code in which to report the result.
Dianne Hackborne72f2372011-03-16 10:43:18 -07001062 * @param titleRes Resource ID of string to display for the title of
1063 * this set of preferences.
Gilles Debunne39725ac2011-06-14 18:52:41 -07001064 * @param shortTitleRes Resource ID of string to display for the short title of
Dianne Hackborne72f2372011-03-16 10:43:18 -07001065 * this set of preferences.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001066 */
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001067 public void startWithFragment(String fragmentName, Bundle args,
Dianne Hackborne72f2372011-03-16 10:43:18 -07001068 Fragment resultTo, int resultRequestCode, int titleRes, int shortTitleRes) {
1069 Intent intent = onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes);
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001070 if (resultTo == null) {
1071 startActivity(intent);
1072 } else {
1073 resultTo.startActivityForResult(intent, resultRequestCode);
1074 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001075 }
1076
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001077 /**
1078 * Change the base title of the bread crumbs for the current preferences.
1079 * This will normally be called for you. See
1080 * {@link android.app.FragmentBreadCrumbs} for more information.
1081 */
1082 public void showBreadCrumbs(CharSequence title, CharSequence shortTitle) {
1083 if (mFragmentBreadCrumbs == null) {
Amith Yamasani3e860402010-12-10 14:20:51 -08001084 View crumbs = findViewById(android.R.id.title);
1085 // For screens with a different kind of title, don't create breadcrumbs.
Dianne Hackborne72f2372011-03-16 10:43:18 -07001086 try {
1087 mFragmentBreadCrumbs = (FragmentBreadCrumbs)crumbs;
1088 } catch (ClassCastException e) {
1089 return;
1090 }
Amith Yamasani3c9f5192010-12-08 16:48:31 -08001091 if (mFragmentBreadCrumbs == null) {
Dianne Hackborne72f2372011-03-16 10:43:18 -07001092 if (title != null) {
1093 setTitle(title);
Amith Yamasani3c9f5192010-12-08 16:48:31 -08001094 }
Dianne Hackborne72f2372011-03-16 10:43:18 -07001095 return;
Jim Millerc57406c2010-12-08 16:01:05 -08001096 }
Amith Yamasani3ec7bac2012-10-03 16:02:51 -07001097 if (mSinglePane) {
1098 mFragmentBreadCrumbs.setVisibility(View.GONE);
1099 // Hide the breadcrumb section completely for single-pane
1100 View bcSection = findViewById(com.android.internal.R.id.breadcrumb_section);
1101 if (bcSection != null) bcSection.setVisibility(View.GONE);
1102 }
Amith Yamasani3c9f5192010-12-08 16:48:31 -08001103 mFragmentBreadCrumbs.setMaxVisible(2);
1104 mFragmentBreadCrumbs.setActivity(this);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001105 }
1106 mFragmentBreadCrumbs.setTitle(title, shortTitle);
Amith Yamasanic9ecb732010-12-14 14:23:21 -08001107 mFragmentBreadCrumbs.setParentTitle(null, null, null);
1108 }
1109
1110 /**
1111 * Should be called after onCreate to ensure that the breadcrumbs, if any, were created.
1112 * This prepends a title to the fragment breadcrumbs and attaches a listener to any clicks
1113 * on the parent entry.
1114 * @param title the title for the breadcrumb
1115 * @param shortTitle the short title for the breadcrumb
1116 */
1117 public void setParentTitle(CharSequence title, CharSequence shortTitle,
1118 OnClickListener listener) {
1119 if (mFragmentBreadCrumbs != null) {
1120 mFragmentBreadCrumbs.setParentTitle(title, shortTitle, listener);
1121 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001122 }
1123
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001124 void setSelectedHeader(Header header) {
1125 mCurHeader = header;
1126 int index = mHeaders.indexOf(header);
1127 if (index >= 0) {
1128 getListView().setItemChecked(index, true);
1129 } else {
1130 getListView().clearChoices();
1131 }
Dianne Hackborn34905a92011-07-21 17:30:07 -07001132 showBreadCrumbs(header);
1133 }
1134
1135 void showBreadCrumbs(Header header) {
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001136 if (header != null) {
Dianne Hackborn50ed8292010-12-03 12:30:21 -08001137 CharSequence title = header.getBreadCrumbTitle(getResources());
1138 if (title == null) title = header.getTitle(getResources());
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001139 if (title == null) title = getTitle();
Dianne Hackborn50ed8292010-12-03 12:30:21 -08001140 showBreadCrumbs(title, header.getBreadCrumbShortTitle(getResources()));
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001141 } else {
1142 showBreadCrumbs(getTitle(), null);
1143 }
1144 }
1145
Amith Yamasanif5cbaed2010-10-27 16:14:20 -07001146 private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
Dianne Hackborn3a57fb92010-11-15 17:58:52 -08001147 getFragmentManager().popBackStack(BACK_STACK_PREFS,
1148 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001149 Fragment f = Fragment.instantiate(this, fragmentName, args);
Dianne Hackborn48e7b452011-01-17 12:28:35 -08001150 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Dianne Hackborn327fbd22011-01-17 14:38:50 -08001151 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
Chet Haase9ff82bf2010-10-05 14:30:51 -07001152 transaction.replace(com.android.internal.R.id.prefs, f);
Dianne Hackborncf407ad2011-03-11 13:17:57 -08001153 transaction.commitAllowingStateLoss();
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001154 }
1155
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001156 /**
1157 * When in two-pane mode, switch the fragment pane to show the given
1158 * preference fragment.
1159 *
1160 * @param fragmentName The name of the fragment to display.
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001161 * @param args Optional arguments to supply to the fragment.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001162 */
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001163 public void switchToHeader(String fragmentName, Bundle args) {
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001164 setSelectedHeader(null);
Amith Yamasanif5cbaed2010-10-27 16:14:20 -07001165 switchToHeaderInner(fragmentName, args, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 }
1167
Andrew Stadleraa904f42010-09-02 14:50:08 -07001168 /**
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001169 * When in two-pane mode, switch to the fragment pane to show the given
1170 * preference fragment.
1171 *
1172 * @param header The new header to display.
1173 */
1174 public void switchToHeader(Header header) {
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001175 if (mCurHeader == header) {
1176 // This is the header we are currently displaying. Just make sure
1177 // to pop the stack up to its root state.
Dianne Hackborn3a57fb92010-11-15 17:58:52 -08001178 getFragmentManager().popBackStack(BACK_STACK_PREFS,
1179 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001180 } else {
Dianne Hackborn19470ec2013-02-12 11:42:51 -08001181 if (header.fragment == null) {
1182 throw new IllegalStateException("can't switch to header that has no fragment");
1183 }
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001184 int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
1185 switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
1186 setSelectedHeader(header);
1187 }
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001188 }
1189
1190 Header findBestMatchingHeader(Header cur, ArrayList<Header> from) {
1191 ArrayList<Header> matches = new ArrayList<Header>();
1192 for (int j=0; j<from.size(); j++) {
1193 Header oh = from.get(j);
1194 if (cur == oh || (cur.id != HEADER_ID_UNDEFINED && cur.id == oh.id)) {
1195 // Must be this one.
1196 matches.clear();
1197 matches.add(oh);
1198 break;
1199 }
1200 if (cur.fragment != null) {
1201 if (cur.fragment.equals(oh.fragment)) {
1202 matches.add(oh);
1203 }
1204 } else if (cur.intent != null) {
1205 if (cur.intent.equals(oh.intent)) {
1206 matches.add(oh);
1207 }
1208 } else if (cur.title != null) {
1209 if (cur.title.equals(oh.title)) {
1210 matches.add(oh);
1211 }
1212 }
1213 }
1214 final int NM = matches.size();
1215 if (NM == 1) {
1216 return matches.get(0);
1217 } else if (NM > 1) {
1218 for (int j=0; j<NM; j++) {
1219 Header oh = matches.get(j);
1220 if (cur.fragmentArguments != null &&
1221 cur.fragmentArguments.equals(oh.fragmentArguments)) {
1222 return oh;
1223 }
1224 if (cur.extras != null && cur.extras.equals(oh.extras)) {
1225 return oh;
1226 }
1227 if (cur.title != null && cur.title.equals(oh.title)) {
1228 return oh;
1229 }
1230 }
1231 }
1232 return null;
1233 }
1234
1235 /**
Andrew Stadleraa904f42010-09-02 14:50:08 -07001236 * Start a new fragment.
1237 *
1238 * @param fragment The fragment to start
1239 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1240 * the current fragment will be replaced.
1241 */
1242 public void startPreferenceFragment(Fragment fragment, boolean push) {
Dianne Hackborn48e7b452011-01-17 12:28:35 -08001243 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001244 transaction.replace(com.android.internal.R.id.prefs, fragment);
Andrew Stadleraa904f42010-09-02 14:50:08 -07001245 if (push) {
Chet Haase9ff82bf2010-10-05 14:30:51 -07001246 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
Andrew Stadleraa904f42010-09-02 14:50:08 -07001247 transaction.addToBackStack(BACK_STACK_PREFS);
Chet Haase9ff82bf2010-10-05 14:30:51 -07001248 } else {
Dianne Hackborn327fbd22011-01-17 14:38:50 -08001249 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
Andrew Stadleraa904f42010-09-02 14:50:08 -07001250 }
Dianne Hackborncf407ad2011-03-11 13:17:57 -08001251 transaction.commitAllowingStateLoss();
Andrew Stadleraa904f42010-09-02 14:50:08 -07001252 }
1253
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001254 /**
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001255 * Start a new fragment containing a preference panel. If the prefences
1256 * are being displayed in multi-pane mode, the given fragment class will
1257 * be instantiated and placed in the appropriate pane. If running in
1258 * single-pane mode, a new activity will be launched in which to show the
1259 * fragment.
1260 *
1261 * @param fragmentClass Full name of the class implementing the fragment.
1262 * @param args Any desired arguments to supply to the fragment.
1263 * @param titleRes Optional resource identifier of the title of this
1264 * fragment.
1265 * @param titleText Optional text of the title of this fragment.
1266 * @param resultTo Optional fragment that result data should be sent to.
1267 * If non-null, resultTo.onActivityResult() will be called when this
1268 * preference panel is done. The launched panel must use
1269 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
1270 * @param resultRequestCode If resultTo is non-null, this is the caller's
1271 * request code to be received with the resut.
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001272 */
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001273 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
1274 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
1275 if (mSinglePane) {
Dianne Hackborne72f2372011-03-16 10:43:18 -07001276 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, 0);
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001277 } else {
1278 Fragment f = Fragment.instantiate(this, fragmentClass, args);
1279 if (resultTo != null) {
1280 f.setTargetFragment(resultTo, resultRequestCode);
1281 }
Dianne Hackborn48e7b452011-01-17 12:28:35 -08001282 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001283 transaction.replace(com.android.internal.R.id.prefs, f);
1284 if (titleRes != 0) {
1285 transaction.setBreadCrumbTitle(titleRes);
1286 } else if (titleText != null) {
1287 transaction.setBreadCrumbTitle(titleText);
1288 }
1289 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1290 transaction.addToBackStack(BACK_STACK_PREFS);
Dianne Hackborncf407ad2011-03-11 13:17:57 -08001291 transaction.commitAllowingStateLoss();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001292 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001293 }
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001294
1295 /**
1296 * Called by a preference panel fragment to finish itself.
1297 *
1298 * @param caller The fragment that is asking to be finished.
1299 * @param resultCode Optional result code to send back to the original
1300 * launching fragment.
1301 * @param resultData Optional result data to send back to the original
1302 * launching fragment.
1303 */
1304 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
1305 if (mSinglePane) {
1306 setResult(resultCode, resultData);
1307 finish();
1308 } else {
Dianne Hackborn3a57fb92010-11-15 17:58:52 -08001309 // XXX be smarter about popping the stack.
1310 onBackPressed();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001311 if (caller != null) {
1312 if (caller.getTargetFragment() != null) {
1313 caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(),
1314 resultCode, resultData);
1315 }
1316 }
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001317 }
1318 }
1319
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -07001320 @Override
1321 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
Dianne Hackborne72f2372011-03-16 10:43:18 -07001322 startPreferencePanel(pref.getFragment(), pref.getExtras(), pref.getTitleRes(),
1323 pref.getTitle(), null, 0);
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -07001324 return true;
1325 }
1326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 /**
1328 * Posts a message to bind the preferences to the list view.
1329 * <p>
1330 * Binding late is preferred as any custom preference types created in
1331 * {@link #onCreate(Bundle)} are able to have their views recycled.
1332 */
1333 private void postBindPreferences() {
1334 if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
1335 mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
1336 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001337
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338 private void bindPreferences() {
1339 final PreferenceScreen preferenceScreen = getPreferenceScreen();
1340 if (preferenceScreen != null) {
1341 preferenceScreen.bind(getListView());
Adam Powelle7fea452010-03-18 14:51:39 -07001342 if (mSavedInstanceState != null) {
1343 super.onRestoreInstanceState(mSavedInstanceState);
1344 mSavedInstanceState = null;
1345 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 }
1347 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 * Returns the {@link PreferenceManager} used by this activity.
1351 * @return The {@link PreferenceManager}.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001352 *
1353 * @deprecated This function is not relevant for a modern fragment-based
1354 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001356 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 public PreferenceManager getPreferenceManager() {
1358 return mPreferenceManager;
1359 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 private void requirePreferenceManager() {
1362 if (mPreferenceManager == null) {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001363 if (mAdapter == null) {
1364 throw new RuntimeException("This should be called after super.onCreate.");
1365 }
1366 throw new RuntimeException(
1367 "Modern two-pane PreferenceActivity requires use of a PreferenceFragment");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 }
1369 }
1370
1371 /**
1372 * Sets the root of the preference hierarchy that this activity is showing.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001373 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001375 *
1376 * @deprecated This function is not relevant for a modern fragment-based
1377 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001379 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001381 requirePreferenceManager();
1382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
1384 postBindPreferences();
1385 CharSequence title = getPreferenceScreen().getTitle();
1386 // Set the title of the activity
1387 if (title != null) {
1388 setTitle(title);
1389 }
1390 }
1391 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001392
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 /**
1394 * Gets the root of the preference hierarchy that this activity is showing.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001395 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 * @return The {@link PreferenceScreen} that is the root of the preference
1397 * hierarchy.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001398 *
1399 * @deprecated This function is not relevant for a modern fragment-based
1400 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001402 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 public PreferenceScreen getPreferenceScreen() {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001404 if (mPreferenceManager != null) {
1405 return mPreferenceManager.getPreferenceScreen();
1406 }
1407 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 /**
1411 * Adds preferences from activities that match the given {@link Intent}.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001412 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 * @param intent The {@link Intent} to query activities.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001414 *
1415 * @deprecated This function is not relevant for a modern fragment-based
1416 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001418 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 public void addPreferencesFromIntent(Intent intent) {
1420 requirePreferenceManager();
Freeman Ng19ea2e02010-03-25 15:09:00 -07001421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422 setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
1423 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001424
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001425 /**
1426 * Inflates the given XML resource and adds the preference hierarchy to the current
1427 * preference hierarchy.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001428 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 * @param preferencesResId The XML resource ID to inflate.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001430 *
1431 * @deprecated This function is not relevant for a modern fragment-based
1432 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001433 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001434 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 public void addPreferencesFromResource(int preferencesResId) {
1436 requirePreferenceManager();
Freeman Ng19ea2e02010-03-25 15:09:00 -07001437
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId,
1439 getPreferenceScreen()));
1440 }
1441
1442 /**
1443 * {@inheritDoc}
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001444 *
1445 * @deprecated This function is not relevant for a modern fragment-based
1446 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001448 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
1450 return false;
1451 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 /**
1454 * Finds a {@link Preference} based on its key.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001455 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 * @param key The key of the preference to retrieve.
1457 * @return The {@link Preference} with the key, or null.
1458 * @see PreferenceGroup#findPreference(CharSequence)
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001459 *
1460 * @deprecated This function is not relevant for a modern fragment-based
1461 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001463 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001464 public Preference findPreference(CharSequence key) {
Freeman Ng19ea2e02010-03-25 15:09:00 -07001465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 if (mPreferenceManager == null) {
1467 return null;
1468 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 return mPreferenceManager.findPreference(key);
1471 }
1472
1473 @Override
1474 protected void onNewIntent(Intent intent) {
1475 if (mPreferenceManager != null) {
1476 mPreferenceManager.dispatchNewIntent(intent);
1477 }
1478 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001479
1480 // give subclasses access to the Next button
1481 /** @hide */
1482 protected boolean hasNextButton() {
1483 return mNextButton != null;
1484 }
1485 /** @hide */
1486 protected Button getNextButton() {
1487 return mNextButton;
1488 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001489}