Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2014 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 static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
     20 
     21 import android.app.ActionBar;
     22 import android.app.Activity;
     23 import android.app.Fragment;
     24 import android.app.FragmentManager;
     25 import android.app.FragmentTransaction;
     26 import android.content.BroadcastReceiver;
     27 import android.content.ComponentName;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.SharedPreferences;
     32 import android.content.pm.ActivityInfo;
     33 import android.content.pm.PackageManager;
     34 import android.content.pm.PackageManager.NameNotFoundException;
     35 import android.content.pm.ResolveInfo;
     36 import android.content.res.Configuration;
     37 import android.content.res.TypedArray;
     38 import android.content.res.XmlResourceParser;
     39 import android.nfc.NfcAdapter;
     40 import android.os.Bundle;
     41 import android.os.Handler;
     42 import android.os.Message;
     43 import android.os.UserHandle;
     44 import android.os.UserManager;
     45 import android.preference.Preference;
     46 import android.preference.PreferenceFragment;
     47 import android.preference.PreferenceManager;
     48 import android.preference.PreferenceScreen;
     49 import android.text.TextUtils;
     50 import android.transition.TransitionManager;
     51 import android.util.ArrayMap;
     52 import android.util.AttributeSet;
     53 import android.util.Log;
     54 import android.util.Pair;
     55 import android.util.TypedValue;
     56 import android.util.Xml;
     57 import android.view.Menu;
     58 import android.view.MenuInflater;
     59 import android.view.MenuItem;
     60 import android.view.View;
     61 import android.view.View.OnClickListener;
     62 import android.view.ViewGroup;
     63 import android.widget.Button;
     64 import android.widget.SearchView;
     65 
     66 import com.android.internal.logging.MetricsLogger;
     67 import com.android.internal.util.ArrayUtils;
     68 import com.android.internal.util.XmlUtils;
     69 import com.android.settings.accessibility.AccessibilitySettings;
     70 import com.android.settings.accessibility.CaptionPropertiesFragment;
     71 import com.android.settings.accounts.AccountSettings;
     72 import com.android.settings.accounts.AccountSyncSettings;
     73 import com.android.settings.applications.DrawOverlayDetails;
     74 import com.android.settings.applications.InstalledAppDetails;
     75 import com.android.settings.applications.ManageApplications;
     76 import com.android.settings.applications.ManageAssist;
     77 import com.android.settings.applications.ProcessStatsSummary;
     78 import com.android.settings.applications.ProcessStatsUi;
     79 import com.android.settings.applications.UsageAccessDetails;
     80 import com.android.settings.applications.WriteSettingsDetails;
     81 import com.android.settings.bluetooth.BluetoothSettings;
     82 import com.android.settings.dashboard.DashboardCategory;
     83 import com.android.settings.dashboard.DashboardSummary;
     84 import com.android.settings.dashboard.DashboardTile;
     85 import com.android.settings.dashboard.NoHomeDialogFragment;
     86 import com.android.settings.dashboard.SearchResultsSummary;
     87 import com.android.settings.deviceinfo.PrivateVolumeForget;
     88 import com.android.settings.deviceinfo.PrivateVolumeSettings;
     89 import com.android.settings.deviceinfo.PublicVolumeSettings;
     90 import com.android.settings.deviceinfo.StorageSettings;
     91 import com.android.settings.fuelgauge.BatterySaverSettings;
     92 import com.android.settings.fuelgauge.PowerUsageDetail;
     93 import com.android.settings.fuelgauge.PowerUsageSummary;
     94 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
     95 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
     96 import com.android.settings.inputmethod.SpellCheckersSettings;
     97 import com.android.settings.inputmethod.UserDictionaryList;
     98 import com.android.settings.location.LocationSettings;
     99 import com.android.settings.nfc.AndroidBeam;
    100 import com.android.settings.nfc.PaymentSettings;
    101 import com.android.settings.notification.AppNotificationSettings;
    102 import com.android.settings.notification.NotificationAccessSettings;
    103 import com.android.settings.notification.NotificationSettings;
    104 import com.android.settings.notification.NotificationStation;
    105 import com.android.settings.notification.OtherSoundSettings;
    106 import com.android.settings.notification.ZenAccessSettings;
    107 import com.android.settings.notification.ZenModeAutomationSettings;
    108 import com.android.settings.notification.ZenModeEventRuleSettings;
    109 import com.android.settings.notification.ZenModeExternalRuleSettings;
    110 import com.android.settings.notification.ZenModePrioritySettings;
    111 import com.android.settings.notification.ZenModeSettings;
    112 import com.android.settings.notification.ZenModeScheduleRuleSettings;
    113 import com.android.settings.print.PrintJobSettingsFragment;
    114 import com.android.settings.print.PrintSettingsFragment;
    115 import com.android.settings.search.DynamicIndexableContentMonitor;
    116 import com.android.settings.search.Index;
    117 import com.android.settings.sim.SimSettings;
    118 import com.android.settings.tts.TextToSpeechSettings;
    119 import com.android.settings.users.UserSettings;
    120 import com.android.settings.vpn2.VpnSettings;
    121 import com.android.settings.wfd.WifiDisplaySettings;
    122 import com.android.settings.widget.SwitchBar;
    123 import com.android.settings.wifi.AdvancedWifiSettings;
    124 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
    125 import com.android.settings.wifi.WifiSettings;
    126 import com.android.settings.wifi.p2p.WifiP2pSettings;
    127 
    128 import org.xmlpull.v1.XmlPullParser;
    129 import org.xmlpull.v1.XmlPullParserException;
    130 
    131 import java.io.IOException;
    132 import java.util.ArrayList;
    133 import java.util.List;
    134 import java.util.Map;
    135 import java.util.Set;
    136 
    137 public class SettingsActivity extends Activity
    138         implements PreferenceManager.OnPreferenceTreeClickListener,
    139         PreferenceFragment.OnPreferenceStartFragmentCallback,
    140         ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
    141         SearchView.OnQueryTextListener, SearchView.OnCloseListener,
    142         MenuItem.OnActionExpandListener {
    143 
    144     private static final String LOG_TAG = "Settings";
    145 
    146     // Constants for state save/restore
    147     private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
    148     private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
    149     private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
    150     private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
    151     private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
    152     private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
    153 
    154     /**
    155      * When starting this activity, the invoking Intent can contain this extra
    156      * string to specify which fragment should be initially displayed.
    157      * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
    158      * will call isValidFragment() to confirm that the fragment class name is valid for this
    159      * activity.
    160      */
    161     public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
    162 
    163     /**
    164      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
    165      * this extra can also be specified to supply a Bundle of arguments to pass
    166      * to that fragment when it is instantiated during the initial creation
    167      * of the activity.
    168      */
    169     public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
    170 
    171     /**
    172      * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
    173      */
    174     public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
    175 
    176     public static final String BACK_STACK_PREFS = ":settings:prefs";
    177 
    178     // extras that allow any preference activity to be launched as part of a wizard
    179 
    180     // show Back and Next buttons? takes boolean parameter
    181     // Back will then return RESULT_CANCELED and Next RESULT_OK
    182     protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
    183 
    184     // add a Skip button?
    185     private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
    186 
    187     // specify custom text for the Back or Next buttons, or cause a button to not appear
    188     // at all by setting it to null
    189     protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
    190     protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
    191 
    192     /**
    193      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
    194      * those extra can also be specify to supply the title or title res id to be shown for
    195      * that fragment.
    196      */
    197     public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
    198     /**
    199      * The package name used to resolve the title resource id.
    200      */
    201     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
    202             ":settings:show_fragment_title_res_package_name";
    203     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
    204             ":settings:show_fragment_title_resid";
    205     public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
    206             ":settings:show_fragment_as_shortcut";
    207 
    208     public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
    209             ":settings:show_fragment_as_subsetting";
    210 
    211     private static final String META_DATA_KEY_FRAGMENT_CLASS =
    212         "com.android.settings.FRAGMENT_CLASS";
    213 
    214     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
    215 
    216     private static final String EMPTY_QUERY = "";
    217 
    218     /**
    219      * Settings will search for system activities of this action and add them as a top level
    220      * settings tile using the following parameters.
    221      *
    222      * <p>A category must be specified in the meta-data for the activity named
    223      * {@link #EXTRA_CATEGORY_KEY}
    224      *
    225      * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
    226      * otherwise the label for the activity will be used.
    227      *
    228      * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
    229      * otherwise the icon for the activity will be used.
    230      *
    231      * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
    232      */
    233     private static final String EXTRA_SETTINGS_ACTION =
    234             "com.android.settings.action.EXTRA_SETTINGS";
    235 
    236     /**
    237      * The key used to get the category from metadata of activities of action
    238      * {@link #EXTRA_SETTINGS_ACTION}
    239      * The value must be one of:
    240      * <li>com.android.settings.category.wireless</li>
    241      * <li>com.android.settings.category.device</li>
    242      * <li>com.android.settings.category.personal</li>
    243      * <li>com.android.settings.category.system</li>
    244      */
    245     private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
    246 
    247     private static boolean sShowNoHomeNotice = false;
    248 
    249     private String mFragmentClass;
    250 
    251     private CharSequence mInitialTitle;
    252     private int mInitialTitleResId;
    253 
    254     // Show only these settings for restricted users
    255     private int[] SETTINGS_FOR_RESTRICTED = {
    256             R.id.wireless_section,
    257             R.id.wifi_settings,
    258             R.id.bluetooth_settings,
    259             R.id.data_usage_settings,
    260             R.id.sim_settings,
    261             R.id.wireless_settings,
    262             R.id.device_section,
    263             R.id.notification_settings,
    264             R.id.display_settings,
    265             R.id.storage_settings,
    266             R.id.application_settings,
    267             R.id.battery_settings,
    268             R.id.personal_section,
    269             R.id.location_settings,
    270             R.id.security_settings,
    271             R.id.language_settings,
    272             R.id.user_settings,
    273             R.id.account_settings,
    274             R.id.system_section,
    275             R.id.date_time_settings,
    276             R.id.about_settings,
    277             R.id.accessibility_settings,
    278             R.id.print_settings,
    279             R.id.nfc_payment_settings,
    280             R.id.home_settings,
    281             R.id.dashboard
    282     };
    283 
    284     private static final String[] ENTRY_FRAGMENTS = {
    285             WirelessSettings.class.getName(),
    286             WifiSettings.class.getName(),
    287             AdvancedWifiSettings.class.getName(),
    288             SavedAccessPointsWifiSettings.class.getName(),
    289             BluetoothSettings.class.getName(),
    290             SimSettings.class.getName(),
    291             TetherSettings.class.getName(),
    292             WifiP2pSettings.class.getName(),
    293             VpnSettings.class.getName(),
    294             DateTimeSettings.class.getName(),
    295             LocalePicker.class.getName(),
    296             InputMethodAndLanguageSettings.class.getName(),
    297             SpellCheckersSettings.class.getName(),
    298             UserDictionaryList.class.getName(),
    299             UserDictionarySettings.class.getName(),
    300             HomeSettings.class.getName(),
    301             DisplaySettings.class.getName(),
    302             DeviceInfoSettings.class.getName(),
    303             ManageApplications.class.getName(),
    304             ManageAssist.class.getName(),
    305             ProcessStatsUi.class.getName(),
    306             NotificationStation.class.getName(),
    307             LocationSettings.class.getName(),
    308             SecuritySettings.class.getName(),
    309             UsageAccessDetails.class.getName(),
    310             PrivacySettings.class.getName(),
    311             DeviceAdminSettings.class.getName(),
    312             AccessibilitySettings.class.getName(),
    313             CaptionPropertiesFragment.class.getName(),
    314             com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
    315             TextToSpeechSettings.class.getName(),
    316             StorageSettings.class.getName(),
    317             PrivateVolumeForget.class.getName(),
    318             PrivateVolumeSettings.class.getName(),
    319             PublicVolumeSettings.class.getName(),
    320             DevelopmentSettings.class.getName(),
    321             AndroidBeam.class.getName(),
    322             WifiDisplaySettings.class.getName(),
    323             PowerUsageSummary.class.getName(),
    324             AccountSyncSettings.class.getName(),
    325             AccountSettings.class.getName(),
    326             CryptKeeperSettings.class.getName(),
    327             DataUsageSummary.class.getName(),
    328             DreamSettings.class.getName(),
    329             UserSettings.class.getName(),
    330             NotificationAccessSettings.class.getName(),
    331             ZenAccessSettings.class.getName(),
    332             PrintSettingsFragment.class.getName(),
    333             PrintJobSettingsFragment.class.getName(),
    334             TrustedCredentialsSettings.class.getName(),
    335             PaymentSettings.class.getName(),
    336             KeyboardLayoutPickerFragment.class.getName(),
    337             ZenModeSettings.class.getName(),
    338             NotificationSettings.class.getName(),
    339             ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
    340             ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
    341             InstalledAppDetails.class.getName(),
    342             BatterySaverSettings.class.getName(),
    343             AppNotificationSettings.class.getName(),
    344             OtherSoundSettings.class.getName(),
    345             ApnSettings.class.getName(),
    346             WifiCallingSettings.class.getName(),
    347             ZenModePrioritySettings.class.getName(),
    348             ZenModeAutomationSettings.class.getName(),
    349             ZenModeScheduleRuleSettings.class.getName(),
    350             ZenModeEventRuleSettings.class.getName(),
    351             ZenModeExternalRuleSettings.class.getName(),
    352             ProcessStatsUi.class.getName(),
    353             PowerUsageDetail.class.getName(),
    354             ProcessStatsSummary.class.getName(),
    355             DrawOverlayDetails.class.getName(),
    356             WriteSettingsDetails.class.getName(),
    357     };
    358 
    359 
    360     private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
    361             "android.settings.APPLICATION_DETAILS_SETTINGS"
    362     };
    363 
    364     private SharedPreferences mDevelopmentPreferences;
    365     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
    366 
    367     private boolean mBatteryPresent = true;
    368     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
    369         @Override
    370         public void onReceive(Context context, Intent intent) {
    371             String action = intent.getAction();
    372             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
    373                 boolean batteryPresent = Utils.isBatteryPresent(intent);
    374 
    375                 if (mBatteryPresent != batteryPresent) {
    376                     mBatteryPresent = batteryPresent;
    377                     invalidateCategories(true);
    378                 }
    379             }
    380         }
    381     };
    382 
    383     private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
    384             new DynamicIndexableContentMonitor();
    385 
    386     private ActionBar mActionBar;
    387     private SwitchBar mSwitchBar;
    388 
    389     private Button mNextButton;
    390 
    391     private boolean mDisplayHomeAsUpEnabled;
    392     private boolean mDisplaySearch;
    393 
    394     private boolean mIsShowingDashboard;
    395     private boolean mIsShortcut;
    396 
    397     private ViewGroup mContent;
    398 
    399     private SearchView mSearchView;
    400     private MenuItem mSearchMenuItem;
    401     private boolean mSearchMenuItemExpanded = false;
    402     private SearchResultsSummary mSearchResultsFragment;
    403     private String mSearchQuery;
    404 
    405     // Categories
    406     private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
    407 
    408     private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
    409     private static final int MSG_BUILD_CATEGORIES = 1;
    410     private Handler mHandler = new Handler() {
    411         @Override
    412         public void handleMessage(Message msg) {
    413             switch (msg.what) {
    414                 case MSG_BUILD_CATEGORIES: {
    415                     final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
    416                     if (forceRefresh) {
    417                         buildDashboardCategories(mCategories);
    418                     }
    419                 } break;
    420             }
    421         }
    422     };
    423 
    424     private boolean mNeedToRevertToInitialFragment = false;
    425     private int mHomeActivitiesCount = 1;
    426 
    427     private Intent mResultIntentData;
    428 
    429     public SwitchBar getSwitchBar() {
    430         return mSwitchBar;
    431     }
    432 
    433     public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
    434         if (forceRefresh || mCategories.size() == 0) {
    435             buildDashboardCategories(mCategories);
    436         }
    437         return mCategories;
    438     }
    439 
    440     @Override
    441     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
    442         // Override the fragment title for Wallpaper settings
    443         int titleRes = pref.getTitleRes();
    444         if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
    445             titleRes = R.string.wallpaper_settings_fragment_title;
    446         } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
    447                 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
    448             if (UserManager.get(this).isLinkedUser()) {
    449                 titleRes = R.string.profile_info_settings_title;
    450             } else {
    451                 titleRes = R.string.user_info_settings_title;
    452             }
    453         }
    454         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
    455                 null, 0);
    456         return true;
    457     }
    458 
    459     @Override
    460     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    461         return false;
    462     }
    463 
    464     private void invalidateCategories(boolean forceRefresh) {
    465         if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
    466             Message msg = new Message();
    467             msg.what = MSG_BUILD_CATEGORIES;
    468             msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
    469         }
    470     }
    471 
    472     @Override
    473     public void onConfigurationChanged(Configuration newConfig) {
    474         super.onConfigurationChanged(newConfig);
    475         Index.getInstance(this).update();
    476     }
    477 
    478     @Override
    479     protected void onStart() {
    480         super.onStart();
    481 
    482         if (mNeedToRevertToInitialFragment) {
    483             revertToInitialFragment();
    484         }
    485     }
    486 
    487     @Override
    488     public boolean onCreateOptionsMenu(Menu menu) {
    489         if (!mDisplaySearch) {
    490             return false;
    491         }
    492 
    493         MenuInflater inflater = getMenuInflater();
    494         inflater.inflate(R.menu.options_menu, menu);
    495 
    496         // Cache the search query (can be overriden by the OnQueryTextListener)
    497         final String query = mSearchQuery;
    498 
    499         mSearchMenuItem = menu.findItem(R.id.search);
    500         mSearchView = (SearchView) mSearchMenuItem.getActionView();
    501 
    502         if (mSearchMenuItem == null || mSearchView == null) {
    503             return false;
    504         }
    505 
    506         if (mSearchResultsFragment != null) {
    507             mSearchResultsFragment.setSearchView(mSearchView);
    508         }
    509 
    510         mSearchMenuItem.setOnActionExpandListener(this);
    511         mSearchView.setOnQueryTextListener(this);
    512         mSearchView.setOnCloseListener(this);
    513 
    514         if (mSearchMenuItemExpanded) {
    515             mSearchMenuItem.expandActionView();
    516         }
    517         mSearchView.setQuery(query, true /* submit */);
    518 
    519         return true;
    520     }
    521 
    522     private static boolean isShortCutIntent(final Intent intent) {
    523         Set<String> categories = intent.getCategories();
    524         return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
    525     }
    526 
    527     private static boolean isLikeShortCutIntent(final Intent intent) {
    528         String action = intent.getAction();
    529         if (action == null) {
    530             return false;
    531         }
    532         for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
    533             if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
    534         }
    535         return false;
    536     }
    537 
    538     @Override
    539     protected void onCreate(Bundle savedState) {
    540         super.onCreate(savedState);
    541 
    542         // Should happen before any call to getIntent()
    543         getMetaData();
    544 
    545         final Intent intent = getIntent();
    546         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
    547             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
    548         }
    549 
    550         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
    551                 Context.MODE_PRIVATE);
    552 
    553         // Getting Intent properties can only be done after the super.onCreate(...)
    554         final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
    555 
    556         mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
    557                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
    558 
    559         final ComponentName cn = intent.getComponent();
    560         final String className = cn.getClassName();
    561 
    562         mIsShowingDashboard = className.equals(Settings.class.getName());
    563 
    564         // This is a "Sub Settings" when:
    565         // - this is a real SubSettings
    566         // - or :settings:show_fragment_as_subsetting is passed to the Intent
    567         final boolean isSubSettings = this instanceof SubSettings ||
    568                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
    569 
    570         // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
    571         if (isSubSettings) {
    572             // Check also that we are not a Theme Dialog as we don't want to override them
    573             final int themeResId = getThemeResId();
    574             if (themeResId != R.style.Theme_DialogWhenLarge &&
    575                     themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
    576                 setTheme(R.style.Theme_SubSettings);
    577             }
    578         }
    579 
    580         setContentView(mIsShowingDashboard ?
    581                 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
    582 
    583         mContent = (ViewGroup) findViewById(R.id.main_content);
    584 
    585         getFragmentManager().addOnBackStackChangedListener(this);
    586 
    587         if (mIsShowingDashboard) {
    588             // Run the Index update only if we have some space
    589             if (!Utils.isLowStorage(this)) {
    590                 Index.getInstance(getApplicationContext()).update();
    591             } else {
    592                 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
    593             }
    594         }
    595 
    596         if (savedState != null) {
    597             // We are restarting from a previous saved state; used that to initialize, instead
    598             // of starting fresh.
    599             mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
    600             mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
    601 
    602             setTitleFromIntent(intent);
    603 
    604             ArrayList<DashboardCategory> categories =
    605                     savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
    606             if (categories != null) {
    607                 mCategories.clear();
    608                 mCategories.addAll(categories);
    609                 setTitleFromBackStack();
    610             }
    611 
    612             mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
    613             mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
    614             mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
    615                     1 /* one home activity by default */);
    616         } else {
    617             if (!mIsShowingDashboard) {
    618                 mDisplaySearch = false;
    619                 // UP will be shown only if it is a sub settings
    620                 if (mIsShortcut) {
    621                     mDisplayHomeAsUpEnabled = isSubSettings;
    622                 } else if (isSubSettings) {
    623                     mDisplayHomeAsUpEnabled = true;
    624                 } else {
    625                     mDisplayHomeAsUpEnabled = false;
    626                 }
    627                 setTitleFromIntent(intent);
    628 
    629                 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
    630                 switchToFragment(initialFragmentName, initialArguments, true, false,
    631                         mInitialTitleResId, mInitialTitle, false);
    632             } else {
    633                 // No UP affordance if we are displaying the main Dashboard
    634                 mDisplayHomeAsUpEnabled = false;
    635                 // Show Search affordance
    636                 mDisplaySearch = true;
    637                 mInitialTitleResId = R.string.dashboard_title;
    638                 switchToFragment(DashboardSummary.class.getName(), null, false, false,
    639                         mInitialTitleResId, mInitialTitle, false);
    640             }
    641         }
    642 
    643         mActionBar = getActionBar();
    644         if (mActionBar != null) {
    645             mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
    646             mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
    647         }
    648         mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
    649 
    650         // see if we should show Back/Next buttons
    651         if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
    652 
    653             View buttonBar = findViewById(R.id.button_bar);
    654             if (buttonBar != null) {
    655                 buttonBar.setVisibility(View.VISIBLE);
    656 
    657                 Button backButton = (Button)findViewById(R.id.back_button);
    658                 backButton.setOnClickListener(new OnClickListener() {
    659                     public void onClick(View v) {
    660                         setResult(RESULT_CANCELED, getResultIntentData());
    661                         finish();
    662                     }
    663                 });
    664                 Button skipButton = (Button)findViewById(R.id.skip_button);
    665                 skipButton.setOnClickListener(new OnClickListener() {
    666                     public void onClick(View v) {
    667                         setResult(RESULT_OK, getResultIntentData());
    668                         finish();
    669                     }
    670                 });
    671                 mNextButton = (Button)findViewById(R.id.next_button);
    672                 mNextButton.setOnClickListener(new OnClickListener() {
    673                     public void onClick(View v) {
    674                         setResult(RESULT_OK, getResultIntentData());
    675                         finish();
    676                     }
    677                 });
    678 
    679                 // set our various button parameters
    680                 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
    681                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
    682                     if (TextUtils.isEmpty(buttonText)) {
    683                         mNextButton.setVisibility(View.GONE);
    684                     }
    685                     else {
    686                         mNextButton.setText(buttonText);
    687                     }
    688                 }
    689                 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
    690                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
    691                     if (TextUtils.isEmpty(buttonText)) {
    692                         backButton.setVisibility(View.GONE);
    693                     }
    694                     else {
    695                         backButton.setText(buttonText);
    696                     }
    697                 }
    698                 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
    699                     skipButton.setVisibility(View.VISIBLE);
    700                 }
    701             }
    702         }
    703 
    704         mHomeActivitiesCount = getHomeActivitiesCount();
    705     }
    706 
    707     private int getHomeActivitiesCount() {
    708         final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
    709         getPackageManager().getHomeActivities(homeApps);
    710         return homeApps.size();
    711     }
    712 
    713     private void setTitleFromIntent(Intent intent) {
    714         final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
    715         if (initialTitleResId > 0) {
    716             mInitialTitle = null;
    717             mInitialTitleResId = initialTitleResId;
    718 
    719             final String initialTitleResPackageName = intent.getStringExtra(
    720                     EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
    721             if (initialTitleResPackageName != null) {
    722                 try {
    723                     Context authContext = createPackageContextAsUser(initialTitleResPackageName,
    724                             0 /* flags */, new UserHandle(UserHandle.myUserId()));
    725                     mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
    726                     setTitle(mInitialTitle);
    727                     mInitialTitleResId = -1;
    728                     return;
    729                 } catch (NameNotFoundException e) {
    730                     Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
    731                 }
    732             } else {
    733                 setTitle(mInitialTitleResId);
    734             }
    735         } else {
    736             mInitialTitleResId = -1;
    737             final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
    738             mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
    739             setTitle(mInitialTitle);
    740         }
    741     }
    742 
    743     @Override
    744     public void onBackStackChanged() {
    745         setTitleFromBackStack();
    746     }
    747 
    748     private int setTitleFromBackStack() {
    749         final int count = getFragmentManager().getBackStackEntryCount();
    750 
    751         if (count == 0) {
    752             if (mInitialTitleResId > 0) {
    753                 setTitle(mInitialTitleResId);
    754             } else {
    755                 setTitle(mInitialTitle);
    756             }
    757             return 0;
    758         }
    759 
    760         FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
    761         setTitleFromBackStackEntry(bse);
    762 
    763         return count;
    764     }
    765 
    766     private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
    767         final CharSequence title;
    768         final int titleRes = bse.getBreadCrumbTitleRes();
    769         if (titleRes > 0) {
    770             title = getText(titleRes);
    771         } else {
    772             title = bse.getBreadCrumbTitle();
    773         }
    774         if (title != null) {
    775             setTitle(title);
    776         }
    777     }
    778 
    779     @Override
    780     protected void onSaveInstanceState(Bundle outState) {
    781         super.onSaveInstanceState(outState);
    782 
    783         if (mCategories.size() > 0) {
    784             outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
    785         }
    786 
    787         outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
    788         outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
    789 
    790         if (mDisplaySearch) {
    791             // The option menus are created if the ActionBar is visible and they are also created
    792             // asynchronously. If you launch Settings with an Intent action like
    793             // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
    794             // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
    795             // menu item and search view are null.
    796             boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
    797             outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
    798 
    799             String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
    800             outState.putString(SAVE_KEY_SEARCH_QUERY, query);
    801         }
    802 
    803         outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
    804     }
    805 
    806     @Override
    807     public void onResume() {
    808         super.onResume();
    809         if (mIsShowingDashboard) {
    810             MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
    811         }
    812 
    813         final int newHomeActivityCount = getHomeActivitiesCount();
    814         if (newHomeActivityCount != mHomeActivitiesCount) {
    815             mHomeActivitiesCount = newHomeActivityCount;
    816             invalidateCategories(true);
    817         }
    818 
    819         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
    820             @Override
    821             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    822                 invalidateCategories(true);
    823             }
    824         };
    825         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
    826                 mDevelopmentPreferencesListener);
    827 
    828         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    829 
    830         mDynamicIndexableContentMonitor.register(this);
    831 
    832         if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
    833             onQueryTextSubmit(mSearchQuery);
    834         }
    835     }
    836 
    837     @Override
    838     public void onPause() {
    839         super.onPause();
    840         if (mIsShowingDashboard) {
    841             MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
    842         }
    843         unregisterReceiver(mBatteryInfoReceiver);
    844         mDynamicIndexableContentMonitor.unregister();
    845     }
    846 
    847     @Override
    848     public void onDestroy() {
    849         super.onDestroy();
    850 
    851         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
    852                 mDevelopmentPreferencesListener);
    853         mDevelopmentPreferencesListener = null;
    854     }
    855 
    856     protected boolean isValidFragment(String fragmentName) {
    857         // Almost all fragments are wrapped in this,
    858         // except for a few that have their own activities.
    859         for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
    860             if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
    861         }
    862         return false;
    863     }
    864 
    865     @Override
    866     public Intent getIntent() {
    867         Intent superIntent = super.getIntent();
    868         String startingFragment = getStartingFragmentClass(superIntent);
    869         // This is called from super.onCreate, isMultiPane() is not yet reliable
    870         // Do not use onIsHidingHeaders either, which relies itself on this method
    871         if (startingFragment != null) {
    872             Intent modIntent = new Intent(superIntent);
    873             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
    874             Bundle args = superIntent.getExtras();
    875             if (args != null) {
    876                 args = new Bundle(args);
    877             } else {
    878                 args = new Bundle();
    879             }
    880             args.putParcelable("intent", superIntent);
    881             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
    882             return modIntent;
    883         }
    884         return superIntent;
    885     }
    886 
    887     /**
    888      * Checks if the component name in the intent is different from the Settings class and
    889      * returns the class name to load as a fragment.
    890      */
    891     private String getStartingFragmentClass(Intent intent) {
    892         if (mFragmentClass != null) return mFragmentClass;
    893 
    894         String intentClass = intent.getComponent().getClassName();
    895         if (intentClass.equals(getClass().getName())) return null;
    896 
    897         if ("com.android.settings.ManageApplications".equals(intentClass)
    898                 || "com.android.settings.RunningServices".equals(intentClass)
    899                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
    900             // Old names of manage apps.
    901             intentClass = com.android.settings.applications.ManageApplications.class.getName();
    902         }
    903 
    904         return intentClass;
    905     }
    906 
    907     /**
    908      * Start a new fragment containing a preference panel.  If the preferences
    909      * are being displayed in multi-pane mode, the given fragment class will
    910      * be instantiated and placed in the appropriate pane.  If running in
    911      * single-pane mode, a new activity will be launched in which to show the
    912      * fragment.
    913      *
    914      * @param fragmentClass Full name of the class implementing the fragment.
    915      * @param args Any desired arguments to supply to the fragment.
    916      * @param titleRes Optional resource identifier of the title of this
    917      * fragment.
    918      * @param titleText Optional text of the title of this fragment.
    919      * @param resultTo Optional fragment that result data should be sent to.
    920      * If non-null, resultTo.onActivityResult() will be called when this
    921      * preference panel is done.  The launched panel must use
    922      * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
    923      * @param resultRequestCode If resultTo is non-null, this is the caller's
    924      * request code to be received with the result.
    925      */
    926     public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
    927             CharSequence titleText, Fragment resultTo, int resultRequestCode) {
    928         String title = null;
    929         if (titleRes < 0) {
    930             if (titleText != null) {
    931                 title = titleText.toString();
    932             } else {
    933                 // There not much we can do in that case
    934                 title = "";
    935             }
    936         }
    937         Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
    938                 titleRes, title, mIsShortcut);
    939     }
    940 
    941     /**
    942      * Start a new fragment in a new activity containing a preference panel for a given user. If the
    943      * preferences are being displayed in multi-pane mode, the given fragment class will be
    944      * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
    945      * activity will be launched in which to show the fragment.
    946      *
    947      * @param fragmentClass Full name of the class implementing the fragment.
    948      * @param args Any desired arguments to supply to the fragment.
    949      * @param titleRes Optional resource identifier of the title of this fragment.
    950      * @param titleText Optional text of the title of this fragment.
    951      * @param userHandle The user for which the panel has to be started.
    952      */
    953     public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
    954             CharSequence titleText, UserHandle userHandle) {
    955         // This is a workaround.
    956         //
    957         // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
    958         // starting the fragment could cause a native stack corruption. See b/17523189. However,
    959         // adding that flag and start the preference panel with the same UserHandler will make it
    960         // impossible to use back button to return to the previous screen. See b/20042570.
    961         //
    962         // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
    963         // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
    964         // when we're calling it as the same user.
    965         if (userHandle.getIdentifier() == UserHandle.myUserId()) {
    966             startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
    967         } else {
    968             String title = null;
    969             if (titleRes < 0) {
    970                 if (titleText != null) {
    971                     title = titleText.toString();
    972                 } else {
    973                     // There not much we can do in that case
    974                     title = "";
    975                 }
    976             }
    977             Utils.startWithFragmentAsUser(this, fragmentClass, args,
    978                     titleRes, title, mIsShortcut, userHandle);
    979         }
    980     }
    981 
    982     /**
    983      * Called by a preference panel fragment to finish itself.
    984      *
    985      * @param caller The fragment that is asking to be finished.
    986      * @param resultCode Optional result code to send back to the original
    987      * launching fragment.
    988      * @param resultData Optional result data to send back to the original
    989      * launching fragment.
    990      */
    991     public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
    992         setResult(resultCode, resultData);
    993         finish();
    994     }
    995 
    996     /**
    997      * Start a new fragment.
    998      *
    999      * @param fragment The fragment to start
   1000      * @param push If true, the current fragment will be pushed onto the back stack.  If false,
   1001      * the current fragment will be replaced.
   1002      */
   1003     public void startPreferenceFragment(Fragment fragment, boolean push) {
   1004         FragmentTransaction transaction = getFragmentManager().beginTransaction();
   1005         transaction.replace(R.id.main_content, fragment);
   1006         if (push) {
   1007             transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
   1008             transaction.addToBackStack(BACK_STACK_PREFS);
   1009         } else {
   1010             transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
   1011         }
   1012         transaction.commitAllowingStateLoss();
   1013     }
   1014 
   1015     /**
   1016      * Switch to a specific Fragment with taking care of validation, Title and BackStack
   1017      */
   1018     private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
   1019             boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
   1020         if (validate && !isValidFragment(fragmentName)) {
   1021             throw new IllegalArgumentException("Invalid fragment for this activity: "
   1022                     + fragmentName);
   1023         }
   1024         Fragment f = Fragment.instantiate(this, fragmentName, args);
   1025         FragmentTransaction transaction = getFragmentManager().beginTransaction();
   1026         transaction.replace(R.id.main_content, f);
   1027         if (withTransition) {
   1028             TransitionManager.beginDelayedTransition(mContent);
   1029         }
   1030         if (addToBackStack) {
   1031             transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
   1032         }
   1033         if (titleResId > 0) {
   1034             transaction.setBreadCrumbTitle(titleResId);
   1035         } else if (title != null) {
   1036             transaction.setBreadCrumbTitle(title);
   1037         }
   1038         transaction.commitAllowingStateLoss();
   1039         getFragmentManager().executePendingTransactions();
   1040         return f;
   1041     }
   1042 
   1043     /**
   1044      * Called when the activity needs its list of categories/tiles built.
   1045      *
   1046      * @param categories The list in which to place the tiles categories.
   1047      */
   1048     private void buildDashboardCategories(List<DashboardCategory> categories) {
   1049         categories.clear();
   1050         loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
   1051         updateTilesList(categories);
   1052     }
   1053 
   1054     /**
   1055      * Parse the given XML file as a categories description, adding each
   1056      * parsed categories and tiles into the target list.
   1057      *
   1058      * @param resid The XML resource to load and parse.
   1059      * @param target The list in which the parsed categories and tiles should be placed.
   1060      */
   1061     public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
   1062             Context context) {
   1063         XmlResourceParser parser = null;
   1064         try {
   1065             parser = context.getResources().getXml(resid);
   1066             AttributeSet attrs = Xml.asAttributeSet(parser);
   1067 
   1068             int type;
   1069             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
   1070                     && type != XmlPullParser.START_TAG) {
   1071                 // Parse next until start tag is found
   1072             }
   1073 
   1074             String nodeName = parser.getName();
   1075             if (!"dashboard-categories".equals(nodeName)) {
   1076                 throw new RuntimeException(
   1077                         "XML document must start with <preference-categories> tag; found"
   1078                                 + nodeName + " at " + parser.getPositionDescription());
   1079             }
   1080 
   1081             Bundle curBundle = null;
   1082 
   1083             final int outerDepth = parser.getDepth();
   1084             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
   1085                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
   1086                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1087                     continue;
   1088                 }
   1089 
   1090                 nodeName = parser.getName();
   1091                 if ("dashboard-category".equals(nodeName)) {
   1092                     DashboardCategory category = new DashboardCategory();
   1093 
   1094                     TypedArray sa = context.obtainStyledAttributes(
   1095                             attrs, com.android.internal.R.styleable.PreferenceHeader);
   1096                     category.id = sa.getResourceId(
   1097                             com.android.internal.R.styleable.PreferenceHeader_id,
   1098                             (int)DashboardCategory.CAT_ID_UNDEFINED);
   1099 
   1100                     TypedValue tv = sa.peekValue(
   1101                             com.android.internal.R.styleable.PreferenceHeader_title);
   1102                     if (tv != null && tv.type == TypedValue.TYPE_STRING) {
   1103                         if (tv.resourceId != 0) {
   1104                             category.titleRes = tv.resourceId;
   1105                         } else {
   1106                             category.title = tv.string;
   1107                         }
   1108                     }
   1109                     sa.recycle();
   1110                     sa = context.obtainStyledAttributes(attrs,
   1111                             com.android.internal.R.styleable.Preference);
   1112                     tv = sa.peekValue(
   1113                             com.android.internal.R.styleable.Preference_key);
   1114                     if (tv != null && tv.type == TypedValue.TYPE_STRING) {
   1115                         if (tv.resourceId != 0) {
   1116                             category.key = context.getString(tv.resourceId);
   1117                         } else {
   1118                             category.key = tv.string.toString();
   1119                         }
   1120                     }
   1121                     sa.recycle();
   1122 
   1123                     final int innerDepth = parser.getDepth();
   1124                     while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
   1125                             && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
   1126                         if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1127                             continue;
   1128                         }
   1129 
   1130                         String innerNodeName = parser.getName();
   1131                         if (innerNodeName.equals("dashboard-tile")) {
   1132                             DashboardTile tile = new DashboardTile();
   1133 
   1134                             sa = context.obtainStyledAttributes(
   1135                                     attrs, com.android.internal.R.styleable.PreferenceHeader);
   1136                             tile.id = sa.getResourceId(
   1137                                     com.android.internal.R.styleable.PreferenceHeader_id,
   1138                                     (int)TILE_ID_UNDEFINED);
   1139                             tv = sa.peekValue(
   1140                                     com.android.internal.R.styleable.PreferenceHeader_title);
   1141                             if (tv != null && tv.type == TypedValue.TYPE_STRING) {
   1142                                 if (tv.resourceId != 0) {
   1143                                     tile.titleRes = tv.resourceId;
   1144                                 } else {
   1145                                     tile.title = tv.string;
   1146                                 }
   1147                             }
   1148                             tv = sa.peekValue(
   1149                                     com.android.internal.R.styleable.PreferenceHeader_summary);
   1150                             if (tv != null && tv.type == TypedValue.TYPE_STRING) {
   1151                                 if (tv.resourceId != 0) {
   1152                                     tile.summaryRes = tv.resourceId;
   1153                                 } else {
   1154                                     tile.summary = tv.string;
   1155                                 }
   1156                             }
   1157                             tile.iconRes = sa.getResourceId(
   1158                                     com.android.internal.R.styleable.PreferenceHeader_icon, 0);
   1159                             tile.fragment = sa.getString(
   1160                                     com.android.internal.R.styleable.PreferenceHeader_fragment);
   1161                             sa.recycle();
   1162 
   1163                             if (curBundle == null) {
   1164                                 curBundle = new Bundle();
   1165                             }
   1166 
   1167                             final int innerDepth2 = parser.getDepth();
   1168                             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
   1169                                     && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
   1170                                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1171                                     continue;
   1172                                 }
   1173 
   1174                                 String innerNodeName2 = parser.getName();
   1175                                 if (innerNodeName2.equals("extra")) {
   1176                                     context.getResources().parseBundleExtra("extra", attrs,
   1177                                             curBundle);
   1178                                     XmlUtils.skipCurrentTag(parser);
   1179 
   1180                                 } else if (innerNodeName2.equals("intent")) {
   1181                                     tile.intent = Intent.parseIntent(context.getResources(), parser,
   1182                                             attrs);
   1183 
   1184                                 } else {
   1185                                     XmlUtils.skipCurrentTag(parser);
   1186                                 }
   1187                             }
   1188 
   1189                             if (curBundle.size() > 0) {
   1190                                 tile.fragmentArguments = curBundle;
   1191                                 curBundle = null;
   1192                             }
   1193 
   1194                             // Show the SIM Cards setting if there are more than 2 SIMs installed.
   1195                             if(tile.id != R.id.sim_settings || Utils.showSimCardTile(context)){
   1196                                 category.addTile(tile);
   1197                             }
   1198 
   1199                         } else if (innerNodeName.equals("external-tiles")) {
   1200                             category.externalIndex = category.getTilesCount();
   1201                         } else {
   1202                             XmlUtils.skipCurrentTag(parser);
   1203                         }
   1204                     }
   1205 
   1206                     target.add(category);
   1207                 } else {
   1208                     XmlUtils.skipCurrentTag(parser);
   1209                 }
   1210             }
   1211 
   1212         } catch (XmlPullParserException e) {
   1213             throw new RuntimeException("Error parsing categories", e);
   1214         } catch (IOException e) {
   1215             throw new RuntimeException("Error parsing categories", e);
   1216         } finally {
   1217             if (parser != null) parser.close();
   1218         }
   1219     }
   1220 
   1221     private void updateTilesList(List<DashboardCategory> target) {
   1222         final boolean showDev = mDevelopmentPreferences.getBoolean(
   1223                 DevelopmentSettings.PREF_SHOW,
   1224                 android.os.Build.TYPE.equals("eng"));
   1225 
   1226         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
   1227 
   1228         final int size = target.size();
   1229         for (int i = 0; i < size; i++) {
   1230 
   1231             DashboardCategory category = target.get(i);
   1232 
   1233             // Ids are integers, so downcasting is ok
   1234             int id = (int) category.id;
   1235             int n = category.getTilesCount() - 1;
   1236             while (n >= 0) {
   1237 
   1238                 DashboardTile tile = category.getTile(n);
   1239                 boolean removeTile = false;
   1240                 id = (int) tile.id;
   1241                 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
   1242                     if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
   1243                         removeTile = true;
   1244                     }
   1245                 } else if (id == R.id.wifi_settings) {
   1246                     // Remove WiFi Settings if WiFi service is not available.
   1247                     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
   1248                         removeTile = true;
   1249                     }
   1250                 } else if (id == R.id.bluetooth_settings) {
   1251                     // Remove Bluetooth Settings if Bluetooth service is not available.
   1252                     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
   1253                         removeTile = true;
   1254                     }
   1255                 } else if (id == R.id.data_usage_settings) {
   1256                     // Remove data usage when kernel module not enabled
   1257                     if (!Utils.isBandwidthControlEnabled()) {
   1258                         removeTile = true;
   1259                     }
   1260                 } else if (id == R.id.battery_settings) {
   1261                     // Remove battery settings when battery is not available. (e.g. TV)
   1262 
   1263                     if (!mBatteryPresent) {
   1264                         removeTile = true;
   1265                     }
   1266                 } else if (id == R.id.home_settings) {
   1267                     if (!updateHomeSettingTiles(tile)) {
   1268                         removeTile = true;
   1269                     }
   1270                 } else if (id == R.id.user_settings) {
   1271                     boolean hasMultipleUsers =
   1272                             ((UserManager) getSystemService(Context.USER_SERVICE))
   1273                                     .getUserCount() > 1;
   1274                     if (!UserHandle.MU_ENABLED
   1275                             || !UserManager.supportsMultipleUsers()
   1276                             || Utils.isMonkeyRunning()) {
   1277                         removeTile = true;
   1278                     }
   1279                 } else if (id == R.id.nfc_payment_settings) {
   1280                     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
   1281                         removeTile = true;
   1282                     } else {
   1283                         // Only show if NFC is on and we have the HCE feature
   1284                         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
   1285                         if (adapter == null || !adapter.isEnabled() ||
   1286                                 !getPackageManager().hasSystemFeature(
   1287                                         PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
   1288                             removeTile = true;
   1289                         }
   1290                     }
   1291                 } else if (id == R.id.print_settings) {
   1292                     boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
   1293                             PackageManager.FEATURE_PRINTING);
   1294                     if (!hasPrintingSupport) {
   1295                         removeTile = true;
   1296                     }
   1297                 } else if (id == R.id.development_settings) {
   1298                     if (!showDev || um.hasUserRestriction(
   1299                             UserManager.DISALLOW_DEBUGGING_FEATURES)) {
   1300                         removeTile = true;
   1301                     }
   1302                 }
   1303 
   1304                 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
   1305                         && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
   1306                     removeTile = true;
   1307                 }
   1308 
   1309                 if (removeTile && n < category.getTilesCount()) {
   1310                     category.removeTile(n);
   1311                 }
   1312                 n--;
   1313             }
   1314         }
   1315         addExternalTiles(target);
   1316     }
   1317 
   1318     private void addExternalTiles(List<DashboardCategory> target) {
   1319         Map<Pair<String, String>, DashboardTile> addedCache =
   1320                 new ArrayMap<Pair<String, String>, DashboardTile>();
   1321         UserManager userManager = UserManager.get(this);
   1322         for (UserHandle user : userManager.getUserProfiles()) {
   1323             addExternalTiles(target, user, addedCache);
   1324         }
   1325     }
   1326 
   1327     private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
   1328             Map<Pair<String, String>, DashboardTile> addedCache) {
   1329         PackageManager pm = getPackageManager();
   1330         Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
   1331         List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
   1332                 PackageManager.GET_META_DATA, user.getIdentifier());
   1333         for (ResolveInfo resolved : results) {
   1334             if (!resolved.system) {
   1335                 // Do not allow any app to add to settings, only system ones.
   1336                 continue;
   1337             }
   1338             ActivityInfo activityInfo = resolved.activityInfo;
   1339             Bundle metaData = activityInfo.metaData;
   1340             if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
   1341                 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
   1342                         + EXTRA_SETTINGS_ACTION + " missing metadata " +
   1343                         (metaData == null ? "" : EXTRA_CATEGORY_KEY));
   1344                 continue;
   1345             }
   1346             String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
   1347             DashboardCategory category = getCategory(target, categoryKey);
   1348             if (category == null) {
   1349                 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
   1350                         + "category key " + categoryKey);
   1351                 continue;
   1352             }
   1353             Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
   1354                     activityInfo.name);
   1355             DashboardTile tile = addedCache.get(key);
   1356             if (tile == null) {
   1357                 tile = new DashboardTile();
   1358                 tile.intent = new Intent().setClassName(
   1359                         activityInfo.packageName, activityInfo.name);
   1360                 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
   1361 
   1362                 if (category.externalIndex == -1) {
   1363                     // If no location for external tiles has been specified for this category,
   1364                     // then just put them at the end.
   1365                     category.addTile(tile);
   1366                 } else {
   1367                     category.addTile(category.externalIndex, tile);
   1368                 }
   1369                 addedCache.put(key, tile);
   1370             }
   1371             tile.userHandle.add(user);
   1372         }
   1373     }
   1374 
   1375     private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
   1376         for (DashboardCategory category : target) {
   1377             if (categoryKey.equals(category.key)) {
   1378                 return category;
   1379             }
   1380         }
   1381         return null;
   1382     }
   1383 
   1384     private boolean updateHomeSettingTiles(DashboardTile tile) {
   1385         // Once we decide to show Home settings, keep showing it forever
   1386         SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
   1387         if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
   1388             return true;
   1389         }
   1390 
   1391         try {
   1392             mHomeActivitiesCount = getHomeActivitiesCount();
   1393             if (mHomeActivitiesCount < 2) {
   1394                 // When there's only one available home app, omit this settings
   1395                 // category entirely at the top level UI.  If the user just
   1396                 // uninstalled the penultimate home app candidiate, we also
   1397                 // now tell them about why they aren't seeing 'Home' in the list.
   1398                 if (sShowNoHomeNotice) {
   1399                     sShowNoHomeNotice = false;
   1400                     NoHomeDialogFragment.show(this);
   1401                 }
   1402                 return false;
   1403             } else {
   1404                 // Okay, we're allowing the Home settings category.  Tell it, when
   1405                 // invoked via this front door, that we'll need to be told about the
   1406                 // case when the user uninstalls all but one home app.
   1407                 if (tile.fragmentArguments == null) {
   1408                     tile.fragmentArguments = new Bundle();
   1409                 }
   1410                 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
   1411             }
   1412         } catch (Exception e) {
   1413             // Can't look up the home activity; bail on configuring the icon
   1414             Log.w(LOG_TAG, "Problem looking up home activity!", e);
   1415         }
   1416 
   1417         sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
   1418         return true;
   1419     }
   1420 
   1421     private void getMetaData() {
   1422         try {
   1423             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
   1424                     PackageManager.GET_META_DATA);
   1425             if (ai == null || ai.metaData == null) return;
   1426             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
   1427         } catch (NameNotFoundException nnfe) {
   1428             // No recovery
   1429             Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
   1430         }
   1431     }
   1432 
   1433     // give subclasses access to the Next button
   1434     public boolean hasNextButton() {
   1435         return mNextButton != null;
   1436     }
   1437 
   1438     public Button getNextButton() {
   1439         return mNextButton;
   1440     }
   1441 
   1442     @Override
   1443     public boolean shouldUpRecreateTask(Intent targetIntent) {
   1444         return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
   1445     }
   1446 
   1447     public static void requestHomeNotice() {
   1448         sShowNoHomeNotice = true;
   1449     }
   1450 
   1451     @Override
   1452     public boolean onQueryTextSubmit(String query) {
   1453         switchToSearchResultsFragmentIfNeeded();
   1454         mSearchQuery = query;
   1455         return mSearchResultsFragment.onQueryTextSubmit(query);
   1456     }
   1457 
   1458     @Override
   1459     public boolean onQueryTextChange(String newText) {
   1460         mSearchQuery = newText;
   1461         if (mSearchResultsFragment == null) {
   1462             return false;
   1463         }
   1464         return mSearchResultsFragment.onQueryTextChange(newText);
   1465     }
   1466 
   1467     @Override
   1468     public boolean onClose() {
   1469         return false;
   1470     }
   1471 
   1472     @Override
   1473     public boolean onMenuItemActionExpand(MenuItem item) {
   1474         if (item.getItemId() == mSearchMenuItem.getItemId()) {
   1475             switchToSearchResultsFragmentIfNeeded();
   1476         }
   1477         return true;
   1478     }
   1479 
   1480     @Override
   1481     public boolean onMenuItemActionCollapse(MenuItem item) {
   1482         if (item.getItemId() == mSearchMenuItem.getItemId()) {
   1483             if (mSearchMenuItemExpanded) {
   1484                 revertToInitialFragment();
   1485             }
   1486         }
   1487         return true;
   1488     }
   1489 
   1490     private void switchToSearchResultsFragmentIfNeeded() {
   1491         if (mSearchResultsFragment != null) {
   1492             return;
   1493         }
   1494         Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
   1495         if (current != null && current instanceof SearchResultsSummary) {
   1496             mSearchResultsFragment = (SearchResultsSummary) current;
   1497         } else {
   1498             mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
   1499                     SearchResultsSummary.class.getName(), null, false, true,
   1500                     R.string.search_results_title, null, true);
   1501         }
   1502         mSearchResultsFragment.setSearchView(mSearchView);
   1503         mSearchMenuItemExpanded = true;
   1504     }
   1505 
   1506     public void needToRevertToInitialFragment() {
   1507         mNeedToRevertToInitialFragment = true;
   1508     }
   1509 
   1510     private void revertToInitialFragment() {
   1511         mNeedToRevertToInitialFragment = false;
   1512         mSearchResultsFragment = null;
   1513         mSearchMenuItemExpanded = false;
   1514         getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
   1515                 FragmentManager.POP_BACK_STACK_INCLUSIVE);
   1516         if (mSearchMenuItem != null) {
   1517             mSearchMenuItem.collapseActionView();
   1518         }
   1519     }
   1520 
   1521     public Intent getResultIntentData() {
   1522         return mResultIntentData;
   1523     }
   1524 
   1525     public void setResultIntentData(Intent resultIntentData) {
   1526         mResultIntentData = resultIntentData;
   1527     }
   1528 }
   1529