blob: a9ee96ec76c793fc348037c006988bf3c40b37e6 [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) {
976 super.onListItemClick(l, v, position, id);
977
978 if (mAdapter != null) {
Gilles Debunne39725ac2011-06-14 18:52:41 -0700979 Object item = mAdapter.getItem(position);
980 if (item instanceof Header) onHeaderClick((Header) item, position);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700981 }
982 }
983
984 /**
985 * Called when the user selects an item in the header list. The default
Dianne Hackborne72f2372011-03-16 10:43:18 -0700986 * implementation will call either
987 * {@link #startWithFragment(String, Bundle, Fragment, int, int, int)}
Dianne Hackborna21e3da2010-09-12 19:27:46 -0700988 * or {@link #switchToHeader(Header)} as appropriate.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -0700989 *
990 * @param header The header that was selected.
991 * @param position The header's position in the list.
992 */
993 public void onHeaderClick(Header header, int position) {
Dianne Hackborn5c769a42010-08-26 17:08:08 -0700994 if (header.fragment != null) {
995 if (mSinglePane) {
Dianne Hackborne72f2372011-03-16 10:43:18 -0700996 int titleRes = header.breadCrumbTitleRes;
997 int shortTitleRes = header.breadCrumbShortTitleRes;
998 if (titleRes == 0) {
999 titleRes = header.titleRes;
1000 shortTitleRes = 0;
1001 }
1002 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
1003 titleRes, shortTitleRes);
Dianne Hackborn5c769a42010-08-26 17:08:08 -07001004 } else {
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001005 switchToHeader(header);
Dianne Hackborn5c769a42010-08-26 17:08:08 -07001006 }
1007 } else if (header.intent != null) {
1008 startActivity(header.intent);
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001009 }
1010 }
1011
1012 /**
Dianne Hackborne72f2372011-03-16 10:43:18 -07001013 * Called by {@link #startWithFragment(String, Bundle, Fragment, int, int, int)} when
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001014 * in single-pane mode, to build an Intent to launch a new activity showing
1015 * the selected fragment. The default implementation constructs an Intent
1016 * that re-launches the current activity with the appropriate arguments to
1017 * display the fragment.
1018 *
1019 * @param fragmentName The name of the fragment to display.
1020 * @param args Optional arguments to supply to the fragment.
Dianne Hackborne72f2372011-03-16 10:43:18 -07001021 * @param titleRes Optional resource ID of title to show for this item.
Gilles Debunne39725ac2011-06-14 18:52:41 -07001022 * @param shortTitleRes Optional resource ID of short title to show for this item.
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001023 * @return Returns an Intent that can be launched to display the given
1024 * fragment.
1025 */
Dianne Hackborne72f2372011-03-16 10:43:18 -07001026 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
1027 int titleRes, int shortTitleRes) {
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001028 Intent intent = new Intent(Intent.ACTION_MAIN);
1029 intent.setClass(this, getClass());
1030 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
1031 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Dianne Hackborne72f2372011-03-16 10:43:18 -07001032 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, titleRes);
1033 intent.putExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, shortTitleRes);
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001034 intent.putExtra(EXTRA_NO_HEADERS, true);
1035 return intent;
1036 }
1037
1038 /**
Dianne Hackborne72f2372011-03-16 10:43:18 -07001039 * Like {@link #startWithFragment(String, Bundle, Fragment, int, int, int)}
1040 * but uses a 0 titleRes.
1041 */
1042 public void startWithFragment(String fragmentName, Bundle args,
1043 Fragment resultTo, int resultRequestCode) {
1044 startWithFragment(fragmentName, args, resultTo, resultRequestCode, 0, 0);
1045 }
1046
1047 /**
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001048 * Start a new instance of this activity, showing only the given
1049 * preference fragment. When launched in this mode, the header list
1050 * will be hidden and the given preference fragment will be instantiated
1051 * and fill the entire activity.
1052 *
1053 * @param fragmentName The name of the fragment to display.
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001054 * @param args Optional arguments to supply to the fragment.
Dianne Hackbornb1a6e4392011-03-15 16:23:01 -07001055 * @param resultTo Option fragment that should receive the result of
1056 * the activity launch.
1057 * @param resultRequestCode If resultTo is non-null, this is the request
1058 * code in which to report the result.
Dianne Hackborne72f2372011-03-16 10:43:18 -07001059 * @param titleRes Resource ID of string to display for the title of
1060 * this set of preferences.
Gilles Debunne39725ac2011-06-14 18:52:41 -07001061 * @param shortTitleRes Resource ID of string to display for the short title of
Dianne Hackborne72f2372011-03-16 10:43:18 -07001062 * this set of preferences.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001063 */
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001064 public void startWithFragment(String fragmentName, Bundle args,
Dianne Hackborne72f2372011-03-16 10:43:18 -07001065 Fragment resultTo, int resultRequestCode, int titleRes, int shortTitleRes) {
1066 Intent intent = onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes);
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001067 if (resultTo == null) {
1068 startActivity(intent);
1069 } else {
1070 resultTo.startActivityForResult(intent, resultRequestCode);
1071 }
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001072 }
1073
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001074 /**
1075 * Change the base title of the bread crumbs for the current preferences.
1076 * This will normally be called for you. See
1077 * {@link android.app.FragmentBreadCrumbs} for more information.
1078 */
1079 public void showBreadCrumbs(CharSequence title, CharSequence shortTitle) {
1080 if (mFragmentBreadCrumbs == null) {
Amith Yamasani3e860402010-12-10 14:20:51 -08001081 View crumbs = findViewById(android.R.id.title);
1082 // For screens with a different kind of title, don't create breadcrumbs.
Dianne Hackborne72f2372011-03-16 10:43:18 -07001083 try {
1084 mFragmentBreadCrumbs = (FragmentBreadCrumbs)crumbs;
1085 } catch (ClassCastException e) {
1086 return;
1087 }
Amith Yamasani3c9f5192010-12-08 16:48:31 -08001088 if (mFragmentBreadCrumbs == null) {
Dianne Hackborne72f2372011-03-16 10:43:18 -07001089 if (title != null) {
1090 setTitle(title);
Amith Yamasani3c9f5192010-12-08 16:48:31 -08001091 }
Dianne Hackborne72f2372011-03-16 10:43:18 -07001092 return;
Jim Millerc57406c2010-12-08 16:01:05 -08001093 }
Amith Yamasani3ec7bac2012-10-03 16:02:51 -07001094 if (mSinglePane) {
1095 mFragmentBreadCrumbs.setVisibility(View.GONE);
1096 // Hide the breadcrumb section completely for single-pane
1097 View bcSection = findViewById(com.android.internal.R.id.breadcrumb_section);
1098 if (bcSection != null) bcSection.setVisibility(View.GONE);
1099 }
Amith Yamasani3c9f5192010-12-08 16:48:31 -08001100 mFragmentBreadCrumbs.setMaxVisible(2);
1101 mFragmentBreadCrumbs.setActivity(this);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001102 }
1103 mFragmentBreadCrumbs.setTitle(title, shortTitle);
Amith Yamasanic9ecb732010-12-14 14:23:21 -08001104 mFragmentBreadCrumbs.setParentTitle(null, null, null);
1105 }
1106
1107 /**
1108 * Should be called after onCreate to ensure that the breadcrumbs, if any, were created.
1109 * This prepends a title to the fragment breadcrumbs and attaches a listener to any clicks
1110 * on the parent entry.
1111 * @param title the title for the breadcrumb
1112 * @param shortTitle the short title for the breadcrumb
1113 */
1114 public void setParentTitle(CharSequence title, CharSequence shortTitle,
1115 OnClickListener listener) {
1116 if (mFragmentBreadCrumbs != null) {
1117 mFragmentBreadCrumbs.setParentTitle(title, shortTitle, listener);
1118 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001119 }
1120
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001121 void setSelectedHeader(Header header) {
1122 mCurHeader = header;
1123 int index = mHeaders.indexOf(header);
1124 if (index >= 0) {
1125 getListView().setItemChecked(index, true);
1126 } else {
1127 getListView().clearChoices();
1128 }
Dianne Hackborn34905a92011-07-21 17:30:07 -07001129 showBreadCrumbs(header);
1130 }
1131
1132 void showBreadCrumbs(Header header) {
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001133 if (header != null) {
Dianne Hackborn50ed8292010-12-03 12:30:21 -08001134 CharSequence title = header.getBreadCrumbTitle(getResources());
1135 if (title == null) title = header.getTitle(getResources());
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001136 if (title == null) title = getTitle();
Dianne Hackborn50ed8292010-12-03 12:30:21 -08001137 showBreadCrumbs(title, header.getBreadCrumbShortTitle(getResources()));
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001138 } else {
1139 showBreadCrumbs(getTitle(), null);
1140 }
1141 }
1142
Amith Yamasanif5cbaed2010-10-27 16:14:20 -07001143 private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
Dianne Hackborn3a57fb92010-11-15 17:58:52 -08001144 getFragmentManager().popBackStack(BACK_STACK_PREFS,
1145 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001146 Fragment f = Fragment.instantiate(this, fragmentName, args);
Dianne Hackborn48e7b452011-01-17 12:28:35 -08001147 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Dianne Hackborn327fbd22011-01-17 14:38:50 -08001148 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
Chet Haase9ff82bf2010-10-05 14:30:51 -07001149 transaction.replace(com.android.internal.R.id.prefs, f);
Dianne Hackborncf407ad2011-03-11 13:17:57 -08001150 transaction.commitAllowingStateLoss();
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001151 }
1152
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001153 /**
1154 * When in two-pane mode, switch the fragment pane to show the given
1155 * preference fragment.
1156 *
1157 * @param fragmentName The name of the fragment to display.
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001158 * @param args Optional arguments to supply to the fragment.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001159 */
Dianne Hackbornb7a2e472010-08-12 16:20:42 -07001160 public void switchToHeader(String fragmentName, Bundle args) {
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001161 setSelectedHeader(null);
Amith Yamasanif5cbaed2010-10-27 16:14:20 -07001162 switchToHeaderInner(fragmentName, args, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 }
1164
Andrew Stadleraa904f42010-09-02 14:50:08 -07001165 /**
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001166 * When in two-pane mode, switch to the fragment pane to show the given
1167 * preference fragment.
1168 *
1169 * @param header The new header to display.
1170 */
1171 public void switchToHeader(Header header) {
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001172 if (mCurHeader == header) {
1173 // This is the header we are currently displaying. Just make sure
1174 // to pop the stack up to its root state.
Dianne Hackborn3a57fb92010-11-15 17:58:52 -08001175 getFragmentManager().popBackStack(BACK_STACK_PREFS,
1176 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001177 } else {
Dianne Hackborn19470ec2013-02-12 11:42:51 -08001178 if (header.fragment == null) {
1179 throw new IllegalStateException("can't switch to header that has no fragment");
1180 }
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001181 int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
1182 switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
1183 setSelectedHeader(header);
1184 }
Dianne Hackborna21e3da2010-09-12 19:27:46 -07001185 }
1186
1187 Header findBestMatchingHeader(Header cur, ArrayList<Header> from) {
1188 ArrayList<Header> matches = new ArrayList<Header>();
1189 for (int j=0; j<from.size(); j++) {
1190 Header oh = from.get(j);
1191 if (cur == oh || (cur.id != HEADER_ID_UNDEFINED && cur.id == oh.id)) {
1192 // Must be this one.
1193 matches.clear();
1194 matches.add(oh);
1195 break;
1196 }
1197 if (cur.fragment != null) {
1198 if (cur.fragment.equals(oh.fragment)) {
1199 matches.add(oh);
1200 }
1201 } else if (cur.intent != null) {
1202 if (cur.intent.equals(oh.intent)) {
1203 matches.add(oh);
1204 }
1205 } else if (cur.title != null) {
1206 if (cur.title.equals(oh.title)) {
1207 matches.add(oh);
1208 }
1209 }
1210 }
1211 final int NM = matches.size();
1212 if (NM == 1) {
1213 return matches.get(0);
1214 } else if (NM > 1) {
1215 for (int j=0; j<NM; j++) {
1216 Header oh = matches.get(j);
1217 if (cur.fragmentArguments != null &&
1218 cur.fragmentArguments.equals(oh.fragmentArguments)) {
1219 return oh;
1220 }
1221 if (cur.extras != null && cur.extras.equals(oh.extras)) {
1222 return oh;
1223 }
1224 if (cur.title != null && cur.title.equals(oh.title)) {
1225 return oh;
1226 }
1227 }
1228 }
1229 return null;
1230 }
1231
1232 /**
Andrew Stadleraa904f42010-09-02 14:50:08 -07001233 * Start a new fragment.
1234 *
1235 * @param fragment The fragment to start
1236 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1237 * the current fragment will be replaced.
1238 */
1239 public void startPreferenceFragment(Fragment fragment, boolean push) {
Dianne Hackborn48e7b452011-01-17 12:28:35 -08001240 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001241 transaction.replace(com.android.internal.R.id.prefs, fragment);
Andrew Stadleraa904f42010-09-02 14:50:08 -07001242 if (push) {
Chet Haase9ff82bf2010-10-05 14:30:51 -07001243 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
Andrew Stadleraa904f42010-09-02 14:50:08 -07001244 transaction.addToBackStack(BACK_STACK_PREFS);
Chet Haase9ff82bf2010-10-05 14:30:51 -07001245 } else {
Dianne Hackborn327fbd22011-01-17 14:38:50 -08001246 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
Andrew Stadleraa904f42010-09-02 14:50:08 -07001247 }
Dianne Hackborncf407ad2011-03-11 13:17:57 -08001248 transaction.commitAllowingStateLoss();
Andrew Stadleraa904f42010-09-02 14:50:08 -07001249 }
1250
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001251 /**
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001252 * Start a new fragment containing a preference panel. If the prefences
1253 * are being displayed in multi-pane mode, the given fragment class will
1254 * be instantiated and placed in the appropriate pane. If running in
1255 * single-pane mode, a new activity will be launched in which to show the
1256 * fragment.
1257 *
1258 * @param fragmentClass Full name of the class implementing the fragment.
1259 * @param args Any desired arguments to supply to the fragment.
1260 * @param titleRes Optional resource identifier of the title of this
1261 * fragment.
1262 * @param titleText Optional text of the title of this fragment.
1263 * @param resultTo Optional fragment that result data should be sent to.
1264 * If non-null, resultTo.onActivityResult() will be called when this
1265 * preference panel is done. The launched panel must use
1266 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
1267 * @param resultRequestCode If resultTo is non-null, this is the caller's
1268 * request code to be received with the resut.
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001269 */
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001270 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
1271 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
1272 if (mSinglePane) {
Dianne Hackborne72f2372011-03-16 10:43:18 -07001273 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, 0);
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001274 } else {
1275 Fragment f = Fragment.instantiate(this, fragmentClass, args);
1276 if (resultTo != null) {
1277 f.setTargetFragment(resultTo, resultRequestCode);
1278 }
Dianne Hackborn48e7b452011-01-17 12:28:35 -08001279 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001280 transaction.replace(com.android.internal.R.id.prefs, f);
1281 if (titleRes != 0) {
1282 transaction.setBreadCrumbTitle(titleRes);
1283 } else if (titleText != null) {
1284 transaction.setBreadCrumbTitle(titleText);
1285 }
1286 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1287 transaction.addToBackStack(BACK_STACK_PREFS);
Dianne Hackborncf407ad2011-03-11 13:17:57 -08001288 transaction.commitAllowingStateLoss();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001289 }
Dianne Hackbornc6669ca2010-09-16 01:33:24 -07001290 }
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001291
1292 /**
1293 * Called by a preference panel fragment to finish itself.
1294 *
1295 * @param caller The fragment that is asking to be finished.
1296 * @param resultCode Optional result code to send back to the original
1297 * launching fragment.
1298 * @param resultData Optional result data to send back to the original
1299 * launching fragment.
1300 */
1301 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
1302 if (mSinglePane) {
1303 setResult(resultCode, resultData);
1304 finish();
1305 } else {
Dianne Hackborn3a57fb92010-11-15 17:58:52 -08001306 // XXX be smarter about popping the stack.
1307 onBackPressed();
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001308 if (caller != null) {
1309 if (caller.getTargetFragment() != null) {
1310 caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(),
1311 resultCode, resultData);
1312 }
1313 }
Dianne Hackborn8eb2e242010-11-01 12:31:24 -07001314 }
1315 }
1316
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -07001317 @Override
1318 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
Dianne Hackborne72f2372011-03-16 10:43:18 -07001319 startPreferencePanel(pref.getFragment(), pref.getExtras(), pref.getTitleRes(),
1320 pref.getTitle(), null, 0);
Dianne Hackbornb3cf10f2010-08-03 13:07:11 -07001321 return true;
1322 }
1323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 /**
1325 * Posts a message to bind the preferences to the list view.
1326 * <p>
1327 * Binding late is preferred as any custom preference types created in
1328 * {@link #onCreate(Bundle)} are able to have their views recycled.
1329 */
1330 private void postBindPreferences() {
1331 if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
1332 mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
1333 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 private void bindPreferences() {
1336 final PreferenceScreen preferenceScreen = getPreferenceScreen();
1337 if (preferenceScreen != null) {
1338 preferenceScreen.bind(getListView());
Adam Powelle7fea452010-03-18 14:51:39 -07001339 if (mSavedInstanceState != null) {
1340 super.onRestoreInstanceState(mSavedInstanceState);
1341 mSavedInstanceState = null;
1342 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 }
1344 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 * Returns the {@link PreferenceManager} used by this activity.
1348 * @return The {@link PreferenceManager}.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001349 *
1350 * @deprecated This function is not relevant for a modern fragment-based
1351 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001353 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 public PreferenceManager getPreferenceManager() {
1355 return mPreferenceManager;
1356 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 private void requirePreferenceManager() {
1359 if (mPreferenceManager == null) {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001360 if (mAdapter == null) {
1361 throw new RuntimeException("This should be called after super.onCreate.");
1362 }
1363 throw new RuntimeException(
1364 "Modern two-pane PreferenceActivity requires use of a PreferenceFragment");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 }
1366 }
1367
1368 /**
1369 * Sets the root of the preference hierarchy that this activity is showing.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001370 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001372 *
1373 * @deprecated This function is not relevant for a modern fragment-based
1374 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001376 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001378 requirePreferenceManager();
1379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
1381 postBindPreferences();
1382 CharSequence title = getPreferenceScreen().getTitle();
1383 // Set the title of the activity
1384 if (title != null) {
1385 setTitle(title);
1386 }
1387 }
1388 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001389
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 /**
1391 * Gets the root of the preference hierarchy that this activity is showing.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001392 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 * @return The {@link PreferenceScreen} that is the root of the preference
1394 * hierarchy.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001395 *
1396 * @deprecated This function is not relevant for a modern fragment-based
1397 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001399 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 public PreferenceScreen getPreferenceScreen() {
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001401 if (mPreferenceManager != null) {
1402 return mPreferenceManager.getPreferenceScreen();
1403 }
1404 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001406
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 /**
1408 * Adds preferences from activities that match the given {@link Intent}.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001409 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 * @param intent The {@link Intent} to query activities.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001411 *
1412 * @deprecated This function is not relevant for a modern fragment-based
1413 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001415 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 public void addPreferencesFromIntent(Intent intent) {
1417 requirePreferenceManager();
Freeman Ng19ea2e02010-03-25 15:09:00 -07001418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
1420 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422 /**
1423 * Inflates the given XML resource and adds the preference hierarchy to the current
1424 * preference hierarchy.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001425 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 * @param preferencesResId The XML resource ID to inflate.
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001427 *
1428 * @deprecated This function is not relevant for a modern fragment-based
1429 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001430 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001431 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 public void addPreferencesFromResource(int preferencesResId) {
1433 requirePreferenceManager();
Freeman Ng19ea2e02010-03-25 15:09:00 -07001434
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId,
1436 getPreferenceScreen()));
1437 }
1438
1439 /**
1440 * {@inheritDoc}
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001441 *
1442 * @deprecated This function is not relevant for a modern fragment-based
1443 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001445 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
1447 return false;
1448 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450 /**
1451 * Finds a {@link Preference} based on its key.
Freeman Ng19ea2e02010-03-25 15:09:00 -07001452 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 * @param key The key of the preference to retrieve.
1454 * @return The {@link Preference} with the key, or null.
1455 * @see PreferenceGroup#findPreference(CharSequence)
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001456 *
1457 * @deprecated This function is not relevant for a modern fragment-based
1458 * PreferenceActivity.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001459 */
Dianne Hackbornb1ad5972010-08-02 17:30:33 -07001460 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 public Preference findPreference(CharSequence key) {
Freeman Ng19ea2e02010-03-25 15:09:00 -07001462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 if (mPreferenceManager == null) {
1464 return null;
1465 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001466
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 return mPreferenceManager.findPreference(key);
1468 }
1469
1470 @Override
1471 protected void onNewIntent(Intent intent) {
1472 if (mPreferenceManager != null) {
1473 mPreferenceManager.dispatchNewIntent(intent);
1474 }
1475 }
Freeman Ng19ea2e02010-03-25 15:09:00 -07001476
1477 // give subclasses access to the Next button
1478 /** @hide */
1479 protected boolean hasNextButton() {
1480 return mNextButton != null;
1481 }
1482 /** @hide */
1483 protected Button getNextButton() {
1484 return mNextButton;
1485 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486}