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