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         AppOpsSummary.class.getName(),
    330         LocationSettings.class.getName(),
    331         SecuritySettings.class.getName(),
    332         PrivacySettings.class.getName(),
    333         DeviceAdminSettings.class.getName(),
    334         AccessibilitySettings.class.getName(),
    335         ToggleCaptioningPreferenceFragment.class.getName(),
    336         TextToSpeechSettings.class.getName(),
    337         Memory.class.getName(),
    338         DevelopmentSettings.class.getName(),
    339         UsbSettings.class.getName(),
    340         AndroidBeam.class.getName(),
    341         WifiDisplaySettings.class.getName(),
    342         PowerUsageSummary.class.getName(),
    343         AccountSyncSettings.class.getName(),
    344         CryptKeeperSettings.class.getName(),
    345         DataUsageSummary.class.getName(),
    346         DreamSettings.class.getName(),
    347         UserSettings.class.getName(),
    348         NotificationAccessSettings.class.getName(),
    349         ManageAccountsSettings.class.getName(),
    350         PrintSettingsFragment.class.getName(),
    351         PrintJobSettingsFragment.class.getName(),
    352         TrustedCredentialsSettings.class.getName(),
    353         PaymentSettings.class.getName(),
    354         KeyboardLayoutPickerFragment.class.getName()
    355     };
    356 
    357     @Override
    358     protected boolean isValidFragment(String fragmentName) {
    359         // Almost all fragments are wrapped in this,
    360         // except for a few that have their own activities.
    361         for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
    362             if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
    363         }
    364         return false;
    365     }
    366 
    367     private void switchToHeaderLocal(Header header) {
    368         mInLocalHeaderSwitch = true;
    369         switchToHeader(header);
    370         mInLocalHeaderSwitch = false;
    371     }
    372 
    373     @Override
    374     public void switchToHeader(Header header) {
    375         if (!mInLocalHeaderSwitch) {
    376             mCurrentHeader = null;
    377             mParentHeader = null;
    378         }
    379         super.switchToHeader(header);
    380     }
    381 
    382     /**
    383      * Switch to parent fragment and store the grand parent's info
    384      * @param className name of the activity wrapper for the parent fragment.
    385      */
    386     private void switchToParent(String className) {
    387         final ComponentName cn = new ComponentName(this, className);
    388         try {
    389             final PackageManager pm = getPackageManager();
    390             final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
    391 
    392             if (parentInfo != null && parentInfo.metaData != null) {
    393                 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
    394                 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
    395                 Header parentHeader = new Header();
    396                 parentHeader.fragment = fragmentClass;
    397                 parentHeader.title = fragmentTitle;
    398                 mCurrentHeader = parentHeader;
    399 
    400                 switchToHeaderLocal(parentHeader);
    401                 highlightHeader(mTopLevelHeaderId);
    402 
    403                 mParentHeader = new Header();
    404                 mParentHeader.fragment
    405                         = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
    406                 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
    407             }
    408         } catch (NameNotFoundException nnfe) {
    409             Log.w(LOG_TAG, "Could not find parent activity : " + className);
    410         }
    411     }
    412 
    413     @Override
    414     public void onNewIntent(Intent intent) {
    415         super.onNewIntent(intent);
    416 
    417         // If it is not launched from history, then reset to top-level
    418         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
    419             if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
    420                 switchToHeaderLocal(mFirstHeader);
    421             }
    422             getListView().setSelectionFromTop(0, 0);
    423         }
    424     }
    425 
    426     private void highlightHeader(int id) {
    427         if (id != 0) {
    428             Integer index = mHeaderIndexMap.get(id);
    429             if (index != null) {
    430                 getListView().setItemChecked(index, true);
    431                 if (isMultiPane()) {
    432                     getListView().smoothScrollToPosition(index);
    433                 }
    434             }
    435         }
    436     }
    437 
    438     @Override
    439     public Intent getIntent() {
    440         Intent superIntent = super.getIntent();
    441         String startingFragment = getStartingFragmentClass(superIntent);
    442         // This is called from super.onCreate, isMultiPane() is not yet reliable
    443         // Do not use onIsHidingHeaders either, which relies itself on this method
    444         if (startingFragment != null && !onIsMultiPane()) {
    445             Intent modIntent = new Intent(superIntent);
    446             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
    447             Bundle args = superIntent.getExtras();
    448             if (args != null) {
    449                 args = new Bundle(args);
    450             } else {
    451                 args = new Bundle();
    452             }
    453             args.putParcelable("intent", superIntent);
    454             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
    455             return modIntent;
    456         }
    457         return superIntent;
    458     }
    459 
    460     /**
    461      * Checks if the component name in the intent is different from the Settings class and
    462      * returns the class name to load as a fragment.
    463      */
    464     protected String getStartingFragmentClass(Intent intent) {
    465         if (mFragmentClass != null) return mFragmentClass;
    466 
    467         String intentClass = intent.getComponent().getClassName();
    468         if (intentClass.equals(getClass().getName())) return null;
    469 
    470         if ("com.android.settings.ManageApplications".equals(intentClass)
    471                 || "com.android.settings.RunningServices".equals(intentClass)
    472                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
    473             // Old names of manage apps.
    474             intentClass = com.android.settings.applications.ManageApplications.class.getName();
    475         }
    476 
    477         return intentClass;
    478     }
    479 
    480     /**
    481      * Override initial header when an activity-alias is causing Settings to be launched
    482      * for a specific fragment encoded in the android:name parameter.
    483      */
    484     @Override
    485     public Header onGetInitialHeader() {
    486         String fragmentClass = getStartingFragmentClass(super.getIntent());
    487         if (fragmentClass != null) {
    488             Header header = new Header();
    489             header.fragment = fragmentClass;
    490             header.title = getTitle();
    491             header.fragmentArguments = getIntent().getExtras();
    492             mCurrentHeader = header;
    493             return header;
    494         }
    495 
    496         return mFirstHeader;
    497     }
    498 
    499     @Override
    500     public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
    501             int titleRes, int shortTitleRes) {
    502         Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
    503                 titleRes, shortTitleRes);
    504 
    505         // Some fragments want split ActionBar; these should stay in sync with
    506         // uiOptions for fragments also defined as activities in manifest.
    507         if (WifiSettings.class.getName().equals(fragmentName) ||
    508                 WifiP2pSettings.class.getName().equals(fragmentName) ||
    509                 WifiDisplaySettings.class.getName().equals(fragmentName) ||
    510                 BluetoothSettings.class.getName().equals(fragmentName) ||
    511                 DreamSettings.class.getName().equals(fragmentName) ||
    512                 LocationSettings.class.getName().equals(fragmentName) ||
    513                 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
    514                 PrintSettingsFragment.class.getName().equals(fragmentName) ||
    515                 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
    516             intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
    517         }
    518 
    519         intent.setClass(this, SubSettings.class);
    520         return intent;
    521     }
    522 
    523     /**
    524      * Populate the activity with the top-level headers.
    525      */
    526     @Override
    527     public void onBuildHeaders(List<Header> headers) {
    528         if (!onIsHidingHeaders()) {
    529             loadHeadersFromResource(R.xml.settings_headers, headers);
    530             updateHeaderList(headers);
    531         }
    532     }
    533 
    534     private void updateHeaderList(List<Header> target) {
    535         final boolean showDev = mDevelopmentPreferences.getBoolean(
    536                 DevelopmentSettings.PREF_SHOW,
    537                 android.os.Build.TYPE.equals("eng"));
    538         int i = 0;
    539 
    540         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
    541         mHeaderIndexMap.clear();
    542         while (i < target.size()) {
    543             Header header = target.get(i);
    544             // Ids are integers, so downcasting
    545             int id = (int) header.id;
    546             if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
    547                 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
    548             } else if (id == R.id.wifi_settings) {
    549                 // Remove WiFi Settings if WiFi service is not available.
    550                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
    551                     target.remove(i);
    552                 }
    553             } else if (id == R.id.bluetooth_settings) {
    554                 // Remove Bluetooth Settings if Bluetooth service is not available.
    555                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
    556                     target.remove(i);
    557                 }
    558             } else if (id == R.id.data_usage_settings) {
    559                 // Remove data usage when kernel module not enabled
    560                 final INetworkManagementService netManager = INetworkManagementService.Stub
    561                         .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
    562                 try {
    563                     if (!netManager.isBandwidthControlEnabled()) {
    564                         target.remove(i);
    565                     }
    566                 } catch (RemoteException e) {
    567                     // ignored
    568                 }
    569             } else if (id == R.id.battery_settings) {
    570                 // Remove battery settings when battery is not available. (e.g. TV)
    571 
    572                 if (!mBatteryPresent) {
    573                     target.remove(i);
    574                 }
    575             } else if (id == R.id.account_settings) {
    576                 int headerIndex = i + 1;
    577                 i = insertAccountsHeaders(target, headerIndex);
    578             } else if (id == R.id.home_settings) {
    579                 if (!updateHomeSettingHeaders(header)) {
    580                     target.remove(i);
    581                 }
    582             } else if (id == R.id.user_settings) {
    583                 if (!UserHandle.MU_ENABLED
    584                         || !UserManager.supportsMultipleUsers()
    585                         || Utils.isMonkeyRunning()) {
    586                     target.remove(i);
    587                 }
    588             } else if (id == R.id.nfc_payment_settings) {
    589                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
    590                     target.remove(i);
    591                 } else {
    592                     // Only show if NFC is on and we have the HCE feature
    593                     NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
    594                     if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
    595                             PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
    596                         target.remove(i);
    597                     }
    598                 }
    599             } else if (id == R.id.development_settings) {
    600                 if (!showDev) {
    601                     target.remove(i);
    602                 }
    603             } else if (id == R.id.account_add) {
    604                 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
    605                     target.remove(i);
    606                 }
    607             }
    608 
    609             if (i < target.size() && target.get(i) == header
    610                     && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
    611                     && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
    612                 target.remove(i);
    613             }
    614 
    615             // Increment if the current one wasn't removed by the Utils code.
    616             if (i < target.size() && target.get(i) == header) {
    617                 // Hold on to the first header, when we need to reset to the top-level
    618                 if (mFirstHeader == null &&
    619                         HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
    620                     mFirstHeader = header;
    621                 }
    622                 mHeaderIndexMap.put(id, i);
    623                 i++;
    624             }
    625         }
    626     }
    627 
    628     private int insertAccountsHeaders(List<Header> target, int headerIndex) {
    629         String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
    630         List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
    631         for (String accountType : accountTypes) {
    632             CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
    633             if (label == null) {
    634                 continue;
    635             }
    636 
    637             Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
    638             boolean skipToAccount = accounts.length == 1
    639                     && !mAuthenticatorHelper.hasAccountPreferences(accountType);
    640             Header accHeader = new Header();
    641             accHeader.title = label;
    642             if (accHeader.extras == null) {
    643                 accHeader.extras = new Bundle();
    644             }
    645             if (skipToAccount) {
    646                 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
    647                 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
    648                 accHeader.fragment = AccountSyncSettings.class.getName();
    649                 accHeader.fragmentArguments = new Bundle();
    650                 // Need this for the icon
    651                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
    652                 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
    653                 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
    654                         accounts[0]);
    655             } else {
    656                 accHeader.breadCrumbTitle = label;
    657                 accHeader.breadCrumbShortTitle = label;
    658                 accHeader.fragment = ManageAccountsSettings.class.getName();
    659                 accHeader.fragmentArguments = new Bundle();
    660                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
    661                 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
    662                         accountType);
    663                 if (!isMultiPane()) {
    664                     accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
    665                             label.toString());
    666                 }
    667             }
    668             accountHeaders.add(accHeader);
    669             mAuthenticatorHelper.preloadDrawableForType(this, accountType);
    670         }
    671 
    672         // Sort by label
    673         Collections.sort(accountHeaders, new Comparator<Header>() {
    674             @Override
    675             public int compare(Header h1, Header h2) {
    676                 return h1.title.toString().compareTo(h2.title.toString());
    677             }
    678         });
    679 
    680         for (Header header : accountHeaders) {
    681             target.add(headerIndex++, header);
    682         }
    683         if (!mListeningToAccountUpdates) {
    684             AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
    685             mListeningToAccountUpdates = true;
    686         }
    687         return headerIndex;
    688     }
    689 
    690     private boolean updateHomeSettingHeaders(Header header) {
    691         // Once we decide to show Home settings, keep showing it forever
    692         SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
    693         if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
    694             return true;
    695         }
    696 
    697         try {
    698             final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
    699             getPackageManager().getHomeActivities(homeApps);
    700             if (homeApps.size() < 2) {
    701                 // When there's only one available home app, omit this settings
    702                 // category entirely at the top level UI.  If the user just
    703                 // uninstalled the penultimate home app candidiate, we also
    704                 // now tell them about why they aren't seeing 'Home' in the list.
    705                 if (sShowNoHomeNotice) {
    706                     sShowNoHomeNotice = false;
    707                     NoHomeDialogFragment.show(this);
    708                 }
    709                 return false;
    710             } else {
    711                 // Okay, we're allowing the Home settings category.  Tell it, when
    712                 // invoked via this front door, that we'll need to be told about the
    713                 // case when the user uninstalls all but one home app.
    714                 if (header.fragmentArguments == null) {
    715                     header.fragmentArguments = new Bundle();
    716                 }
    717                 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
    718             }
    719         } catch (Exception e) {
    720             // Can't look up the home activity; bail on configuring the icon
    721             Log.w(LOG_TAG, "Problem looking up home activity!", e);
    722         }
    723 
    724         sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
    725         return true;
    726     }
    727 
    728     private void getMetaData() {
    729         try {
    730             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
    731                     PackageManager.GET_META_DATA);
    732             if (ai == null || ai.metaData == null) return;
    733             mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
    734             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
    735 
    736             // Check if it has a parent specified and create a Header object
    737             final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
    738             String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
    739             if (parentFragmentClass != null) {
    740                 mParentHeader = new Header();
    741                 mParentHeader.fragment = parentFragmentClass;
    742                 if (parentHeaderTitleRes != 0) {
    743                     mParentHeader.title = getResources().getString(parentHeaderTitleRes);
    744                 }
    745             }
    746         } catch (NameNotFoundException nnfe) {
    747             // No recovery
    748         }
    749     }
    750 
    751     @Override
    752     public boolean hasNextButton() {
    753         return super.hasNextButton();
    754     }
    755 
    756     @Override
    757     public Button getNextButton() {
    758         return super.getNextButton();
    759     }
    760 
    761     public static class NoHomeDialogFragment extends DialogFragment {
    762         public static void show(Activity parent) {
    763             final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
    764             dialog.show(parent.getFragmentManager(), null);
    765         }
    766 
    767         @Override
    768         public Dialog onCreateDialog(Bundle savedInstanceState) {
    769             return new AlertDialog.Builder(getActivity())
    770                     .setMessage(R.string.only_one_home_message)
    771                     .setPositiveButton(android.R.string.ok, null)
    772                     .create();
    773         }
    774     }
    775 
    776     private static class HeaderAdapter extends ArrayAdapter<Header> {
    777         static final int HEADER_TYPE_CATEGORY = 0;
    778         static final int HEADER_TYPE_NORMAL = 1;
    779         static final int HEADER_TYPE_SWITCH = 2;
    780         static final int HEADER_TYPE_BUTTON = 3;
    781         private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
    782 
    783         private final WifiEnabler mWifiEnabler;
    784         private final BluetoothEnabler mBluetoothEnabler;
    785         private AuthenticatorHelper mAuthHelper;
    786         private DevicePolicyManager mDevicePolicyManager;
    787 
    788         private static class HeaderViewHolder {
    789             ImageView icon;
    790             TextView title;
    791             TextView summary;
    792             Switch switch_;
    793             ImageButton button_;
    794             View divider_;
    795         }
    796 
    797         private LayoutInflater mInflater;
    798 
    799         static int getHeaderType(Header header) {
    800             if (header.fragment == null && header.intent == null) {
    801                 return HEADER_TYPE_CATEGORY;
    802             } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
    803                 return HEADER_TYPE_SWITCH;
    804             } else if (header.id == R.id.security_settings) {
    805                 return HEADER_TYPE_BUTTON;
    806             } else {
    807                 return HEADER_TYPE_NORMAL;
    808             }
    809         }
    810 
    811         @Override
    812         public int getItemViewType(int position) {
    813             Header header = getItem(position);
    814             return getHeaderType(header);
    815         }
    816 
    817         @Override
    818         public boolean areAllItemsEnabled() {
    819             return false; // because of categories
    820         }
    821 
    822         @Override
    823         public boolean isEnabled(int position) {
    824             return getItemViewType(position) != HEADER_TYPE_CATEGORY;
    825         }
    826 
    827         @Override
    828         public int getViewTypeCount() {
    829             return HEADER_TYPE_COUNT;
    830         }
    831 
    832         @Override
    833         public boolean hasStableIds() {
    834             return true;
    835         }
    836 
    837         public HeaderAdapter(Context context, List<Header> objects,
    838                 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
    839             super(context, 0, objects);
    840 
    841             mAuthHelper = authenticatorHelper;
    842             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    843 
    844             // Temp Switches provided as placeholder until the adapter replaces these with actual
    845             // Switches inflated from their layouts. Must be done before adapter is set in super
    846             mWifiEnabler = new WifiEnabler(context, new Switch(context));
    847             mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
    848             mDevicePolicyManager = dpm;
    849         }
    850 
    851         @Override
    852         public View getView(int position, View convertView, ViewGroup parent) {
    853             HeaderViewHolder holder;
    854             Header header = getItem(position);
    855             int headerType = getHeaderType(header);
    856             View view = null;
    857 
    858             if (convertView == null) {
    859                 holder = new HeaderViewHolder();
    860                 switch (headerType) {
    861                     case HEADER_TYPE_CATEGORY:
    862                         view = new TextView(getContext(), null,
    863                                 android.R.attr.listSeparatorTextViewStyle);
    864                         holder.title = (TextView) view;
    865                         break;
    866 
    867                     case HEADER_TYPE_SWITCH:
    868                         view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
    869                                 false);
    870                         holder.icon = (ImageView) view.findViewById(R.id.icon);
    871                         holder.title = (TextView)
    872                                 view.findViewById(com.android.internal.R.id.title);
    873                         holder.summary = (TextView)
    874                                 view.findViewById(com.android.internal.R.id.summary);
    875                         holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
    876                         break;
    877 
    878                     case HEADER_TYPE_BUTTON:
    879                         view = mInflater.inflate(R.layout.preference_header_button_item, parent,
    880                                 false);
    881                         holder.icon = (ImageView) view.findViewById(R.id.icon);
    882                         holder.title = (TextView)
    883                                 view.findViewById(com.android.internal.R.id.title);
    884                         holder.summary = (TextView)
    885                                 view.findViewById(com.android.internal.R.id.summary);
    886                         holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
    887                         holder.divider_ = view.findViewById(R.id.divider);
    888                         break;
    889 
    890                     case HEADER_TYPE_NORMAL:
    891                         view = mInflater.inflate(
    892                                 R.layout.preference_header_item, parent,
    893                                 false);
    894                         holder.icon = (ImageView) view.findViewById(R.id.icon);
    895                         holder.title = (TextView)
    896                                 view.findViewById(com.android.internal.R.id.title);
    897                         holder.summary = (TextView)
    898                                 view.findViewById(com.android.internal.R.id.summary);
    899                         break;
    900                 }
    901                 view.setTag(holder);
    902             } else {
    903                 view = convertView;
    904                 holder = (HeaderViewHolder) view.getTag();
    905             }
    906 
    907             // All view fields must be updated every time, because the view may be recycled
    908             switch (headerType) {
    909                 case HEADER_TYPE_CATEGORY:
    910                     holder.title.setText(header.getTitle(getContext().getResources()));
    911                     break;
    912 
    913                 case HEADER_TYPE_SWITCH:
    914                     // Would need a different treatment if the main menu had more switches
    915                     if (header.id == R.id.wifi_settings) {
    916                         mWifiEnabler.setSwitch(holder.switch_);
    917                     } else {
    918                         mBluetoothEnabler.setSwitch(holder.switch_);
    919                     }
    920                     updateCommonHeaderView(header, holder);
    921                     break;
    922 
    923                 case HEADER_TYPE_BUTTON:
    924                     if (header.id == R.id.security_settings) {
    925                         boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
    926                         if (hasCert) {
    927                             holder.button_.setVisibility(View.VISIBLE);
    928                             holder.divider_.setVisibility(View.VISIBLE);
    929                             boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
    930                             if (isManaged) {
    931                                 holder.button_.setImageResource(R.drawable.ic_settings_about);
    932                             } else {
    933                                 holder.button_.setImageResource(
    934                                         android.R.drawable.stat_notify_error);
    935                             }
    936                             holder.button_.setOnClickListener(new OnClickListener() {
    937                                 @Override
    938                                 public void onClick(View v) {
    939                                     Intent intent = new Intent(
    940                                             android.provider.Settings.ACTION_MONITORING_CERT_INFO);
    941                                     getContext().startActivity(intent);
    942                                 }
    943                             });
    944                         } else {
    945                             holder.button_.setVisibility(View.GONE);
    946                             holder.divider_.setVisibility(View.GONE);
    947                         }
    948                     }
    949                     updateCommonHeaderView(header, holder);
    950                     break;
    951 
    952                 case HEADER_TYPE_NORMAL:
    953                     updateCommonHeaderView(header, holder);
    954                     break;
    955             }
    956 
    957             return view;
    958         }
    959 
    960         private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
    961                 if (header.extras != null
    962                         && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
    963                     String accType = header.extras.getString(
    964                             ManageAccountsSettings.KEY_ACCOUNT_TYPE);
    965                     Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
    966                     setHeaderIcon(holder, icon);
    967                 } else {
    968                     holder.icon.setImageResource(header.iconRes);
    969                 }
    970                 holder.title.setText(header.getTitle(getContext().getResources()));
    971                 CharSequence summary = header.getSummary(getContext().getResources());
    972                 if (!TextUtils.isEmpty(summary)) {
    973                     holder.summary.setVisibility(View.VISIBLE);
    974                     holder.summary.setText(summary);
    975                 } else {
    976                     holder.summary.setVisibility(View.GONE);
    977                 }
    978             }
    979 
    980         private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
    981             ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
    982             lp.width = getContext().getResources().getDimensionPixelSize(
    983                     R.dimen.header_icon_width);
    984             lp.height = lp.width;
    985             holder.icon.setLayoutParams(lp);
    986             holder.icon.setImageDrawable(icon);
    987         }
    988 
    989         public void resume() {
    990             mWifiEnabler.resume();
    991             mBluetoothEnabler.resume();
    992         }
    993 
    994         public void pause() {
    995             mWifiEnabler.pause();
    996             mBluetoothEnabler.pause();
    997         }
    998     }
    999 
   1000     @Override
   1001     public void onHeaderClick(Header header, int position) {
   1002         boolean revert = false;
   1003         if (header.id == R.id.account_add) {
   1004             revert = true;
   1005         }
   1006 
   1007         super.onHeaderClick(header, position);
   1008 
   1009         if (revert && mLastHeader != null) {
   1010             highlightHeader((int) mLastHeader.id);
   1011         } else {
   1012             mLastHeader = header;
   1013         }
   1014     }
   1015 
   1016     @Override
   1017     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
   1018         // Override the fragment title for Wallpaper settings
   1019         int titleRes = pref.getTitleRes();
   1020         if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
   1021             titleRes = R.string.wallpaper_settings_fragment_title;
   1022         } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
   1023                 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
   1024             if (UserManager.get(this).isLinkedUser()) {
   1025                 titleRes = R.string.profile_info_settings_title;
   1026             } else {
   1027                 titleRes = R.string.user_info_settings_title;
   1028             }
   1029         }
   1030         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
   1031                 null, 0);
   1032         return true;
   1033     }
   1034 
   1035     @Override
   1036     public boolean shouldUpRecreateTask(Intent targetIntent) {
   1037         return super.shouldUpRecreateTask(new Intent(this, Settings.class));
   1038     }
   1039 
   1040     @Override
   1041     public void setListAdapter(ListAdapter adapter) {
   1042         if (adapter == null) {
   1043             super.setListAdapter(null);
   1044         } else {
   1045             DevicePolicyManager dpm =
   1046                     (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
   1047             super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
   1048         }
   1049     }
   1050 
   1051     @Override
   1052     public void onAccountsUpdated(Account[] accounts) {
   1053         // TODO: watch for package upgrades to invalidate cache; see 7206643
   1054         mAuthenticatorHelper.updateAuthDescriptions(this);
   1055         mAuthenticatorHelper.onAccountsUpdated(this, accounts);
   1056         invalidateHeaders();
   1057     }
   1058 
   1059     public static void requestHomeNotice() {
   1060         sShowNoHomeNotice = true;
   1061     }
   1062 
   1063     /*
   1064      * Settings subclasses for launching independently.
   1065      */
   1066     public static class BluetoothSettingsActivity extends Settings { /* empty */ }
   1067     public static class WirelessSettingsActivity extends Settings { /* empty */ }
   1068     public static class TetherSettingsActivity extends Settings { /* empty */ }
   1069     public static class VpnSettingsActivity extends Settings { /* empty */ }
   1070     public static class DateTimeSettingsActivity extends Settings { /* empty */ }
   1071     public static class StorageSettingsActivity extends Settings { /* empty */ }
   1072     public static class WifiSettingsActivity extends Settings { /* empty */ }
   1073     public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
   1074     public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
   1075     public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
   1076     public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
   1077     public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
   1078     public static class LocalePickerActivity extends Settings { /* empty */ }
   1079     public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
   1080     public static class SoundSettingsActivity extends Settings { /* empty */ }
   1081     public static class DisplaySettingsActivity extends Settings { /* empty */ }
   1082     public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
   1083     public static class ApplicationSettingsActivity extends Settings { /* empty */ }
   1084     public static class ManageApplicationsActivity extends Settings { /* empty */ }
   1085     public static class AppOpsSummaryActivity extends Settings { /* empty */ }
   1086     public static class StorageUseActivity extends Settings { /* empty */ }
   1087     public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
   1088     public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
   1089     public static class CaptioningSettingsActivity extends Settings { /* empty */ }
   1090     public static class SecuritySettingsActivity extends Settings { /* empty */ }
   1091     public static class LocationSettingsActivity extends Settings { /* empty */ }
   1092     public static class PrivacySettingsActivity extends Settings { /* empty */ }
   1093     public static class RunningServicesActivity extends Settings { /* empty */ }
   1094     public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
   1095     public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
   1096     public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
   1097     public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
   1098     public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
   1099     public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
   1100     public static class DataUsageSummaryActivity extends Settings { /* empty */ }
   1101     public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
   1102     public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
   1103     public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
   1104     public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
   1105     public static class DreamSettingsActivity extends Settings { /* empty */ }
   1106     public static class NotificationStationActivity extends Settings { /* empty */ }
   1107     public static class UserSettingsActivity extends Settings { /* empty */ }
   1108     public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
   1109     public static class UsbSettingsActivity extends Settings { /* empty */ }
   1110     public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
   1111     public static class PaymentSettingsActivity extends Settings { /* empty */ }
   1112     public static class PrintSettingsActivity extends Settings { /* empty */ }
   1113     public static class PrintJobSettingsActivity extends Settings { /* empty */ }
   1114 }
   1115