2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
19 import android.app.ActionBar;
20 import android.app.Fragment;
21 import android.app.FragmentManager;
22 import android.app.FragmentTransaction;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.SharedPreferences;
29 import android.content.pm.ActivityInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.PackageManager.NameNotFoundException;
32 import android.content.res.Configuration;
33 import android.nfc.NfcAdapter;
34 import android.os.AsyncTask;
35 import android.os.Bundle;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.support.v14.preference.PreferenceFragment;
39 import android.support.v7.preference.Preference;
40 import android.support.v7.preference.PreferenceManager;
41 import android.text.TextUtils;
42 import android.transition.TransitionManager;
43 import android.util.Log;
44 import android.view.Menu;
45 import android.view.MenuInflater;
46 import android.view.MenuItem;
47 import android.view.View;
48 import android.view.View.OnClickListener;
49 import android.view.ViewGroup;
50 import android.widget.Button;
51 import android.widget.SearchView;
53 import com.android.internal.util.ArrayUtils;
54 import com.android.settings.Settings.WifiSettingsActivity;
55 import com.android.settings.accessibility.AccessibilitySettings;
56 import com.android.settings.accessibility.AccessibilitySettingsForSetupWizard;
57 import com.android.settings.accessibility.CaptionPropertiesFragment;
58 import com.android.settings.accounts.AccountSettings;
59 import com.android.settings.accounts.AccountSyncSettings;
60 import com.android.settings.accounts.ChooseAccountActivity;
61 import com.android.settings.accounts.ManagedProfileSettings;
62 import com.android.settings.applications.AdvancedAppSettings;
63 import com.android.settings.applications.DrawOverlayDetails;
64 import com.android.settings.applications.InstalledAppDetails;
65 import com.android.settings.applications.ManageApplications;
66 import com.android.settings.applications.ManageAssist;
67 import com.android.settings.applications.NotificationApps;
68 import com.android.settings.applications.ProcessStatsSummary;
69 import com.android.settings.applications.ProcessStatsUi;
70 import com.android.settings.applications.UsageAccessDetails;
71 import com.android.settings.applications.VrListenerSettings;
72 import com.android.settings.applications.WriteSettingsDetails;
73 import com.android.settings.bluetooth.BluetoothSettings;
74 import com.android.settings.dashboard.DashboardContainerFragment;
75 import com.android.settings.dashboard.SearchResultsSummary;
76 import com.android.settings.datausage.DataUsageSummary;
77 import com.android.settings.deviceinfo.ImeiInformation;
78 import com.android.settings.deviceinfo.PrivateVolumeForget;
79 import com.android.settings.deviceinfo.PrivateVolumeSettings;
80 import com.android.settings.deviceinfo.PublicVolumeSettings;
81 import com.android.settings.deviceinfo.SimStatus;
82 import com.android.settings.deviceinfo.Status;
83 import com.android.settings.deviceinfo.StorageSettings;
84 import com.android.settings.display.NightDisplaySettings;
85 import com.android.settings.fuelgauge.BatterySaverSettings;
86 import com.android.settings.fuelgauge.PowerUsageDetail;
87 import com.android.settings.fuelgauge.PowerUsageSummary;
88 import com.android.settings.gestures.GestureSettings;
89 import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
90 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
91 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
92 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment2;
93 import com.android.settings.inputmethod.PhysicalKeyboardFragment;
94 import com.android.settings.inputmethod.SpellCheckersSettings;
95 import com.android.settings.inputmethod.UserDictionaryList;
96 import com.android.settings.localepicker.LocaleListEditor;
97 import com.android.settings.location.LocationSettings;
98 import com.android.settings.nfc.AndroidBeam;
99 import com.android.settings.nfc.PaymentSettings;
100 import com.android.settings.notification.AppNotificationSettings;
101 import com.android.settings.notification.ConfigureNotificationSettings;
102 import com.android.settings.notification.NotificationAccessSettings;
103 import com.android.settings.notification.NotificationStation;
104 import com.android.settings.notification.OtherSoundSettings;
105 import com.android.settings.notification.SoundSettings;
106 import com.android.settings.notification.ZenAccessSettings;
107 import com.android.settings.notification.ZenModeAutomationSettings;
108 import com.android.settings.notification.ZenModeEventRuleSettings;
109 import com.android.settings.notification.ZenModePrioritySettings;
110 import com.android.settings.notification.ZenModeScheduleRuleSettings;
111 import com.android.settings.notification.ZenModeSettings;
112 import com.android.settings.notification.ZenModeVisualInterruptionSettings;
113 import com.android.settings.print.PrintJobSettingsFragment;
114 import com.android.settings.print.PrintSettingsFragment;
115 import com.android.settings.qstile.DevelopmentTiles;
116 import com.android.settings.search.DynamicIndexableContentMonitor;
117 import com.android.settings.search.Index;
118 import com.android.settings.sim.SimSettings;
119 import com.android.settings.tts.TextToSpeechSettings;
120 import com.android.settings.users.UserSettings;
121 import com.android.settings.vpn2.VpnSettings;
122 import com.android.settings.wfd.WifiDisplaySettings;
123 import com.android.settings.widget.SwitchBar;
124 import com.android.settings.wifi.AdvancedWifiSettings;
125 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
126 import com.android.settings.wifi.WifiAPITest;
127 import com.android.settings.wifi.WifiInfo;
128 import com.android.settings.wifi.WifiSettings;
129 import com.android.settings.wifi.p2p.WifiP2pSettings;
130 import com.android.settingslib.drawer.DashboardCategory;
131 import com.android.settingslib.drawer.SettingsDrawerActivity;
132 import com.android.settingslib.drawer.Tile;
134 import java.net.URISyntaxException;
135 import java.util.ArrayList;
136 import java.util.List;
137 import java.util.Set;
139 public class SettingsActivity extends SettingsDrawerActivity
140 implements PreferenceManager.OnPreferenceTreeClickListener,
141 PreferenceFragment.OnPreferenceStartFragmentCallback,
142 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
143 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
144 MenuItem.OnActionExpandListener {
146 private static final String LOG_TAG = "Settings";
148 private static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
150 // Constants for state save/restore
151 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
152 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
153 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
154 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
155 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
156 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
159 * When starting this activity, the invoking Intent can contain this extra
160 * string to specify which fragment should be initially displayed.
161 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
162 * will call isValidFragment() to confirm that the fragment class name is valid for this
165 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
168 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
169 * this extra can also be specified to supply a Bundle of arguments to pass
170 * to that fragment when it is instantiated during the initial creation
173 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
176 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
178 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
180 public static final String BACK_STACK_PREFS = ":settings:prefs";
182 // extras that allow any preference activity to be launched as part of a wizard
184 // show Back and Next buttons? takes boolean parameter
185 // Back will then return RESULT_CANCELED and Next RESULT_OK
186 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
188 // add a Skip button?
189 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
191 // specify custom text for the Back or Next buttons, or cause a button to not appear
192 // at all by setting it to null
193 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
194 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
197 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
198 * those extra can also be specify to supply the title or title res id to be shown for
201 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
203 * The package name used to resolve the title resource id.
205 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
206 ":settings:show_fragment_title_res_package_name";
207 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
208 ":settings:show_fragment_title_resid";
209 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
210 ":settings:show_fragment_as_shortcut";
212 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
213 ":settings:show_fragment_as_subsetting";
215 public static final String EXTRA_HIDE_DRAWER = ":settings:hide_drawer";
217 public static final String META_DATA_KEY_FRAGMENT_CLASS =
218 "com.android.settings.FRAGMENT_CLASS";
220 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
222 private static final String EMPTY_QUERY = "";
224 private static final int REQUEST_SUGGESTION = 42;
226 private String mFragmentClass;
228 private CharSequence mInitialTitle;
229 private int mInitialTitleResId;
231 // Show only these settings for restricted users
232 private String[] SETTINGS_FOR_RESTRICTED = {
234 WifiSettingsActivity.class.getName(),
235 Settings.BluetoothSettingsActivity.class.getName(),
236 Settings.DataUsageSummaryActivity.class.getName(),
237 Settings.SimSettingsActivity.class.getName(),
238 Settings.WirelessSettingsActivity.class.getName(),
240 Settings.HomeSettingsActivity.class.getName(),
241 Settings.SoundSettingsActivity.class.getName(),
242 Settings.DisplaySettingsActivity.class.getName(),
243 Settings.StorageSettingsActivity.class.getName(),
244 Settings.ManageApplicationsActivity.class.getName(),
245 Settings.PowerUsageSummaryActivity.class.getName(),
246 Settings.GestureSettingsActivity.class.getName(),
248 Settings.LocationSettingsActivity.class.getName(),
249 Settings.SecuritySettingsActivity.class.getName(),
250 Settings.InputMethodAndLanguageSettingsActivity.class.getName(),
251 Settings.UserSettingsActivity.class.getName(),
252 Settings.AccountSettingsActivity.class.getName(),
254 Settings.DateTimeSettingsActivity.class.getName(),
255 Settings.DeviceInfoSettingsActivity.class.getName(),
256 Settings.AccessibilitySettingsActivity.class.getName(),
257 Settings.PrintSettingsActivity.class.getName(),
258 Settings.PaymentSettingsActivity.class.getName(),
261 private static final String[] ENTRY_FRAGMENTS = {
262 WirelessSettings.class.getName(),
263 WifiSettings.class.getName(),
264 AdvancedWifiSettings.class.getName(),
265 SavedAccessPointsWifiSettings.class.getName(),
266 BluetoothSettings.class.getName(),
267 SimSettings.class.getName(),
268 TetherSettings.class.getName(),
269 WifiP2pSettings.class.getName(),
270 VpnSettings.class.getName(),
271 DateTimeSettings.class.getName(),
272 LocaleListEditor.class.getName(),
273 InputMethodAndLanguageSettings.class.getName(),
274 AvailableVirtualKeyboardFragment.class.getName(),
275 SpellCheckersSettings.class.getName(),
276 UserDictionaryList.class.getName(),
277 UserDictionarySettings.class.getName(),
278 HomeSettings.class.getName(),
279 DisplaySettings.class.getName(),
280 DeviceInfoSettings.class.getName(),
281 ManageApplications.class.getName(),
282 NotificationApps.class.getName(),
283 ManageAssist.class.getName(),
284 ProcessStatsUi.class.getName(),
285 NotificationStation.class.getName(),
286 LocationSettings.class.getName(),
287 SecuritySettings.class.getName(),
288 UsageAccessDetails.class.getName(),
289 PrivacySettings.class.getName(),
290 DeviceAdminSettings.class.getName(),
291 AccessibilitySettings.class.getName(),
292 AccessibilitySettingsForSetupWizard.class.getName(),
293 CaptionPropertiesFragment.class.getName(),
294 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
295 TextToSpeechSettings.class.getName(),
296 StorageSettings.class.getName(),
297 PrivateVolumeForget.class.getName(),
298 PrivateVolumeSettings.class.getName(),
299 PublicVolumeSettings.class.getName(),
300 DevelopmentSettings.class.getName(),
301 AndroidBeam.class.getName(),
302 WifiDisplaySettings.class.getName(),
303 PowerUsageSummary.class.getName(),
304 AccountSyncSettings.class.getName(),
305 AccountSettings.class.getName(),
306 GestureSettings.class.getName(),
307 CryptKeeperSettings.class.getName(),
308 DataUsageSummary.class.getName(),
309 DreamSettings.class.getName(),
310 UserSettings.class.getName(),
311 NotificationAccessSettings.class.getName(),
312 ZenAccessSettings.class.getName(),
313 PrintSettingsFragment.class.getName(),
314 PrintJobSettingsFragment.class.getName(),
315 TrustedCredentialsSettings.class.getName(),
316 PaymentSettings.class.getName(),
317 KeyboardLayoutPickerFragment.class.getName(),
318 KeyboardLayoutPickerFragment2.class.getName(),
319 PhysicalKeyboardFragment.class.getName(),
320 ZenModeSettings.class.getName(),
321 SoundSettings.class.getName(),
322 ConfigureNotificationSettings.class.getName(),
323 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
324 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
325 InstalledAppDetails.class.getName(),
326 BatterySaverSettings.class.getName(),
327 AppNotificationSettings.class.getName(),
328 OtherSoundSettings.class.getName(),
329 ApnSettings.class.getName(),
330 ApnEditor.class.getName(),
331 WifiCallingSettings.class.getName(),
332 ZenModePrioritySettings.class.getName(),
333 ZenModeAutomationSettings.class.getName(),
334 ZenModeScheduleRuleSettings.class.getName(),
335 ZenModeEventRuleSettings.class.getName(),
336 ZenModeVisualInterruptionSettings.class.getName(),
337 ProcessStatsUi.class.getName(),
338 PowerUsageDetail.class.getName(),
339 ProcessStatsSummary.class.getName(),
340 DrawOverlayDetails.class.getName(),
341 WriteSettingsDetails.class.getName(),
342 AdvancedAppSettings.class.getName(),
343 WallpaperTypeSettings.class.getName(),
344 VrListenerSettings.class.getName(),
345 ManagedProfileSettings.class.getName(),
346 ChooseAccountActivity.class.getName(),
347 IccLockSettings.class.getName(),
348 ImeiInformation.class.getName(),
349 SimStatus.class.getName(),
350 Status.class.getName(),
351 TestingSettings.class.getName(),
352 WifiAPITest.class.getName(),
353 WifiInfo.class.getName(),
354 MasterClear.class.getName(),
355 NightDisplaySettings.class.getName(),
359 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
360 "android.settings.APPLICATION_DETAILS_SETTINGS"
363 private SharedPreferences mDevelopmentPreferences;
364 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
366 private boolean mBatteryPresent = true;
367 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
369 public void onReceive(Context context, Intent intent) {
370 String action = intent.getAction();
371 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
372 boolean batteryPresent = Utils.isBatteryPresent(intent);
374 if (mBatteryPresent != batteryPresent) {
375 mBatteryPresent = batteryPresent;
382 private final BroadcastReceiver mUserAddRemoveReceiver = new BroadcastReceiver() {
384 public void onReceive(Context context, Intent intent) {
385 String action = intent.getAction();
386 if (action.equals(Intent.ACTION_USER_ADDED)
387 || action.equals(Intent.ACTION_USER_REMOVED)) {
388 Index.getInstance(getApplicationContext()).update();
393 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
394 new DynamicIndexableContentMonitor();
396 private ActionBar mActionBar;
397 private SwitchBar mSwitchBar;
399 private Button mNextButton;
401 private boolean mDisplayHomeAsUpEnabled;
402 private boolean mDisplaySearch;
404 private boolean mIsShowingDashboard;
405 private boolean mIsShortcut;
407 private ViewGroup mContent;
409 private SearchView mSearchView;
410 private MenuItem mSearchMenuItem;
411 private boolean mSearchMenuItemExpanded = false;
412 private SearchResultsSummary mSearchResultsFragment;
413 private String mSearchQuery;
416 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
418 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
420 private boolean mNeedToRevertToInitialFragment = false;
422 private Intent mResultIntentData;
423 private ComponentName mCurrentSuggestion;
425 public SwitchBar getSwitchBar() {
430 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
431 startPreferencePanel(pref.getFragment(), pref.getExtras(), -1, pref.getTitle(),
437 public boolean onPreferenceTreeClick(Preference preference) {
442 public void onConfigurationChanged(Configuration newConfig) {
443 super.onConfigurationChanged(newConfig);
444 Index.getInstance(this).update();
448 public boolean onCreateOptionsMenu(Menu menu) {
449 if (!mDisplaySearch) {
453 MenuInflater inflater = getMenuInflater();
454 inflater.inflate(R.menu.options_menu, menu);
456 // Cache the search query (can be overriden by the OnQueryTextListener)
457 final String query = mSearchQuery;
459 mSearchMenuItem = menu.findItem(R.id.search);
460 mSearchView = (SearchView) mSearchMenuItem.getActionView();
462 if (mSearchMenuItem == null || mSearchView == null) {
466 if (mSearchResultsFragment != null) {
467 mSearchResultsFragment.setSearchView(mSearchView);
470 mSearchMenuItem.setOnActionExpandListener(this);
471 mSearchView.setOnQueryTextListener(this);
472 mSearchView.setOnCloseListener(this);
474 if (mSearchMenuItemExpanded) {
475 mSearchMenuItem.expandActionView();
477 mSearchView.setQuery(query, true /* submit */);
483 public SharedPreferences getSharedPreferences(String name, int mode) {
484 if (name.equals(getPackageName() + "_preferences")) {
485 return new SharedPreferencesLogger(this, getMetricsTag());
487 return super.getSharedPreferences(name, mode);
490 private String getMetricsTag() {
491 String tag = getClass().getName();
492 if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
493 tag = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
495 if (tag.startsWith("com.android.settings.")) {
496 tag = tag.replace("com.android.settings.", "");
501 private static boolean isShortCutIntent(final Intent intent) {
502 Set<String> categories = intent.getCategories();
503 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
506 private static boolean isLikeShortCutIntent(final Intent intent) {
507 String action = intent.getAction();
508 if (action == null) {
511 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
512 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
518 protected void onCreate(Bundle savedState) {
519 super.onCreate(savedState);
520 long startTime = System.currentTimeMillis();
522 // Should happen before any call to getIntent()
525 final Intent intent = getIntent();
526 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
527 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
529 if (intent.getBooleanExtra(EXTRA_HIDE_DRAWER, false)) {
530 setIsDrawerPresent(false);
533 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
534 Context.MODE_PRIVATE);
536 // Getting Intent properties can only be done after the super.onCreate(...)
537 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
539 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
540 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
542 final ComponentName cn = intent.getComponent();
543 final String className = cn.getClassName();
545 mIsShowingDashboard = className.equals(Settings.class.getName())
546 || className.equals(Settings.WirelessSettings.class.getName())
547 || className.equals(Settings.DeviceSettings.class.getName())
548 || className.equals(Settings.PersonalSettings.class.getName())
549 || className.equals(Settings.WirelessSettings.class.getName());
551 // This is a "Sub Settings" when:
552 // - this is a real SubSettings
553 // - or :settings:show_fragment_as_subsetting is passed to the Intent
554 final boolean isSubSettings = this instanceof SubSettings ||
555 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
557 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
559 // Check also that we are not a Theme Dialog as we don't want to override them
560 final int themeResId = getThemeResId();
561 if (themeResId != R.style.Theme_DialogWhenLarge &&
562 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
563 setTheme(R.style.Theme_SubSettings);
567 setContentView(mIsShowingDashboard ?
568 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
570 mContent = (ViewGroup) findViewById(R.id.main_content);
572 getFragmentManager().addOnBackStackChangedListener(this);
574 if (mIsShowingDashboard) {
575 // Run the Index update only if we have some space
576 if (!Utils.isLowStorage(this)) {
577 long indexStartTime = System.currentTimeMillis();
578 Index.getInstance(getApplicationContext()).update();
579 if (DEBUG_TIMING) Log.d(LOG_TAG, "Index.update() took "
580 + (System.currentTimeMillis() - indexStartTime) + " ms");
582 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
586 if (savedState != null) {
587 // We are restarting from a previous saved state; used that to initialize, instead
588 // of starting fresh.
589 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
590 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
592 setTitleFromIntent(intent);
594 ArrayList<DashboardCategory> categories =
595 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
596 if (categories != null) {
598 mCategories.addAll(categories);
599 setTitleFromBackStack();
602 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
603 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
605 if (!mIsShowingDashboard) {
606 mDisplaySearch = false;
607 // UP will be shown only if it is a sub settings
609 mDisplayHomeAsUpEnabled = isSubSettings;
610 } else if (isSubSettings) {
611 mDisplayHomeAsUpEnabled = true;
613 mDisplayHomeAsUpEnabled = false;
615 setTitleFromIntent(intent);
617 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
618 switchToFragment(initialFragmentName, initialArguments, true, false,
619 mInitialTitleResId, mInitialTitle, false);
621 // No UP affordance if we are displaying the main Dashboard
622 mDisplayHomeAsUpEnabled = false;
623 // Show Search affordance
624 mDisplaySearch = true;
625 mInitialTitleResId = R.string.dashboard_title;
626 switchToFragment(DashboardContainerFragment.class.getName(), null, false, false,
627 mInitialTitleResId, mInitialTitle, false);
631 mActionBar = getActionBar();
632 if (mActionBar != null) {
633 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
634 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
636 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
637 if (mSwitchBar != null) {
638 mSwitchBar.setMetricsTag(getMetricsTag());
641 // see if we should show Back/Next buttons
642 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
644 View buttonBar = findViewById(R.id.button_bar);
645 if (buttonBar != null) {
646 buttonBar.setVisibility(View.VISIBLE);
648 Button backButton = (Button)findViewById(R.id.back_button);
649 backButton.setOnClickListener(new OnClickListener() {
650 public void onClick(View v) {
651 setResult(RESULT_CANCELED, getResultIntentData());
655 Button skipButton = (Button)findViewById(R.id.skip_button);
656 skipButton.setOnClickListener(new OnClickListener() {
657 public void onClick(View v) {
658 setResult(RESULT_OK, getResultIntentData());
662 mNextButton = (Button)findViewById(R.id.next_button);
663 mNextButton.setOnClickListener(new OnClickListener() {
664 public void onClick(View v) {
665 setResult(RESULT_OK, getResultIntentData());
670 // set our various button parameters
671 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
672 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
673 if (TextUtils.isEmpty(buttonText)) {
674 mNextButton.setVisibility(View.GONE);
677 mNextButton.setText(buttonText);
680 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
681 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
682 if (TextUtils.isEmpty(buttonText)) {
683 backButton.setVisibility(View.GONE);
686 backButton.setText(buttonText);
689 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
690 skipButton.setVisibility(View.VISIBLE);
695 if (DEBUG_TIMING) Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
699 public void setDisplaySearchMenu(boolean displaySearch) {
700 if (displaySearch != mDisplaySearch) {
701 mDisplaySearch = displaySearch;
702 invalidateOptionsMenu();
706 private void setTitleFromIntent(Intent intent) {
707 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
708 if (initialTitleResId > 0) {
709 mInitialTitle = null;
710 mInitialTitleResId = initialTitleResId;
712 final String initialTitleResPackageName = intent.getStringExtra(
713 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
714 if (initialTitleResPackageName != null) {
716 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
717 0 /* flags */, new UserHandle(UserHandle.myUserId()));
718 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
719 setTitle(mInitialTitle);
720 mInitialTitleResId = -1;
722 } catch (NameNotFoundException e) {
723 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
726 setTitle(mInitialTitleResId);
729 mInitialTitleResId = -1;
730 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
731 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
732 setTitle(mInitialTitle);
737 public void onBackStackChanged() {
738 setTitleFromBackStack();
741 private void setTitleFromBackStack() {
742 final int count = getFragmentManager().getBackStackEntryCount();
745 if (mInitialTitleResId > 0) {
746 setTitle(mInitialTitleResId);
748 setTitle(mInitialTitle);
753 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
754 setTitleFromBackStackEntry(bse);
757 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
758 final CharSequence title;
759 final int titleRes = bse.getBreadCrumbTitleRes();
761 title = getText(titleRes);
763 title = bse.getBreadCrumbTitle();
771 protected void onSaveInstanceState(Bundle outState) {
772 super.onSaveInstanceState(outState);
774 if (mCategories.size() > 0) {
775 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
778 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
779 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
781 if (mDisplaySearch) {
782 // The option menus are created if the ActionBar is visible and they are also created
783 // asynchronously. If you launch Settings with an Intent action like
784 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
785 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
786 // menu item and search view are null.
787 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
788 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
790 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
791 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
796 protected void onStart() {
799 if (mNeedToRevertToInitialFragment) {
800 revertToInitialFragment();
803 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
805 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
809 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
810 mDevelopmentPreferencesListener);
812 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
813 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_ADDED));
814 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
816 mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
818 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
819 onQueryTextSubmit(mSearchQuery);
825 protected void onStop() {
827 unregisterReceiver(mBatteryInfoReceiver);
828 unregisterReceiver(mUserAddRemoveReceiver);
829 mDynamicIndexableContentMonitor.unregister();
833 public void onDestroy() {
836 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
837 mDevelopmentPreferencesListener);
838 mDevelopmentPreferencesListener = null;
841 protected boolean isValidFragment(String fragmentName) {
842 // Almost all fragments are wrapped in this,
843 // except for a few that have their own activities.
844 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
845 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
851 public Intent getIntent() {
852 Intent superIntent = super.getIntent();
853 String startingFragment = getStartingFragmentClass(superIntent);
854 // This is called from super.onCreate, isMultiPane() is not yet reliable
855 // Do not use onIsHidingHeaders either, which relies itself on this method
856 if (startingFragment != null) {
857 Intent modIntent = new Intent(superIntent);
858 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
859 Bundle args = superIntent.getExtras();
861 args = new Bundle(args);
865 args.putParcelable("intent", superIntent);
866 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
873 * Checks if the component name in the intent is different from the Settings class and
874 * returns the class name to load as a fragment.
876 private String getStartingFragmentClass(Intent intent) {
877 if (mFragmentClass != null) return mFragmentClass;
879 String intentClass = intent.getComponent().getClassName();
880 if (intentClass.equals(getClass().getName())) return null;
882 if ("com.android.settings.ManageApplications".equals(intentClass)
883 || "com.android.settings.RunningServices".equals(intentClass)
884 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
885 // Old names of manage apps.
886 intentClass = com.android.settings.applications.ManageApplications.class.getName();
893 * Start a new fragment containing a preference panel. If the preferences
894 * are being displayed in multi-pane mode, the given fragment class will
895 * be instantiated and placed in the appropriate pane. If running in
896 * single-pane mode, a new activity will be launched in which to show the
899 * @param fragmentClass Full name of the class implementing the fragment.
900 * @param args Any desired arguments to supply to the fragment.
901 * @param titleRes Optional resource identifier of the title of this
903 * @param titleText Optional text of the title of this fragment.
904 * @param resultTo Optional fragment that result data should be sent to.
905 * If non-null, resultTo.onActivityResult() will be called when this
906 * preference panel is done. The launched panel must use
907 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
908 * @param resultRequestCode If resultTo is non-null, this is the caller's
909 * request code to be received with the result.
911 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
912 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
915 if (titleText != null) {
916 title = titleText.toString();
918 // There not much we can do in that case
922 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
923 titleRes, title, mIsShortcut);
927 * Start a new fragment in a new activity containing a preference panel for a given user. If the
928 * preferences are being displayed in multi-pane mode, the given fragment class will be
929 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
930 * activity will be launched in which to show the fragment.
932 * @param fragmentClass Full name of the class implementing the fragment.
933 * @param args Any desired arguments to supply to the fragment.
934 * @param titleRes Optional resource identifier of the title of this fragment.
935 * @param titleText Optional text of the title of this fragment.
936 * @param userHandle The user for which the panel has to be started.
938 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
939 CharSequence titleText, UserHandle userHandle) {
940 // This is a workaround.
942 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
943 // starting the fragment could cause a native stack corruption. See b/17523189. However,
944 // adding that flag and start the preference panel with the same UserHandler will make it
945 // impossible to use back button to return to the previous screen. See b/20042570.
947 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
948 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
949 // when we're calling it as the same user.
950 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
951 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
955 if (titleText != null) {
956 title = titleText.toString();
958 // There not much we can do in that case
962 Utils.startWithFragmentAsUser(this, fragmentClass, args,
963 titleRes, title, mIsShortcut, userHandle);
968 * Called by a preference panel fragment to finish itself.
970 * @param caller The fragment that is asking to be finished.
971 * @param resultCode Optional result code to send back to the original
972 * launching fragment.
973 * @param resultData Optional result data to send back to the original
974 * launching fragment.
976 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
977 setResult(resultCode, resultData);
982 * Start a new fragment.
984 * @param fragment The fragment to start
985 * @param push If true, the current fragment will be pushed onto the back stack. If false,
986 * the current fragment will be replaced.
988 public void startPreferenceFragment(Fragment fragment, boolean push) {
989 FragmentTransaction transaction = getFragmentManager().beginTransaction();
990 transaction.replace(R.id.main_content, fragment);
992 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
993 transaction.addToBackStack(BACK_STACK_PREFS);
995 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
997 transaction.commitAllowingStateLoss();
1001 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1003 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1004 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1005 if (validate && !isValidFragment(fragmentName)) {
1006 throw new IllegalArgumentException("Invalid fragment for this activity: "
1009 Fragment f = Fragment.instantiate(this, fragmentName, args);
1010 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1011 transaction.replace(R.id.main_content, f);
1012 if (withTransition) {
1013 TransitionManager.beginDelayedTransition(mContent);
1015 if (addToBackStack) {
1016 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1018 if (titleResId > 0) {
1019 transaction.setBreadCrumbTitle(titleResId);
1020 } else if (title != null) {
1021 transaction.setBreadCrumbTitle(title);
1023 transaction.commitAllowingStateLoss();
1024 getFragmentManager().executePendingTransactions();
1028 private void updateTilesList() {
1029 // Generally the items that are will be changing from these updates will
1030 // not be in the top list of tiles, so run it in the background and the
1031 // SettingsDrawerActivity will pick up on the updates automatically.
1032 AsyncTask.execute(new Runnable() {
1035 doUpdateTilesList();
1040 private void doUpdateTilesList() {
1041 PackageManager pm = getPackageManager();
1042 final UserManager um = UserManager.get(this);
1043 final boolean isAdmin = um.isAdminUser();
1045 String packageName = getPackageName();
1046 setTileEnabled(new ComponentName(packageName, WifiSettingsActivity.class.getName()),
1047 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin, pm);
1049 setTileEnabled(new ComponentName(packageName,
1050 Settings.BluetoothSettingsActivity.class.getName()),
1051 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin, pm);
1053 setTileEnabled(new ComponentName(packageName,
1054 Settings.DataUsageSummaryActivity.class.getName()),
1055 Utils.isBandwidthControlEnabled(), isAdmin, pm);
1057 setTileEnabled(new ComponentName(packageName,
1058 Settings.SimSettingsActivity.class.getName()),
1059 Utils.showSimCardTile(this), isAdmin, pm);
1061 setTileEnabled(new ComponentName(packageName,
1062 Settings.PowerUsageSummaryActivity.class.getName()),
1063 mBatteryPresent, isAdmin, pm);
1065 setTileEnabled(new ComponentName(packageName,
1066 Settings.UserSettingsActivity.class.getName()),
1067 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
1068 && !Utils.isMonkeyRunning(), isAdmin, pm);
1070 setTileEnabled(new ComponentName(packageName,
1071 Settings.WirelessSettingsActivity.class.getName()),
1072 !UserManager.isDeviceInDemoMode(this), isAdmin, pm);
1074 setTileEnabled(new ComponentName(packageName,
1075 Settings.DateTimeSettingsActivity.class.getName()),
1076 !UserManager.isDeviceInDemoMode(this), isAdmin, pm);
1077 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1078 setTileEnabled(new ComponentName(packageName,
1079 Settings.PaymentSettingsActivity.class.getName()),
1080 pm.hasSystemFeature(PackageManager.FEATURE_NFC)
1081 && pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
1082 && adapter != null && adapter.isEnabled(), isAdmin, pm);
1084 setTileEnabled(new ComponentName(packageName,
1085 Settings.PrintSettingsActivity.class.getName()),
1086 pm.hasSystemFeature(PackageManager.FEATURE_PRINTING), isAdmin, pm);
1088 final boolean showDev = mDevelopmentPreferences.getBoolean(
1089 DevelopmentSettings.PREF_SHOW, android.os.Build.TYPE.equals("eng"))
1090 && !um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
1091 setTileEnabled(new ComponentName(packageName,
1092 Settings.DevelopmentSettingsActivity.class.getName()),
1093 showDev, isAdmin, pm);
1095 // Reveal development-only quick settings tiles
1096 DevelopmentTiles.setTilesEnabled(this, showDev);
1098 if (UserHandle.MU_ENABLED && !isAdmin) {
1099 // When on restricted users, disable all extra categories (but only the settings ones).
1100 List<DashboardCategory> categories = getDashboardCategories();
1101 for (DashboardCategory category : categories) {
1102 for (Tile tile : category.tiles) {
1103 ComponentName component = tile.intent.getComponent();
1104 if (packageName.equals(component.getPackageName()) && !ArrayUtils.contains(
1105 SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1106 setTileEnabled(component, false, isAdmin, pm);
1112 String backupIntent = getResources().getString(R.string.config_backup_settings_intent);
1113 boolean useDefaultBackup = TextUtils.isEmpty(backupIntent);
1114 setTileEnabled(new ComponentName(packageName,
1115 Settings.PrivacySettingsActivity.class.getName()), useDefaultBackup, isAdmin, pm);
1116 boolean hasBackupActivity = false;
1117 if (!useDefaultBackup) {
1119 Intent intent = Intent.parseUri(backupIntent, 0);
1120 hasBackupActivity = !getPackageManager().queryIntentActivities(intent, 0).isEmpty();
1121 } catch (URISyntaxException e) {
1122 Log.e(LOG_TAG, "Invalid backup intent URI!", e);
1125 setTileEnabled(new ComponentName(packageName,
1126 BackupSettingsActivity.class.getName()), hasBackupActivity, isAdmin, pm);
1130 private void setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin,
1131 PackageManager pm) {
1132 if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName())
1133 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1136 setTileEnabled(component, enabled);
1139 private void getMetaData() {
1141 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1142 PackageManager.GET_META_DATA);
1143 if (ai == null || ai.metaData == null) return;
1144 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1145 } catch (NameNotFoundException nnfe) {
1147 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1151 // give subclasses access to the Next button
1152 public boolean hasNextButton() {
1153 return mNextButton != null;
1156 public Button getNextButton() {
1161 public boolean shouldUpRecreateTask(Intent targetIntent) {
1162 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1166 public boolean onQueryTextSubmit(String query) {
1167 switchToSearchResultsFragmentIfNeeded();
1168 mSearchQuery = query;
1169 return mSearchResultsFragment.onQueryTextSubmit(query);
1173 public boolean onQueryTextChange(String newText) {
1174 mSearchQuery = newText;
1175 if (mSearchResultsFragment == null) {
1178 return mSearchResultsFragment.onQueryTextChange(newText);
1182 public boolean onClose() {
1187 public boolean onMenuItemActionExpand(MenuItem item) {
1188 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1189 switchToSearchResultsFragmentIfNeeded();
1195 public boolean onMenuItemActionCollapse(MenuItem item) {
1196 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1197 if (mSearchMenuItemExpanded) {
1198 revertToInitialFragment();
1205 protected void onTileClicked(Tile tile) {
1206 if (mIsShowingDashboard) {
1207 // If on dashboard, don't finish so the back comes back to here.
1210 super.onTileClicked(tile);
1215 public void onProfileTileOpen() {
1216 if (!mIsShowingDashboard) {
1221 private void switchToSearchResultsFragmentIfNeeded() {
1222 if (mSearchResultsFragment != null) {
1225 Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1226 if (current != null && current instanceof SearchResultsSummary) {
1227 mSearchResultsFragment = (SearchResultsSummary) current;
1229 setContentHeaderView(null);
1230 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1231 SearchResultsSummary.class.getName(), null, false, true,
1232 R.string.search_results_title, null, true);
1234 mSearchResultsFragment.setSearchView(mSearchView);
1235 mSearchMenuItemExpanded = true;
1238 public void needToRevertToInitialFragment() {
1239 mNeedToRevertToInitialFragment = true;
1242 private void revertToInitialFragment() {
1243 mNeedToRevertToInitialFragment = false;
1244 mSearchResultsFragment = null;
1245 mSearchMenuItemExpanded = false;
1246 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1247 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1248 if (mSearchMenuItem != null) {
1249 mSearchMenuItem.collapseActionView();
1253 public Intent getResultIntentData() {
1254 return mResultIntentData;
1257 public void setResultIntentData(Intent resultIntentData) {
1258 mResultIntentData = resultIntentData;
1261 public void startSuggestion(Intent intent) {
1262 mCurrentSuggestion = intent.getComponent();
1263 startActivityForResult(intent, REQUEST_SUGGESTION);
1267 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1268 if (requestCode == REQUEST_SUGGESTION && mCurrentSuggestion != null
1269 && resultCode != RESULT_CANCELED) {
1270 getPackageManager().setComponentEnabledSetting(mCurrentSuggestion,
1271 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
1273 super.onActivityResult(requestCode, resultCode, data);