OSDN Git Service

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