OSDN Git Service

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