OSDN Git Service

Merge "Fix manifest entry for CAPTIONING_SETTINGS action" 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.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.SharedPreferences;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageManager.NameNotFoundException;
31 import android.graphics.drawable.Drawable;
32 import android.os.Bundle;
33 import android.os.INetworkManagementService;
34 import android.os.RemoteException;
35 import android.os.ServiceManager;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.preference.Preference;
39 import android.preference.PreferenceActivity;
40 import android.preference.PreferenceFragment;
41 import android.text.TextUtils;
42 import android.util.Log;
43 import android.view.LayoutInflater;
44 import android.view.View;
45 import android.view.View.OnClickListener;
46 import android.view.ViewGroup;
47 import android.widget.ArrayAdapter;
48 import android.widget.Button;
49 import android.widget.ImageView;
50 import android.widget.ListAdapter;
51 import android.widget.Switch;
52 import android.widget.TextView;
53
54 import com.android.internal.util.ArrayUtils;
55 import com.android.settings.accessibility.AccessibilitySettings;
56 import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
57 import com.android.settings.accessibility.ToggleCaptioningPreferenceFragment;
58 import com.android.settings.accounts.AccountSyncSettings;
59 import com.android.settings.accounts.AuthenticatorHelper;
60 import com.android.settings.accounts.ManageAccountsSettings;
61 import com.android.settings.applications.AppOpsSummary;
62 import com.android.settings.applications.ManageApplications;
63 import com.android.settings.applications.ProcessStatsUi;
64 import com.android.settings.bluetooth.BluetoothEnabler;
65 import com.android.settings.bluetooth.BluetoothSettings;
66 import com.android.settings.deviceinfo.Memory;
67 import com.android.settings.deviceinfo.UsbSettings;
68 import com.android.settings.fuelgauge.PowerUsageSummary;
69 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
70 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
71 import com.android.settings.inputmethod.SpellCheckersSettings;
72 import com.android.settings.inputmethod.UserDictionaryList;
73 import com.android.settings.location.LocationSettings;
74 import com.android.settings.nfc.AndroidBeam;
75 import com.android.settings.nfc.PaymentSettings;
76 import com.android.settings.print.PrintServiceSettingsFragment;
77 import com.android.settings.print.PrintSettingsFragment;
78 import com.android.settings.tts.TextToSpeechSettings;
79 import com.android.settings.users.UserSettings;
80 import com.android.settings.vpn2.VpnSettings;
81 import com.android.settings.wfd.WifiDisplaySettings;
82 import com.android.settings.wifi.AdvancedWifiSettings;
83 import com.android.settings.wifi.WifiEnabler;
84 import com.android.settings.wifi.WifiSettings;
85 import com.android.settings.wifi.p2p.WifiP2pSettings;
86
87 import java.util.ArrayList;
88 import java.util.Collections;
89 import java.util.Comparator;
90 import java.util.HashMap;
91 import java.util.List;
92
93 /**
94  * Top-level settings activity to handle single pane and double pane UI layout.
95  */
96 public class Settings extends PreferenceActivity
97         implements ButtonBarHandler, OnAccountsUpdateListener {
98
99     private static final String LOG_TAG = "Settings";
100
101     private static final String META_DATA_KEY_HEADER_ID =
102         "com.android.settings.TOP_LEVEL_HEADER_ID";
103     private static final String META_DATA_KEY_FRAGMENT_CLASS =
104         "com.android.settings.FRAGMENT_CLASS";
105     private static final String META_DATA_KEY_PARENT_TITLE =
106         "com.android.settings.PARENT_FRAGMENT_TITLE";
107     private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
108         "com.android.settings.PARENT_FRAGMENT_CLASS";
109
110     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
111
112     private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
113     private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
114
115     private String mFragmentClass;
116     private int mTopLevelHeaderId;
117     private Header mFirstHeader;
118     private Header mCurrentHeader;
119     private Header mParentHeader;
120     private boolean mInLocalHeaderSwitch;
121
122     // Show only these settings for restricted users
123     private int[] SETTINGS_FOR_RESTRICTED = {
124             R.id.wireless_section,
125             R.id.wifi_settings,
126             R.id.bluetooth_settings,
127             R.id.data_usage_settings,
128             R.id.wireless_settings,
129             R.id.device_section,
130             R.id.sound_settings,
131             R.id.display_settings,
132             R.id.storage_settings,
133             R.id.application_settings,
134             R.id.battery_settings,
135             R.id.personal_section,
136             R.id.location_settings,
137             R.id.security_settings,
138             R.id.language_settings,
139             R.id.user_settings,
140             R.id.account_settings,
141             R.id.account_add,
142             R.id.system_section,
143             R.id.date_time_settings,
144             R.id.about_settings,
145             R.id.accessibility_settings,
146             R.id.print_settings,
147             R.id.nfc_payment_settings
148     };
149
150     private SharedPreferences mDevelopmentPreferences;
151     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
152
153     // TODO: Update Call Settings based on airplane mode state.
154
155     protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
156
157     private AuthenticatorHelper mAuthenticatorHelper;
158     private Header mLastHeader;
159     private boolean mListeningToAccountUpdates;
160
161     private boolean mBatteryPresent = true;
162     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
163
164         @Override
165         public void onReceive(Context context, Intent intent) {
166             String action = intent.getAction();
167             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
168                 boolean batteryPresent = Utils.isBatteryPresent(intent);
169
170                 if (mBatteryPresent != batteryPresent) {
171                     mBatteryPresent = batteryPresent;
172                     invalidateHeaders();
173                 }
174             }
175         }
176     };
177
178     @Override
179     protected void onCreate(Bundle savedInstanceState) {
180         if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
181             getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
182         }
183
184         mAuthenticatorHelper = new AuthenticatorHelper();
185         mAuthenticatorHelper.updateAuthDescriptions(this);
186         mAuthenticatorHelper.onAccountsUpdated(this, null);
187
188         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
189                 Context.MODE_PRIVATE);
190
191         getMetaData();
192         mInLocalHeaderSwitch = true;
193         super.onCreate(savedInstanceState);
194         mInLocalHeaderSwitch = false;
195
196         if (!onIsHidingHeaders() && onIsMultiPane()) {
197             highlightHeader(mTopLevelHeaderId);
198             // Force the title so that it doesn't get overridden by a direct launch of
199             // a specific settings screen.
200             setTitle(R.string.settings_label);
201         }
202
203         // Retrieve any saved state
204         if (savedInstanceState != null) {
205             mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
206             mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
207         }
208
209         // If the current header was saved, switch to it
210         if (savedInstanceState != null && mCurrentHeader != null) {
211             //switchToHeaderLocal(mCurrentHeader);
212             showBreadCrumbs(mCurrentHeader.title, null);
213         }
214
215         if (mParentHeader != null) {
216             setParentTitle(mParentHeader.title, null, new OnClickListener() {
217                 @Override
218                 public void onClick(View v) {
219                     switchToParent(mParentHeader.fragment);
220                 }
221             });
222         }
223
224         // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
225         if (onIsMultiPane()) {
226             getActionBar().setDisplayHomeAsUpEnabled(false);
227             getActionBar().setHomeButtonEnabled(false);
228         }
229     }
230
231     @Override
232     protected void onSaveInstanceState(Bundle outState) {
233         super.onSaveInstanceState(outState);
234
235         // Save the current fragment, if it is the same as originally launched
236         if (mCurrentHeader != null) {
237             outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
238         }
239         if (mParentHeader != null) {
240             outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
241         }
242     }
243
244     @Override
245     public void onResume() {
246         super.onResume();
247
248         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
249             @Override
250             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
251                 invalidateHeaders();
252             }
253         };
254         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
255                 mDevelopmentPreferencesListener);
256
257         ListAdapter listAdapter = getListAdapter();
258         if (listAdapter instanceof HeaderAdapter) {
259             ((HeaderAdapter) listAdapter).resume();
260         }
261         invalidateHeaders();
262
263         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
264     }
265
266     @Override
267     public void onPause() {
268         super.onPause();
269
270         unregisterReceiver(mBatteryInfoReceiver);
271
272         ListAdapter listAdapter = getListAdapter();
273         if (listAdapter instanceof HeaderAdapter) {
274             ((HeaderAdapter) listAdapter).pause();
275         }
276
277         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
278                 mDevelopmentPreferencesListener);
279         mDevelopmentPreferencesListener = null;
280     }
281
282     @Override
283     public void onDestroy() {
284         super.onDestroy();
285         if (mListeningToAccountUpdates) {
286             AccountManager.get(this).removeOnAccountsUpdatedListener(this);
287         }
288     }
289
290     @Override
291     public boolean onIsMultiPane() {
292         return false;
293     }
294
295     private static final String[] ENTRY_FRAGMENTS = {
296         WirelessSettings.class.getName(),
297         WifiSettings.class.getName(),
298         AdvancedWifiSettings.class.getName(),
299         BluetoothSettings.class.getName(),
300         TetherSettings.class.getName(),
301         WifiP2pSettings.class.getName(),
302         VpnSettings.class.getName(),
303         DateTimeSettings.class.getName(),
304         LocalePicker.class.getName(),
305         InputMethodAndLanguageSettings.class.getName(),
306         SpellCheckersSettings.class.getName(),
307         UserDictionaryList.class.getName(),
308         UserDictionarySettings.class.getName(),
309         SoundSettings.class.getName(),
310         DisplaySettings.class.getName(),
311         DeviceInfoSettings.class.getName(),
312         ManageApplications.class.getName(),
313         ProcessStatsUi.class.getName(),
314         NotificationStation.class.getName(),
315         AppOpsSummary.class.getName(),
316         LocationSettings.class.getName(),
317         SecuritySettings.class.getName(),
318         PrivacySettings.class.getName(),
319         DeviceAdminSettings.class.getName(),
320         AccessibilitySettings.class.getName(),
321         ToggleCaptioningPreferenceFragment.class.getName(),
322         TextToSpeechSettings.class.getName(),
323         Memory.class.getName(),
324         DevelopmentSettings.class.getName(),
325         UsbSettings.class.getName(),
326         AndroidBeam.class.getName(),
327         WifiDisplaySettings.class.getName(),
328         PowerUsageSummary.class.getName(),
329         AccountSyncSettings.class.getName(),
330         CryptKeeperSettings.class.getName(),
331         DataUsageSummary.class.getName(),
332         DreamSettings.class.getName(),
333         UserSettings.class.getName(),
334         NotificationAccessSettings.class.getName(),
335         ManageAccountsSettings.class.getName(),
336         PrintSettingsFragment.class.getName(),
337         TrustedCredentialsSettings.class.getName(),
338         PaymentSettings.class.getName(),
339         KeyboardLayoutPickerFragment.class.getName()
340     };
341
342     @Override
343     protected boolean isValidFragment(String fragmentName) {
344         // Almost all fragments are wrapped in this,
345         // except for a few that have their own activities.
346         for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
347             if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
348         }
349         return false;
350     }
351
352     private void switchToHeaderLocal(Header header) {
353         mInLocalHeaderSwitch = true;
354         switchToHeader(header);
355         mInLocalHeaderSwitch = false;
356     }
357
358     @Override
359     public void switchToHeader(Header header) {
360         if (!mInLocalHeaderSwitch) {
361             mCurrentHeader = null;
362             mParentHeader = null;
363         }
364         super.switchToHeader(header);
365     }
366
367     /**
368      * Switch to parent fragment and store the grand parent's info
369      * @param className name of the activity wrapper for the parent fragment.
370      */
371     private void switchToParent(String className) {
372         final ComponentName cn = new ComponentName(this, className);
373         try {
374             final PackageManager pm = getPackageManager();
375             final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
376
377             if (parentInfo != null && parentInfo.metaData != null) {
378                 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
379                 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
380                 Header parentHeader = new Header();
381                 parentHeader.fragment = fragmentClass;
382                 parentHeader.title = fragmentTitle;
383                 mCurrentHeader = parentHeader;
384
385                 switchToHeaderLocal(parentHeader);
386                 highlightHeader(mTopLevelHeaderId);
387
388                 mParentHeader = new Header();
389                 mParentHeader.fragment
390                         = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
391                 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
392             }
393         } catch (NameNotFoundException nnfe) {
394             Log.w(LOG_TAG, "Could not find parent activity : " + className);
395         }
396     }
397
398     @Override
399     public void onNewIntent(Intent intent) {
400         super.onNewIntent(intent);
401
402         // If it is not launched from history, then reset to top-level
403         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
404             if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
405                 switchToHeaderLocal(mFirstHeader);
406             }
407             getListView().setSelectionFromTop(0, 0);
408         }
409     }
410
411     private void highlightHeader(int id) {
412         if (id != 0) {
413             Integer index = mHeaderIndexMap.get(id);
414             if (index != null) {
415                 getListView().setItemChecked(index, true);
416                 if (isMultiPane()) {
417                     getListView().smoothScrollToPosition(index);
418                 }
419             }
420         }
421     }
422
423     @Override
424     public Intent getIntent() {
425         Intent superIntent = super.getIntent();
426         String startingFragment = getStartingFragmentClass(superIntent);
427         // This is called from super.onCreate, isMultiPane() is not yet reliable
428         // Do not use onIsHidingHeaders either, which relies itself on this method
429         if (startingFragment != null && !onIsMultiPane()) {
430             Intent modIntent = new Intent(superIntent);
431             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
432             Bundle args = superIntent.getExtras();
433             if (args != null) {
434                 args = new Bundle(args);
435             } else {
436                 args = new Bundle();
437             }
438             args.putParcelable("intent", superIntent);
439             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
440             return modIntent;
441         }
442         return superIntent;
443     }
444
445     /**
446      * Checks if the component name in the intent is different from the Settings class and
447      * returns the class name to load as a fragment.
448      */
449     protected String getStartingFragmentClass(Intent intent) {
450         if (mFragmentClass != null) return mFragmentClass;
451
452         String intentClass = intent.getComponent().getClassName();
453         if (intentClass.equals(getClass().getName())) return null;
454
455         if ("com.android.settings.ManageApplications".equals(intentClass)
456                 || "com.android.settings.RunningServices".equals(intentClass)
457                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
458             // Old names of manage apps.
459             intentClass = com.android.settings.applications.ManageApplications.class.getName();
460         }
461
462         return intentClass;
463     }
464
465     /**
466      * Override initial header when an activity-alias is causing Settings to be launched
467      * for a specific fragment encoded in the android:name parameter.
468      */
469     @Override
470     public Header onGetInitialHeader() {
471         String fragmentClass = getStartingFragmentClass(super.getIntent());
472         if (fragmentClass != null) {
473             Header header = new Header();
474             header.fragment = fragmentClass;
475             header.title = getTitle();
476             header.fragmentArguments = getIntent().getExtras();
477             mCurrentHeader = header;
478             return header;
479         }
480
481         return mFirstHeader;
482     }
483
484     @Override
485     public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
486             int titleRes, int shortTitleRes) {
487         Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
488                 titleRes, shortTitleRes);
489
490         // Some fragments want split ActionBar; these should stay in sync with
491         // uiOptions for fragments also defined as activities in manifest.
492         if (WifiSettings.class.getName().equals(fragmentName) ||
493                 WifiP2pSettings.class.getName().equals(fragmentName) ||
494                 WifiDisplaySettings.class.getName().equals(fragmentName) ||
495                 BluetoothSettings.class.getName().equals(fragmentName) ||
496                 DreamSettings.class.getName().equals(fragmentName) ||
497                 LocationSettings.class.getName().equals(fragmentName) ||
498                 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
499                 PrintSettingsFragment.class.getName().equals(fragmentName) ||
500                 PrintServiceSettingsFragment.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 CaptioningSettingsActivity extends Settings { /* empty */ }
946     public static class SecuritySettingsActivity extends Settings { /* empty */ }
947     public static class LocationSettingsActivity extends Settings { /* empty */ }
948     public static class PrivacySettingsActivity extends Settings { /* empty */ }
949     public static class RunningServicesActivity extends Settings { /* empty */ }
950     public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
951     public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
952     public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
953     public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
954     public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
955     public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
956     public static class DataUsageSummaryActivity extends Settings { /* empty */ }
957     public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
958     public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
959     public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
960     public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
961     public static class DreamSettingsActivity extends Settings { /* empty */ }
962     public static class NotificationStationActivity extends Settings { /* empty */ }
963     public static class UserSettingsActivity extends Settings { /* empty */ }
964     public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
965     public static class UsbSettingsActivity extends Settings { /* empty */ }
966     public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
967     public static class PaymentSettingsActivity extends Settings { /* empty */ }
968     public static class PrintSettingsActivity extends Settings { /* empty */ }
969 }