blob: fd6c9862d9b5a783ea7d92fbcf27e4af1aa95737 [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(),
John Spurlock72438062014-02-27 18:01:20 -0500281 DashboardSummary.class.getName(),
282 ZenModeSettings.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800283 };
284
285 private SharedPreferences mDevelopmentPreferences;
286 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
287
288 // TODO: Update Call Settings based on airplane mode state.
289
290 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
291
292 private AuthenticatorHelper mAuthenticatorHelper;
293 private boolean mListeningToAccountUpdates;
294
295 private Button mNextButton;
296
297 private boolean mBatteryPresent = true;
298 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
299
300 @Override
301 public void onReceive(Context context, Intent intent) {
302 String action = intent.getAction();
303 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
304 boolean batteryPresent = Utils.isBatteryPresent(intent);
305
306 if (mBatteryPresent != batteryPresent) {
307 mBatteryPresent = batteryPresent;
308 invalidateHeaders();
309 }
310 }
311 }
312 };
313
314 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800315 private HeaderAdapter mHeaderAdapter;
316
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800317 private DrawerLayout mDrawerLayout;
318 private ListView mDrawer;
319 private ActionBarDrawerToggle mDrawerToggle;
320 private ActionBar mActionBar;
321
322 private static final int MSG_BUILD_HEADERS = 1;
323 private Handler mHandler = new Handler() {
324 @Override
325 public void handleMessage(Message msg) {
326 switch (msg.what) {
327 case MSG_BUILD_HEADERS: {
328 mHeaders.clear();
329 onBuildHeaders(mHeaders);
330 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800331 if (mCurrentHeader != null) {
332 Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800333 if (mappedHeader != null) {
334 setSelectedHeader(mappedHeader);
335 }
336 }
337 } break;
338 }
339 }
340 };
341
342 @Override
343 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
344 // Override the fragment title for Wallpaper settings
345 int titleRes = pref.getTitleRes();
346 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
347 titleRes = R.string.wallpaper_settings_fragment_title;
348 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
349 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
350 if (UserManager.get(this).isLinkedUser()) {
351 titleRes = R.string.profile_info_settings_title;
352 } else {
353 titleRes = R.string.user_info_settings_title;
354 }
355 }
356 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
357 null, 0);
358 return true;
359 }
360
361 @Override
362 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
363 return false;
364 }
365
366 private class DrawerListener implements DrawerLayout.DrawerListener {
367 @Override
368 public void onDrawerOpened(View drawerView) {
369 mDrawerToggle.onDrawerOpened(drawerView);
370 }
371
372 @Override
373 public void onDrawerClosed(View drawerView) {
374 mDrawerToggle.onDrawerClosed(drawerView);
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800375 // Cannot process clicks when the App is finishing
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800376 if (isFinishing() || mSelectedHeader == null) {
377 return;
378 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800379 switchToHeader(mSelectedHeader, false, false);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800380 mSelectedHeader = null;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800381 }
382
383 @Override
384 public void onDrawerSlide(View drawerView, float slideOffset) {
385 mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
386 }
387
388 @Override
389 public void onDrawerStateChanged(int newState) {
390 mDrawerToggle.onDrawerStateChanged(newState);
391 }
392 }
393
394 private class DrawerItemClickListener implements ListView.OnItemClickListener {
395 @Override
396 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
397 mDrawerLayout.closeDrawer(mDrawer);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000398 onListItemClick((ListView)parent, view, position, id);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800399 }
400 }
401
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800402 private Header findBestMatchingHeader(Header current, ArrayList<Header> from) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800403 ArrayList<Header> matches = new ArrayList<Header>();
404 for (int j=0; j<from.size(); j++) {
405 Header oh = from.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800406 if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800407 // Must be this one.
408 matches.clear();
409 matches.add(oh);
410 break;
411 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800412 if (current.fragment != null) {
413 if (current.fragment.equals(oh.fragment)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800414 matches.add(oh);
415 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800416 } else if (current.intent != null) {
417 if (current.intent.equals(oh.intent)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800418 matches.add(oh);
419 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800420 } else if (current.title != null) {
421 if (current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800422 matches.add(oh);
423 }
424 }
425 }
426 final int NM = matches.size();
427 if (NM == 1) {
428 return matches.get(0);
429 } else if (NM > 1) {
430 for (int j=0; j<NM; j++) {
431 Header oh = matches.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800432 if (current.fragmentArguments != null &&
433 current.fragmentArguments.equals(oh.fragmentArguments)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800434 return oh;
435 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800436 if (current.extras != null && current.extras.equals(oh.extras)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800437 return oh;
438 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800439 if (current.title != null && current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800440 return oh;
441 }
442 }
443 }
444 return null;
445 }
446
447 private void invalidateHeaders() {
448 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
449 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
450 }
451 }
452
453 @Override
454 protected void onPostCreate(Bundle savedInstanceState) {
455 super.onPostCreate(savedInstanceState);
456
457 // Sync the toggle state after onRestoreInstanceState has occurred.
458 if (mDrawerToggle != null) {
459 mDrawerToggle.syncState();
460 }
461 }
462
463 @Override
464 public void onConfigurationChanged(Configuration newConfig) {
465 super.onConfigurationChanged(newConfig);
466 if (mDrawerToggle != null) {
467 mDrawerToggle.onConfigurationChanged(newConfig);
468 }
469 }
470
471 @Override
472 public boolean onOptionsItemSelected(MenuItem item) {
473 if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
474 return true;
475 }
476 return super.onOptionsItemSelected(item);
477 }
478
479 @Override
480 protected void onCreate(Bundle savedInstanceState) {
481 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
482 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
483 }
484
485 mAuthenticatorHelper = new AuthenticatorHelper();
486 mAuthenticatorHelper.updateAuthDescriptions(this);
487 mAuthenticatorHelper.onAccountsUpdated(this, null);
488
489 DevicePolicyManager dpm =
490 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800491
Fabrice Di Meglio0e2f9492014-02-25 14:26:27 -0800492 mHeaderAdapter= new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800493
494 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
495 Context.MODE_PRIVATE);
496
497 getMetaData();
498
499 super.onCreate(savedInstanceState);
500
501 setContentView(R.layout.settings_main);
502
503 getFragmentManager().addOnBackStackChangedListener(this);
504
505 mActionBar = getActionBar();
506 if (mActionBar != null) {
507 mActionBar.setDisplayHomeAsUpEnabled(true);
508 mActionBar.setHomeButtonEnabled(true);
509
510 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800511
512 mDrawer = (ListView) findViewById(R.id.headers_drawer);
513 mDrawer.setAdapter(mHeaderAdapter);
514 mDrawer.setOnItemClickListener(new DrawerItemClickListener());
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000515 mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800516
517 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
518 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
519 }
520
521 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
522 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
523
524 if (savedInstanceState != null) {
525 // We are restarting from a previous saved state; used that to
526 // initialize, instead of starting fresh.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800527 mInitialTitle = getTitle();
528 setTitleFromBackStack();
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800529
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800530 ArrayList<Header> headers =
531 savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800532 if (headers != null) {
533 mHeaders.addAll(headers);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800534 int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG,
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800535 (int) HEADER_ID_UNDEFINED);
536 if (curHeader >= 0 && curHeader < mHeaders.size()) {
537 setSelectedHeader(mHeaders.get(curHeader));
538 }
539 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800540 } else {
541 if (initialFragment != null) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000542 // If we are just showing a fragment, we want to run in
543 // new fragment mode, but don't need to compute and show
544 // the headers.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800545 final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
546 mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000547
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800548 switchToHeader(initialFragment, initialArguments, true, mInitialTitle);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000549 } else {
550 // We need to try to build the headers.
551 onBuildHeaders(mHeaders);
552
553 // If there are headers, then at this point we need to show
554 // them and, depending on the screen, we may also show in-line
555 // the currently selected preference fragment.
556 if (mHeaders.size() > 0) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800557 Header h = onGetInitialHeader();
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800558 mInitialTitle = getHeaderTitle(h);
559 switchToHeader(h, false, true);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000560 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800561 }
562 }
563
564 // see if we should show Back/Next buttons
565 Intent intent = getIntent();
566 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
567
568 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
569 if (buttonBar != null) {
570 buttonBar.setVisibility(View.VISIBLE);
571
572 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
573 backButton.setOnClickListener(new OnClickListener() {
574 public void onClick(View v) {
575 setResult(RESULT_CANCELED);
576 finish();
577 }
578 });
579 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
580 skipButton.setOnClickListener(new OnClickListener() {
581 public void onClick(View v) {
582 setResult(RESULT_OK);
583 finish();
584 }
585 });
586 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
587 mNextButton.setOnClickListener(new OnClickListener() {
588 public void onClick(View v) {
589 setResult(RESULT_OK);
590 finish();
591 }
592 });
593
594 // set our various button parameters
595 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
596 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
597 if (TextUtils.isEmpty(buttonText)) {
598 mNextButton.setVisibility(View.GONE);
599 }
600 else {
601 mNextButton.setText(buttonText);
602 }
603 }
604 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
605 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
606 if (TextUtils.isEmpty(buttonText)) {
607 backButton.setVisibility(View.GONE);
608 }
609 else {
610 backButton.setText(buttonText);
611 }
612 }
613 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
614 skipButton.setVisibility(View.VISIBLE);
615 }
616 }
617 }
618
619 if (!onIsHidingHeaders()) {
620 highlightHeader(mTopLevelHeaderId);
621 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800622 }
623
624 @Override
625 public void onBackStackChanged() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800626 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800627 }
628
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800629 private void setTitleFromBackStack() {
630 final int count = getFragmentManager().getBackStackEntryCount();
631 if (count == 0) {
632 setTitle(mInitialTitle);
633 return;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800634 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800635 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
636 setTitleFromBackStackEntry(bse);
637 }
638
639 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
640 final CharSequence title;
641 final int titleRes = bse.getBreadCrumbTitleRes();
642 if (titleRes > 0) {
643 title = getText(titleRes);
644 } else {
645 title = bse.getBreadCrumbTitle();
646 }
647 if (title != null) {
648 setTitle(title);
649 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800650 }
651
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800652 /**
653 * Returns the Header list
654 */
655 private List<Header> getHeaders() {
656 return mHeaders;
657 }
658
659 @Override
660 protected void onSaveInstanceState(Bundle outState) {
661 super.onSaveInstanceState(outState);
662
663 if (mHeaders.size() > 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800664 outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders);
665 if (mCurrentHeader != null) {
666 int index = mHeaders.indexOf(mCurrentHeader);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800667 if (index >= 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800668 outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800669 }
670 }
671 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800672 }
673
674 @Override
675 public void onResume() {
676 super.onResume();
677
678 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
679 @Override
680 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
681 invalidateHeaders();
682 }
683 };
684 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
685 mDevelopmentPreferencesListener);
686
Matthew Xiea504c4d2014-02-14 16:32:32 -0800687 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800688 invalidateHeaders();
689
690 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800691
692 mDrawerLayout.setDrawerListener(new DrawerListener());
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800693 }
694
695 @Override
696 public void onPause() {
697 super.onPause();
698
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800699 mDrawerLayout.setDrawerListener(null);
700
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800701 unregisterReceiver(mBatteryInfoReceiver);
702
703 mHeaderAdapter.pause();
704
705 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
706 mDevelopmentPreferencesListener);
707
708 mDevelopmentPreferencesListener = null;
709 }
710
711 @Override
712 public void onDestroy() {
713 super.onDestroy();
714 if (mListeningToAccountUpdates) {
715 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
716 }
717 }
718
719 /**
720 * @hide
721 */
722 protected boolean isValidFragment(String fragmentName) {
723 // Almost all fragments are wrapped in this,
724 // except for a few that have their own activities.
725 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
726 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
727 }
728 return false;
729 }
730
731 /**
732 * When in two-pane mode, switch to the fragment pane to show the given
733 * preference fragment.
734 *
735 * @param header The new header to display.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800736 * @param validate true means that the fragment's Header needs to be validated.
737 * @param initial true means that it is the initial Header.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800738 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800739 private void switchToHeader(Header header, boolean validate, boolean initial) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800740 if (header == null) {
741 return;
742 }
743 if (header != null && mCurrentHeader != null && header.id == mCurrentHeader.id &&
Fabrice Di Meglio61a77ab2014-02-19 14:07:18 -0800744 header.id != R.id.account_add &&
745 !header.fragment.equals(ManageAccountsSettings.class.getName())) {
746 // This is the header we are currently displaying (except "Add Account" or
747 // "Corporate"/"Google" Account entries that share the same fragment). Just make sure
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000748 // to pop the stack up to its root state.
749 getFragmentManager().popBackStack(BACK_STACK_PREFS,
750 FragmentManager.POP_BACK_STACK_INCLUSIVE);
751 } else {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800752 if (header.fragment != null) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800753 switchToHeaderInner(header.fragment, header.fragmentArguments, validate, !initial,
754 getHeaderTitle(header));
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800755 setSelectedHeader(header);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800756 } else if (header.intent != null) {
757 setSelectedHeader(header);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800758 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800759 } else {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800760 throw new IllegalStateException(
761 "Can't switch to header that has no Fragment nor Intent");
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800762 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800763 }
764 }
765
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800766 private CharSequence getHeaderTitle(Header header) {
767 final CharSequence title;
768 if (header.fragment.equals(DashboardSummary.class.getName())) {
769 title = getResources().getString(R.string.settings_label);
770 } else {
771 title = header.getTitle(getResources());
772 }
773 return title;
774 }
775
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800776 private void setSelectedHeader(Header header) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800777 if (header == null) {
778 mCurrentHeader = null;
779 return;
780 }
781 // Update selected Header into Drawer only if it is not "Add Account"
782 if (header.id == R.id.account_add) {
783 mDrawer.clearChoices();
784 return;
785 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800786 mCurrentHeader = header;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800787 int index = mHeaders.indexOf(header);
788 if (mDrawer != null) {
789 if (index >= 0) {
790 mDrawer.setItemChecked(index, true);
791 } else {
792 mDrawer.clearChoices();
793 }
794 }
795 }
796
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000797 public Header onGetInitialHeader() {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800798 String fragmentClass = getStartingFragmentClass(super.getIntent());
799 if (fragmentClass != null) {
800 Header header = new Header();
801 header.fragment = fragmentClass;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000802 header.title = getTitle();
803 header.fragmentArguments = getIntent().getExtras();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800804 return header;
805 }
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000806
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800807 return mFirstHeader;
808 }
809
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000810 /**
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800811 * Switch the fragment pane to show the given preference fragment.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000812 *
813 * @param fragmentName The name of the fragment to display.
814 * @param args Optional arguments to supply to the fragment.
815 * @param validate true means that the fragment's Header needs to be validated
816 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800817 private void switchToHeader(String fragmentName, Bundle args, boolean validate,
818 CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000819 setSelectedHeader(null);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800820 switchToHeaderInner(fragmentName, args, validate, false, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000821 }
822
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800823 private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
824 boolean addToBackStack, CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000825 getFragmentManager().popBackStack(BACK_STACK_PREFS,
826 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800827 if (validate && !isValidFragment(fragmentName)) {
828 throw new IllegalArgumentException("Invalid fragment for this activity: "
829 + fragmentName);
830 }
831 Fragment f = Fragment.instantiate(this, fragmentName, args);
832 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fabrice Di Meglio4cc95a52014-02-07 18:53:14 -0800833 transaction.replace(R.id.prefs, f);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800834 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
835 if (addToBackStack) {
836 transaction.addToBackStack(BACK_STACK_PREFS);
837 }
838 if (title != null) {
839 transaction.setBreadCrumbTitle(title);
840 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800841 transaction.commitAllowingStateLoss();
842 }
843
844 @Override
845 public void onNewIntent(Intent intent) {
846 super.onNewIntent(intent);
847
848 // If it is not launched from history, then reset to top-level
849 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
850 if (mDrawer != null) {
851 mDrawer.setSelectionFromTop(0, 0);
852 }
853 }
854 }
855
856 /**
857 * Called to determine whether the header list should be hidden.
858 * The default implementation returns the
859 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
860 * This is set to false, for example, when the activity is being re-launched
861 * to show a particular preference activity.
862 */
863 public boolean onIsHidingHeaders() {
864 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
865 }
866
867 private void highlightHeader(int id) {
868 if (id != 0) {
869 Integer index = mHeaderIndexMap.get(id);
870 if (index != null && mDrawer != null) {
871 mDrawer.setItemChecked(index, true);
872 if (mDrawer.getVisibility() == View.VISIBLE) {
873 mDrawer.smoothScrollToPosition(index);
874 }
875 }
876 }
877 }
878
879 @Override
880 public Intent getIntent() {
881 Intent superIntent = super.getIntent();
882 String startingFragment = getStartingFragmentClass(superIntent);
883 // This is called from super.onCreate, isMultiPane() is not yet reliable
884 // Do not use onIsHidingHeaders either, which relies itself on this method
885 if (startingFragment != null) {
886 Intent modIntent = new Intent(superIntent);
887 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
888 Bundle args = superIntent.getExtras();
889 if (args != null) {
890 args = new Bundle(args);
891 } else {
892 args = new Bundle();
893 }
894 args.putParcelable("intent", superIntent);
895 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
896 return modIntent;
897 }
898 return superIntent;
899 }
900
901 /**
902 * Checks if the component name in the intent is different from the Settings class and
903 * returns the class name to load as a fragment.
904 */
905 private String getStartingFragmentClass(Intent intent) {
906 if (mFragmentClass != null) return mFragmentClass;
907
908 String intentClass = intent.getComponent().getClassName();
909 if (intentClass.equals(getClass().getName())) return null;
910
911 if ("com.android.settings.ManageApplications".equals(intentClass)
912 || "com.android.settings.RunningServices".equals(intentClass)
913 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
914 // Old names of manage apps.
915 intentClass = com.android.settings.applications.ManageApplications.class.getName();
916 }
917
918 return intentClass;
919 }
920
921 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000922 * Start a new fragment containing a preference panel. If the preferences
923 * are being displayed in multi-pane mode, the given fragment class will
924 * be instantiated and placed in the appropriate pane. If running in
925 * single-pane mode, a new activity will be launched in which to show the
926 * fragment.
927 *
928 * @param fragmentClass Full name of the class implementing the fragment.
929 * @param args Any desired arguments to supply to the fragment.
930 * @param titleRes Optional resource identifier of the title of this
931 * fragment.
932 * @param titleText Optional text of the title of this fragment.
933 * @param resultTo Optional fragment that result data should be sent to.
934 * If non-null, resultTo.onActivityResult() will be called when this
935 * preference panel is done. The launched panel must use
936 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
937 * @param resultRequestCode If resultTo is non-null, this is the caller's
938 * request code to be received with the resut.
939 */
940 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
941 CharSequence titleText, Fragment resultTo,
942 int resultRequestCode) {
943 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
944 }
945
946 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800947 * Called by a preference panel fragment to finish itself.
948 *
949 * @param caller The fragment that is asking to be finished.
950 * @param resultCode Optional result code to send back to the original
951 * launching fragment.
952 * @param resultData Optional result data to send back to the original
953 * launching fragment.
954 */
955 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
956 setResult(resultCode, resultData);
957 }
958
959 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000960 * Start a new fragment.
961 *
962 * @param fragment The fragment to start
963 * @param push If true, the current fragment will be pushed onto the back stack. If false,
964 * the current fragment will be replaced.
965 */
966 public void startPreferenceFragment(Fragment fragment, boolean push) {
967 FragmentTransaction transaction = getFragmentManager().beginTransaction();
968 transaction.replace(R.id.prefs, fragment);
969 if (push) {
970 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
971 transaction.addToBackStack(BACK_STACK_PREFS);
972 } else {
973 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
974 }
975 transaction.commitAllowingStateLoss();
976 }
977
978 /**
979 * Start a new fragment.
980 *
981 * @param fragmentName The name of the fragment to display.
982 * @param args Optional arguments to supply to the fragment.
983 * @param resultTo Option fragment that should receive the result of
984 * the activity launch.
985 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
986 * report the result.
987 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
988 * valid one then it will be used to get the title. Otherwise the titleText
989 * argument will be used as the title.
990 * @param titleText string to display for the title of.
991 */
992 private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
993 int resultRequestCode, int titleRes, CharSequence titleText) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800994 final CharSequence cs;
995 if (titleRes != 0) {
996 cs = getText(titleRes);
997 } else {
998 cs = titleText;
999 }
1000
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001001 Fragment f = Fragment.instantiate(this, fragmentName, args);
1002 if (resultTo != null) {
1003 f.setTargetFragment(resultTo, resultRequestCode);
1004 }
1005 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1006 transaction.replace(R.id.prefs, f);
1007 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1008 transaction.addToBackStack(BACK_STACK_PREFS);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001009 transaction.setBreadCrumbTitle(cs);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001010 transaction.commitAllowingStateLoss();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001011 }
1012
1013 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001014 * Called when the activity needs its list of headers build. By
1015 * implementing this and adding at least one item to the list, you
1016 * will cause the activity to run in its modern fragment mode. Note
1017 * that this function may not always be called; for example, if the
1018 * activity has been asked to display a particular fragment without
1019 * the header list, there is no need to build the headers.
1020 *
1021 * <p>Typical implementations will use {@link #loadHeadersFromResource}
1022 * to fill in the list from a resource.
1023 *
1024 * @param headers The list in which to place the headers.
1025 */
1026 private void onBuildHeaders(List<Header> headers) {
1027 loadHeadersFromResource(R.xml.settings_headers, headers);
1028 updateHeaderList(headers);
1029 }
1030
1031 /**
1032 * Parse the given XML file as a header description, adding each
1033 * parsed Header into the target list.
1034 *
1035 * @param resid The XML resource to load and parse.
1036 * @param target The list in which the parsed headers should be placed.
1037 */
1038 private void loadHeadersFromResource(int resid, List<Header> target) {
1039 XmlResourceParser parser = null;
1040 try {
1041 parser = getResources().getXml(resid);
1042 AttributeSet attrs = Xml.asAttributeSet(parser);
1043
1044 int type;
1045 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1046 && type != XmlPullParser.START_TAG) {
1047 // Parse next until start tag is found
1048 }
1049
1050 String nodeName = parser.getName();
1051 if (!"preference-headers".equals(nodeName)) {
1052 throw new RuntimeException(
1053 "XML document must start with <preference-headers> tag; found"
1054 + nodeName + " at " + parser.getPositionDescription());
1055 }
1056
1057 Bundle curBundle = null;
1058
1059 final int outerDepth = parser.getDepth();
1060 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1061 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1062 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1063 continue;
1064 }
1065
1066 nodeName = parser.getName();
1067 if ("header".equals(nodeName)) {
1068 Header header = new Header();
1069
1070 TypedArray sa = obtainStyledAttributes(
1071 attrs, com.android.internal.R.styleable.PreferenceHeader);
1072 header.id = sa.getResourceId(
1073 com.android.internal.R.styleable.PreferenceHeader_id,
1074 (int)HEADER_ID_UNDEFINED);
1075 TypedValue tv = sa.peekValue(
1076 com.android.internal.R.styleable.PreferenceHeader_title);
1077 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1078 if (tv.resourceId != 0) {
1079 header.titleRes = tv.resourceId;
1080 } else {
1081 header.title = tv.string;
1082 }
1083 }
1084 tv = sa.peekValue(
1085 com.android.internal.R.styleable.PreferenceHeader_summary);
1086 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1087 if (tv.resourceId != 0) {
1088 header.summaryRes = tv.resourceId;
1089 } else {
1090 header.summary = tv.string;
1091 }
1092 }
1093 header.iconRes = sa.getResourceId(
1094 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1095 header.fragment = sa.getString(
1096 com.android.internal.R.styleable.PreferenceHeader_fragment);
1097 sa.recycle();
1098
1099 if (curBundle == null) {
1100 curBundle = new Bundle();
1101 }
1102
1103 final int innerDepth = parser.getDepth();
1104 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1105 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1106 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1107 continue;
1108 }
1109
1110 String innerNodeName = parser.getName();
1111 if (innerNodeName.equals("extra")) {
1112 getResources().parseBundleExtra("extra", attrs, curBundle);
1113 XmlUtils.skipCurrentTag(parser);
1114
1115 } else if (innerNodeName.equals("intent")) {
1116 header.intent = Intent.parseIntent(getResources(), parser, attrs);
1117
1118 } else {
1119 XmlUtils.skipCurrentTag(parser);
1120 }
1121 }
1122
1123 if (curBundle.size() > 0) {
1124 header.fragmentArguments = curBundle;
1125 curBundle = null;
1126 }
1127
1128 target.add(header);
1129 } else {
1130 XmlUtils.skipCurrentTag(parser);
1131 }
1132 }
1133
1134 } catch (XmlPullParserException e) {
1135 throw new RuntimeException("Error parsing headers", e);
1136 } catch (IOException e) {
1137 throw new RuntimeException("Error parsing headers", e);
1138 } finally {
1139 if (parser != null) parser.close();
1140 }
1141 }
1142
1143 private void updateHeaderList(List<Header> target) {
1144 final boolean showDev = mDevelopmentPreferences.getBoolean(
1145 DevelopmentSettings.PREF_SHOW,
1146 android.os.Build.TYPE.equals("eng"));
1147 int i = 0;
1148
1149 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1150 mHeaderIndexMap.clear();
1151 while (i < target.size()) {
1152 Header header = target.get(i);
1153 // Ids are integers, so downcasting
1154 int id = (int) header.id;
1155 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1156 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1157 } else if (id == R.id.wifi_settings) {
1158 // Remove WiFi Settings if WiFi service is not available.
1159 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1160 target.remove(i);
1161 }
1162 } else if (id == R.id.bluetooth_settings) {
1163 // Remove Bluetooth Settings if Bluetooth service is not available.
1164 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1165 target.remove(i);
1166 }
1167 } else if (id == R.id.data_usage_settings) {
1168 // Remove data usage when kernel module not enabled
1169 final INetworkManagementService netManager = INetworkManagementService.Stub
1170 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1171 try {
1172 if (!netManager.isBandwidthControlEnabled()) {
1173 target.remove(i);
1174 }
1175 } catch (RemoteException e) {
1176 // ignored
1177 }
1178 } else if (id == R.id.battery_settings) {
1179 // Remove battery settings when battery is not available. (e.g. TV)
1180
1181 if (!mBatteryPresent) {
1182 target.remove(i);
1183 }
1184 } else if (id == R.id.account_settings) {
1185 int headerIndex = i + 1;
1186 i = insertAccountsHeaders(target, headerIndex);
1187 } else if (id == R.id.home_settings) {
1188 if (!updateHomeSettingHeaders(header)) {
1189 target.remove(i);
1190 }
1191 } else if (id == R.id.user_settings) {
1192 if (!UserHandle.MU_ENABLED
1193 || !UserManager.supportsMultipleUsers()
1194 || Utils.isMonkeyRunning()) {
1195 target.remove(i);
1196 }
1197 } else if (id == R.id.nfc_payment_settings) {
1198 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1199 target.remove(i);
1200 } else {
1201 // Only show if NFC is on and we have the HCE feature
1202 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1203 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1204 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1205 target.remove(i);
1206 }
1207 }
1208 } else if (id == R.id.development_settings) {
1209 if (!showDev) {
1210 target.remove(i);
1211 }
1212 } else if (id == R.id.account_add) {
1213 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1214 target.remove(i);
1215 }
1216 }
1217
1218 if (i < target.size() && target.get(i) == header
1219 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1220 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1221 target.remove(i);
1222 }
1223
1224 // Increment if the current one wasn't removed by the Utils code.
1225 if (i < target.size() && target.get(i) == header) {
1226 // Hold on to the first header, when we need to reset to the top-level
1227 if (mFirstHeader == null &&
1228 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
1229 mFirstHeader = header;
1230 }
1231 mHeaderIndexMap.put(id, i);
1232 i++;
1233 }
1234 }
1235 }
1236
1237 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1238 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1239 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1240 for (String accountType : accountTypes) {
1241 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1242 if (label == null) {
1243 continue;
1244 }
1245
1246 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1247 boolean skipToAccount = accounts.length == 1
1248 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1249 Header accHeader = new Header();
1250 accHeader.title = label;
1251 if (accHeader.extras == null) {
1252 accHeader.extras = new Bundle();
1253 }
1254 if (skipToAccount) {
1255 accHeader.fragment = AccountSyncSettings.class.getName();
1256 accHeader.fragmentArguments = new Bundle();
1257 // Need this for the icon
1258 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1259 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1260 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1261 accounts[0]);
1262 } else {
1263 accHeader.fragment = ManageAccountsSettings.class.getName();
1264 accHeader.fragmentArguments = new Bundle();
1265 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1266 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1267 accountType);
1268 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1269 label.toString());
1270 }
1271 accountHeaders.add(accHeader);
1272 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1273 }
1274
1275 // Sort by label
1276 Collections.sort(accountHeaders, new Comparator<Header>() {
1277 @Override
1278 public int compare(Header h1, Header h2) {
1279 return h1.title.toString().compareTo(h2.title.toString());
1280 }
1281 });
1282
1283 for (Header header : accountHeaders) {
1284 target.add(headerIndex++, header);
1285 }
1286 if (!mListeningToAccountUpdates) {
1287 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1288 mListeningToAccountUpdates = true;
1289 }
1290 return headerIndex;
1291 }
1292
1293 private boolean updateHomeSettingHeaders(Header header) {
1294 // Once we decide to show Home settings, keep showing it forever
1295 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1296 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1297 return true;
1298 }
1299
1300 try {
1301 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1302 getPackageManager().getHomeActivities(homeApps);
1303 if (homeApps.size() < 2) {
1304 // When there's only one available home app, omit this settings
1305 // category entirely at the top level UI. If the user just
1306 // uninstalled the penultimate home app candidiate, we also
1307 // now tell them about why they aren't seeing 'Home' in the list.
1308 if (sShowNoHomeNotice) {
1309 sShowNoHomeNotice = false;
1310 NoHomeDialogFragment.show(this);
1311 }
1312 return false;
1313 } else {
1314 // Okay, we're allowing the Home settings category. Tell it, when
1315 // invoked via this front door, that we'll need to be told about the
1316 // case when the user uninstalls all but one home app.
1317 if (header.fragmentArguments == null) {
1318 header.fragmentArguments = new Bundle();
1319 }
1320 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1321 }
1322 } catch (Exception e) {
1323 // Can't look up the home activity; bail on configuring the icon
1324 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1325 }
1326
1327 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1328 return true;
1329 }
1330
1331 private void getMetaData() {
1332 try {
1333 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1334 PackageManager.GET_META_DATA);
1335 if (ai == null || ai.metaData == null) return;
1336 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
1337 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1338 } catch (NameNotFoundException nnfe) {
1339 // No recovery
1340 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1341 }
1342 }
1343
1344 // give subclasses access to the Next button
1345 public boolean hasNextButton() {
1346 return mNextButton != null;
1347 }
1348
1349 public Button getNextButton() {
1350 return mNextButton;
1351 }
1352
1353 public static class NoHomeDialogFragment extends DialogFragment {
1354 public static void show(Activity parent) {
1355 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
1356 dialog.show(parent.getFragmentManager(), null);
1357 }
1358
1359 @Override
1360 public Dialog onCreateDialog(Bundle savedInstanceState) {
1361 return new AlertDialog.Builder(getActivity())
1362 .setMessage(R.string.only_one_home_message)
1363 .setPositiveButton(android.R.string.ok, null)
1364 .create();
1365 }
1366 }
1367
1368 private static class HeaderAdapter extends ArrayAdapter<Header> {
1369 static final int HEADER_TYPE_CATEGORY = 0;
1370 static final int HEADER_TYPE_NORMAL = 1;
1371 static final int HEADER_TYPE_SWITCH = 2;
1372 static final int HEADER_TYPE_BUTTON = 3;
1373 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
1374
1375 private final WifiEnabler mWifiEnabler;
1376 private final BluetoothEnabler mBluetoothEnabler;
1377 private AuthenticatorHelper mAuthHelper;
1378 private DevicePolicyManager mDevicePolicyManager;
1379
1380 private static class HeaderViewHolder {
1381 ImageView mIcon;
1382 TextView mTitle;
1383 TextView mSummary;
1384 Switch mSwitch;
1385 ImageButton mButton;
1386 View mDivider;
1387 }
1388
1389 private LayoutInflater mInflater;
1390
1391 static int getHeaderType(Header header) {
1392 if (header.fragment == null && header.intent == null) {
1393 return HEADER_TYPE_CATEGORY;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001394 } else if (header.id == R.id.security_settings) {
1395 return HEADER_TYPE_BUTTON;
1396 } else {
1397 return HEADER_TYPE_NORMAL;
1398 }
1399 }
1400
1401 @Override
1402 public int getItemViewType(int position) {
1403 Header header = getItem(position);
1404 return getHeaderType(header);
1405 }
1406
1407 @Override
1408 public boolean areAllItemsEnabled() {
1409 return false; // because of categories
1410 }
1411
1412 @Override
1413 public boolean isEnabled(int position) {
1414 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
1415 }
1416
1417 @Override
1418 public int getViewTypeCount() {
1419 return HEADER_TYPE_COUNT;
1420 }
1421
1422 @Override
1423 public boolean hasStableIds() {
1424 return true;
1425 }
1426
1427 public HeaderAdapter(Context context, List<Header> objects,
1428 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
1429 super(context, 0, objects);
1430
1431 mAuthHelper = authenticatorHelper;
1432 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1433
1434 // Temp Switches provided as placeholder until the adapter replaces these with actual
1435 // Switches inflated from their layouts. Must be done before adapter is set in super
1436 mWifiEnabler = new WifiEnabler(context, new Switch(context));
1437 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
1438 mDevicePolicyManager = dpm;
1439 }
1440
1441 @Override
1442 public View getView(int position, View convertView, ViewGroup parent) {
1443 HeaderViewHolder holder;
1444 Header header = getItem(position);
1445 int headerType = getHeaderType(header);
1446 View view = null;
1447
1448 if (convertView == null) {
1449 holder = new HeaderViewHolder();
1450 switch (headerType) {
1451 case HEADER_TYPE_CATEGORY:
1452 view = new TextView(getContext(), null,
1453 android.R.attr.listSeparatorTextViewStyle);
1454 holder.mTitle = (TextView) view;
1455 break;
1456
1457 case HEADER_TYPE_SWITCH:
1458 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
1459 false);
1460 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1461 holder.mTitle = (TextView)
1462 view.findViewById(com.android.internal.R.id.title);
1463 holder.mSummary = (TextView)
1464 view.findViewById(com.android.internal.R.id.summary);
1465 holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget);
1466 break;
1467
1468 case HEADER_TYPE_BUTTON:
1469 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
1470 false);
1471 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1472 holder.mTitle = (TextView)
1473 view.findViewById(com.android.internal.R.id.title);
1474 holder.mSummary = (TextView)
1475 view.findViewById(com.android.internal.R.id.summary);
1476 holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget);
1477 holder.mDivider = view.findViewById(R.id.divider);
1478 break;
1479
1480 case HEADER_TYPE_NORMAL:
1481 view = mInflater.inflate(
1482 R.layout.preference_header_item, parent,
1483 false);
1484 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1485 holder.mTitle = (TextView)
1486 view.findViewById(com.android.internal.R.id.title);
1487 holder.mSummary = (TextView)
1488 view.findViewById(com.android.internal.R.id.summary);
1489 break;
1490 }
Fabrice Di Meglio0e2f9492014-02-25 14:26:27 -08001491 if (holder.mIcon != null) {
1492 holder.mIcon.setBackgroundResource(R.color.background_drawer_icon);
1493 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001494 view.setTag(holder);
1495 } else {
1496 view = convertView;
1497 holder = (HeaderViewHolder) view.getTag();
1498 }
1499
1500 // All view fields must be updated every time, because the view may be recycled
1501 switch (headerType) {
1502 case HEADER_TYPE_CATEGORY:
1503 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1504 break;
1505
1506 case HEADER_TYPE_SWITCH:
1507 // Would need a different treatment if the main menu had more switches
1508 if (header.id == R.id.wifi_settings) {
1509 mWifiEnabler.setSwitch(holder.mSwitch);
1510 } else {
1511 mBluetoothEnabler.setSwitch(holder.mSwitch);
1512 }
1513 updateCommonHeaderView(header, holder);
1514 break;
1515
1516 case HEADER_TYPE_BUTTON:
1517 if (header.id == R.id.security_settings) {
1518 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
1519 if (hasCert) {
1520 holder.mButton.setVisibility(View.VISIBLE);
1521 holder.mDivider.setVisibility(View.VISIBLE);
1522 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
1523 if (isManaged) {
1524 holder.mButton.setImageResource(R.drawable.ic_settings_about);
1525 } else {
1526 holder.mButton.setImageResource(
1527 android.R.drawable.stat_notify_error);
1528 }
1529 holder.mButton.setOnClickListener(new OnClickListener() {
1530 @Override
1531 public void onClick(View v) {
1532 Intent intent = new Intent(
1533 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
1534 getContext().startActivity(intent);
1535 }
1536 });
1537 } else {
1538 holder.mButton.setVisibility(View.GONE);
1539 holder.mDivider.setVisibility(View.GONE);
1540 }
1541 }
1542 updateCommonHeaderView(header, holder);
1543 break;
1544
1545 case HEADER_TYPE_NORMAL:
1546 updateCommonHeaderView(header, holder);
1547 break;
1548 }
1549
1550 return view;
1551 }
1552
1553 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
1554 if (header.extras != null
1555 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
1556 String accType = header.extras.getString(
1557 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
1558 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
1559 setHeaderIcon(holder, icon);
1560 } else {
1561 holder.mIcon.setImageResource(header.iconRes);
1562 }
1563 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1564 CharSequence summary = header.getSummary(getContext().getResources());
1565 if (!TextUtils.isEmpty(summary)) {
1566 holder.mSummary.setVisibility(View.VISIBLE);
1567 holder.mSummary.setText(summary);
1568 } else {
1569 holder.mSummary.setVisibility(View.GONE);
1570 }
1571 }
1572
1573 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
1574 ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams();
1575 lp.width = getContext().getResources().getDimensionPixelSize(
1576 R.dimen.header_icon_width);
1577 lp.height = lp.width;
1578 holder.mIcon.setLayoutParams(lp);
1579 holder.mIcon.setImageDrawable(icon);
1580 }
1581
Matthew Xiea504c4d2014-02-14 16:32:32 -08001582 public void resume(Context context) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001583 mWifiEnabler.resume();
Matthew Xiea504c4d2014-02-14 16:32:32 -08001584 mBluetoothEnabler.resume(context);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001585 }
1586
1587 public void pause() {
1588 mWifiEnabler.pause();
1589 mBluetoothEnabler.pause();
1590 }
1591 }
1592
1593 private void onListItemClick(ListView l, View v, int position, long id) {
1594 if (!isResumed()) {
1595 return;
1596 }
1597 Object item = mHeaderAdapter.getItem(position);
1598 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001599 mSelectedHeader = (Header) item;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001600 }
1601 }
1602
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001603 @Override
1604 public boolean shouldUpRecreateTask(Intent targetIntent) {
1605 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1606 }
1607
1608 @Override
1609 public void onAccountsUpdated(Account[] accounts) {
1610 // TODO: watch for package upgrades to invalidate cache; see 7206643
1611 mAuthenticatorHelper.updateAuthDescriptions(this);
1612 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1613 invalidateHeaders();
1614 }
1615
1616 public static void requestHomeNotice() {
1617 sShowNoHomeNotice = true;
1618 }
1619
1620 /**
1621 * Default value for {@link Header#id Header.id} indicating that no
1622 * identifier value is set. All other values (including those below -1)
1623 * are valid.
1624 */
1625 private static final long HEADER_ID_UNDEFINED = -1;
1626
1627 /**
1628 * Description of a single Header item that the user can select.
1629 */
1630 static final class Header implements Parcelable {
1631 /**
1632 * Identifier for this header, to correlate with a new list when
1633 * it is updated. The default value is
1634 * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id.
1635 * @attr ref android.R.styleable#PreferenceHeader_id
1636 */
1637 public long id = HEADER_ID_UNDEFINED;
1638
1639 /**
1640 * Resource ID of title of the header that is shown to the user.
1641 * @attr ref android.R.styleable#PreferenceHeader_title
1642 */
1643 public int titleRes;
1644
1645 /**
1646 * Title of the header that is shown to the user.
1647 * @attr ref android.R.styleable#PreferenceHeader_title
1648 */
1649 public CharSequence title;
1650
1651 /**
1652 * Resource ID of optional summary describing what this header controls.
1653 * @attr ref android.R.styleable#PreferenceHeader_summary
1654 */
1655 public int summaryRes;
1656
1657 /**
1658 * Optional summary describing what this header controls.
1659 * @attr ref android.R.styleable#PreferenceHeader_summary
1660 */
1661 public CharSequence summary;
1662
1663 /**
1664 * Optional icon resource to show for this header.
1665 * @attr ref android.R.styleable#PreferenceHeader_icon
1666 */
1667 public int iconRes;
1668
1669 /**
1670 * Full class name of the fragment to display when this header is
1671 * selected.
1672 * @attr ref android.R.styleable#PreferenceHeader_fragment
1673 */
1674 public String fragment;
1675
1676 /**
1677 * Optional arguments to supply to the fragment when it is
1678 * instantiated.
1679 */
1680 public Bundle fragmentArguments;
1681
1682 /**
1683 * Intent to launch when the preference is selected.
1684 */
1685 public Intent intent;
1686
1687 /**
1688 * Optional additional data for use by subclasses of the activity
1689 */
1690 public Bundle extras;
1691
1692 public Header() {
1693 // Empty
1694 }
1695
1696 /**
1697 * Return the currently set title. If {@link #titleRes} is set,
1698 * this resource is loaded from <var>res</var> and returned. Otherwise
1699 * {@link #title} is returned.
1700 */
1701 public CharSequence getTitle(Resources res) {
1702 if (titleRes != 0) {
1703 return res.getText(titleRes);
1704 }
1705 return title;
1706 }
1707
1708 /**
1709 * Return the currently set summary. If {@link #summaryRes} is set,
1710 * this resource is loaded from <var>res</var> and returned. Otherwise
1711 * {@link #summary} is returned.
1712 */
1713 public CharSequence getSummary(Resources res) {
1714 if (summaryRes != 0) {
1715 return res.getText(summaryRes);
1716 }
1717 return summary;
1718 }
1719
1720 @Override
1721 public int describeContents() {
1722 return 0;
1723 }
1724
1725 @Override
1726 public void writeToParcel(Parcel dest, int flags) {
1727 dest.writeLong(id);
1728 dest.writeInt(titleRes);
1729 TextUtils.writeToParcel(title, dest, flags);
1730 dest.writeInt(summaryRes);
1731 TextUtils.writeToParcel(summary, dest, flags);
1732 dest.writeInt(iconRes);
1733 dest.writeString(fragment);
1734 dest.writeBundle(fragmentArguments);
1735 if (intent != null) {
1736 dest.writeInt(1);
1737 intent.writeToParcel(dest, flags);
1738 } else {
1739 dest.writeInt(0);
1740 }
1741 dest.writeBundle(extras);
1742 }
1743
1744 public void readFromParcel(Parcel in) {
1745 id = in.readLong();
1746 titleRes = in.readInt();
1747 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1748 summaryRes = in.readInt();
1749 summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1750 iconRes = in.readInt();
1751 fragment = in.readString();
1752 fragmentArguments = in.readBundle();
1753 if (in.readInt() != 0) {
1754 intent = Intent.CREATOR.createFromParcel(in);
1755 }
1756 extras = in.readBundle();
1757 }
1758
1759 Header(Parcel in) {
1760 readFromParcel(in);
1761 }
1762
1763 public static final Creator<Header> CREATOR = new Creator<Header>() {
1764 public Header createFromParcel(Parcel source) {
1765 return new Header(source);
1766 }
1767 public Header[] newArray(int size) {
1768 return new Header[size];
1769 }
1770 };
1771 }
1772}