OSDN Git Service

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