OSDN Git Service

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