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