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