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