blob: 443013899f45124fb6f537b84e5e5768695e26ac [file] [log] [blame]
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001/*
2 * Copyright (C) 2014 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 com.android.settings;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
22import android.app.ActionBar;
23import android.app.Activity;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
27import android.app.AlertDialog;
28import android.app.Dialog;
29import android.app.DialogFragment;
30import android.app.admin.DevicePolicyManager;
31import android.content.BroadcastReceiver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.SharedPreferences;
36import android.content.pm.ActivityInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.content.pm.ResolveInfo;
40import android.content.res.Configuration;
41import android.content.res.Resources;
42import android.content.res.TypedArray;
43import android.content.res.XmlResourceParser;
44import android.graphics.drawable.Drawable;
45import android.nfc.NfcAdapter;
46import android.os.Bundle;
47import android.os.Handler;
48import android.os.INetworkManagementService;
49import android.os.Message;
50import android.os.Parcel;
51import android.os.Parcelable;
52import android.os.RemoteException;
53import android.os.ServiceManager;
54import android.os.UserHandle;
55import android.os.UserManager;
56import android.preference.Preference;
57import android.preference.PreferenceFragment;
58import android.preference.PreferenceManager;
59import android.preference.PreferenceScreen;
60import android.support.v4.app.ActionBarDrawerToggle;
61import android.support.v4.widget.DrawerLayout;
62import android.text.TextUtils;
63import android.util.AttributeSet;
64import android.util.Log;
65import android.util.Pair;
66import android.util.TypedValue;
67import android.util.Xml;
Fabrice Di Megliodca28062014-02-21 17:42:56 -080068import android.view.ContextThemeWrapper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080069import android.view.LayoutInflater;
70import android.view.MenuItem;
71import android.view.View;
72import android.view.View.OnClickListener;
73import android.view.ViewGroup;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +000074import android.widget.AbsListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080075import android.widget.AdapterView;
76import android.widget.ArrayAdapter;
77import android.widget.Button;
78import android.widget.ImageButton;
79import android.widget.ImageView;
80import android.widget.ListView;
81import android.widget.Switch;
82import android.widget.TextView;
83
84import com.android.internal.util.ArrayUtils;
85import com.android.internal.util.XmlUtils;
86import com.android.settings.accessibility.AccessibilitySettings;
87import com.android.settings.accessibility.CaptionPropertiesFragment;
88import com.android.settings.accounts.AccountSyncSettings;
89import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080090import com.android.settings.accounts.ManageAccountsSettings;
91import com.android.settings.applications.ManageApplications;
92import com.android.settings.applications.ProcessStatsUi;
93import com.android.settings.bluetooth.BluetoothEnabler;
94import com.android.settings.bluetooth.BluetoothSettings;
95import com.android.settings.dashboard.DashboardSummary;
96import com.android.settings.deviceinfo.Memory;
97import com.android.settings.deviceinfo.UsbSettings;
98import com.android.settings.fuelgauge.PowerUsageSummary;
99import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
100import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
101import com.android.settings.inputmethod.SpellCheckersSettings;
102import com.android.settings.inputmethod.UserDictionaryList;
103import com.android.settings.location.LocationSettings;
104import com.android.settings.nfc.AndroidBeam;
105import com.android.settings.nfc.PaymentSettings;
106import com.android.settings.print.PrintJobSettingsFragment;
107import com.android.settings.print.PrintSettingsFragment;
108import com.android.settings.tts.TextToSpeechSettings;
109import com.android.settings.users.UserSettings;
110import com.android.settings.vpn2.VpnSettings;
111import com.android.settings.wfd.WifiDisplaySettings;
112import com.android.settings.wifi.AdvancedWifiSettings;
113import com.android.settings.wifi.WifiEnabler;
114import com.android.settings.wifi.WifiSettings;
115import com.android.settings.wifi.p2p.WifiP2pSettings;
116import org.xmlpull.v1.XmlPullParser;
117import org.xmlpull.v1.XmlPullParserException;
118
119import java.io.IOException;
120import java.util.ArrayList;
121import java.util.Collections;
122import java.util.Comparator;
123import java.util.HashMap;
124import java.util.List;
125
126public class SettingsActivity extends Activity
127 implements PreferenceManager.OnPreferenceTreeClickListener,
128 PreferenceFragment.OnPreferenceStartFragmentCallback,
129 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener {
130
131 private static final String LOG_TAG = "Settings";
132
133 // Constants for state save/restore
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800134 private static final String SAVE_KEY_HEADERS_TAG = ":settings:headers";
135 private static final String SAVE_KEY_CURRENT_HEADER_TAG = ":settings:cur_header";
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800136 private static final String SAVE_KEY_TITLES_TAG = ":settings:titles";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800137
138 /**
139 * When starting this activity, the invoking Intent can contain this extra
140 * string to specify which fragment should be initially displayed.
141 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
142 * will call isValidFragment() to confirm that the fragment class name is valid for this
143 * activity.
144 */
145 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
146
147 /**
148 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
149 * this extra can also be specified to supply a Bundle of arguments to pass
150 * to that fragment when it is instantiated during the initial creation
151 * of the activity.
152 */
153 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
154
155 /**
156 * When starting this activity, the invoking Intent can contain this extra
157 * boolean that the header list should not be displayed. This is most often
158 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
159 * the activity to display a specific fragment that the user has navigated
160 * to.
161 */
162 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
163
164 // extras that allow any preference activity to be launched as part of a wizard
165
166 // show Back and Next buttons? takes boolean parameter
167 // Back will then return RESULT_CANCELED and Next RESULT_OK
168 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
169
170 // add a Skip button?
171 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
172
173 // specify custom text for the Back or Next buttons, or cause a button to not appear
174 // at all by setting it to null
175 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
176 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
177
178 /**
179 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
180 * this extra can also be specify to supply the title to be shown for
181 * that fragment.
182 */
183 protected static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
184
185 private static final String BACK_STACK_PREFS = ":settings:prefs";
186
187 private static final String META_DATA_KEY_HEADER_ID =
188 "com.android.settings.TOP_LEVEL_HEADER_ID";
189
190 private static final String META_DATA_KEY_FRAGMENT_CLASS =
191 "com.android.settings.FRAGMENT_CLASS";
192
193 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800195 private static boolean sShowNoHomeNotice = false;
196
197 private String mFragmentClass;
198 private int mTopLevelHeaderId;
199 private Header mFirstHeader;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800200 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800201 private Header mCurrentHeader;
202
203 // Show only these settings for restricted users
204 private int[] SETTINGS_FOR_RESTRICTED = {
205 R.id.wireless_section,
206 R.id.wifi_settings,
207 R.id.bluetooth_settings,
208 R.id.data_usage_settings,
209 R.id.wireless_settings,
210 R.id.device_section,
211 R.id.sound_settings,
212 R.id.display_settings,
213 R.id.storage_settings,
214 R.id.application_settings,
215 R.id.battery_settings,
216 R.id.personal_section,
217 R.id.location_settings,
218 R.id.security_settings,
219 R.id.language_settings,
220 R.id.user_settings,
221 R.id.account_settings,
222 R.id.account_add,
223 R.id.system_section,
224 R.id.date_time_settings,
225 R.id.about_settings,
226 R.id.accessibility_settings,
227 R.id.print_settings,
228 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800229 R.id.home_settings,
230 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800231 };
232
233 private static final String[] ENTRY_FRAGMENTS = {
234 WirelessSettings.class.getName(),
235 WifiSettings.class.getName(),
236 AdvancedWifiSettings.class.getName(),
237 BluetoothSettings.class.getName(),
238 TetherSettings.class.getName(),
239 WifiP2pSettings.class.getName(),
240 VpnSettings.class.getName(),
241 DateTimeSettings.class.getName(),
242 LocalePicker.class.getName(),
243 InputMethodAndLanguageSettings.class.getName(),
244 SpellCheckersSettings.class.getName(),
245 UserDictionaryList.class.getName(),
246 UserDictionarySettings.class.getName(),
247 SoundSettings.class.getName(),
248 DisplaySettings.class.getName(),
249 DeviceInfoSettings.class.getName(),
250 ManageApplications.class.getName(),
251 ProcessStatsUi.class.getName(),
252 NotificationStation.class.getName(),
253 LocationSettings.class.getName(),
254 SecuritySettings.class.getName(),
255 PrivacySettings.class.getName(),
256 DeviceAdminSettings.class.getName(),
257 AccessibilitySettings.class.getName(),
258 CaptionPropertiesFragment.class.getName(),
259 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
260 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
261 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
262 TextToSpeechSettings.class.getName(),
263 Memory.class.getName(),
264 DevelopmentSettings.class.getName(),
265 UsbSettings.class.getName(),
266 AndroidBeam.class.getName(),
267 WifiDisplaySettings.class.getName(),
268 PowerUsageSummary.class.getName(),
269 AccountSyncSettings.class.getName(),
270 CryptKeeperSettings.class.getName(),
271 DataUsageSummary.class.getName(),
272 DreamSettings.class.getName(),
273 UserSettings.class.getName(),
274 NotificationAccessSettings.class.getName(),
275 ManageAccountsSettings.class.getName(),
276 PrintSettingsFragment.class.getName(),
277 PrintJobSettingsFragment.class.getName(),
278 TrustedCredentialsSettings.class.getName(),
279 PaymentSettings.class.getName(),
280 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Meglioe6c9a5d2014-02-11 19:03:27 -0800281 DashboardSummary.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800282 };
283
284 private SharedPreferences mDevelopmentPreferences;
285 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
286
287 // TODO: Update Call Settings based on airplane mode state.
288
289 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
290
291 private AuthenticatorHelper mAuthenticatorHelper;
292 private boolean mListeningToAccountUpdates;
293
294 private Button mNextButton;
295
296 private boolean mBatteryPresent = true;
297 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
298
299 @Override
300 public void onReceive(Context context, Intent intent) {
301 String action = intent.getAction();
302 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
303 boolean batteryPresent = Utils.isBatteryPresent(intent);
304
305 if (mBatteryPresent != batteryPresent) {
306 mBatteryPresent = batteryPresent;
307 invalidateHeaders();
308 }
309 }
310 }
311 };
312
313 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800314 private HeaderAdapter mHeaderAdapter;
315
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800316 static private class TitlePair extends Pair<Integer, CharSequence> implements Parcelable {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800317
318 public TitlePair(Integer first, CharSequence second) {
319 super(first, second);
320 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800321
322 @Override
323 public int describeContents() {
324 return 0;
325 }
326
327 @Override
328 public void writeToParcel(Parcel dest, int flags) {
329 dest.writeInt(first);
330 TextUtils.writeToParcel(second, dest, flags);
331 }
332
333 TitlePair(Parcel in) {
334 super(in.readInt(), TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
335 }
336
337 public static final Creator<TitlePair> CREATOR = new Creator<TitlePair>() {
338 public TitlePair createFromParcel(Parcel source) {
339 return new TitlePair(source);
340 }
341 public TitlePair[] newArray(int size) {
342 return new TitlePair[size];
343 }
344 };
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800345 }
346
347 private final ArrayList<TitlePair> mTitleStack = new ArrayList<TitlePair>();
348
349 private DrawerLayout mDrawerLayout;
350 private ListView mDrawer;
351 private ActionBarDrawerToggle mDrawerToggle;
352 private ActionBar mActionBar;
353
354 private static final int MSG_BUILD_HEADERS = 1;
355 private Handler mHandler = new Handler() {
356 @Override
357 public void handleMessage(Message msg) {
358 switch (msg.what) {
359 case MSG_BUILD_HEADERS: {
360 mHeaders.clear();
361 onBuildHeaders(mHeaders);
362 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800363 if (mCurrentHeader != null) {
364 Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800365 if (mappedHeader != null) {
366 setSelectedHeader(mappedHeader);
367 }
368 }
369 } break;
370 }
371 }
372 };
373
374 @Override
375 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
376 // Override the fragment title for Wallpaper settings
377 int titleRes = pref.getTitleRes();
378 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
379 titleRes = R.string.wallpaper_settings_fragment_title;
380 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
381 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
382 if (UserManager.get(this).isLinkedUser()) {
383 titleRes = R.string.profile_info_settings_title;
384 } else {
385 titleRes = R.string.user_info_settings_title;
386 }
387 }
388 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
389 null, 0);
390 return true;
391 }
392
393 @Override
394 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
395 return false;
396 }
397
398 private class DrawerListener implements DrawerLayout.DrawerListener {
399 @Override
400 public void onDrawerOpened(View drawerView) {
401 mDrawerToggle.onDrawerOpened(drawerView);
402 }
403
404 @Override
405 public void onDrawerClosed(View drawerView) {
406 mDrawerToggle.onDrawerClosed(drawerView);
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800407 // Cannot process clicks when the App is finishing
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800408 if (isFinishing() || mSelectedHeader == null) {
409 return;
410 }
411 switchToHeader(mSelectedHeader, false);
412 mSelectedHeader = null;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800413 }
414
415 @Override
416 public void onDrawerSlide(View drawerView, float slideOffset) {
417 mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
418 }
419
420 @Override
421 public void onDrawerStateChanged(int newState) {
422 mDrawerToggle.onDrawerStateChanged(newState);
423 }
424 }
425
426 private class DrawerItemClickListener implements ListView.OnItemClickListener {
427 @Override
428 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
429 mDrawerLayout.closeDrawer(mDrawer);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000430 onListItemClick((ListView)parent, view, position, id);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800431 }
432 }
433
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800434 private Header findBestMatchingHeader(Header current, ArrayList<Header> from) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800435 ArrayList<Header> matches = new ArrayList<Header>();
436 for (int j=0; j<from.size(); j++) {
437 Header oh = from.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800438 if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800439 // Must be this one.
440 matches.clear();
441 matches.add(oh);
442 break;
443 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800444 if (current.fragment != null) {
445 if (current.fragment.equals(oh.fragment)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800446 matches.add(oh);
447 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800448 } else if (current.intent != null) {
449 if (current.intent.equals(oh.intent)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800450 matches.add(oh);
451 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800452 } else if (current.title != null) {
453 if (current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800454 matches.add(oh);
455 }
456 }
457 }
458 final int NM = matches.size();
459 if (NM == 1) {
460 return matches.get(0);
461 } else if (NM > 1) {
462 for (int j=0; j<NM; j++) {
463 Header oh = matches.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800464 if (current.fragmentArguments != null &&
465 current.fragmentArguments.equals(oh.fragmentArguments)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800466 return oh;
467 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800468 if (current.extras != null && current.extras.equals(oh.extras)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800469 return oh;
470 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800471 if (current.title != null && current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800472 return oh;
473 }
474 }
475 }
476 return null;
477 }
478
479 private void invalidateHeaders() {
480 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
481 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
482 }
483 }
484
485 @Override
486 protected void onPostCreate(Bundle savedInstanceState) {
487 super.onPostCreate(savedInstanceState);
488
489 // Sync the toggle state after onRestoreInstanceState has occurred.
490 if (mDrawerToggle != null) {
491 mDrawerToggle.syncState();
492 }
493 }
494
495 @Override
496 public void onConfigurationChanged(Configuration newConfig) {
497 super.onConfigurationChanged(newConfig);
498 if (mDrawerToggle != null) {
499 mDrawerToggle.onConfigurationChanged(newConfig);
500 }
501 }
502
503 @Override
504 public boolean onOptionsItemSelected(MenuItem item) {
505 if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
506 return true;
507 }
508 return super.onOptionsItemSelected(item);
509 }
510
511 @Override
512 protected void onCreate(Bundle savedInstanceState) {
513 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
514 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
515 }
516
517 mAuthenticatorHelper = new AuthenticatorHelper();
518 mAuthenticatorHelper.updateAuthDescriptions(this);
519 mAuthenticatorHelper.onAccountsUpdated(this, null);
520
521 DevicePolicyManager dpm =
522 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800523
Fabrice Di Meglio0e2f9492014-02-25 14:26:27 -0800524 mHeaderAdapter= new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800525
526 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
527 Context.MODE_PRIVATE);
528
529 getMetaData();
530
531 super.onCreate(savedInstanceState);
532
533 setContentView(R.layout.settings_main);
534
535 getFragmentManager().addOnBackStackChangedListener(this);
536
537 mActionBar = getActionBar();
538 if (mActionBar != null) {
539 mActionBar.setDisplayHomeAsUpEnabled(true);
540 mActionBar.setHomeButtonEnabled(true);
541
542 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800543
544 mDrawer = (ListView) findViewById(R.id.headers_drawer);
545 mDrawer.setAdapter(mHeaderAdapter);
546 mDrawer.setOnItemClickListener(new DrawerItemClickListener());
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000547 mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800548
549 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
550 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
551 }
552
553 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
554 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
555
556 if (savedInstanceState != null) {
557 // We are restarting from a previous saved state; used that to
558 // initialize, instead of starting fresh.
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800559
560 ArrayList<TitlePair> titles =
561 savedInstanceState.getParcelableArrayList(SAVE_KEY_TITLES_TAG);
562 if (titles != null) {
563 mTitleStack.addAll(titles);
564 }
565 final int lastTitle = mTitleStack.size() - 1;
566 if (lastTitle >= 0) {
567 final TitlePair last = mTitleStack.get(lastTitle);
568 setTitleFromPair(last);
569 }
570
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800571 ArrayList<Header> headers =
572 savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800573 if (headers != null) {
574 mHeaders.addAll(headers);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800575 int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG,
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800576 (int) HEADER_ID_UNDEFINED);
577 if (curHeader >= 0 && curHeader < mHeaders.size()) {
578 setSelectedHeader(mHeaders.get(curHeader));
579 }
580 }
581
582 } else {
583 if (initialFragment != null) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000584 // If we are just showing a fragment, we want to run in
585 // new fragment mode, but don't need to compute and show
586 // the headers.
587 switchToHeader(initialFragment, initialArguments, true);
588
589 final int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
590 if (initialTitle != 0) {
591 setTitle(getText(initialTitle));
592 }
593 } else {
594 // We need to try to build the headers.
595 onBuildHeaders(mHeaders);
596
597 // If there are headers, then at this point we need to show
598 // them and, depending on the screen, we may also show in-line
599 // the currently selected preference fragment.
600 if (mHeaders.size() > 0) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800601 Header h = onGetInitialHeader();
602 switchToHeader(h, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000603 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800604 }
605 }
606
607 // see if we should show Back/Next buttons
608 Intent intent = getIntent();
609 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
610
611 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
612 if (buttonBar != null) {
613 buttonBar.setVisibility(View.VISIBLE);
614
615 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
616 backButton.setOnClickListener(new OnClickListener() {
617 public void onClick(View v) {
618 setResult(RESULT_CANCELED);
619 finish();
620 }
621 });
622 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
623 skipButton.setOnClickListener(new OnClickListener() {
624 public void onClick(View v) {
625 setResult(RESULT_OK);
626 finish();
627 }
628 });
629 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
630 mNextButton.setOnClickListener(new OnClickListener() {
631 public void onClick(View v) {
632 setResult(RESULT_OK);
633 finish();
634 }
635 });
636
637 // set our various button parameters
638 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
639 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
640 if (TextUtils.isEmpty(buttonText)) {
641 mNextButton.setVisibility(View.GONE);
642 }
643 else {
644 mNextButton.setText(buttonText);
645 }
646 }
647 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
648 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
649 if (TextUtils.isEmpty(buttonText)) {
650 backButton.setVisibility(View.GONE);
651 }
652 else {
653 backButton.setText(buttonText);
654 }
655 }
656 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
657 skipButton.setVisibility(View.VISIBLE);
658 }
659 }
660 }
661
662 if (!onIsHidingHeaders()) {
663 highlightHeader(mTopLevelHeaderId);
664 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800665 }
666
667 @Override
668 public void onBackStackChanged() {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000669 final int count = getFragmentManager().getBackStackEntryCount() + 1;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800670 TitlePair pair = null;
671 int last;
672 while (mTitleStack.size() > count) {
673 last = mTitleStack.size() - 1;
674 pair = mTitleStack.remove(last);
675 }
676 // Check if we go back
677 if (pair != null) {
678 int size = mTitleStack.size();
679 if (size > 0) {
680 last = mTitleStack.size() - 1;
681 pair = mTitleStack.get(last);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800682 setTitleFromPair(pair);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800683 }
684 }
685 }
686
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800687 private void setTitleFromPair(TitlePair pair) {
688 final CharSequence title;
689 if (pair.first > 0) {
690 title = getText(pair.first);
691 } else {
692 title = pair.second;
693 }
694 setTitle(title);
695 }
696
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800697 /**
698 * Returns the Header list
699 */
700 private List<Header> getHeaders() {
701 return mHeaders;
702 }
703
704 @Override
705 protected void onSaveInstanceState(Bundle outState) {
706 super.onSaveInstanceState(outState);
707
708 if (mHeaders.size() > 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800709 outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders);
710 if (mCurrentHeader != null) {
711 int index = mHeaders.indexOf(mCurrentHeader);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800712 if (index >= 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800713 outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800714 }
715 }
716 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800717
718 if (mTitleStack.size() > 0) {
719 outState.putParcelableList(SAVE_KEY_TITLES_TAG, mTitleStack);
720 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800721 }
722
723 @Override
724 public void onResume() {
725 super.onResume();
726
727 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
728 @Override
729 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
730 invalidateHeaders();
731 }
732 };
733 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
734 mDevelopmentPreferencesListener);
735
736 mHeaderAdapter.resume();
737 invalidateHeaders();
738
739 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800740
741 mDrawerLayout.setDrawerListener(new DrawerListener());
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800742 }
743
744 @Override
745 public void onPause() {
746 super.onPause();
747
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800748 mDrawerLayout.setDrawerListener(null);
749
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800750 unregisterReceiver(mBatteryInfoReceiver);
751
752 mHeaderAdapter.pause();
753
754 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
755 mDevelopmentPreferencesListener);
756
757 mDevelopmentPreferencesListener = null;
758 }
759
760 @Override
761 public void onDestroy() {
762 super.onDestroy();
763 if (mListeningToAccountUpdates) {
764 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
765 }
766 }
767
768 /**
769 * @hide
770 */
771 protected boolean isValidFragment(String fragmentName) {
772 // Almost all fragments are wrapped in this,
773 // except for a few that have their own activities.
774 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
775 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
776 }
777 return false;
778 }
779
780 /**
781 * When in two-pane mode, switch to the fragment pane to show the given
782 * preference fragment.
783 *
784 * @param header The new header to display.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000785 * @param validate true means that the fragment's Header needs to be validated
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800786 */
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000787 private void switchToHeader(Header header, boolean validate) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800788 if (header == null) {
789 return;
790 }
791 if (header != null && mCurrentHeader != null && header.id == mCurrentHeader.id &&
Fabrice Di Meglio61a77ab2014-02-19 14:07:18 -0800792 header.id != R.id.account_add &&
793 !header.fragment.equals(ManageAccountsSettings.class.getName())) {
794 // This is the header we are currently displaying (except "Add Account" or
795 // "Corporate"/"Google" Account entries that share the same fragment). Just make sure
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000796 // to pop the stack up to its root state.
797 getFragmentManager().popBackStack(BACK_STACK_PREFS,
798 FragmentManager.POP_BACK_STACK_INCLUSIVE);
799 } else {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800800 if (header.fragment != null) {
801 mTitleStack.clear();
802 switchToHeaderInner(header.fragment, header.fragmentArguments, validate);
803 setSelectedHeader(header);
804 final TitlePair pair = new TitlePair(0, getHeaderTitle(header));
805 mTitleStack.add(pair);
806 setTitle(pair.second);
807 } else if (header.intent != null) {
808 setSelectedHeader(header);
809 mTitleStack.clear();
810 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800811 } else {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800812 throw new IllegalStateException(
813 "Can't switch to header that has no Fragment nor Intent");
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800814 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800815 }
816 }
817
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800818 private CharSequence getHeaderTitle(Header header) {
819 final CharSequence title;
820 if (header.fragment.equals(DashboardSummary.class.getName())) {
821 title = getResources().getString(R.string.settings_label);
822 } else {
823 title = header.getTitle(getResources());
824 }
825 return title;
826 }
827
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800828 private void setSelectedHeader(Header header) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800829 if (header == null) {
830 mCurrentHeader = null;
831 return;
832 }
833 // Update selected Header into Drawer only if it is not "Add Account"
834 if (header.id == R.id.account_add) {
835 mDrawer.clearChoices();
836 return;
837 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800838 mCurrentHeader = header;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800839 int index = mHeaders.indexOf(header);
840 if (mDrawer != null) {
841 if (index >= 0) {
842 mDrawer.setItemChecked(index, true);
843 } else {
844 mDrawer.clearChoices();
845 }
846 }
847 }
848
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000849 public Header onGetInitialHeader() {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800850 String fragmentClass = getStartingFragmentClass(super.getIntent());
851 if (fragmentClass != null) {
852 Header header = new Header();
853 header.fragment = fragmentClass;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000854 header.title = getTitle();
855 header.fragmentArguments = getIntent().getExtras();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800856 return header;
857 }
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000858
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800859 return mFirstHeader;
860 }
861
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000862 /**
863 * When in two-pane mode, switch the fragment pane to show the given
864 * preference fragment.
865 *
866 * @param fragmentName The name of the fragment to display.
867 * @param args Optional arguments to supply to the fragment.
868 * @param validate true means that the fragment's Header needs to be validated
869 */
870 private void switchToHeader(String fragmentName, Bundle args, boolean validate) {
871 setSelectedHeader(null);
872 switchToHeaderInner(fragmentName, args, validate);
873 }
874
875 private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate) {
876 getFragmentManager().popBackStack(BACK_STACK_PREFS,
877 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800878 if (validate && !isValidFragment(fragmentName)) {
879 throw new IllegalArgumentException("Invalid fragment for this activity: "
880 + fragmentName);
881 }
882 Fragment f = Fragment.instantiate(this, fragmentName, args);
883 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fabrice Di Meglio4cc95a52014-02-07 18:53:14 -0800884 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
Fabrice Di Meglio4cc95a52014-02-07 18:53:14 -0800885 transaction.replace(R.id.prefs, f);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800886 transaction.commitAllowingStateLoss();
887 }
888
889 @Override
890 public void onNewIntent(Intent intent) {
891 super.onNewIntent(intent);
892
893 // If it is not launched from history, then reset to top-level
894 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
895 if (mDrawer != null) {
896 mDrawer.setSelectionFromTop(0, 0);
897 }
898 }
899 }
900
901 /**
902 * Called to determine whether the header list should be hidden.
903 * The default implementation returns the
904 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
905 * This is set to false, for example, when the activity is being re-launched
906 * to show a particular preference activity.
907 */
908 public boolean onIsHidingHeaders() {
909 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
910 }
911
912 private void highlightHeader(int id) {
913 if (id != 0) {
914 Integer index = mHeaderIndexMap.get(id);
915 if (index != null && mDrawer != null) {
916 mDrawer.setItemChecked(index, true);
917 if (mDrawer.getVisibility() == View.VISIBLE) {
918 mDrawer.smoothScrollToPosition(index);
919 }
920 }
921 }
922 }
923
924 @Override
925 public Intent getIntent() {
926 Intent superIntent = super.getIntent();
927 String startingFragment = getStartingFragmentClass(superIntent);
928 // This is called from super.onCreate, isMultiPane() is not yet reliable
929 // Do not use onIsHidingHeaders either, which relies itself on this method
930 if (startingFragment != null) {
931 Intent modIntent = new Intent(superIntent);
932 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
933 Bundle args = superIntent.getExtras();
934 if (args != null) {
935 args = new Bundle(args);
936 } else {
937 args = new Bundle();
938 }
939 args.putParcelable("intent", superIntent);
940 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
941 return modIntent;
942 }
943 return superIntent;
944 }
945
946 /**
947 * Checks if the component name in the intent is different from the Settings class and
948 * returns the class name to load as a fragment.
949 */
950 private String getStartingFragmentClass(Intent intent) {
951 if (mFragmentClass != null) return mFragmentClass;
952
953 String intentClass = intent.getComponent().getClassName();
954 if (intentClass.equals(getClass().getName())) return null;
955
956 if ("com.android.settings.ManageApplications".equals(intentClass)
957 || "com.android.settings.RunningServices".equals(intentClass)
958 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
959 // Old names of manage apps.
960 intentClass = com.android.settings.applications.ManageApplications.class.getName();
961 }
962
963 return intentClass;
964 }
965
966 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000967 * Start a new fragment containing a preference panel. If the preferences
968 * are being displayed in multi-pane mode, the given fragment class will
969 * be instantiated and placed in the appropriate pane. If running in
970 * single-pane mode, a new activity will be launched in which to show the
971 * fragment.
972 *
973 * @param fragmentClass Full name of the class implementing the fragment.
974 * @param args Any desired arguments to supply to the fragment.
975 * @param titleRes Optional resource identifier of the title of this
976 * fragment.
977 * @param titleText Optional text of the title of this fragment.
978 * @param resultTo Optional fragment that result data should be sent to.
979 * If non-null, resultTo.onActivityResult() will be called when this
980 * preference panel is done. The launched panel must use
981 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
982 * @param resultRequestCode If resultTo is non-null, this is the caller's
983 * request code to be received with the resut.
984 */
985 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
986 CharSequence titleText, Fragment resultTo,
987 int resultRequestCode) {
988 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
989 }
990
991 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800992 * Called by a preference panel fragment to finish itself.
993 *
994 * @param caller The fragment that is asking to be finished.
995 * @param resultCode Optional result code to send back to the original
996 * launching fragment.
997 * @param resultData Optional result data to send back to the original
998 * launching fragment.
999 */
1000 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
1001 setResult(resultCode, resultData);
1002 }
1003
1004 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001005 * Start a new fragment.
1006 *
1007 * @param fragment The fragment to start
1008 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1009 * the current fragment will be replaced.
1010 */
1011 public void startPreferenceFragment(Fragment fragment, boolean push) {
1012 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1013 transaction.replace(R.id.prefs, fragment);
1014 if (push) {
1015 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1016 transaction.addToBackStack(BACK_STACK_PREFS);
1017 } else {
1018 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1019 }
1020 transaction.commitAllowingStateLoss();
1021 }
1022
1023 /**
1024 * Start a new fragment.
1025 *
1026 * @param fragmentName The name of the fragment to display.
1027 * @param args Optional arguments to supply to the fragment.
1028 * @param resultTo Option fragment that should receive the result of
1029 * the activity launch.
1030 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
1031 * report the result.
1032 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
1033 * valid one then it will be used to get the title. Otherwise the titleText
1034 * argument will be used as the title.
1035 * @param titleText string to display for the title of.
1036 */
1037 private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
1038 int resultRequestCode, int titleRes, CharSequence titleText) {
1039 Fragment f = Fragment.instantiate(this, fragmentName, args);
1040 if (resultTo != null) {
1041 f.setTargetFragment(resultTo, resultRequestCode);
1042 }
1043 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1044 transaction.replace(R.id.prefs, f);
1045 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1046 transaction.addToBackStack(BACK_STACK_PREFS);
1047 transaction.commitAllowingStateLoss();
1048
1049 final TitlePair pair;
1050 final CharSequence cs;
1051 if (titleRes != 0) {
1052 pair = new TitlePair(titleRes, null);
1053 cs = getText(titleRes);
1054 } else {
1055 pair = new TitlePair(0, titleText);
1056 cs = titleText;
1057 }
1058 setTitle(cs);
1059 mTitleStack.add(pair);
1060 }
1061
1062 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001063 * Called when the activity needs its list of headers build. By
1064 * implementing this and adding at least one item to the list, you
1065 * will cause the activity to run in its modern fragment mode. Note
1066 * that this function may not always be called; for example, if the
1067 * activity has been asked to display a particular fragment without
1068 * the header list, there is no need to build the headers.
1069 *
1070 * <p>Typical implementations will use {@link #loadHeadersFromResource}
1071 * to fill in the list from a resource.
1072 *
1073 * @param headers The list in which to place the headers.
1074 */
1075 private void onBuildHeaders(List<Header> headers) {
1076 loadHeadersFromResource(R.xml.settings_headers, headers);
1077 updateHeaderList(headers);
1078 }
1079
1080 /**
1081 * Parse the given XML file as a header description, adding each
1082 * parsed Header into the target list.
1083 *
1084 * @param resid The XML resource to load and parse.
1085 * @param target The list in which the parsed headers should be placed.
1086 */
1087 private void loadHeadersFromResource(int resid, List<Header> target) {
1088 XmlResourceParser parser = null;
1089 try {
1090 parser = getResources().getXml(resid);
1091 AttributeSet attrs = Xml.asAttributeSet(parser);
1092
1093 int type;
1094 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1095 && type != XmlPullParser.START_TAG) {
1096 // Parse next until start tag is found
1097 }
1098
1099 String nodeName = parser.getName();
1100 if (!"preference-headers".equals(nodeName)) {
1101 throw new RuntimeException(
1102 "XML document must start with <preference-headers> tag; found"
1103 + nodeName + " at " + parser.getPositionDescription());
1104 }
1105
1106 Bundle curBundle = null;
1107
1108 final int outerDepth = parser.getDepth();
1109 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1110 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1111 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1112 continue;
1113 }
1114
1115 nodeName = parser.getName();
1116 if ("header".equals(nodeName)) {
1117 Header header = new Header();
1118
1119 TypedArray sa = obtainStyledAttributes(
1120 attrs, com.android.internal.R.styleable.PreferenceHeader);
1121 header.id = sa.getResourceId(
1122 com.android.internal.R.styleable.PreferenceHeader_id,
1123 (int)HEADER_ID_UNDEFINED);
1124 TypedValue tv = sa.peekValue(
1125 com.android.internal.R.styleable.PreferenceHeader_title);
1126 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1127 if (tv.resourceId != 0) {
1128 header.titleRes = tv.resourceId;
1129 } else {
1130 header.title = tv.string;
1131 }
1132 }
1133 tv = sa.peekValue(
1134 com.android.internal.R.styleable.PreferenceHeader_summary);
1135 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1136 if (tv.resourceId != 0) {
1137 header.summaryRes = tv.resourceId;
1138 } else {
1139 header.summary = tv.string;
1140 }
1141 }
1142 header.iconRes = sa.getResourceId(
1143 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1144 header.fragment = sa.getString(
1145 com.android.internal.R.styleable.PreferenceHeader_fragment);
1146 sa.recycle();
1147
1148 if (curBundle == null) {
1149 curBundle = new Bundle();
1150 }
1151
1152 final int innerDepth = parser.getDepth();
1153 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1154 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1155 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1156 continue;
1157 }
1158
1159 String innerNodeName = parser.getName();
1160 if (innerNodeName.equals("extra")) {
1161 getResources().parseBundleExtra("extra", attrs, curBundle);
1162 XmlUtils.skipCurrentTag(parser);
1163
1164 } else if (innerNodeName.equals("intent")) {
1165 header.intent = Intent.parseIntent(getResources(), parser, attrs);
1166
1167 } else {
1168 XmlUtils.skipCurrentTag(parser);
1169 }
1170 }
1171
1172 if (curBundle.size() > 0) {
1173 header.fragmentArguments = curBundle;
1174 curBundle = null;
1175 }
1176
1177 target.add(header);
1178 } else {
1179 XmlUtils.skipCurrentTag(parser);
1180 }
1181 }
1182
1183 } catch (XmlPullParserException e) {
1184 throw new RuntimeException("Error parsing headers", e);
1185 } catch (IOException e) {
1186 throw new RuntimeException("Error parsing headers", e);
1187 } finally {
1188 if (parser != null) parser.close();
1189 }
1190 }
1191
1192 private void updateHeaderList(List<Header> target) {
1193 final boolean showDev = mDevelopmentPreferences.getBoolean(
1194 DevelopmentSettings.PREF_SHOW,
1195 android.os.Build.TYPE.equals("eng"));
1196 int i = 0;
1197
1198 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1199 mHeaderIndexMap.clear();
1200 while (i < target.size()) {
1201 Header header = target.get(i);
1202 // Ids are integers, so downcasting
1203 int id = (int) header.id;
1204 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1205 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1206 } else if (id == R.id.wifi_settings) {
1207 // Remove WiFi Settings if WiFi service is not available.
1208 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1209 target.remove(i);
1210 }
1211 } else if (id == R.id.bluetooth_settings) {
1212 // Remove Bluetooth Settings if Bluetooth service is not available.
1213 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1214 target.remove(i);
1215 }
1216 } else if (id == R.id.data_usage_settings) {
1217 // Remove data usage when kernel module not enabled
1218 final INetworkManagementService netManager = INetworkManagementService.Stub
1219 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1220 try {
1221 if (!netManager.isBandwidthControlEnabled()) {
1222 target.remove(i);
1223 }
1224 } catch (RemoteException e) {
1225 // ignored
1226 }
1227 } else if (id == R.id.battery_settings) {
1228 // Remove battery settings when battery is not available. (e.g. TV)
1229
1230 if (!mBatteryPresent) {
1231 target.remove(i);
1232 }
1233 } else if (id == R.id.account_settings) {
1234 int headerIndex = i + 1;
1235 i = insertAccountsHeaders(target, headerIndex);
1236 } else if (id == R.id.home_settings) {
1237 if (!updateHomeSettingHeaders(header)) {
1238 target.remove(i);
1239 }
1240 } else if (id == R.id.user_settings) {
1241 if (!UserHandle.MU_ENABLED
1242 || !UserManager.supportsMultipleUsers()
1243 || Utils.isMonkeyRunning()) {
1244 target.remove(i);
1245 }
1246 } else if (id == R.id.nfc_payment_settings) {
1247 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1248 target.remove(i);
1249 } else {
1250 // Only show if NFC is on and we have the HCE feature
1251 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1252 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1253 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1254 target.remove(i);
1255 }
1256 }
1257 } else if (id == R.id.development_settings) {
1258 if (!showDev) {
1259 target.remove(i);
1260 }
1261 } else if (id == R.id.account_add) {
1262 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1263 target.remove(i);
1264 }
1265 }
1266
1267 if (i < target.size() && target.get(i) == header
1268 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1269 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1270 target.remove(i);
1271 }
1272
1273 // Increment if the current one wasn't removed by the Utils code.
1274 if (i < target.size() && target.get(i) == header) {
1275 // Hold on to the first header, when we need to reset to the top-level
1276 if (mFirstHeader == null &&
1277 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
1278 mFirstHeader = header;
1279 }
1280 mHeaderIndexMap.put(id, i);
1281 i++;
1282 }
1283 }
1284 }
1285
1286 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1287 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1288 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1289 for (String accountType : accountTypes) {
1290 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1291 if (label == null) {
1292 continue;
1293 }
1294
1295 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1296 boolean skipToAccount = accounts.length == 1
1297 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1298 Header accHeader = new Header();
1299 accHeader.title = label;
1300 if (accHeader.extras == null) {
1301 accHeader.extras = new Bundle();
1302 }
1303 if (skipToAccount) {
1304 accHeader.fragment = AccountSyncSettings.class.getName();
1305 accHeader.fragmentArguments = new Bundle();
1306 // Need this for the icon
1307 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1308 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1309 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1310 accounts[0]);
1311 } else {
1312 accHeader.fragment = ManageAccountsSettings.class.getName();
1313 accHeader.fragmentArguments = new Bundle();
1314 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1315 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1316 accountType);
1317 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1318 label.toString());
1319 }
1320 accountHeaders.add(accHeader);
1321 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1322 }
1323
1324 // Sort by label
1325 Collections.sort(accountHeaders, new Comparator<Header>() {
1326 @Override
1327 public int compare(Header h1, Header h2) {
1328 return h1.title.toString().compareTo(h2.title.toString());
1329 }
1330 });
1331
1332 for (Header header : accountHeaders) {
1333 target.add(headerIndex++, header);
1334 }
1335 if (!mListeningToAccountUpdates) {
1336 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1337 mListeningToAccountUpdates = true;
1338 }
1339 return headerIndex;
1340 }
1341
1342 private boolean updateHomeSettingHeaders(Header header) {
1343 // Once we decide to show Home settings, keep showing it forever
1344 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1345 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1346 return true;
1347 }
1348
1349 try {
1350 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1351 getPackageManager().getHomeActivities(homeApps);
1352 if (homeApps.size() < 2) {
1353 // When there's only one available home app, omit this settings
1354 // category entirely at the top level UI. If the user just
1355 // uninstalled the penultimate home app candidiate, we also
1356 // now tell them about why they aren't seeing 'Home' in the list.
1357 if (sShowNoHomeNotice) {
1358 sShowNoHomeNotice = false;
1359 NoHomeDialogFragment.show(this);
1360 }
1361 return false;
1362 } else {
1363 // Okay, we're allowing the Home settings category. Tell it, when
1364 // invoked via this front door, that we'll need to be told about the
1365 // case when the user uninstalls all but one home app.
1366 if (header.fragmentArguments == null) {
1367 header.fragmentArguments = new Bundle();
1368 }
1369 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1370 }
1371 } catch (Exception e) {
1372 // Can't look up the home activity; bail on configuring the icon
1373 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1374 }
1375
1376 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1377 return true;
1378 }
1379
1380 private void getMetaData() {
1381 try {
1382 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1383 PackageManager.GET_META_DATA);
1384 if (ai == null || ai.metaData == null) return;
1385 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
1386 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1387 } catch (NameNotFoundException nnfe) {
1388 // No recovery
1389 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1390 }
1391 }
1392
1393 // give subclasses access to the Next button
1394 public boolean hasNextButton() {
1395 return mNextButton != null;
1396 }
1397
1398 public Button getNextButton() {
1399 return mNextButton;
1400 }
1401
1402 public static class NoHomeDialogFragment extends DialogFragment {
1403 public static void show(Activity parent) {
1404 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
1405 dialog.show(parent.getFragmentManager(), null);
1406 }
1407
1408 @Override
1409 public Dialog onCreateDialog(Bundle savedInstanceState) {
1410 return new AlertDialog.Builder(getActivity())
1411 .setMessage(R.string.only_one_home_message)
1412 .setPositiveButton(android.R.string.ok, null)
1413 .create();
1414 }
1415 }
1416
1417 private static class HeaderAdapter extends ArrayAdapter<Header> {
1418 static final int HEADER_TYPE_CATEGORY = 0;
1419 static final int HEADER_TYPE_NORMAL = 1;
1420 static final int HEADER_TYPE_SWITCH = 2;
1421 static final int HEADER_TYPE_BUTTON = 3;
1422 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
1423
1424 private final WifiEnabler mWifiEnabler;
1425 private final BluetoothEnabler mBluetoothEnabler;
1426 private AuthenticatorHelper mAuthHelper;
1427 private DevicePolicyManager mDevicePolicyManager;
1428
1429 private static class HeaderViewHolder {
1430 ImageView mIcon;
1431 TextView mTitle;
1432 TextView mSummary;
1433 Switch mSwitch;
1434 ImageButton mButton;
1435 View mDivider;
1436 }
1437
1438 private LayoutInflater mInflater;
1439
1440 static int getHeaderType(Header header) {
1441 if (header.fragment == null && header.intent == null) {
1442 return HEADER_TYPE_CATEGORY;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001443 } else if (header.id == R.id.security_settings) {
1444 return HEADER_TYPE_BUTTON;
1445 } else {
1446 return HEADER_TYPE_NORMAL;
1447 }
1448 }
1449
1450 @Override
1451 public int getItemViewType(int position) {
1452 Header header = getItem(position);
1453 return getHeaderType(header);
1454 }
1455
1456 @Override
1457 public boolean areAllItemsEnabled() {
1458 return false; // because of categories
1459 }
1460
1461 @Override
1462 public boolean isEnabled(int position) {
1463 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
1464 }
1465
1466 @Override
1467 public int getViewTypeCount() {
1468 return HEADER_TYPE_COUNT;
1469 }
1470
1471 @Override
1472 public boolean hasStableIds() {
1473 return true;
1474 }
1475
1476 public HeaderAdapter(Context context, List<Header> objects,
1477 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
1478 super(context, 0, objects);
1479
1480 mAuthHelper = authenticatorHelper;
1481 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1482
1483 // Temp Switches provided as placeholder until the adapter replaces these with actual
1484 // Switches inflated from their layouts. Must be done before adapter is set in super
1485 mWifiEnabler = new WifiEnabler(context, new Switch(context));
1486 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
1487 mDevicePolicyManager = dpm;
1488 }
1489
1490 @Override
1491 public View getView(int position, View convertView, ViewGroup parent) {
1492 HeaderViewHolder holder;
1493 Header header = getItem(position);
1494 int headerType = getHeaderType(header);
1495 View view = null;
1496
1497 if (convertView == null) {
1498 holder = new HeaderViewHolder();
1499 switch (headerType) {
1500 case HEADER_TYPE_CATEGORY:
1501 view = new TextView(getContext(), null,
1502 android.R.attr.listSeparatorTextViewStyle);
1503 holder.mTitle = (TextView) view;
1504 break;
1505
1506 case HEADER_TYPE_SWITCH:
1507 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
1508 false);
1509 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1510 holder.mTitle = (TextView)
1511 view.findViewById(com.android.internal.R.id.title);
1512 holder.mSummary = (TextView)
1513 view.findViewById(com.android.internal.R.id.summary);
1514 holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget);
1515 break;
1516
1517 case HEADER_TYPE_BUTTON:
1518 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
1519 false);
1520 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1521 holder.mTitle = (TextView)
1522 view.findViewById(com.android.internal.R.id.title);
1523 holder.mSummary = (TextView)
1524 view.findViewById(com.android.internal.R.id.summary);
1525 holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget);
1526 holder.mDivider = view.findViewById(R.id.divider);
1527 break;
1528
1529 case HEADER_TYPE_NORMAL:
1530 view = mInflater.inflate(
1531 R.layout.preference_header_item, parent,
1532 false);
1533 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1534 holder.mTitle = (TextView)
1535 view.findViewById(com.android.internal.R.id.title);
1536 holder.mSummary = (TextView)
1537 view.findViewById(com.android.internal.R.id.summary);
1538 break;
1539 }
Fabrice Di Meglio0e2f9492014-02-25 14:26:27 -08001540 if (holder.mIcon != null) {
1541 holder.mIcon.setBackgroundResource(R.color.background_drawer_icon);
1542 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001543 view.setTag(holder);
1544 } else {
1545 view = convertView;
1546 holder = (HeaderViewHolder) view.getTag();
1547 }
1548
1549 // All view fields must be updated every time, because the view may be recycled
1550 switch (headerType) {
1551 case HEADER_TYPE_CATEGORY:
1552 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1553 break;
1554
1555 case HEADER_TYPE_SWITCH:
1556 // Would need a different treatment if the main menu had more switches
1557 if (header.id == R.id.wifi_settings) {
1558 mWifiEnabler.setSwitch(holder.mSwitch);
1559 } else {
1560 mBluetoothEnabler.setSwitch(holder.mSwitch);
1561 }
1562 updateCommonHeaderView(header, holder);
1563 break;
1564
1565 case HEADER_TYPE_BUTTON:
1566 if (header.id == R.id.security_settings) {
1567 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
1568 if (hasCert) {
1569 holder.mButton.setVisibility(View.VISIBLE);
1570 holder.mDivider.setVisibility(View.VISIBLE);
1571 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
1572 if (isManaged) {
1573 holder.mButton.setImageResource(R.drawable.ic_settings_about);
1574 } else {
1575 holder.mButton.setImageResource(
1576 android.R.drawable.stat_notify_error);
1577 }
1578 holder.mButton.setOnClickListener(new OnClickListener() {
1579 @Override
1580 public void onClick(View v) {
1581 Intent intent = new Intent(
1582 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
1583 getContext().startActivity(intent);
1584 }
1585 });
1586 } else {
1587 holder.mButton.setVisibility(View.GONE);
1588 holder.mDivider.setVisibility(View.GONE);
1589 }
1590 }
1591 updateCommonHeaderView(header, holder);
1592 break;
1593
1594 case HEADER_TYPE_NORMAL:
1595 updateCommonHeaderView(header, holder);
1596 break;
1597 }
1598
1599 return view;
1600 }
1601
1602 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
1603 if (header.extras != null
1604 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
1605 String accType = header.extras.getString(
1606 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
1607 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
1608 setHeaderIcon(holder, icon);
1609 } else {
1610 holder.mIcon.setImageResource(header.iconRes);
1611 }
1612 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1613 CharSequence summary = header.getSummary(getContext().getResources());
1614 if (!TextUtils.isEmpty(summary)) {
1615 holder.mSummary.setVisibility(View.VISIBLE);
1616 holder.mSummary.setText(summary);
1617 } else {
1618 holder.mSummary.setVisibility(View.GONE);
1619 }
1620 }
1621
1622 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
1623 ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams();
1624 lp.width = getContext().getResources().getDimensionPixelSize(
1625 R.dimen.header_icon_width);
1626 lp.height = lp.width;
1627 holder.mIcon.setLayoutParams(lp);
1628 holder.mIcon.setImageDrawable(icon);
1629 }
1630
1631 public void resume() {
1632 mWifiEnabler.resume();
1633 mBluetoothEnabler.resume();
1634 }
1635
1636 public void pause() {
1637 mWifiEnabler.pause();
1638 mBluetoothEnabler.pause();
1639 }
1640 }
1641
1642 private void onListItemClick(ListView l, View v, int position, long id) {
1643 if (!isResumed()) {
1644 return;
1645 }
1646 Object item = mHeaderAdapter.getItem(position);
1647 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001648 mSelectedHeader = (Header) item;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001649 }
1650 }
1651
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001652 @Override
1653 public boolean shouldUpRecreateTask(Intent targetIntent) {
1654 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1655 }
1656
1657 @Override
1658 public void onAccountsUpdated(Account[] accounts) {
1659 // TODO: watch for package upgrades to invalidate cache; see 7206643
1660 mAuthenticatorHelper.updateAuthDescriptions(this);
1661 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1662 invalidateHeaders();
1663 }
1664
1665 public static void requestHomeNotice() {
1666 sShowNoHomeNotice = true;
1667 }
1668
1669 /**
1670 * Default value for {@link Header#id Header.id} indicating that no
1671 * identifier value is set. All other values (including those below -1)
1672 * are valid.
1673 */
1674 private static final long HEADER_ID_UNDEFINED = -1;
1675
1676 /**
1677 * Description of a single Header item that the user can select.
1678 */
1679 static final class Header implements Parcelable {
1680 /**
1681 * Identifier for this header, to correlate with a new list when
1682 * it is updated. The default value is
1683 * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id.
1684 * @attr ref android.R.styleable#PreferenceHeader_id
1685 */
1686 public long id = HEADER_ID_UNDEFINED;
1687
1688 /**
1689 * Resource ID of title of the header that is shown to the user.
1690 * @attr ref android.R.styleable#PreferenceHeader_title
1691 */
1692 public int titleRes;
1693
1694 /**
1695 * Title of the header that is shown to the user.
1696 * @attr ref android.R.styleable#PreferenceHeader_title
1697 */
1698 public CharSequence title;
1699
1700 /**
1701 * Resource ID of optional summary describing what this header controls.
1702 * @attr ref android.R.styleable#PreferenceHeader_summary
1703 */
1704 public int summaryRes;
1705
1706 /**
1707 * Optional summary describing what this header controls.
1708 * @attr ref android.R.styleable#PreferenceHeader_summary
1709 */
1710 public CharSequence summary;
1711
1712 /**
1713 * Optional icon resource to show for this header.
1714 * @attr ref android.R.styleable#PreferenceHeader_icon
1715 */
1716 public int iconRes;
1717
1718 /**
1719 * Full class name of the fragment to display when this header is
1720 * selected.
1721 * @attr ref android.R.styleable#PreferenceHeader_fragment
1722 */
1723 public String fragment;
1724
1725 /**
1726 * Optional arguments to supply to the fragment when it is
1727 * instantiated.
1728 */
1729 public Bundle fragmentArguments;
1730
1731 /**
1732 * Intent to launch when the preference is selected.
1733 */
1734 public Intent intent;
1735
1736 /**
1737 * Optional additional data for use by subclasses of the activity
1738 */
1739 public Bundle extras;
1740
1741 public Header() {
1742 // Empty
1743 }
1744
1745 /**
1746 * Return the currently set title. If {@link #titleRes} is set,
1747 * this resource is loaded from <var>res</var> and returned. Otherwise
1748 * {@link #title} is returned.
1749 */
1750 public CharSequence getTitle(Resources res) {
1751 if (titleRes != 0) {
1752 return res.getText(titleRes);
1753 }
1754 return title;
1755 }
1756
1757 /**
1758 * Return the currently set summary. If {@link #summaryRes} is set,
1759 * this resource is loaded from <var>res</var> and returned. Otherwise
1760 * {@link #summary} is returned.
1761 */
1762 public CharSequence getSummary(Resources res) {
1763 if (summaryRes != 0) {
1764 return res.getText(summaryRes);
1765 }
1766 return summary;
1767 }
1768
1769 @Override
1770 public int describeContents() {
1771 return 0;
1772 }
1773
1774 @Override
1775 public void writeToParcel(Parcel dest, int flags) {
1776 dest.writeLong(id);
1777 dest.writeInt(titleRes);
1778 TextUtils.writeToParcel(title, dest, flags);
1779 dest.writeInt(summaryRes);
1780 TextUtils.writeToParcel(summary, dest, flags);
1781 dest.writeInt(iconRes);
1782 dest.writeString(fragment);
1783 dest.writeBundle(fragmentArguments);
1784 if (intent != null) {
1785 dest.writeInt(1);
1786 intent.writeToParcel(dest, flags);
1787 } else {
1788 dest.writeInt(0);
1789 }
1790 dest.writeBundle(extras);
1791 }
1792
1793 public void readFromParcel(Parcel in) {
1794 id = in.readLong();
1795 titleRes = in.readInt();
1796 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1797 summaryRes = in.readInt();
1798 summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1799 iconRes = in.readInt();
1800 fragment = in.readString();
1801 fragmentArguments = in.readBundle();
1802 if (in.readInt() != 0) {
1803 intent = Intent.CREATOR.createFromParcel(in);
1804 }
1805 extras = in.readBundle();
1806 }
1807
1808 Header(Parcel in) {
1809 readFromParcel(in);
1810 }
1811
1812 public static final Creator<Header> CREATOR = new Creator<Header>() {
1813 public Header createFromParcel(Parcel source) {
1814 return new Header(source);
1815 }
1816 public Header[] newArray(int size) {
1817 return new Header[size];
1818 }
1819 };
1820 }
1821}