OSDN Git Service

Merge "Adapt to renaming of SettingInjectorService API constants" into klp-dev
[android-x86/packages-apps-Settings.git] / src / com / android / settings / Settings.java
1 /*
2  * Copyright (C) 2008 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
17 package com.android.settings;
18
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.accounts.OnAccountsUpdateListener;
22 import android.app.Fragment;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.RestrictionEntry;
29 import android.content.SharedPreferences;
30 import android.content.pm.ActivityInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.PackageManager.NameNotFoundException;
33 import android.graphics.drawable.Drawable;
34 import android.os.Bundle;
35 import android.os.INetworkManagementService;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.preference.Preference;
41 import android.preference.PreferenceActivity;
42 import android.preference.PreferenceFragment;
43 import android.text.TextUtils;
44 import android.util.Log;
45 import android.view.LayoutInflater;
46 import android.view.View;
47 import android.view.View.OnClickListener;
48 import android.view.ViewGroup;
49 import android.widget.ArrayAdapter;
50 import android.widget.Button;
51 import android.widget.ImageView;
52 import android.widget.ListAdapter;
53 import android.widget.Switch;
54 import android.widget.TextView;
55
56 import com.android.internal.util.ArrayUtils;
57 import com.android.settings.accessibility.AccessibilitySettings;
58 import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
59 import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment;
60 import com.android.settings.ChooseLockPassword.ChooseLockPasswordFragment;
61 import com.android.settings.ChooseLockPattern.ChooseLockPatternFragment;
62 import com.android.settings.accounts.AccountSyncSettings;
63 import com.android.settings.accounts.AuthenticatorHelper;
64 import com.android.settings.accounts.ManageAccountsSettings;
65 import com.android.settings.applications.AppOpsSummary;
66 import com.android.settings.applications.ManageApplications;
67 import com.android.settings.applications.ProcessStatsUi;
68 import com.android.settings.bluetooth.BluetoothEnabler;
69 import com.android.settings.bluetooth.BluetoothSettings;
70 import com.android.settings.deviceinfo.Memory;
71 import com.android.settings.deviceinfo.UsbSettings;
72 import com.android.settings.fuelgauge.PowerUsageSummary;
73 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
74 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
75 import com.android.settings.inputmethod.SpellCheckersSettings;
76 import com.android.settings.inputmethod.UserDictionaryList;
77 import com.android.settings.location.LocationSettings;
78 import com.android.settings.nfc.AndroidBeam;
79 import com.android.settings.nfc.PaymentSettings;
80 import com.android.settings.tts.TextToSpeechSettings;
81 import com.android.settings.users.AppRestrictionsFragment;
82 import com.android.settings.users.UserSettings;
83 import com.android.settings.vpn2.VpnSettings;
84 import com.android.settings.wfd.WifiDisplaySettings;
85 import com.android.settings.wifi.AdvancedWifiSettings;
86 import com.android.settings.wifi.WifiEnabler;
87 import com.android.settings.wifi.WifiSettings;
88 import com.android.settings.wifi.p2p.WifiP2pSettings;
89
90 import java.util.ArrayList;
91 import java.util.Collections;
92 import java.util.Comparator;
93 import java.util.HashMap;
94 import java.util.List;
95
96 /**
97  * Top-level settings activity to handle single pane and double pane UI layout.
98  */
99 public class Settings extends PreferenceActivity
100         implements ButtonBarHandler, OnAccountsUpdateListener {
101
102     private static final String LOG_TAG = "Settings";
103
104     private static final String META_DATA_KEY_HEADER_ID =
105         "com.android.settings.TOP_LEVEL_HEADER_ID";
106     private static final String META_DATA_KEY_FRAGMENT_CLASS =
107         "com.android.settings.FRAGMENT_CLASS";
108     private static final String META_DATA_KEY_PARENT_TITLE =
109         "com.android.settings.PARENT_FRAGMENT_TITLE";
110     private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
111         "com.android.settings.PARENT_FRAGMENT_CLASS";
112
113     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
114
115     private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
116     private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
117
118     private String mFragmentClass;
119     private int mTopLevelHeaderId;
120     private Header mFirstHeader;
121     private Header mCurrentHeader;
122     private Header mParentHeader;
123     private boolean mInLocalHeaderSwitch;
124
125     // Show only these settings for restricted users
126     private int[] SETTINGS_FOR_RESTRICTED = {
127             R.id.wireless_section,
128             R.id.wifi_settings,
129             R.id.bluetooth_settings,
130             R.id.data_usage_settings,
131             R.id.wireless_settings,
132             R.id.device_section,
133             R.id.sound_settings,
134             R.id.display_settings,
135             R.id.storage_settings,
136             R.id.application_settings,
137             R.id.battery_settings,
138             R.id.personal_section,
139             R.id.location_settings,
140             R.id.security_settings,
141             R.id.language_settings,
142             R.id.user_settings,
143             R.id.account_settings,
144             R.id.account_add,
145             R.id.system_section,
146             R.id.date_time_settings,
147             R.id.about_settings,
148             R.id.accessibility_settings,
149             R.id.print_settings,
150             R.id.nfc_payment_settings
151     };
152
153     private SharedPreferences mDevelopmentPreferences;
154     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
155
156     // TODO: Update Call Settings based on airplane mode state.
157
158     protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
159
160     private AuthenticatorHelper mAuthenticatorHelper;
161     private Header mLastHeader;
162     private boolean mListeningToAccountUpdates;
163
164     private boolean mBatteryPresent = true;
165     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
166
167         @Override
168         public void onReceive(Context context, Intent intent) {
169             String action = intent.getAction();
170             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
171                 boolean batteryPresent = Utils.isBatteryPresent(intent);
172
173                 if (mBatteryPresent != batteryPresent) {
174                     mBatteryPresent = batteryPresent;
175                     invalidateHeaders();
176                 }
177             }
178         }
179     };
180
181     @Override
182     protected void onCreate(Bundle savedInstanceState) {
183         if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
184             getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
185         }
186
187         mAuthenticatorHelper = new AuthenticatorHelper();
188         mAuthenticatorHelper.updateAuthDescriptions(this);
189         mAuthenticatorHelper.onAccountsUpdated(this, null);
190
191         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
192                 Context.MODE_PRIVATE);
193
194         getMetaData();
195         mInLocalHeaderSwitch = true;
196         super.onCreate(savedInstanceState);
197         mInLocalHeaderSwitch = false;
198
199         if (!onIsHidingHeaders() && onIsMultiPane()) {
200             highlightHeader(mTopLevelHeaderId);
201             // Force the title so that it doesn't get overridden by a direct launch of
202             // a specific settings screen.
203             setTitle(R.string.settings_label);
204         }
205
206         // Retrieve any saved state
207         if (savedInstanceState != null) {
208             mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
209             mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
210         }
211
212         // If the current header was saved, switch to it
213         if (savedInstanceState != null && mCurrentHeader != null) {
214             //switchToHeaderLocal(mCurrentHeader);
215             showBreadCrumbs(mCurrentHeader.title, null);
216         }
217
218         if (mParentHeader != null) {
219             setParentTitle(mParentHeader.title, null, new OnClickListener() {
220                 @Override
221                 public void onClick(View v) {
222                     switchToParent(mParentHeader.fragment);
223                 }
224             });
225         }
226
227         // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
228         if (onIsMultiPane()) {
229             getActionBar().setDisplayHomeAsUpEnabled(false);
230             getActionBar().setHomeButtonEnabled(false);
231         }
232     }
233
234     @Override
235     protected void onSaveInstanceState(Bundle outState) {
236         super.onSaveInstanceState(outState);
237
238         // Save the current fragment, if it is the same as originally launched
239         if (mCurrentHeader != null) {
240             outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
241         }
242         if (mParentHeader != null) {
243             outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
244         }
245     }
246
247     @Override
248     public void onResume() {
249         super.onResume();
250
251         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
252             @Override
253             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
254                 invalidateHeaders();
255             }
256         };
257         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
258                 mDevelopmentPreferencesListener);
259
260         ListAdapter listAdapter = getListAdapter();
261         if (listAdapter instanceof HeaderAdapter) {
262             ((HeaderAdapter) listAdapter).resume();
263         }
264         invalidateHeaders();
265
266         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
267     }
268
269     @Override
270     public void onPause() {
271         super.onPause();
272
273         unregisterReceiver(mBatteryInfoReceiver);
274
275         ListAdapter listAdapter = getListAdapter();
276         if (listAdapter instanceof HeaderAdapter) {
277             ((HeaderAdapter) listAdapter).pause();
278         }
279
280         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
281                 mDevelopmentPreferencesListener);
282         mDevelopmentPreferencesListener = null;
283     }
284
285     @Override
286     public void onDestroy() {
287         super.onDestroy();
288         if (mListeningToAccountUpdates) {
289             AccountManager.get(this).removeOnAccountsUpdatedListener(this);
290         }
291     }
292
293     @Override
294     public boolean onIsMultiPane() {
295         return false;
296     }
297
298     private static final String[] ENTRY_FRAGMENTS = {
299         WirelessSettings.class.getName(),
300         WifiSettings.class.getName(),
301         AdvancedWifiSettings.class.getName(),
302         BluetoothSettings.class.getName(),
303         TetherSettings.class.getName(),
304         WifiP2pSettings.class.getName(),
305         VpnSettings.class.getName(),
306         DateTimeSettings.class.getName(),
307         LocalePicker.class.getName(),
308         InputMethodAndLanguageSettings.class.getName(),
309         SpellCheckersSettings.class.getName(),
310         UserDictionaryList.class.getName(),
311         UserDictionarySettings.class.getName(),
312         SoundSettings.class.getName(),
313         DisplaySettings.class.getName(),
314         DeviceInfoSettings.class.getName(),
315         ManageApplications.class.getName(),
316         ProcessStatsUi.class.getName(),
317         NotificationStation.class.getName(),
318         AppOpsSummary.class.getName(),
319         LocationSettings.class.getName(),
320         SecuritySettings.class.getName(),
321         PrivacySettings.class.getName(),
322         DeviceAdminSettings.class.getName(),
323         AccessibilitySettings.class.getName(),
324         TextToSpeechSettings.class.getName(),
325         Memory.class.getName(),
326         DevelopmentSettings.class.getName(),
327         UsbSettings.class.getName(),
328         AndroidBeam.class.getName(),
329         WifiDisplaySettings.class.getName(),
330         PowerUsageSummary.class.getName(),
331         AccountSyncSettings.class.getName(),
332         CryptKeeperSettings.class.getName(),
333         DataUsageSummary.class.getName(),
334         DreamSettings.class.getName(),
335         UserSettings.class.getName(),
336         NotificationAccessSettings.class.getName(),
337         ManageAccountsSettings.class.getName(),
338         PrintingSettings.class.getName(),
339         TrustedCredentialsSettings.class.getName(),
340         PaymentSettings.class.getName(),
341         KeyboardLayoutPickerFragment.class.getName()
342     };
343
344     @Override
345     protected boolean isValidFragment(String fragmentName) {
346         // Almost all fragments are wrapped in this,
347         // except for a few that have their own activities.
348         for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
349             if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
350         }
351         return false;
352     }
353
354     private void switchToHeaderLocal(Header header) {
355         mInLocalHeaderSwitch = true;
356         switchToHeader(header);
357         mInLocalHeaderSwitch = false;
358     }
359
360     @Override
361     public void switchToHeader(Header header) {
362         if (!mInLocalHeaderSwitch) {
363             mCurrentHeader = null;
364             mParentHeader = null;
365         }
366         super.switchToHeader(header);
367     }
368
369     /**
370      * Switch to parent fragment and store the grand parent's info
371      * @param className name of the activity wrapper for the parent fragment.
372      */
373     private void switchToParent(String className) {
374         final ComponentName cn = new ComponentName(this, className);
375         try {
376             final PackageManager pm = getPackageManager();
377             final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
378
379             if (parentInfo != null && parentInfo.metaData != null) {
380                 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
381                 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
382                 Header parentHeader = new Header();
383                 parentHeader.fragment = fragmentClass;
384                 parentHeader.title = fragmentTitle;
385                 mCurrentHeader = parentHeader;
386
387                 switchToHeaderLocal(parentHeader);
388                 highlightHeader(mTopLevelHeaderId);
389
390                 mParentHeader = new Header();
391                 mParentHeader.fragment
392                         = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
393                 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
394             }
395         } catch (NameNotFoundException nnfe) {
396             Log.w(LOG_TAG, "Could not find parent activity : " + className);
397         }
398     }
399
400     @Override
401     public void onNewIntent(Intent intent) {
402         super.onNewIntent(intent);
403
404         // If it is not launched from history, then reset to top-level
405         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
406             if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
407                 switchToHeaderLocal(mFirstHeader);
408             }
409             getListView().setSelectionFromTop(0, 0);
410         }
411     }
412
413     private void highlightHeader(int id) {
414         if (id != 0) {
415             Integer index = mHeaderIndexMap.get(id);
416             if (index != null) {
417                 getListView().setItemChecked(index, true);
418                 if (isMultiPane()) {
419                     getListView().smoothScrollToPosition(index);
420                 }
421             }
422         }
423     }
424
425     @Override
426     public Intent getIntent() {
427         Intent superIntent = super.getIntent();
428         String startingFragment = getStartingFragmentClass(superIntent);
429         // This is called from super.onCreate, isMultiPane() is not yet reliable
430         // Do not use onIsHidingHeaders either, which relies itself on this method
431         if (startingFragment != null && !onIsMultiPane()) {
432             Intent modIntent = new Intent(superIntent);
433             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
434             Bundle args = superIntent.getExtras();
435             if (args != null) {
436                 args = new Bundle(args);
437             } else {
438                 args = new Bundle();
439             }
440             args.putParcelable("intent", superIntent);
441             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
442             return modIntent;
443         }
444         return superIntent;
445     }
446
447     /**
448      * Checks if the component name in the intent is different from the Settings class and
449      * returns the class name to load as a fragment.
450      */
451     protected String getStartingFragmentClass(Intent intent) {
452         if (mFragmentClass != null) return mFragmentClass;
453
454         String intentClass = intent.getComponent().getClassName();
455         if (intentClass.equals(getClass().getName())) return null;
456
457         if ("com.android.settings.ManageApplications".equals(intentClass)
458                 || "com.android.settings.RunningServices".equals(intentClass)
459                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
460             // Old names of manage apps.
461             intentClass = com.android.settings.applications.ManageApplications.class.getName();
462         }
463
464         return intentClass;
465     }
466
467     /**
468      * Override initial header when an activity-alias is causing Settings to be launched
469      * for a specific fragment encoded in the android:name parameter.
470      */
471     @Override
472     public Header onGetInitialHeader() {
473         String fragmentClass = getStartingFragmentClass(super.getIntent());
474         if (fragmentClass != null) {
475             Header header = new Header();
476             header.fragment = fragmentClass;
477             header.title = getTitle();
478             header.fragmentArguments = getIntent().getExtras();
479             mCurrentHeader = header;
480             return header;
481         }
482
483         return mFirstHeader;
484     }
485
486     @Override
487     public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
488             int titleRes, int shortTitleRes) {
489         Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
490                 titleRes, shortTitleRes);
491
492         // Some fragments want split ActionBar; these should stay in sync with
493         // uiOptions for fragments also defined as activities in manifest.
494         if (WifiSettings.class.getName().equals(fragmentName) ||
495                 WifiP2pSettings.class.getName().equals(fragmentName) ||
496                 WifiDisplaySettings.class.getName().equals(fragmentName) ||
497                 BluetoothSettings.class.getName().equals(fragmentName) ||
498                 DreamSettings.class.getName().equals(fragmentName) ||
499                 LocationSettings.class.getName().equals(fragmentName) ||
500                 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName)) {
501             intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
502         }
503
504         intent.setClass(this, SubSettings.class);
505         return intent;
506     }
507
508     /**
509      * Populate the activity with the top-level headers.
510      */
511     @Override
512     public void onBuildHeaders(List<Header> headers) {
513         loadHeadersFromResource(R.xml.settings_headers, headers);
514         updateHeaderList(headers);
515     }
516
517     private void updateHeaderList(List<Header> target) {
518         final boolean showDev = mDevelopmentPreferences.getBoolean(
519                 DevelopmentSettings.PREF_SHOW,
520                 android.os.Build.TYPE.equals("eng"));
521         int i = 0;
522
523         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
524         mHeaderIndexMap.clear();
525         while (i < target.size()) {
526             Header header = target.get(i);
527             // Ids are integers, so downcasting
528             int id = (int) header.id;
529             if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
530                 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
531             } else if (id == R.id.wifi_settings) {
532                 // Remove WiFi Settings if WiFi service is not available.
533                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
534                     target.remove(i);
535                 }
536             } else if (id == R.id.bluetooth_settings) {
537                 // Remove Bluetooth Settings if Bluetooth service is not available.
538                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
539                     target.remove(i);
540                 }
541             } else if (id == R.id.data_usage_settings) {
542                 // Remove data usage when kernel module not enabled
543                 final INetworkManagementService netManager = INetworkManagementService.Stub
544                         .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
545                 try {
546                     if (!netManager.isBandwidthControlEnabled()) {
547                         target.remove(i);
548                     }
549                 } catch (RemoteException e) {
550                     // ignored
551                 }
552             } else if (id == R.id.battery_settings) {
553                 // Remove battery settings when battery is not available. (e.g. TV)
554
555                 if (!mBatteryPresent) {
556                     target.remove(i);
557                 }
558             } else if (id == R.id.account_settings) {
559                 int headerIndex = i + 1;
560                 i = insertAccountsHeaders(target, headerIndex);
561             } else if (id == R.id.user_settings) {
562                 if (!UserHandle.MU_ENABLED
563                         || !UserManager.supportsMultipleUsers()
564                         || Utils.isMonkeyRunning()) {
565                     target.remove(i);
566                 }
567             } else if (id == R.id.nfc_payment_settings) {
568                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
569                     target.remove(i);
570                 }
571             } else if (id == R.id.development_settings) {
572                 if (!showDev) {
573                     target.remove(i);
574                 }
575             } else if (id == R.id.account_add) {
576                 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
577                     target.remove(i);
578                 }
579             }
580
581             if (i < target.size() && target.get(i) == header
582                     && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
583                     && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
584                 target.remove(i);
585             }
586
587             // Increment if the current one wasn't removed by the Utils code.
588             if (i < target.size() && target.get(i) == header) {
589                 // Hold on to the first header, when we need to reset to the top-level
590                 if (mFirstHeader == null &&
591                         HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
592                     mFirstHeader = header;
593                 }
594                 mHeaderIndexMap.put(id, i);
595                 i++;
596             }
597         }
598     }
599
600     private int insertAccountsHeaders(List<Header> target, int headerIndex) {
601         String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
602         List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
603         for (String accountType : accountTypes) {
604             CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
605             if (label == null) {
606                 continue;
607             }
608
609             Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
610             boolean skipToAccount = accounts.length == 1
611                     && !mAuthenticatorHelper.hasAccountPreferences(accountType);
612             Header accHeader = new Header();
613             accHeader.title = label;
614             if (accHeader.extras == null) {
615                 accHeader.extras = new Bundle();
616             }
617             if (skipToAccount) {
618                 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
619                 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
620                 accHeader.fragment = AccountSyncSettings.class.getName();
621                 accHeader.fragmentArguments = new Bundle();
622                 // Need this for the icon
623                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
624                 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
625                 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
626                         accounts[0]);
627             } else {
628                 accHeader.breadCrumbTitle = label;
629                 accHeader.breadCrumbShortTitle = label;
630                 accHeader.fragment = ManageAccountsSettings.class.getName();
631                 accHeader.fragmentArguments = new Bundle();
632                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
633                 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
634                         accountType);
635                 if (!isMultiPane()) {
636                     accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
637                             label.toString());
638                 }
639             }
640             accountHeaders.add(accHeader);
641         }
642
643         // Sort by label
644         Collections.sort(accountHeaders, new Comparator<Header>() {
645             @Override
646             public int compare(Header h1, Header h2) {
647                 return h1.title.toString().compareTo(h2.title.toString());
648             }
649         });
650
651         for (Header header : accountHeaders) {
652             target.add(headerIndex++, header);
653         }
654         if (!mListeningToAccountUpdates) {
655             AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
656             mListeningToAccountUpdates = true;
657         }
658         return headerIndex;
659     }
660
661     private void getMetaData() {
662         try {
663             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
664                     PackageManager.GET_META_DATA);
665             if (ai == null || ai.metaData == null) return;
666             mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
667             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
668
669             // Check if it has a parent specified and create a Header object
670             final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
671             String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
672             if (parentFragmentClass != null) {
673                 mParentHeader = new Header();
674                 mParentHeader.fragment = parentFragmentClass;
675                 if (parentHeaderTitleRes != 0) {
676                     mParentHeader.title = getResources().getString(parentHeaderTitleRes);
677                 }
678             }
679         } catch (NameNotFoundException nnfe) {
680             // No recovery
681         }
682     }
683
684     @Override
685     public boolean hasNextButton() {
686         return super.hasNextButton();
687     }
688
689     @Override
690     public Button getNextButton() {
691         return super.getNextButton();
692     }
693
694     private static class HeaderAdapter extends ArrayAdapter<Header> {
695         static final int HEADER_TYPE_CATEGORY = 0;
696         static final int HEADER_TYPE_NORMAL = 1;
697         static final int HEADER_TYPE_SWITCH = 2;
698         private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1;
699
700         private final WifiEnabler mWifiEnabler;
701         private final BluetoothEnabler mBluetoothEnabler;
702         private AuthenticatorHelper mAuthHelper;
703
704         private static class HeaderViewHolder {
705             ImageView icon;
706             TextView title;
707             TextView summary;
708             Switch switch_;
709         }
710
711         private LayoutInflater mInflater;
712
713         static int getHeaderType(Header header) {
714             if (header.fragment == null && header.intent == null) {
715                 return HEADER_TYPE_CATEGORY;
716             } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
717                 return HEADER_TYPE_SWITCH;
718             } else {
719                 return HEADER_TYPE_NORMAL;
720             }
721         }
722
723         @Override
724         public int getItemViewType(int position) {
725             Header header = getItem(position);
726             return getHeaderType(header);
727         }
728
729         @Override
730         public boolean areAllItemsEnabled() {
731             return false; // because of categories
732         }
733
734         @Override
735         public boolean isEnabled(int position) {
736             return getItemViewType(position) != HEADER_TYPE_CATEGORY;
737         }
738
739         @Override
740         public int getViewTypeCount() {
741             return HEADER_TYPE_COUNT;
742         }
743
744         @Override
745         public boolean hasStableIds() {
746             return true;
747         }
748
749         public HeaderAdapter(Context context, List<Header> objects,
750                 AuthenticatorHelper authenticatorHelper) {
751             super(context, 0, objects);
752
753             mAuthHelper = authenticatorHelper;
754             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
755
756             // Temp Switches provided as placeholder until the adapter replaces these with actual
757             // Switches inflated from their layouts. Must be done before adapter is set in super
758             mWifiEnabler = new WifiEnabler(context, new Switch(context));
759             mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
760         }
761
762         @Override
763         public View getView(int position, View convertView, ViewGroup parent) {
764             HeaderViewHolder holder;
765             Header header = getItem(position);
766             int headerType = getHeaderType(header);
767             View view = null;
768
769             if (convertView == null) {
770                 holder = new HeaderViewHolder();
771                 switch (headerType) {
772                     case HEADER_TYPE_CATEGORY:
773                         view = new TextView(getContext(), null,
774                                 android.R.attr.listSeparatorTextViewStyle);
775                         holder.title = (TextView) view;
776                         break;
777
778                     case HEADER_TYPE_SWITCH:
779                         view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
780                                 false);
781                         holder.icon = (ImageView) view.findViewById(R.id.icon);
782                         holder.title = (TextView)
783                                 view.findViewById(com.android.internal.R.id.title);
784                         holder.summary = (TextView)
785                                 view.findViewById(com.android.internal.R.id.summary);
786                         holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
787                         break;
788
789                     case HEADER_TYPE_NORMAL:
790                         view = mInflater.inflate(
791                                 R.layout.preference_header_item, parent,
792                                 false);
793                         holder.icon = (ImageView) view.findViewById(R.id.icon);
794                         holder.title = (TextView)
795                                 view.findViewById(com.android.internal.R.id.title);
796                         holder.summary = (TextView)
797                                 view.findViewById(com.android.internal.R.id.summary);
798                         break;
799                 }
800                 view.setTag(holder);
801             } else {
802                 view = convertView;
803                 holder = (HeaderViewHolder) view.getTag();
804             }
805
806             // All view fields must be updated every time, because the view may be recycled
807             switch (headerType) {
808                 case HEADER_TYPE_CATEGORY:
809                     holder.title.setText(header.getTitle(getContext().getResources()));
810                     break;
811
812                 case HEADER_TYPE_SWITCH:
813                     // Would need a different treatment if the main menu had more switches
814                     if (header.id == R.id.wifi_settings) {
815                         mWifiEnabler.setSwitch(holder.switch_);
816                     } else {
817                         mBluetoothEnabler.setSwitch(holder.switch_);
818                     }
819                     // No break, fall through on purpose to update common fields
820
821                     //$FALL-THROUGH$
822                 case HEADER_TYPE_NORMAL:
823                     if (header.extras != null
824                             && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
825                         String accType = header.extras.getString(
826                                 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
827                         ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
828                         lp.width = getContext().getResources().getDimensionPixelSize(
829                                 R.dimen.header_icon_width);
830                         lp.height = lp.width;
831                         holder.icon.setLayoutParams(lp);
832                         Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
833                         holder.icon.setImageDrawable(icon);
834                     } else {
835                         holder.icon.setImageResource(header.iconRes);
836                     }
837                     holder.title.setText(header.getTitle(getContext().getResources()));
838                     CharSequence summary = header.getSummary(getContext().getResources());
839                     if (!TextUtils.isEmpty(summary)) {
840                         holder.summary.setVisibility(View.VISIBLE);
841                         holder.summary.setText(summary);
842                     } else {
843                         holder.summary.setVisibility(View.GONE);
844                     }
845                     break;
846             }
847
848             return view;
849         }
850
851         public void resume() {
852             mWifiEnabler.resume();
853             mBluetoothEnabler.resume();
854         }
855
856         public void pause() {
857             mWifiEnabler.pause();
858             mBluetoothEnabler.pause();
859         }
860     }
861
862     @Override
863     public void onHeaderClick(Header header, int position) {
864         boolean revert = false;
865         if (header.id == R.id.account_add) {
866             revert = true;
867         }
868
869         super.onHeaderClick(header, position);
870
871         if (revert && mLastHeader != null) {
872             highlightHeader((int) mLastHeader.id);
873         } else {
874             mLastHeader = header;
875         }
876     }
877
878     @Override
879     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
880         // Override the fragment title for Wallpaper settings
881         int titleRes = pref.getTitleRes();
882         if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
883             titleRes = R.string.wallpaper_settings_fragment_title;
884         } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
885                 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
886             if (UserManager.get(this).isLinkedUser()) {
887                 titleRes = R.string.profile_info_settings_title;
888             } else {
889                 titleRes = R.string.user_info_settings_title;
890             }
891         }
892         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
893                 null, 0);
894         return true;
895     }
896
897     @Override
898     public boolean shouldUpRecreateTask(Intent targetIntent) {
899         return super.shouldUpRecreateTask(new Intent(this, Settings.class));
900     }
901
902     @Override
903     public void setListAdapter(ListAdapter adapter) {
904         if (adapter == null) {
905             super.setListAdapter(null);
906         } else {
907             super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper));
908         }
909     }
910
911     @Override
912     public void onAccountsUpdated(Account[] accounts) {
913         // TODO: watch for package upgrades to invalidate cache; see 7206643
914         mAuthenticatorHelper.updateAuthDescriptions(this);
915         mAuthenticatorHelper.onAccountsUpdated(this, accounts);
916         invalidateHeaders();
917     }
918
919     /*
920      * Settings subclasses for launching independently.
921      */
922     public static class BluetoothSettingsActivity extends Settings { /* empty */ }
923     public static class WirelessSettingsActivity extends Settings { /* empty */ }
924     public static class TetherSettingsActivity extends Settings { /* empty */ }
925     public static class VpnSettingsActivity extends Settings { /* empty */ }
926     public static class DateTimeSettingsActivity extends Settings { /* empty */ }
927     public static class StorageSettingsActivity extends Settings { /* empty */ }
928     public static class WifiSettingsActivity extends Settings { /* empty */ }
929     public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
930     public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
931     public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
932     public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
933     public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
934     public static class LocalePickerActivity extends Settings { /* empty */ }
935     public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
936     public static class SoundSettingsActivity extends Settings { /* empty */ }
937     public static class DisplaySettingsActivity extends Settings { /* empty */ }
938     public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
939     public static class ApplicationSettingsActivity extends Settings { /* empty */ }
940     public static class ManageApplicationsActivity extends Settings { /* empty */ }
941     public static class AppOpsSummaryActivity extends Settings { /* empty */ }
942     public static class StorageUseActivity extends Settings { /* empty */ }
943     public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
944     public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
945     public static class SecuritySettingsActivity extends Settings { /* empty */ }
946     public static class LocationSettingsActivity extends Settings { /* empty */ }
947     public static class PrivacySettingsActivity extends Settings { /* empty */ }
948     public static class RunningServicesActivity extends Settings { /* empty */ }
949     public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
950     public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
951     public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
952     public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
953     public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
954     public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
955     public static class DataUsageSummaryActivity extends Settings { /* empty */ }
956     public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
957     public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
958     public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
959     public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
960     public static class DreamSettingsActivity extends Settings { /* empty */ }
961     public static class NotificationStationActivity extends Settings { /* empty */ }
962     public static class UserSettingsActivity extends Settings { /* empty */ }
963     public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
964     public static class UsbSettingsActivity extends Settings { /* empty */ }
965     public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
966     public static class PaymentSettingsActivity extends Settings { /* empty */ }
967 }