OSDN Git Service

am f7b5ad80: (-s ours) am da7395d6: (-s ours) am c7902845: (-s ours) Import translati...
[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.SpellCheckersSettings;
75 import com.android.settings.inputmethod.UserDictionaryList;
76 import com.android.settings.location.LocationSettings;
77 import com.android.settings.nfc.AndroidBeam;
78 import com.android.settings.nfc.PaymentSettings;
79 import com.android.settings.tts.TextToSpeechSettings;
80 import com.android.settings.users.AppRestrictionsFragment;
81 import com.android.settings.users.UserSettings;
82 import com.android.settings.vpn2.VpnSettings;
83 import com.android.settings.wfd.WifiDisplaySettings;
84 import com.android.settings.wifi.AdvancedWifiSettings;
85 import com.android.settings.wifi.WifiEnabler;
86 import com.android.settings.wifi.WifiSettings;
87 import com.android.settings.wifi.p2p.WifiP2pSettings;
88
89 import java.util.ArrayList;
90 import java.util.Collections;
91 import java.util.Comparator;
92 import java.util.HashMap;
93 import java.util.List;
94
95 /**
96  * Top-level settings activity to handle single pane and double pane UI layout.
97  */
98 public class Settings extends PreferenceActivity
99         implements ButtonBarHandler, OnAccountsUpdateListener {
100
101     private static final String LOG_TAG = "Settings";
102
103     private static final String META_DATA_KEY_HEADER_ID =
104         "com.android.settings.TOP_LEVEL_HEADER_ID";
105     private static final String META_DATA_KEY_FRAGMENT_CLASS =
106         "com.android.settings.FRAGMENT_CLASS";
107     private static final String META_DATA_KEY_PARENT_TITLE =
108         "com.android.settings.PARENT_FRAGMENT_TITLE";
109     private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
110         "com.android.settings.PARENT_FRAGMENT_CLASS";
111
112     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
113
114     private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
115     private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
116
117     private String mFragmentClass;
118     private int mTopLevelHeaderId;
119     private Header mFirstHeader;
120     private Header mCurrentHeader;
121     private Header mParentHeader;
122     private boolean mInLocalHeaderSwitch;
123
124     // Show only these settings for restricted users
125     private int[] SETTINGS_FOR_RESTRICTED = {
126             R.id.wireless_section,
127             R.id.wifi_settings,
128             R.id.bluetooth_settings,
129             R.id.data_usage_settings,
130             R.id.wireless_settings,
131             R.id.device_section,
132             R.id.sound_settings,
133             R.id.display_settings,
134             R.id.storage_settings,
135             R.id.application_settings,
136             R.id.battery_settings,
137             R.id.personal_section,
138             R.id.location_settings,
139             R.id.security_settings,
140             R.id.language_settings,
141             R.id.user_settings,
142             R.id.account_settings,
143             R.id.account_add,
144             R.id.system_section,
145             R.id.date_time_settings,
146             R.id.about_settings,
147             R.id.accessibility_settings,
148             R.id.print_settings,
149             R.id.nfc_payment_settings
150     };
151
152     private SharedPreferences mDevelopmentPreferences;
153     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
154
155     // TODO: Update Call Settings based on airplane mode state.
156
157     protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
158
159     private AuthenticatorHelper mAuthenticatorHelper;
160     private Header mLastHeader;
161     private boolean mListeningToAccountUpdates;
162
163     private boolean mBatteryPresent = true;
164     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
165
166         @Override
167         public void onReceive(Context context, Intent intent) {
168             String action = intent.getAction();
169             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
170                 boolean batteryPresent = Utils.isBatteryPresent(intent);
171
172                 if (mBatteryPresent != batteryPresent) {
173                     mBatteryPresent = batteryPresent;
174                     invalidateHeaders();
175                 }
176             }
177         }
178     };
179
180     @Override
181     protected void onCreate(Bundle savedInstanceState) {
182         if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
183             getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
184         }
185
186         mAuthenticatorHelper = new AuthenticatorHelper();
187         mAuthenticatorHelper.updateAuthDescriptions(this);
188         mAuthenticatorHelper.onAccountsUpdated(this, null);
189
190         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
191                 Context.MODE_PRIVATE);
192
193         getMetaData();
194         mInLocalHeaderSwitch = true;
195         super.onCreate(savedInstanceState);
196         mInLocalHeaderSwitch = false;
197
198         if (!onIsHidingHeaders() && onIsMultiPane()) {
199             highlightHeader(mTopLevelHeaderId);
200             // Force the title so that it doesn't get overridden by a direct launch of
201             // a specific settings screen.
202             setTitle(R.string.settings_label);
203         }
204
205         // Retrieve any saved state
206         if (savedInstanceState != null) {
207             mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
208             mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
209         }
210
211         // If the current header was saved, switch to it
212         if (savedInstanceState != null && mCurrentHeader != null) {
213             //switchToHeaderLocal(mCurrentHeader);
214             showBreadCrumbs(mCurrentHeader.title, null);
215         }
216
217         if (mParentHeader != null) {
218             setParentTitle(mParentHeader.title, null, new OnClickListener() {
219                 @Override
220                 public void onClick(View v) {
221                     switchToParent(mParentHeader.fragment);
222                 }
223             });
224         }
225
226         // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
227         if (onIsMultiPane()) {
228             getActionBar().setDisplayHomeAsUpEnabled(false);
229             getActionBar().setHomeButtonEnabled(false);
230         }
231     }
232
233     @Override
234     protected void onSaveInstanceState(Bundle outState) {
235         super.onSaveInstanceState(outState);
236
237         // Save the current fragment, if it is the same as originally launched
238         if (mCurrentHeader != null) {
239             outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
240         }
241         if (mParentHeader != null) {
242             outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
243         }
244     }
245
246     @Override
247     public void onResume() {
248         super.onResume();
249
250         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
251             @Override
252             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
253                 invalidateHeaders();
254             }
255         };
256         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
257                 mDevelopmentPreferencesListener);
258
259         ListAdapter listAdapter = getListAdapter();
260         if (listAdapter instanceof HeaderAdapter) {
261             ((HeaderAdapter) listAdapter).resume();
262         }
263         invalidateHeaders();
264
265         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
266     }
267
268     @Override
269     public void onPause() {
270         super.onPause();
271
272         unregisterReceiver(mBatteryInfoReceiver);
273
274         ListAdapter listAdapter = getListAdapter();
275         if (listAdapter instanceof HeaderAdapter) {
276             ((HeaderAdapter) listAdapter).pause();
277         }
278
279         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
280                 mDevelopmentPreferencesListener);
281         mDevelopmentPreferencesListener = null;
282     }
283
284     @Override
285     public void onDestroy() {
286         super.onDestroy();
287         if (mListeningToAccountUpdates) {
288             AccountManager.get(this).removeOnAccountsUpdatedListener(this);
289         }
290     }
291
292     @Override
293     public boolean onIsMultiPane() {
294         return false;
295     }
296
297     private static final String[] ENTRY_FRAGMENTS = {
298         WirelessSettings.class.getName(),
299         WifiSettings.class.getName(),
300         AdvancedWifiSettings.class.getName(),
301         BluetoothSettings.class.getName(),
302         TetherSettings.class.getName(),
303         WifiP2pSettings.class.getName(),
304         VpnSettings.class.getName(),
305         DateTimeSettings.class.getName(),
306         LocalePicker.class.getName(),
307         InputMethodAndLanguageSettings.class.getName(),
308         SpellCheckersSettings.class.getName(),
309         UserDictionaryList.class.getName(),
310         UserDictionarySettings.class.getName(),
311         SoundSettings.class.getName(),
312         DisplaySettings.class.getName(),
313         DeviceInfoSettings.class.getName(),
314         ManageApplications.class.getName(),
315         ProcessStatsUi.class.getName(),
316         NotificationStation.class.getName(),
317         AppOpsSummary.class.getName(),
318         LocationSettings.class.getName(),
319         SecuritySettings.class.getName(),
320         PrivacySettings.class.getName(),
321         DeviceAdminSettings.class.getName(),
322         AccessibilitySettings.class.getName(),
323         TextToSpeechSettings.class.getName(),
324         Memory.class.getName(),
325         DevelopmentSettings.class.getName(),
326         UsbSettings.class.getName(),
327         AndroidBeam.class.getName(),
328         WifiDisplaySettings.class.getName(),
329         PowerUsageSummary.class.getName(),
330         AccountSyncSettings.class.getName(),
331         CryptKeeperSettings.class.getName(),
332         DataUsageSummary.class.getName(),
333         DreamSettings.class.getName(),
334         UserSettings.class.getName(),
335         NotificationAccessSettings.class.getName(),
336         ManageAccountsSettings.class.getName(),
337         PrintingSettings.class.getName(),
338         TrustedCredentialsSettings.class.getName(),
339         PaymentSettings.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             intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
500         }
501
502         intent.setClass(this, SubSettings.class);
503         return intent;
504     }
505
506     /**
507      * Populate the activity with the top-level headers.
508      */
509     @Override
510     public void onBuildHeaders(List<Header> headers) {
511         loadHeadersFromResource(R.xml.settings_headers, headers);
512         updateHeaderList(headers);
513     }
514
515     private void updateHeaderList(List<Header> target) {
516         final boolean showDev = mDevelopmentPreferences.getBoolean(
517                 DevelopmentSettings.PREF_SHOW,
518                 android.os.Build.TYPE.equals("eng"));
519         int i = 0;
520
521         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
522         mHeaderIndexMap.clear();
523         while (i < target.size()) {
524             Header header = target.get(i);
525             // Ids are integers, so downcasting
526             int id = (int) header.id;
527             if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
528                 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
529             } else if (id == R.id.wifi_settings) {
530                 // Remove WiFi Settings if WiFi service is not available.
531                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
532                     target.remove(i);
533                 }
534             } else if (id == R.id.bluetooth_settings) {
535                 // Remove Bluetooth Settings if Bluetooth service is not available.
536                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
537                     target.remove(i);
538                 }
539             } else if (id == R.id.data_usage_settings) {
540                 // Remove data usage when kernel module not enabled
541                 final INetworkManagementService netManager = INetworkManagementService.Stub
542                         .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
543                 try {
544                     if (!netManager.isBandwidthControlEnabled()) {
545                         target.remove(i);
546                     }
547                 } catch (RemoteException e) {
548                     // ignored
549                 }
550             } else if (id == R.id.battery_settings) {
551                 // Remove battery settings when battery is not available. (e.g. TV)
552
553                 if (!mBatteryPresent) {
554                     target.remove(i);
555                 }
556             } else if (id == R.id.account_settings) {
557                 int headerIndex = i + 1;
558                 i = insertAccountsHeaders(target, headerIndex);
559             } else if (id == R.id.user_settings) {
560                 if (!UserHandle.MU_ENABLED
561                         || !UserManager.supportsMultipleUsers()
562                         || Utils.isMonkeyRunning()) {
563                     target.remove(i);
564                 }
565             } else if (id == R.id.nfc_payment_settings) {
566                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
567                     target.remove(i);
568                 }
569             } else if (id == R.id.development_settings) {
570                 if (!showDev) {
571                     target.remove(i);
572                 }
573             } else if (id == R.id.account_add) {
574                 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
575                     target.remove(i);
576                 }
577             }
578
579             if (i < target.size() && target.get(i) == header
580                     && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
581                     && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
582                 target.remove(i);
583             }
584
585             // Increment if the current one wasn't removed by the Utils code.
586             if (i < target.size() && target.get(i) == header) {
587                 // Hold on to the first header, when we need to reset to the top-level
588                 if (mFirstHeader == null &&
589                         HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
590                     mFirstHeader = header;
591                 }
592                 mHeaderIndexMap.put(id, i);
593                 i++;
594             }
595         }
596     }
597
598     private int insertAccountsHeaders(List<Header> target, int headerIndex) {
599         String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
600         List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
601         for (String accountType : accountTypes) {
602             CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
603             if (label == null) {
604                 continue;
605             }
606
607             Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
608             boolean skipToAccount = accounts.length == 1
609                     && !mAuthenticatorHelper.hasAccountPreferences(accountType);
610             Header accHeader = new Header();
611             accHeader.title = label;
612             if (accHeader.extras == null) {
613                 accHeader.extras = new Bundle();
614             }
615             if (skipToAccount) {
616                 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
617                 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
618                 accHeader.fragment = AccountSyncSettings.class.getName();
619                 accHeader.fragmentArguments = new Bundle();
620                 // Need this for the icon
621                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
622                 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
623                 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
624                         accounts[0]);
625             } else {
626                 accHeader.breadCrumbTitle = label;
627                 accHeader.breadCrumbShortTitle = label;
628                 accHeader.fragment = ManageAccountsSettings.class.getName();
629                 accHeader.fragmentArguments = new Bundle();
630                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
631                 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
632                         accountType);
633                 if (!isMultiPane()) {
634                     accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
635                             label.toString());
636                 }
637             }
638             accountHeaders.add(accHeader);
639         }
640
641         // Sort by label
642         Collections.sort(accountHeaders, new Comparator<Header>() {
643             @Override
644             public int compare(Header h1, Header h2) {
645                 return h1.title.toString().compareTo(h2.title.toString());
646             }
647         });
648
649         for (Header header : accountHeaders) {
650             target.add(headerIndex++, header);
651         }
652         if (!mListeningToAccountUpdates) {
653             AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
654             mListeningToAccountUpdates = true;
655         }
656         return headerIndex;
657     }
658
659     private void getMetaData() {
660         try {
661             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
662                     PackageManager.GET_META_DATA);
663             if (ai == null || ai.metaData == null) return;
664             mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
665             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
666
667             // Check if it has a parent specified and create a Header object
668             final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
669             String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
670             if (parentFragmentClass != null) {
671                 mParentHeader = new Header();
672                 mParentHeader.fragment = parentFragmentClass;
673                 if (parentHeaderTitleRes != 0) {
674                     mParentHeader.title = getResources().getString(parentHeaderTitleRes);
675                 }
676             }
677         } catch (NameNotFoundException nnfe) {
678             // No recovery
679         }
680     }
681
682     @Override
683     public boolean hasNextButton() {
684         return super.hasNextButton();
685     }
686
687     @Override
688     public Button getNextButton() {
689         return super.getNextButton();
690     }
691
692     private static class HeaderAdapter extends ArrayAdapter<Header> {
693         static final int HEADER_TYPE_CATEGORY = 0;
694         static final int HEADER_TYPE_NORMAL = 1;
695         static final int HEADER_TYPE_SWITCH = 2;
696         private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1;
697
698         private final WifiEnabler mWifiEnabler;
699         private final BluetoothEnabler mBluetoothEnabler;
700         private AuthenticatorHelper mAuthHelper;
701
702         private static class HeaderViewHolder {
703             ImageView icon;
704             TextView title;
705             TextView summary;
706             Switch switch_;
707         }
708
709         private LayoutInflater mInflater;
710
711         static int getHeaderType(Header header) {
712             if (header.fragment == null && header.intent == null) {
713                 return HEADER_TYPE_CATEGORY;
714             } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
715                 return HEADER_TYPE_SWITCH;
716             } else {
717                 return HEADER_TYPE_NORMAL;
718             }
719         }
720
721         @Override
722         public int getItemViewType(int position) {
723             Header header = getItem(position);
724             return getHeaderType(header);
725         }
726
727         @Override
728         public boolean areAllItemsEnabled() {
729             return false; // because of categories
730         }
731
732         @Override
733         public boolean isEnabled(int position) {
734             return getItemViewType(position) != HEADER_TYPE_CATEGORY;
735         }
736
737         @Override
738         public int getViewTypeCount() {
739             return HEADER_TYPE_COUNT;
740         }
741
742         @Override
743         public boolean hasStableIds() {
744             return true;
745         }
746
747         public HeaderAdapter(Context context, List<Header> objects,
748                 AuthenticatorHelper authenticatorHelper) {
749             super(context, 0, objects);
750
751             mAuthHelper = authenticatorHelper;
752             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
753
754             // Temp Switches provided as placeholder until the adapter replaces these with actual
755             // Switches inflated from their layouts. Must be done before adapter is set in super
756             mWifiEnabler = new WifiEnabler(context, new Switch(context));
757             mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
758         }
759
760         @Override
761         public View getView(int position, View convertView, ViewGroup parent) {
762             HeaderViewHolder holder;
763             Header header = getItem(position);
764             int headerType = getHeaderType(header);
765             View view = null;
766
767             if (convertView == null) {
768                 holder = new HeaderViewHolder();
769                 switch (headerType) {
770                     case HEADER_TYPE_CATEGORY:
771                         view = new TextView(getContext(), null,
772                                 android.R.attr.listSeparatorTextViewStyle);
773                         holder.title = (TextView) view;
774                         break;
775
776                     case HEADER_TYPE_SWITCH:
777                         view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
778                                 false);
779                         holder.icon = (ImageView) view.findViewById(R.id.icon);
780                         holder.title = (TextView)
781                                 view.findViewById(com.android.internal.R.id.title);
782                         holder.summary = (TextView)
783                                 view.findViewById(com.android.internal.R.id.summary);
784                         holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
785                         break;
786
787                     case HEADER_TYPE_NORMAL:
788                         view = mInflater.inflate(
789                                 R.layout.preference_header_item, parent,
790                                 false);
791                         holder.icon = (ImageView) view.findViewById(R.id.icon);
792                         holder.title = (TextView)
793                                 view.findViewById(com.android.internal.R.id.title);
794                         holder.summary = (TextView)
795                                 view.findViewById(com.android.internal.R.id.summary);
796                         break;
797                 }
798                 view.setTag(holder);
799             } else {
800                 view = convertView;
801                 holder = (HeaderViewHolder) view.getTag();
802             }
803
804             // All view fields must be updated every time, because the view may be recycled
805             switch (headerType) {
806                 case HEADER_TYPE_CATEGORY:
807                     holder.title.setText(header.getTitle(getContext().getResources()));
808                     break;
809
810                 case HEADER_TYPE_SWITCH:
811                     // Would need a different treatment if the main menu had more switches
812                     if (header.id == R.id.wifi_settings) {
813                         mWifiEnabler.setSwitch(holder.switch_);
814                     } else {
815                         mBluetoothEnabler.setSwitch(holder.switch_);
816                     }
817                     // No break, fall through on purpose to update common fields
818
819                     //$FALL-THROUGH$
820                 case HEADER_TYPE_NORMAL:
821                     if (header.extras != null
822                             && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
823                         String accType = header.extras.getString(
824                                 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
825                         ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
826                         lp.width = getContext().getResources().getDimensionPixelSize(
827                                 R.dimen.header_icon_width);
828                         lp.height = lp.width;
829                         holder.icon.setLayoutParams(lp);
830                         Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
831                         holder.icon.setImageDrawable(icon);
832                     } else {
833                         holder.icon.setImageResource(header.iconRes);
834                     }
835                     holder.title.setText(header.getTitle(getContext().getResources()));
836                     CharSequence summary = header.getSummary(getContext().getResources());
837                     if (!TextUtils.isEmpty(summary)) {
838                         holder.summary.setVisibility(View.VISIBLE);
839                         holder.summary.setText(summary);
840                     } else {
841                         holder.summary.setVisibility(View.GONE);
842                     }
843                     break;
844             }
845
846             return view;
847         }
848
849         public void resume() {
850             mWifiEnabler.resume();
851             mBluetoothEnabler.resume();
852         }
853
854         public void pause() {
855             mWifiEnabler.pause();
856             mBluetoothEnabler.pause();
857         }
858     }
859
860     @Override
861     public void onHeaderClick(Header header, int position) {
862         boolean revert = false;
863         if (header.id == R.id.account_add) {
864             revert = true;
865         }
866
867         super.onHeaderClick(header, position);
868
869         if (revert && mLastHeader != null) {
870             highlightHeader((int) mLastHeader.id);
871         } else {
872             mLastHeader = header;
873         }
874     }
875
876     @Override
877     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
878         // Override the fragment title for Wallpaper settings
879         int titleRes = pref.getTitleRes();
880         if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
881             titleRes = R.string.wallpaper_settings_fragment_title;
882         } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
883                 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
884             if (UserManager.get(this).isLinkedUser()) {
885                 titleRes = R.string.profile_info_settings_title;
886             } else {
887                 titleRes = R.string.user_info_settings_title;
888             }
889         }
890         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
891                 null, 0);
892         return true;
893     }
894
895     @Override
896     public boolean shouldUpRecreateTask(Intent targetIntent) {
897         return super.shouldUpRecreateTask(new Intent(this, Settings.class));
898     }
899
900     @Override
901     public void setListAdapter(ListAdapter adapter) {
902         if (adapter == null) {
903             super.setListAdapter(null);
904         } else {
905             super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper));
906         }
907     }
908
909     @Override
910     public void onAccountsUpdated(Account[] accounts) {
911         // TODO: watch for package upgrades to invalidate cache; see 7206643
912         mAuthenticatorHelper.updateAuthDescriptions(this);
913         mAuthenticatorHelper.onAccountsUpdated(this, accounts);
914         invalidateHeaders();
915     }
916
917     /*
918      * Settings subclasses for launching independently.
919      */
920     public static class BluetoothSettingsActivity extends Settings { /* empty */ }
921     public static class WirelessSettingsActivity extends Settings { /* empty */ }
922     public static class TetherSettingsActivity extends Settings { /* empty */ }
923     public static class VpnSettingsActivity extends Settings { /* empty */ }
924     public static class DateTimeSettingsActivity extends Settings { /* empty */ }
925     public static class StorageSettingsActivity extends Settings { /* empty */ }
926     public static class WifiSettingsActivity extends Settings { /* empty */ }
927     public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
928     public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
929     public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
930     public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
931     public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
932     public static class LocalePickerActivity extends Settings { /* empty */ }
933     public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
934     public static class SoundSettingsActivity extends Settings { /* empty */ }
935     public static class DisplaySettingsActivity extends Settings { /* empty */ }
936     public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
937     public static class ApplicationSettingsActivity extends Settings { /* empty */ }
938     public static class ManageApplicationsActivity extends Settings { /* empty */ }
939     public static class AppOpsSummaryActivity extends Settings { /* empty */ }
940     public static class StorageUseActivity extends Settings { /* empty */ }
941     public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
942     public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
943     public static class SecuritySettingsActivity extends Settings { /* empty */ }
944     public static class LocationSettingsActivity extends Settings { /* empty */ }
945     public static class PrivacySettingsActivity extends Settings { /* empty */ }
946     public static class RunningServicesActivity extends Settings { /* empty */ }
947     public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
948     public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
949     public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
950     public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
951     public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
952     public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
953     public static class DataUsageSummaryActivity extends Settings { /* empty */ }
954     public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
955     public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
956     public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
957     public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
958     public static class DreamSettingsActivity extends Settings { /* empty */ }
959     public static class NotificationStationActivity extends Settings { /* empty */ }
960     public static class UserSettingsActivity extends Settings { /* empty */ }
961     public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
962     public static class UsbSettingsActivity extends Settings { /* empty */ }
963     public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
964     public static class PaymentSettingsActivity extends Settings { /* empty */ }
965 }