OSDN Git Service

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