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