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