OSDN Git Service

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