OSDN Git Service

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