OSDN Git Service

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