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