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