blob: b7d29fa7b35e9bba556584480db0cbbafa506b93 [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;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080065import android.util.TypedValue;
66import android.util.Xml;
67import android.view.LayoutInflater;
68import android.view.MenuItem;
69import android.view.View;
70import android.view.View.OnClickListener;
71import android.view.ViewGroup;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +000072import android.widget.AbsListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080073import android.widget.AdapterView;
74import android.widget.ArrayAdapter;
75import android.widget.Button;
76import android.widget.ImageButton;
77import android.widget.ImageView;
78import android.widget.ListView;
79import android.widget.Switch;
80import android.widget.TextView;
81
82import com.android.internal.util.ArrayUtils;
83import com.android.internal.util.XmlUtils;
84import com.android.settings.accessibility.AccessibilitySettings;
85import com.android.settings.accessibility.CaptionPropertiesFragment;
86import com.android.settings.accounts.AccountSyncSettings;
87import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080088import com.android.settings.accounts.ManageAccountsSettings;
89import com.android.settings.applications.ManageApplications;
90import com.android.settings.applications.ProcessStatsUi;
91import com.android.settings.bluetooth.BluetoothEnabler;
92import com.android.settings.bluetooth.BluetoothSettings;
93import com.android.settings.dashboard.DashboardSummary;
94import com.android.settings.deviceinfo.Memory;
95import com.android.settings.deviceinfo.UsbSettings;
96import com.android.settings.fuelgauge.PowerUsageSummary;
97import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
98import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
99import com.android.settings.inputmethod.SpellCheckersSettings;
100import com.android.settings.inputmethod.UserDictionaryList;
101import com.android.settings.location.LocationSettings;
102import com.android.settings.nfc.AndroidBeam;
103import com.android.settings.nfc.PaymentSettings;
104import com.android.settings.print.PrintJobSettingsFragment;
105import com.android.settings.print.PrintSettingsFragment;
106import com.android.settings.tts.TextToSpeechSettings;
107import com.android.settings.users.UserSettings;
108import com.android.settings.vpn2.VpnSettings;
109import com.android.settings.wfd.WifiDisplaySettings;
110import com.android.settings.wifi.AdvancedWifiSettings;
111import com.android.settings.wifi.WifiEnabler;
112import com.android.settings.wifi.WifiSettings;
113import com.android.settings.wifi.p2p.WifiP2pSettings;
114import org.xmlpull.v1.XmlPullParser;
115import org.xmlpull.v1.XmlPullParserException;
116
117import java.io.IOException;
118import java.util.ArrayList;
119import java.util.Collections;
120import java.util.Comparator;
121import java.util.HashMap;
122import java.util.List;
123
124public class SettingsActivity extends Activity
125 implements PreferenceManager.OnPreferenceTreeClickListener,
126 PreferenceFragment.OnPreferenceStartFragmentCallback,
127 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener {
128
129 private static final String LOG_TAG = "Settings";
130
131 // Constants for state save/restore
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800132 private static final String SAVE_KEY_HEADERS_TAG = ":settings:headers";
133 private static final String SAVE_KEY_CURRENT_HEADER_TAG = ":settings:cur_header";
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800134 private static final String SAVE_KEY_TITLES_TAG = ":settings:titles";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800135
136 /**
137 * When starting this activity, the invoking Intent can contain this extra
138 * string to specify which fragment should be initially displayed.
139 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
140 * will call isValidFragment() to confirm that the fragment class name is valid for this
141 * activity.
142 */
143 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
144
145 /**
146 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
147 * this extra can also be specified to supply a Bundle of arguments to pass
148 * to that fragment when it is instantiated during the initial creation
149 * of the activity.
150 */
151 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
152
153 /**
154 * When starting this activity, the invoking Intent can contain this extra
155 * boolean that the header list should not be displayed. This is most often
156 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
157 * the activity to display a specific fragment that the user has navigated
158 * to.
159 */
160 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
161
162 // extras that allow any preference activity to be launched as part of a wizard
163
164 // show Back and Next buttons? takes boolean parameter
165 // Back will then return RESULT_CANCELED and Next RESULT_OK
166 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
167
168 // add a Skip button?
169 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
170
171 // specify custom text for the Back or Next buttons, or cause a button to not appear
172 // at all by setting it to null
173 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
174 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
175
176 /**
177 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
178 * this extra can also be specify to supply the title to be shown for
179 * that fragment.
180 */
181 protected static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
182
183 private static final String BACK_STACK_PREFS = ":settings:prefs";
184
185 private static final String META_DATA_KEY_HEADER_ID =
186 "com.android.settings.TOP_LEVEL_HEADER_ID";
187
188 private static final String META_DATA_KEY_FRAGMENT_CLASS =
189 "com.android.settings.FRAGMENT_CLASS";
190
191 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
192
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800193 private static boolean sShowNoHomeNotice = false;
194
195 private String mFragmentClass;
196 private int mTopLevelHeaderId;
197 private Header mFirstHeader;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800198 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800199 private Header mCurrentHeader;
200
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800201 private CharSequence mInitialTitle;
202
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800203 // 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 Meglio263bcc82014-01-17 19:17:58 -0800316 private DrawerLayout mDrawerLayout;
317 private ListView mDrawer;
318 private ActionBarDrawerToggle mDrawerToggle;
319 private ActionBar mActionBar;
320
321 private static final int MSG_BUILD_HEADERS = 1;
322 private Handler mHandler = new Handler() {
323 @Override
324 public void handleMessage(Message msg) {
325 switch (msg.what) {
326 case MSG_BUILD_HEADERS: {
327 mHeaders.clear();
328 onBuildHeaders(mHeaders);
329 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800330 if (mCurrentHeader != null) {
331 Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800332 if (mappedHeader != null) {
333 setSelectedHeader(mappedHeader);
334 }
335 }
336 } break;
337 }
338 }
339 };
340
341 @Override
342 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
343 // Override the fragment title for Wallpaper settings
344 int titleRes = pref.getTitleRes();
345 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
346 titleRes = R.string.wallpaper_settings_fragment_title;
347 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
348 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
349 if (UserManager.get(this).isLinkedUser()) {
350 titleRes = R.string.profile_info_settings_title;
351 } else {
352 titleRes = R.string.user_info_settings_title;
353 }
354 }
355 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
356 null, 0);
357 return true;
358 }
359
360 @Override
361 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
362 return false;
363 }
364
365 private class DrawerListener implements DrawerLayout.DrawerListener {
366 @Override
367 public void onDrawerOpened(View drawerView) {
368 mDrawerToggle.onDrawerOpened(drawerView);
369 }
370
371 @Override
372 public void onDrawerClosed(View drawerView) {
373 mDrawerToggle.onDrawerClosed(drawerView);
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800374 // Cannot process clicks when the App is finishing
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800375 if (isFinishing() || mSelectedHeader == null) {
376 return;
377 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800378 switchToHeader(mSelectedHeader, false, false);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800379 mSelectedHeader = null;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800380 }
381
382 @Override
383 public void onDrawerSlide(View drawerView, float slideOffset) {
384 mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
385 }
386
387 @Override
388 public void onDrawerStateChanged(int newState) {
389 mDrawerToggle.onDrawerStateChanged(newState);
390 }
391 }
392
393 private class DrawerItemClickListener implements ListView.OnItemClickListener {
394 @Override
395 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
396 mDrawerLayout.closeDrawer(mDrawer);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000397 onListItemClick((ListView)parent, view, position, id);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800398 }
399 }
400
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800401 private Header findBestMatchingHeader(Header current, ArrayList<Header> from) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800402 ArrayList<Header> matches = new ArrayList<Header>();
403 for (int j=0; j<from.size(); j++) {
404 Header oh = from.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800405 if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800406 // Must be this one.
407 matches.clear();
408 matches.add(oh);
409 break;
410 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800411 if (current.fragment != null) {
412 if (current.fragment.equals(oh.fragment)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800413 matches.add(oh);
414 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800415 } else if (current.intent != null) {
416 if (current.intent.equals(oh.intent)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800417 matches.add(oh);
418 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800419 } else if (current.title != null) {
420 if (current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800421 matches.add(oh);
422 }
423 }
424 }
425 final int NM = matches.size();
426 if (NM == 1) {
427 return matches.get(0);
428 } else if (NM > 1) {
429 for (int j=0; j<NM; j++) {
430 Header oh = matches.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800431 if (current.fragmentArguments != null &&
432 current.fragmentArguments.equals(oh.fragmentArguments)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800433 return oh;
434 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800435 if (current.extras != null && current.extras.equals(oh.extras)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800436 return oh;
437 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800438 if (current.title != null && current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800439 return oh;
440 }
441 }
442 }
443 return null;
444 }
445
446 private void invalidateHeaders() {
447 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
448 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
449 }
450 }
451
452 @Override
453 protected void onPostCreate(Bundle savedInstanceState) {
454 super.onPostCreate(savedInstanceState);
455
456 // Sync the toggle state after onRestoreInstanceState has occurred.
457 if (mDrawerToggle != null) {
458 mDrawerToggle.syncState();
459 }
460 }
461
462 @Override
463 public void onConfigurationChanged(Configuration newConfig) {
464 super.onConfigurationChanged(newConfig);
465 if (mDrawerToggle != null) {
466 mDrawerToggle.onConfigurationChanged(newConfig);
467 }
468 }
469
470 @Override
471 public boolean onOptionsItemSelected(MenuItem item) {
472 if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
473 return true;
474 }
475 return super.onOptionsItemSelected(item);
476 }
477
478 @Override
479 protected void onCreate(Bundle savedInstanceState) {
480 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
481 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
482 }
483
484 mAuthenticatorHelper = new AuthenticatorHelper();
485 mAuthenticatorHelper.updateAuthDescriptions(this);
486 mAuthenticatorHelper.onAccountsUpdated(this, null);
487
488 DevicePolicyManager dpm =
489 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800490
Fabrice Di Meglio0e2f9492014-02-25 14:26:27 -0800491 mHeaderAdapter= new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800492
493 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
494 Context.MODE_PRIVATE);
495
496 getMetaData();
497
498 super.onCreate(savedInstanceState);
499
500 setContentView(R.layout.settings_main);
501
502 getFragmentManager().addOnBackStackChangedListener(this);
503
504 mActionBar = getActionBar();
505 if (mActionBar != null) {
506 mActionBar.setDisplayHomeAsUpEnabled(true);
507 mActionBar.setHomeButtonEnabled(true);
508
509 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800510
511 mDrawer = (ListView) findViewById(R.id.headers_drawer);
512 mDrawer.setAdapter(mHeaderAdapter);
513 mDrawer.setOnItemClickListener(new DrawerItemClickListener());
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000514 mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800515
516 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
517 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
518 }
519
520 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
521 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
522
523 if (savedInstanceState != null) {
524 // We are restarting from a previous saved state; used that to
525 // initialize, instead of starting fresh.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800526 mInitialTitle = getTitle();
527 setTitleFromBackStack();
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800528
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800529 ArrayList<Header> headers =
530 savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800531 if (headers != null) {
532 mHeaders.addAll(headers);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800533 int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG,
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800534 (int) HEADER_ID_UNDEFINED);
535 if (curHeader >= 0 && curHeader < mHeaders.size()) {
536 setSelectedHeader(mHeaders.get(curHeader));
537 }
538 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800539 } else {
540 if (initialFragment != null) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000541 // If we are just showing a fragment, we want to run in
542 // new fragment mode, but don't need to compute and show
543 // the headers.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800544 final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
545 mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000546
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800547 switchToHeader(initialFragment, initialArguments, true, mInitialTitle);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000548 } else {
549 // We need to try to build the headers.
550 onBuildHeaders(mHeaders);
551
552 // If there are headers, then at this point we need to show
553 // them and, depending on the screen, we may also show in-line
554 // the currently selected preference fragment.
555 if (mHeaders.size() > 0) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800556 Header h = onGetInitialHeader();
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800557 mInitialTitle = getHeaderTitle(h);
558 switchToHeader(h, false, true);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000559 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800560 }
561 }
562
563 // see if we should show Back/Next buttons
564 Intent intent = getIntent();
565 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
566
567 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
568 if (buttonBar != null) {
569 buttonBar.setVisibility(View.VISIBLE);
570
571 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
572 backButton.setOnClickListener(new OnClickListener() {
573 public void onClick(View v) {
574 setResult(RESULT_CANCELED);
575 finish();
576 }
577 });
578 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
579 skipButton.setOnClickListener(new OnClickListener() {
580 public void onClick(View v) {
581 setResult(RESULT_OK);
582 finish();
583 }
584 });
585 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
586 mNextButton.setOnClickListener(new OnClickListener() {
587 public void onClick(View v) {
588 setResult(RESULT_OK);
589 finish();
590 }
591 });
592
593 // set our various button parameters
594 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
595 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
596 if (TextUtils.isEmpty(buttonText)) {
597 mNextButton.setVisibility(View.GONE);
598 }
599 else {
600 mNextButton.setText(buttonText);
601 }
602 }
603 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
604 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
605 if (TextUtils.isEmpty(buttonText)) {
606 backButton.setVisibility(View.GONE);
607 }
608 else {
609 backButton.setText(buttonText);
610 }
611 }
612 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
613 skipButton.setVisibility(View.VISIBLE);
614 }
615 }
616 }
617
618 if (!onIsHidingHeaders()) {
619 highlightHeader(mTopLevelHeaderId);
620 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800621 }
622
623 @Override
624 public void onBackStackChanged() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800625 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800626 }
627
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800628 private void setTitleFromBackStack() {
629 final int count = getFragmentManager().getBackStackEntryCount();
630 if (count == 0) {
631 setTitle(mInitialTitle);
632 return;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800633 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800634 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
635 setTitleFromBackStackEntry(bse);
636 }
637
638 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
639 final CharSequence title;
640 final int titleRes = bse.getBreadCrumbTitleRes();
641 if (titleRes > 0) {
642 title = getText(titleRes);
643 } else {
644 title = bse.getBreadCrumbTitle();
645 }
646 if (title != null) {
647 setTitle(title);
648 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800649 }
650
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800651 /**
652 * Returns the Header list
653 */
654 private List<Header> getHeaders() {
655 return mHeaders;
656 }
657
658 @Override
659 protected void onSaveInstanceState(Bundle outState) {
660 super.onSaveInstanceState(outState);
661
662 if (mHeaders.size() > 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800663 outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders);
664 if (mCurrentHeader != null) {
665 int index = mHeaders.indexOf(mCurrentHeader);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800666 if (index >= 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800667 outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800668 }
669 }
670 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800671 }
672
673 @Override
674 public void onResume() {
675 super.onResume();
676
677 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
678 @Override
679 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
680 invalidateHeaders();
681 }
682 };
683 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
684 mDevelopmentPreferencesListener);
685
686 mHeaderAdapter.resume();
687 invalidateHeaders();
688
689 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800690
691 mDrawerLayout.setDrawerListener(new DrawerListener());
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800692 }
693
694 @Override
695 public void onPause() {
696 super.onPause();
697
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800698 mDrawerLayout.setDrawerListener(null);
699
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800700 unregisterReceiver(mBatteryInfoReceiver);
701
702 mHeaderAdapter.pause();
703
704 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
705 mDevelopmentPreferencesListener);
706
707 mDevelopmentPreferencesListener = null;
708 }
709
710 @Override
711 public void onDestroy() {
712 super.onDestroy();
713 if (mListeningToAccountUpdates) {
714 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
715 }
716 }
717
718 /**
719 * @hide
720 */
721 protected boolean isValidFragment(String fragmentName) {
722 // Almost all fragments are wrapped in this,
723 // except for a few that have their own activities.
724 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
725 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
726 }
727 return false;
728 }
729
730 /**
731 * When in two-pane mode, switch to the fragment pane to show the given
732 * preference fragment.
733 *
734 * @param header The new header to display.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800735 * @param validate true means that the fragment's Header needs to be validated.
736 * @param initial true means that it is the initial Header.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800737 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800738 private void switchToHeader(Header header, boolean validate, boolean initial) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800739 if (header == null) {
740 return;
741 }
742 if (header != null && mCurrentHeader != null && header.id == mCurrentHeader.id &&
Fabrice Di Meglio61a77ab2014-02-19 14:07:18 -0800743 header.id != R.id.account_add &&
744 !header.fragment.equals(ManageAccountsSettings.class.getName())) {
745 // This is the header we are currently displaying (except "Add Account" or
746 // "Corporate"/"Google" Account entries that share the same fragment). Just make sure
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000747 // to pop the stack up to its root state.
748 getFragmentManager().popBackStack(BACK_STACK_PREFS,
749 FragmentManager.POP_BACK_STACK_INCLUSIVE);
750 } else {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800751 if (header.fragment != null) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800752 switchToHeaderInner(header.fragment, header.fragmentArguments, validate, !initial,
753 getHeaderTitle(header));
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800754 setSelectedHeader(header);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800755 } else if (header.intent != null) {
756 setSelectedHeader(header);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800757 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800758 } else {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800759 throw new IllegalStateException(
760 "Can't switch to header that has no Fragment nor Intent");
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800761 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800762 }
763 }
764
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800765 private CharSequence getHeaderTitle(Header header) {
766 final CharSequence title;
767 if (header.fragment.equals(DashboardSummary.class.getName())) {
768 title = getResources().getString(R.string.settings_label);
769 } else {
770 title = header.getTitle(getResources());
771 }
772 return title;
773 }
774
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800775 private void setSelectedHeader(Header header) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800776 if (header == null) {
777 mCurrentHeader = null;
778 return;
779 }
780 // Update selected Header into Drawer only if it is not "Add Account"
781 if (header.id == R.id.account_add) {
782 mDrawer.clearChoices();
783 return;
784 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800785 mCurrentHeader = header;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800786 int index = mHeaders.indexOf(header);
787 if (mDrawer != null) {
788 if (index >= 0) {
789 mDrawer.setItemChecked(index, true);
790 } else {
791 mDrawer.clearChoices();
792 }
793 }
794 }
795
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000796 public Header onGetInitialHeader() {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800797 String fragmentClass = getStartingFragmentClass(super.getIntent());
798 if (fragmentClass != null) {
799 Header header = new Header();
800 header.fragment = fragmentClass;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000801 header.title = getTitle();
802 header.fragmentArguments = getIntent().getExtras();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800803 return header;
804 }
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000805
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800806 return mFirstHeader;
807 }
808
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000809 /**
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800810 * Switch the fragment pane to show the given preference fragment.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000811 *
812 * @param fragmentName The name of the fragment to display.
813 * @param args Optional arguments to supply to the fragment.
814 * @param validate true means that the fragment's Header needs to be validated
815 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800816 private void switchToHeader(String fragmentName, Bundle args, boolean validate,
817 CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000818 setSelectedHeader(null);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800819 switchToHeaderInner(fragmentName, args, validate, false, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000820 }
821
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800822 private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
823 boolean addToBackStack, CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000824 getFragmentManager().popBackStack(BACK_STACK_PREFS,
825 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800826 if (validate && !isValidFragment(fragmentName)) {
827 throw new IllegalArgumentException("Invalid fragment for this activity: "
828 + fragmentName);
829 }
830 Fragment f = Fragment.instantiate(this, fragmentName, args);
831 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fabrice Di Meglio4cc95a52014-02-07 18:53:14 -0800832 transaction.replace(R.id.prefs, f);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800833 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
834 if (addToBackStack) {
835 transaction.addToBackStack(BACK_STACK_PREFS);
836 }
837 if (title != null) {
838 transaction.setBreadCrumbTitle(title);
839 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800840 transaction.commitAllowingStateLoss();
841 }
842
843 @Override
844 public void onNewIntent(Intent intent) {
845 super.onNewIntent(intent);
846
847 // If it is not launched from history, then reset to top-level
848 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
849 if (mDrawer != null) {
850 mDrawer.setSelectionFromTop(0, 0);
851 }
852 }
853 }
854
855 /**
856 * Called to determine whether the header list should be hidden.
857 * The default implementation returns the
858 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
859 * This is set to false, for example, when the activity is being re-launched
860 * to show a particular preference activity.
861 */
862 public boolean onIsHidingHeaders() {
863 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
864 }
865
866 private void highlightHeader(int id) {
867 if (id != 0) {
868 Integer index = mHeaderIndexMap.get(id);
869 if (index != null && mDrawer != null) {
870 mDrawer.setItemChecked(index, true);
871 if (mDrawer.getVisibility() == View.VISIBLE) {
872 mDrawer.smoothScrollToPosition(index);
873 }
874 }
875 }
876 }
877
878 @Override
879 public Intent getIntent() {
880 Intent superIntent = super.getIntent();
881 String startingFragment = getStartingFragmentClass(superIntent);
882 // This is called from super.onCreate, isMultiPane() is not yet reliable
883 // Do not use onIsHidingHeaders either, which relies itself on this method
884 if (startingFragment != null) {
885 Intent modIntent = new Intent(superIntent);
886 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
887 Bundle args = superIntent.getExtras();
888 if (args != null) {
889 args = new Bundle(args);
890 } else {
891 args = new Bundle();
892 }
893 args.putParcelable("intent", superIntent);
894 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
895 return modIntent;
896 }
897 return superIntent;
898 }
899
900 /**
901 * Checks if the component name in the intent is different from the Settings class and
902 * returns the class name to load as a fragment.
903 */
904 private String getStartingFragmentClass(Intent intent) {
905 if (mFragmentClass != null) return mFragmentClass;
906
907 String intentClass = intent.getComponent().getClassName();
908 if (intentClass.equals(getClass().getName())) return null;
909
910 if ("com.android.settings.ManageApplications".equals(intentClass)
911 || "com.android.settings.RunningServices".equals(intentClass)
912 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
913 // Old names of manage apps.
914 intentClass = com.android.settings.applications.ManageApplications.class.getName();
915 }
916
917 return intentClass;
918 }
919
920 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000921 * Start a new fragment containing a preference panel. If the preferences
922 * are being displayed in multi-pane mode, the given fragment class will
923 * be instantiated and placed in the appropriate pane. If running in
924 * single-pane mode, a new activity will be launched in which to show the
925 * fragment.
926 *
927 * @param fragmentClass Full name of the class implementing the fragment.
928 * @param args Any desired arguments to supply to the fragment.
929 * @param titleRes Optional resource identifier of the title of this
930 * fragment.
931 * @param titleText Optional text of the title of this fragment.
932 * @param resultTo Optional fragment that result data should be sent to.
933 * If non-null, resultTo.onActivityResult() will be called when this
934 * preference panel is done. The launched panel must use
935 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
936 * @param resultRequestCode If resultTo is non-null, this is the caller's
937 * request code to be received with the resut.
938 */
939 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
940 CharSequence titleText, Fragment resultTo,
941 int resultRequestCode) {
942 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
943 }
944
945 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800946 * Called by a preference panel fragment to finish itself.
947 *
948 * @param caller The fragment that is asking to be finished.
949 * @param resultCode Optional result code to send back to the original
950 * launching fragment.
951 * @param resultData Optional result data to send back to the original
952 * launching fragment.
953 */
954 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
955 setResult(resultCode, resultData);
956 }
957
958 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000959 * Start a new fragment.
960 *
961 * @param fragment The fragment to start
962 * @param push If true, the current fragment will be pushed onto the back stack. If false,
963 * the current fragment will be replaced.
964 */
965 public void startPreferenceFragment(Fragment fragment, boolean push) {
966 FragmentTransaction transaction = getFragmentManager().beginTransaction();
967 transaction.replace(R.id.prefs, fragment);
968 if (push) {
969 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
970 transaction.addToBackStack(BACK_STACK_PREFS);
971 } else {
972 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
973 }
974 transaction.commitAllowingStateLoss();
975 }
976
977 /**
978 * Start a new fragment.
979 *
980 * @param fragmentName The name of the fragment to display.
981 * @param args Optional arguments to supply to the fragment.
982 * @param resultTo Option fragment that should receive the result of
983 * the activity launch.
984 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
985 * report the result.
986 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
987 * valid one then it will be used to get the title. Otherwise the titleText
988 * argument will be used as the title.
989 * @param titleText string to display for the title of.
990 */
991 private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
992 int resultRequestCode, int titleRes, CharSequence titleText) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800993 final CharSequence cs;
994 if (titleRes != 0) {
995 cs = getText(titleRes);
996 } else {
997 cs = titleText;
998 }
999
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001000 Fragment f = Fragment.instantiate(this, fragmentName, args);
1001 if (resultTo != null) {
1002 f.setTargetFragment(resultTo, resultRequestCode);
1003 }
1004 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1005 transaction.replace(R.id.prefs, f);
1006 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1007 transaction.addToBackStack(BACK_STACK_PREFS);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001008 transaction.setBreadCrumbTitle(cs);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001009 transaction.commitAllowingStateLoss();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001010 }
1011
1012 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001013 * Called when the activity needs its list of headers build. By
1014 * implementing this and adding at least one item to the list, you
1015 * will cause the activity to run in its modern fragment mode. Note
1016 * that this function may not always be called; for example, if the
1017 * activity has been asked to display a particular fragment without
1018 * the header list, there is no need to build the headers.
1019 *
1020 * <p>Typical implementations will use {@link #loadHeadersFromResource}
1021 * to fill in the list from a resource.
1022 *
1023 * @param headers The list in which to place the headers.
1024 */
1025 private void onBuildHeaders(List<Header> headers) {
1026 loadHeadersFromResource(R.xml.settings_headers, headers);
1027 updateHeaderList(headers);
1028 }
1029
1030 /**
1031 * Parse the given XML file as a header description, adding each
1032 * parsed Header into the target list.
1033 *
1034 * @param resid The XML resource to load and parse.
1035 * @param target The list in which the parsed headers should be placed.
1036 */
1037 private void loadHeadersFromResource(int resid, List<Header> target) {
1038 XmlResourceParser parser = null;
1039 try {
1040 parser = getResources().getXml(resid);
1041 AttributeSet attrs = Xml.asAttributeSet(parser);
1042
1043 int type;
1044 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1045 && type != XmlPullParser.START_TAG) {
1046 // Parse next until start tag is found
1047 }
1048
1049 String nodeName = parser.getName();
1050 if (!"preference-headers".equals(nodeName)) {
1051 throw new RuntimeException(
1052 "XML document must start with <preference-headers> tag; found"
1053 + nodeName + " at " + parser.getPositionDescription());
1054 }
1055
1056 Bundle curBundle = null;
1057
1058 final int outerDepth = parser.getDepth();
1059 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1060 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1061 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1062 continue;
1063 }
1064
1065 nodeName = parser.getName();
1066 if ("header".equals(nodeName)) {
1067 Header header = new Header();
1068
1069 TypedArray sa = obtainStyledAttributes(
1070 attrs, com.android.internal.R.styleable.PreferenceHeader);
1071 header.id = sa.getResourceId(
1072 com.android.internal.R.styleable.PreferenceHeader_id,
1073 (int)HEADER_ID_UNDEFINED);
1074 TypedValue tv = sa.peekValue(
1075 com.android.internal.R.styleable.PreferenceHeader_title);
1076 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1077 if (tv.resourceId != 0) {
1078 header.titleRes = tv.resourceId;
1079 } else {
1080 header.title = tv.string;
1081 }
1082 }
1083 tv = sa.peekValue(
1084 com.android.internal.R.styleable.PreferenceHeader_summary);
1085 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1086 if (tv.resourceId != 0) {
1087 header.summaryRes = tv.resourceId;
1088 } else {
1089 header.summary = tv.string;
1090 }
1091 }
1092 header.iconRes = sa.getResourceId(
1093 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1094 header.fragment = sa.getString(
1095 com.android.internal.R.styleable.PreferenceHeader_fragment);
1096 sa.recycle();
1097
1098 if (curBundle == null) {
1099 curBundle = new Bundle();
1100 }
1101
1102 final int innerDepth = parser.getDepth();
1103 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1104 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1105 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1106 continue;
1107 }
1108
1109 String innerNodeName = parser.getName();
1110 if (innerNodeName.equals("extra")) {
1111 getResources().parseBundleExtra("extra", attrs, curBundle);
1112 XmlUtils.skipCurrentTag(parser);
1113
1114 } else if (innerNodeName.equals("intent")) {
1115 header.intent = Intent.parseIntent(getResources(), parser, attrs);
1116
1117 } else {
1118 XmlUtils.skipCurrentTag(parser);
1119 }
1120 }
1121
1122 if (curBundle.size() > 0) {
1123 header.fragmentArguments = curBundle;
1124 curBundle = null;
1125 }
1126
1127 target.add(header);
1128 } else {
1129 XmlUtils.skipCurrentTag(parser);
1130 }
1131 }
1132
1133 } catch (XmlPullParserException e) {
1134 throw new RuntimeException("Error parsing headers", e);
1135 } catch (IOException e) {
1136 throw new RuntimeException("Error parsing headers", e);
1137 } finally {
1138 if (parser != null) parser.close();
1139 }
1140 }
1141
1142 private void updateHeaderList(List<Header> target) {
1143 final boolean showDev = mDevelopmentPreferences.getBoolean(
1144 DevelopmentSettings.PREF_SHOW,
1145 android.os.Build.TYPE.equals("eng"));
1146 int i = 0;
1147
1148 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1149 mHeaderIndexMap.clear();
1150 while (i < target.size()) {
1151 Header header = target.get(i);
1152 // Ids are integers, so downcasting
1153 int id = (int) header.id;
1154 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1155 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1156 } else if (id == R.id.wifi_settings) {
1157 // Remove WiFi Settings if WiFi service is not available.
1158 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1159 target.remove(i);
1160 }
1161 } else if (id == R.id.bluetooth_settings) {
1162 // Remove Bluetooth Settings if Bluetooth service is not available.
1163 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1164 target.remove(i);
1165 }
1166 } else if (id == R.id.data_usage_settings) {
1167 // Remove data usage when kernel module not enabled
1168 final INetworkManagementService netManager = INetworkManagementService.Stub
1169 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1170 try {
1171 if (!netManager.isBandwidthControlEnabled()) {
1172 target.remove(i);
1173 }
1174 } catch (RemoteException e) {
1175 // ignored
1176 }
1177 } else if (id == R.id.battery_settings) {
1178 // Remove battery settings when battery is not available. (e.g. TV)
1179
1180 if (!mBatteryPresent) {
1181 target.remove(i);
1182 }
1183 } else if (id == R.id.account_settings) {
1184 int headerIndex = i + 1;
1185 i = insertAccountsHeaders(target, headerIndex);
1186 } else if (id == R.id.home_settings) {
1187 if (!updateHomeSettingHeaders(header)) {
1188 target.remove(i);
1189 }
1190 } else if (id == R.id.user_settings) {
1191 if (!UserHandle.MU_ENABLED
1192 || !UserManager.supportsMultipleUsers()
1193 || Utils.isMonkeyRunning()) {
1194 target.remove(i);
1195 }
1196 } else if (id == R.id.nfc_payment_settings) {
1197 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1198 target.remove(i);
1199 } else {
1200 // Only show if NFC is on and we have the HCE feature
1201 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1202 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1203 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1204 target.remove(i);
1205 }
1206 }
1207 } else if (id == R.id.development_settings) {
1208 if (!showDev) {
1209 target.remove(i);
1210 }
1211 } else if (id == R.id.account_add) {
1212 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1213 target.remove(i);
1214 }
1215 }
1216
1217 if (i < target.size() && target.get(i) == header
1218 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1219 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1220 target.remove(i);
1221 }
1222
1223 // Increment if the current one wasn't removed by the Utils code.
1224 if (i < target.size() && target.get(i) == header) {
1225 // Hold on to the first header, when we need to reset to the top-level
1226 if (mFirstHeader == null &&
1227 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
1228 mFirstHeader = header;
1229 }
1230 mHeaderIndexMap.put(id, i);
1231 i++;
1232 }
1233 }
1234 }
1235
1236 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1237 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1238 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1239 for (String accountType : accountTypes) {
1240 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1241 if (label == null) {
1242 continue;
1243 }
1244
1245 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1246 boolean skipToAccount = accounts.length == 1
1247 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1248 Header accHeader = new Header();
1249 accHeader.title = label;
1250 if (accHeader.extras == null) {
1251 accHeader.extras = new Bundle();
1252 }
1253 if (skipToAccount) {
1254 accHeader.fragment = AccountSyncSettings.class.getName();
1255 accHeader.fragmentArguments = new Bundle();
1256 // Need this for the icon
1257 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1258 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1259 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1260 accounts[0]);
1261 } else {
1262 accHeader.fragment = ManageAccountsSettings.class.getName();
1263 accHeader.fragmentArguments = new Bundle();
1264 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1265 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1266 accountType);
1267 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1268 label.toString());
1269 }
1270 accountHeaders.add(accHeader);
1271 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1272 }
1273
1274 // Sort by label
1275 Collections.sort(accountHeaders, new Comparator<Header>() {
1276 @Override
1277 public int compare(Header h1, Header h2) {
1278 return h1.title.toString().compareTo(h2.title.toString());
1279 }
1280 });
1281
1282 for (Header header : accountHeaders) {
1283 target.add(headerIndex++, header);
1284 }
1285 if (!mListeningToAccountUpdates) {
1286 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1287 mListeningToAccountUpdates = true;
1288 }
1289 return headerIndex;
1290 }
1291
1292 private boolean updateHomeSettingHeaders(Header header) {
1293 // Once we decide to show Home settings, keep showing it forever
1294 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1295 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1296 return true;
1297 }
1298
1299 try {
1300 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1301 getPackageManager().getHomeActivities(homeApps);
1302 if (homeApps.size() < 2) {
1303 // When there's only one available home app, omit this settings
1304 // category entirely at the top level UI. If the user just
1305 // uninstalled the penultimate home app candidiate, we also
1306 // now tell them about why they aren't seeing 'Home' in the list.
1307 if (sShowNoHomeNotice) {
1308 sShowNoHomeNotice = false;
1309 NoHomeDialogFragment.show(this);
1310 }
1311 return false;
1312 } else {
1313 // Okay, we're allowing the Home settings category. Tell it, when
1314 // invoked via this front door, that we'll need to be told about the
1315 // case when the user uninstalls all but one home app.
1316 if (header.fragmentArguments == null) {
1317 header.fragmentArguments = new Bundle();
1318 }
1319 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1320 }
1321 } catch (Exception e) {
1322 // Can't look up the home activity; bail on configuring the icon
1323 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1324 }
1325
1326 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1327 return true;
1328 }
1329
1330 private void getMetaData() {
1331 try {
1332 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1333 PackageManager.GET_META_DATA);
1334 if (ai == null || ai.metaData == null) return;
1335 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
1336 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1337 } catch (NameNotFoundException nnfe) {
1338 // No recovery
1339 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1340 }
1341 }
1342
1343 // give subclasses access to the Next button
1344 public boolean hasNextButton() {
1345 return mNextButton != null;
1346 }
1347
1348 public Button getNextButton() {
1349 return mNextButton;
1350 }
1351
1352 public static class NoHomeDialogFragment extends DialogFragment {
1353 public static void show(Activity parent) {
1354 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
1355 dialog.show(parent.getFragmentManager(), null);
1356 }
1357
1358 @Override
1359 public Dialog onCreateDialog(Bundle savedInstanceState) {
1360 return new AlertDialog.Builder(getActivity())
1361 .setMessage(R.string.only_one_home_message)
1362 .setPositiveButton(android.R.string.ok, null)
1363 .create();
1364 }
1365 }
1366
1367 private static class HeaderAdapter extends ArrayAdapter<Header> {
1368 static final int HEADER_TYPE_CATEGORY = 0;
1369 static final int HEADER_TYPE_NORMAL = 1;
1370 static final int HEADER_TYPE_SWITCH = 2;
1371 static final int HEADER_TYPE_BUTTON = 3;
1372 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
1373
1374 private final WifiEnabler mWifiEnabler;
1375 private final BluetoothEnabler mBluetoothEnabler;
1376 private AuthenticatorHelper mAuthHelper;
1377 private DevicePolicyManager mDevicePolicyManager;
1378
1379 private static class HeaderViewHolder {
1380 ImageView mIcon;
1381 TextView mTitle;
1382 TextView mSummary;
1383 Switch mSwitch;
1384 ImageButton mButton;
1385 View mDivider;
1386 }
1387
1388 private LayoutInflater mInflater;
1389
1390 static int getHeaderType(Header header) {
1391 if (header.fragment == null && header.intent == null) {
1392 return HEADER_TYPE_CATEGORY;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001393 } else if (header.id == R.id.security_settings) {
1394 return HEADER_TYPE_BUTTON;
1395 } else {
1396 return HEADER_TYPE_NORMAL;
1397 }
1398 }
1399
1400 @Override
1401 public int getItemViewType(int position) {
1402 Header header = getItem(position);
1403 return getHeaderType(header);
1404 }
1405
1406 @Override
1407 public boolean areAllItemsEnabled() {
1408 return false; // because of categories
1409 }
1410
1411 @Override
1412 public boolean isEnabled(int position) {
1413 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
1414 }
1415
1416 @Override
1417 public int getViewTypeCount() {
1418 return HEADER_TYPE_COUNT;
1419 }
1420
1421 @Override
1422 public boolean hasStableIds() {
1423 return true;
1424 }
1425
1426 public HeaderAdapter(Context context, List<Header> objects,
1427 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
1428 super(context, 0, objects);
1429
1430 mAuthHelper = authenticatorHelper;
1431 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1432
1433 // Temp Switches provided as placeholder until the adapter replaces these with actual
1434 // Switches inflated from their layouts. Must be done before adapter is set in super
1435 mWifiEnabler = new WifiEnabler(context, new Switch(context));
1436 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
1437 mDevicePolicyManager = dpm;
1438 }
1439
1440 @Override
1441 public View getView(int position, View convertView, ViewGroup parent) {
1442 HeaderViewHolder holder;
1443 Header header = getItem(position);
1444 int headerType = getHeaderType(header);
1445 View view = null;
1446
1447 if (convertView == null) {
1448 holder = new HeaderViewHolder();
1449 switch (headerType) {
1450 case HEADER_TYPE_CATEGORY:
1451 view = new TextView(getContext(), null,
1452 android.R.attr.listSeparatorTextViewStyle);
1453 holder.mTitle = (TextView) view;
1454 break;
1455
1456 case HEADER_TYPE_SWITCH:
1457 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
1458 false);
1459 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1460 holder.mTitle = (TextView)
1461 view.findViewById(com.android.internal.R.id.title);
1462 holder.mSummary = (TextView)
1463 view.findViewById(com.android.internal.R.id.summary);
1464 holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget);
1465 break;
1466
1467 case HEADER_TYPE_BUTTON:
1468 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
1469 false);
1470 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1471 holder.mTitle = (TextView)
1472 view.findViewById(com.android.internal.R.id.title);
1473 holder.mSummary = (TextView)
1474 view.findViewById(com.android.internal.R.id.summary);
1475 holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget);
1476 holder.mDivider = view.findViewById(R.id.divider);
1477 break;
1478
1479 case HEADER_TYPE_NORMAL:
1480 view = mInflater.inflate(
1481 R.layout.preference_header_item, parent,
1482 false);
1483 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1484 holder.mTitle = (TextView)
1485 view.findViewById(com.android.internal.R.id.title);
1486 holder.mSummary = (TextView)
1487 view.findViewById(com.android.internal.R.id.summary);
1488 break;
1489 }
Fabrice Di Meglio0e2f9492014-02-25 14:26:27 -08001490 if (holder.mIcon != null) {
1491 holder.mIcon.setBackgroundResource(R.color.background_drawer_icon);
1492 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001493 view.setTag(holder);
1494 } else {
1495 view = convertView;
1496 holder = (HeaderViewHolder) view.getTag();
1497 }
1498
1499 // All view fields must be updated every time, because the view may be recycled
1500 switch (headerType) {
1501 case HEADER_TYPE_CATEGORY:
1502 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1503 break;
1504
1505 case HEADER_TYPE_SWITCH:
1506 // Would need a different treatment if the main menu had more switches
1507 if (header.id == R.id.wifi_settings) {
1508 mWifiEnabler.setSwitch(holder.mSwitch);
1509 } else {
1510 mBluetoothEnabler.setSwitch(holder.mSwitch);
1511 }
1512 updateCommonHeaderView(header, holder);
1513 break;
1514
1515 case HEADER_TYPE_BUTTON:
1516 if (header.id == R.id.security_settings) {
1517 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
1518 if (hasCert) {
1519 holder.mButton.setVisibility(View.VISIBLE);
1520 holder.mDivider.setVisibility(View.VISIBLE);
1521 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
1522 if (isManaged) {
1523 holder.mButton.setImageResource(R.drawable.ic_settings_about);
1524 } else {
1525 holder.mButton.setImageResource(
1526 android.R.drawable.stat_notify_error);
1527 }
1528 holder.mButton.setOnClickListener(new OnClickListener() {
1529 @Override
1530 public void onClick(View v) {
1531 Intent intent = new Intent(
1532 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
1533 getContext().startActivity(intent);
1534 }
1535 });
1536 } else {
1537 holder.mButton.setVisibility(View.GONE);
1538 holder.mDivider.setVisibility(View.GONE);
1539 }
1540 }
1541 updateCommonHeaderView(header, holder);
1542 break;
1543
1544 case HEADER_TYPE_NORMAL:
1545 updateCommonHeaderView(header, holder);
1546 break;
1547 }
1548
1549 return view;
1550 }
1551
1552 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
1553 if (header.extras != null
1554 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
1555 String accType = header.extras.getString(
1556 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
1557 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
1558 setHeaderIcon(holder, icon);
1559 } else {
1560 holder.mIcon.setImageResource(header.iconRes);
1561 }
1562 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1563 CharSequence summary = header.getSummary(getContext().getResources());
1564 if (!TextUtils.isEmpty(summary)) {
1565 holder.mSummary.setVisibility(View.VISIBLE);
1566 holder.mSummary.setText(summary);
1567 } else {
1568 holder.mSummary.setVisibility(View.GONE);
1569 }
1570 }
1571
1572 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
1573 ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams();
1574 lp.width = getContext().getResources().getDimensionPixelSize(
1575 R.dimen.header_icon_width);
1576 lp.height = lp.width;
1577 holder.mIcon.setLayoutParams(lp);
1578 holder.mIcon.setImageDrawable(icon);
1579 }
1580
1581 public void resume() {
1582 mWifiEnabler.resume();
1583 mBluetoothEnabler.resume();
1584 }
1585
1586 public void pause() {
1587 mWifiEnabler.pause();
1588 mBluetoothEnabler.pause();
1589 }
1590 }
1591
1592 private void onListItemClick(ListView l, View v, int position, long id) {
1593 if (!isResumed()) {
1594 return;
1595 }
1596 Object item = mHeaderAdapter.getItem(position);
1597 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001598 mSelectedHeader = (Header) item;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001599 }
1600 }
1601
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001602 @Override
1603 public boolean shouldUpRecreateTask(Intent targetIntent) {
1604 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1605 }
1606
1607 @Override
1608 public void onAccountsUpdated(Account[] accounts) {
1609 // TODO: watch for package upgrades to invalidate cache; see 7206643
1610 mAuthenticatorHelper.updateAuthDescriptions(this);
1611 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1612 invalidateHeaders();
1613 }
1614
1615 public static void requestHomeNotice() {
1616 sShowNoHomeNotice = true;
1617 }
1618
1619 /**
1620 * Default value for {@link Header#id Header.id} indicating that no
1621 * identifier value is set. All other values (including those below -1)
1622 * are valid.
1623 */
1624 private static final long HEADER_ID_UNDEFINED = -1;
1625
1626 /**
1627 * Description of a single Header item that the user can select.
1628 */
1629 static final class Header implements Parcelable {
1630 /**
1631 * Identifier for this header, to correlate with a new list when
1632 * it is updated. The default value is
1633 * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id.
1634 * @attr ref android.R.styleable#PreferenceHeader_id
1635 */
1636 public long id = HEADER_ID_UNDEFINED;
1637
1638 /**
1639 * Resource ID of title of the header that is shown to the user.
1640 * @attr ref android.R.styleable#PreferenceHeader_title
1641 */
1642 public int titleRes;
1643
1644 /**
1645 * Title of the header that is shown to the user.
1646 * @attr ref android.R.styleable#PreferenceHeader_title
1647 */
1648 public CharSequence title;
1649
1650 /**
1651 * Resource ID of optional summary describing what this header controls.
1652 * @attr ref android.R.styleable#PreferenceHeader_summary
1653 */
1654 public int summaryRes;
1655
1656 /**
1657 * Optional summary describing what this header controls.
1658 * @attr ref android.R.styleable#PreferenceHeader_summary
1659 */
1660 public CharSequence summary;
1661
1662 /**
1663 * Optional icon resource to show for this header.
1664 * @attr ref android.R.styleable#PreferenceHeader_icon
1665 */
1666 public int iconRes;
1667
1668 /**
1669 * Full class name of the fragment to display when this header is
1670 * selected.
1671 * @attr ref android.R.styleable#PreferenceHeader_fragment
1672 */
1673 public String fragment;
1674
1675 /**
1676 * Optional arguments to supply to the fragment when it is
1677 * instantiated.
1678 */
1679 public Bundle fragmentArguments;
1680
1681 /**
1682 * Intent to launch when the preference is selected.
1683 */
1684 public Intent intent;
1685
1686 /**
1687 * Optional additional data for use by subclasses of the activity
1688 */
1689 public Bundle extras;
1690
1691 public Header() {
1692 // Empty
1693 }
1694
1695 /**
1696 * Return the currently set title. If {@link #titleRes} is set,
1697 * this resource is loaded from <var>res</var> and returned. Otherwise
1698 * {@link #title} is returned.
1699 */
1700 public CharSequence getTitle(Resources res) {
1701 if (titleRes != 0) {
1702 return res.getText(titleRes);
1703 }
1704 return title;
1705 }
1706
1707 /**
1708 * Return the currently set summary. If {@link #summaryRes} is set,
1709 * this resource is loaded from <var>res</var> and returned. Otherwise
1710 * {@link #summary} is returned.
1711 */
1712 public CharSequence getSummary(Resources res) {
1713 if (summaryRes != 0) {
1714 return res.getText(summaryRes);
1715 }
1716 return summary;
1717 }
1718
1719 @Override
1720 public int describeContents() {
1721 return 0;
1722 }
1723
1724 @Override
1725 public void writeToParcel(Parcel dest, int flags) {
1726 dest.writeLong(id);
1727 dest.writeInt(titleRes);
1728 TextUtils.writeToParcel(title, dest, flags);
1729 dest.writeInt(summaryRes);
1730 TextUtils.writeToParcel(summary, dest, flags);
1731 dest.writeInt(iconRes);
1732 dest.writeString(fragment);
1733 dest.writeBundle(fragmentArguments);
1734 if (intent != null) {
1735 dest.writeInt(1);
1736 intent.writeToParcel(dest, flags);
1737 } else {
1738 dest.writeInt(0);
1739 }
1740 dest.writeBundle(extras);
1741 }
1742
1743 public void readFromParcel(Parcel in) {
1744 id = in.readLong();
1745 titleRes = in.readInt();
1746 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1747 summaryRes = in.readInt();
1748 summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1749 iconRes = in.readInt();
1750 fragment = in.readString();
1751 fragmentArguments = in.readBundle();
1752 if (in.readInt() != 0) {
1753 intent = Intent.CREATOR.createFromParcel(in);
1754 }
1755 extras = in.readBundle();
1756 }
1757
1758 Header(Parcel in) {
1759 readFromParcel(in);
1760 }
1761
1762 public static final Creator<Header> CREATOR = new Creator<Header>() {
1763 public Header createFromParcel(Parcel source) {
1764 return new Header(source);
1765 }
1766 public Header[] newArray(int size) {
1767 return new Header[size];
1768 }
1769 };
1770 }
1771}