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