OSDN Git Service

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