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