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