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;
52 import com.android.internal.util.ArrayUtils;
53 import com.android.settings.Settings.WifiSettingsActivity;
54 import com.android.settings.accessibility.AccessibilitySettings;
55 import com.android.settings.accessibility.AccessibilitySettingsForSetupWizard;
56 import com.android.settings.accessibility.CaptionPropertiesFragment;
57 import com.android.settings.accounts.AccountSettings;
58 import com.android.settings.accounts.AccountSyncSettings;
59 import com.android.settings.accounts.ChooseAccountActivity;
60 import com.android.settings.accounts.ManagedProfileSettings;
61 import com.android.settings.applications.AdvancedAppSettings;
62 import com.android.settings.applications.DrawOverlayDetails;
63 import com.android.settings.applications.InstalledAppDetails;
64 import com.android.settings.applications.ManageApplications;
65 import com.android.settings.applications.ManageAssist;
66 import com.android.settings.applications.NotificationApps;
67 import com.android.settings.applications.ProcessStatsSummary;
68 import com.android.settings.applications.ProcessStatsUi;
69 import com.android.settings.applications.UsageAccessDetails;
70 import com.android.settings.applications.WriteSettingsDetails;
71 import com.android.settings.applications.VrListenerSettings;
72 import com.android.settings.bluetooth.BluetoothSettings;
73 import com.android.settings.dashboard.DashboardSummary;
74 import com.android.settings.dashboard.SearchResultsSummary;
75 import com.android.settings.datausage.DataUsageSummary;
76 import com.android.settings.deviceinfo.ImeiInformation;
77 import com.android.settings.deviceinfo.PrivateVolumeForget;
78 import com.android.settings.deviceinfo.PrivateVolumeSettings;
79 import com.android.settings.deviceinfo.PublicVolumeSettings;
80 import com.android.settings.deviceinfo.SimStatus;
81 import com.android.settings.deviceinfo.Status;
82 import com.android.settings.deviceinfo.StorageSettings;
83 import com.android.settings.fuelgauge.BatterySaverSettings;
84 import com.android.settings.fuelgauge.PowerUsageDetail;
85 import com.android.settings.fuelgauge.PowerUsageSummary;
86 import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
87 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
88 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
89 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment2;
90 import com.android.settings.inputmethod.PhysicalKeyboardFragment;
91 import com.android.settings.inputmethod.SpellCheckersSettings;
92 import com.android.settings.inputmethod.UserDictionaryList;
93 import com.android.settings.localepicker.LocaleListEditor;
94 import com.android.settings.location.LocationSettings;
95 import com.android.settings.nfc.AndroidBeam;
96 import com.android.settings.nfc.PaymentSettings;
97 import com.android.settings.notification.AppNotificationSettings;
98 import com.android.settings.notification.ConfigureNotificationSettings;
99 import com.android.settings.notification.NotificationAccessSettings;
100 import com.android.settings.notification.NotificationStation;
101 import com.android.settings.notification.OtherSoundSettings;
102 import com.android.settings.notification.SoundSettings;
103 import com.android.settings.notification.ZenAccessSettings;
104 import com.android.settings.notification.ZenModeAutomationSettings;
105 import com.android.settings.notification.ZenModeEventRuleSettings;
106 import com.android.settings.notification.ZenModePrioritySettings;
107 import com.android.settings.notification.ZenModeScheduleRuleSettings;
108 import com.android.settings.notification.ZenModeSettings;
109 import com.android.settings.notification.ZenModeVisualInterruptionSettings;
110 import com.android.settings.print.PrintJobSettingsFragment;
111 import com.android.settings.print.PrintSettingsFragment;
112 import com.android.settings.qstile.DevelopmentTiles;
113 import com.android.settings.search.DynamicIndexableContentMonitor;
114 import com.android.settings.search.Index;
115 import com.android.settings.sim.SimSettings;
116 import com.android.settings.tts.TextToSpeechSettings;
117 import com.android.settings.users.UserSettings;
118 import com.android.settings.vpn2.VpnSettings;
119 import com.android.settings.wfd.WifiDisplaySettings;
120 import com.android.settings.widget.SwitchBar;
121 import com.android.settings.wifi.AdvancedWifiSettings;
122 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
123 import com.android.settings.wifi.WifiAPITest;
124 import com.android.settings.wifi.WifiInfo;
125 import com.android.settings.wifi.WifiSettings;
126 import com.android.settings.wifi.p2p.WifiP2pSettings;
127 import com.android.settingslib.drawer.DashboardCategory;
128 import com.android.settingslib.drawer.SettingsDrawerActivity;
129 import com.android.settingslib.drawer.Tile;
131 import java.util.ArrayList;
132 import java.util.List;
133 import java.util.Set;
135 public class SettingsActivity extends SettingsDrawerActivity
136 implements PreferenceManager.OnPreferenceTreeClickListener,
137 PreferenceFragment.OnPreferenceStartFragmentCallback,
138 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
139 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
140 MenuItem.OnActionExpandListener {
142 private static final String LOG_TAG = "Settings";
144 private static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
146 // Constants for state save/restore
147 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
148 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
149 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
150 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
151 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
152 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
155 * When starting this activity, the invoking Intent can contain this extra
156 * string to specify which fragment should be initially displayed.
157 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
158 * will call isValidFragment() to confirm that the fragment class name is valid for this
161 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
164 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
165 * this extra can also be specified to supply a Bundle of arguments to pass
166 * to that fragment when it is instantiated during the initial creation
169 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
172 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
174 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
176 public static final String BACK_STACK_PREFS = ":settings:prefs";
178 // extras that allow any preference activity to be launched as part of a wizard
180 // show Back and Next buttons? takes boolean parameter
181 // Back will then return RESULT_CANCELED and Next RESULT_OK
182 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
184 // add a Skip button?
185 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
187 // specify custom text for the Back or Next buttons, or cause a button to not appear
188 // at all by setting it to null
189 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
190 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
193 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
194 * those extra can also be specify to supply the title or title res id to be shown for
197 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
199 * The package name used to resolve the title resource id.
201 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
202 ":settings:show_fragment_title_res_package_name";
203 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
204 ":settings:show_fragment_title_resid";
205 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
206 ":settings:show_fragment_as_shortcut";
208 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
209 ":settings:show_fragment_as_subsetting";
211 public static final String EXTRA_HIDE_DRAWER = ":settings:hide_drawer";
213 public static final String META_DATA_KEY_FRAGMENT_CLASS =
214 "com.android.settings.FRAGMENT_CLASS";
216 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
218 private static final String EMPTY_QUERY = "";
220 private static final int REQUEST_SUGGESTION = 42;
222 private String mFragmentClass;
224 private CharSequence mInitialTitle;
225 private int mInitialTitleResId;
227 // Show only these settings for restricted users
228 private String[] SETTINGS_FOR_RESTRICTED = {
230 WifiSettingsActivity.class.getName(),
231 Settings.BluetoothSettingsActivity.class.getName(),
232 Settings.DataUsageSummaryActivity.class.getName(),
233 Settings.SimSettingsActivity.class.getName(),
234 Settings.WirelessSettingsActivity.class.getName(),
236 Settings.HomeSettingsActivity.class.getName(),
237 Settings.SoundSettingsActivity.class.getName(),
238 Settings.DisplaySettingsActivity.class.getName(),
239 Settings.StorageSettingsActivity.class.getName(),
240 Settings.ManageApplicationsActivity.class.getName(),
241 Settings.PowerUsageSummaryActivity.class.getName(),
243 Settings.LocationSettingsActivity.class.getName(),
244 Settings.SecuritySettingsActivity.class.getName(),
245 Settings.InputMethodAndLanguageSettingsActivity.class.getName(),
246 Settings.UserSettingsActivity.class.getName(),
247 Settings.AccountSettingsActivity.class.getName(),
249 Settings.DateTimeSettingsActivity.class.getName(),
250 Settings.DeviceInfoSettingsActivity.class.getName(),
251 Settings.AccessibilitySettingsActivity.class.getName(),
252 Settings.PrintSettingsActivity.class.getName(),
253 Settings.PaymentSettingsActivity.class.getName(),
256 private static final String[] ENTRY_FRAGMENTS = {
257 WirelessSettings.class.getName(),
258 WifiSettings.class.getName(),
259 AdvancedWifiSettings.class.getName(),
260 SavedAccessPointsWifiSettings.class.getName(),
261 BluetoothSettings.class.getName(),
262 SimSettings.class.getName(),
263 TetherSettings.class.getName(),
264 WifiP2pSettings.class.getName(),
265 VpnSettings.class.getName(),
266 DateTimeSettings.class.getName(),
267 LocaleListEditor.class.getName(),
268 InputMethodAndLanguageSettings.class.getName(),
269 AvailableVirtualKeyboardFragment.class.getName(),
270 SpellCheckersSettings.class.getName(),
271 UserDictionaryList.class.getName(),
272 UserDictionarySettings.class.getName(),
273 HomeSettings.class.getName(),
274 DisplaySettings.class.getName(),
275 DeviceInfoSettings.class.getName(),
276 ManageApplications.class.getName(),
277 NotificationApps.class.getName(),
278 ManageAssist.class.getName(),
279 ProcessStatsUi.class.getName(),
280 NotificationStation.class.getName(),
281 LocationSettings.class.getName(),
282 SecuritySettings.class.getName(),
283 UsageAccessDetails.class.getName(),
284 PrivacySettings.class.getName(),
285 DeviceAdminSettings.class.getName(),
286 AccessibilitySettings.class.getName(),
287 AccessibilitySettingsForSetupWizard.class.getName(),
288 CaptionPropertiesFragment.class.getName(),
289 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
290 TextToSpeechSettings.class.getName(),
291 StorageSettings.class.getName(),
292 PrivateVolumeForget.class.getName(),
293 PrivateVolumeSettings.class.getName(),
294 PublicVolumeSettings.class.getName(),
295 DevelopmentSettings.class.getName(),
296 AndroidBeam.class.getName(),
297 WifiDisplaySettings.class.getName(),
298 PowerUsageSummary.class.getName(),
299 AccountSyncSettings.class.getName(),
300 AccountSettings.class.getName(),
301 CryptKeeperSettings.class.getName(),
302 DataUsageSummary.class.getName(),
303 DreamSettings.class.getName(),
304 UserSettings.class.getName(),
305 NotificationAccessSettings.class.getName(),
306 ZenAccessSettings.class.getName(),
307 PrintSettingsFragment.class.getName(),
308 PrintJobSettingsFragment.class.getName(),
309 TrustedCredentialsSettings.class.getName(),
310 PaymentSettings.class.getName(),
311 KeyboardLayoutPickerFragment.class.getName(),
312 KeyboardLayoutPickerFragment2.class.getName(),
313 PhysicalKeyboardFragment.class.getName(),
314 ZenModeSettings.class.getName(),
315 SoundSettings.class.getName(),
316 ConfigureNotificationSettings.class.getName(),
317 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
318 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
319 InstalledAppDetails.class.getName(),
320 BatterySaverSettings.class.getName(),
321 AppNotificationSettings.class.getName(),
322 OtherSoundSettings.class.getName(),
323 ApnSettings.class.getName(),
324 ApnEditor.class.getName(),
325 WifiCallingSettings.class.getName(),
326 ZenModePrioritySettings.class.getName(),
327 ZenModeAutomationSettings.class.getName(),
328 ZenModeScheduleRuleSettings.class.getName(),
329 ZenModeEventRuleSettings.class.getName(),
330 ZenModeVisualInterruptionSettings.class.getName(),
331 ProcessStatsUi.class.getName(),
332 PowerUsageDetail.class.getName(),
333 ProcessStatsSummary.class.getName(),
334 DrawOverlayDetails.class.getName(),
335 WriteSettingsDetails.class.getName(),
336 AdvancedAppSettings.class.getName(),
337 WallpaperTypeSettings.class.getName(),
338 VrListenerSettings.class.getName(),
339 ManagedProfileSettings.class.getName(),
340 ChooseAccountActivity.class.getName(),
341 IccLockSettings.class.getName(),
342 ImeiInformation.class.getName(),
343 SimStatus.class.getName(),
344 Status.class.getName(),
345 TestingSettings.class.getName(),
346 WifiAPITest.class.getName(),
347 WifiInfo.class.getName(),
351 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
352 "android.settings.APPLICATION_DETAILS_SETTINGS"
355 private SharedPreferences mDevelopmentPreferences;
356 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
358 private boolean mBatteryPresent = true;
359 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
361 public void onReceive(Context context, Intent intent) {
362 String action = intent.getAction();
363 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
364 boolean batteryPresent = Utils.isBatteryPresent(intent);
366 if (mBatteryPresent != batteryPresent) {
367 mBatteryPresent = batteryPresent;
374 private final BroadcastReceiver mUserAddRemoveReceiver = new BroadcastReceiver() {
376 public void onReceive(Context context, Intent intent) {
377 String action = intent.getAction();
378 if (action.equals(Intent.ACTION_USER_ADDED)
379 || action.equals(Intent.ACTION_USER_REMOVED)) {
380 Index.getInstance(getApplicationContext()).update();
385 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
386 new DynamicIndexableContentMonitor();
388 private ActionBar mActionBar;
389 private SwitchBar mSwitchBar;
391 private Button mNextButton;
393 private boolean mDisplayHomeAsUpEnabled;
394 private boolean mDisplaySearch;
396 private boolean mIsShowingDashboard;
397 private boolean mIsShortcut;
399 private int mMainContentId = R.id.main_content;
400 private ViewGroup mContent;
402 private SearchView mSearchView;
403 private MenuItem mSearchMenuItem;
404 private boolean mSearchMenuItemExpanded = false;
405 private SearchResultsSummary mSearchResultsFragment;
406 private String mSearchQuery;
409 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
411 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
413 private boolean mNeedToRevertToInitialFragment = false;
415 private Intent mResultIntentData;
416 private ComponentName mCurrentSuggestion;
418 public SwitchBar getSwitchBar() {
423 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
424 // Override the fragment title for Wallpaper settings
425 CharSequence title = pref.getTitle();
426 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
427 title = getString(R.string.wallpaper_settings_fragment_title);
429 startPreferencePanel(pref.getFragment(), pref.getExtras(), -1, title,
435 public boolean onPreferenceTreeClick(Preference preference) {
440 public void onConfigurationChanged(Configuration newConfig) {
441 super.onConfigurationChanged(newConfig);
442 Index.getInstance(this).update();
446 public boolean onCreateOptionsMenu(Menu menu) {
447 if (!mDisplaySearch) {
451 MenuInflater inflater = getMenuInflater();
452 inflater.inflate(R.menu.options_menu, menu);
454 // Cache the search query (can be overriden by the OnQueryTextListener)
455 final String query = mSearchQuery;
457 mSearchMenuItem = menu.findItem(R.id.search);
458 mSearchView = (SearchView) mSearchMenuItem.getActionView();
460 if (mSearchMenuItem == null || mSearchView == null) {
464 if (mSearchResultsFragment != null) {
465 mSearchResultsFragment.setSearchView(mSearchView);
468 mSearchMenuItem.setOnActionExpandListener(this);
469 mSearchView.setOnQueryTextListener(this);
470 mSearchView.setOnCloseListener(this);
472 if (mSearchMenuItemExpanded) {
473 mSearchMenuItem.expandActionView();
475 mSearchView.setQuery(query, true /* submit */);
481 public SharedPreferences getSharedPreferences(String name, int mode) {
482 if (name.equals(getPackageName() + "_preferences")) {
483 return new SharedPreferencesLogger(this, getMetricsTag());
485 return super.getSharedPreferences(name, mode);
488 private String getMetricsTag() {
489 String tag = getClass().getName();
490 if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
491 tag = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
493 if (tag.startsWith("com.android.settings.")) {
494 tag = tag.replace("com.android.settings.", "");
499 private static boolean isShortCutIntent(final Intent intent) {
500 Set<String> categories = intent.getCategories();
501 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
504 private static boolean isLikeShortCutIntent(final Intent intent) {
505 String action = intent.getAction();
506 if (action == null) {
509 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
510 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
516 protected void onCreate(Bundle savedState) {
517 super.onCreate(savedState);
518 long startTime = System.currentTimeMillis();
520 // Should happen before any call to getIntent()
523 final Intent intent = getIntent();
524 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
525 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
527 if (intent.getBooleanExtra(EXTRA_HIDE_DRAWER, false)) {
528 setIsDrawerPresent(false);
531 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
532 Context.MODE_PRIVATE);
534 // Getting Intent properties can only be done after the super.onCreate(...)
535 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
537 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
538 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
540 final ComponentName cn = intent.getComponent();
541 final String className = cn.getClassName();
543 mIsShowingDashboard = className.equals(Settings.class.getName())
544 || className.equals(Settings.WirelessSettings.class.getName())
545 || className.equals(Settings.DeviceSettings.class.getName())
546 || className.equals(Settings.PersonalSettings.class.getName())
547 || className.equals(Settings.WirelessSettings.class.getName());
549 // This is a "Sub Settings" when:
550 // - this is a real SubSettings
551 // - or :settings:show_fragment_as_subsetting is passed to the Intent
552 final boolean isSubSettings = this instanceof SubSettings ||
553 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
555 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
557 // Check also that we are not a Theme Dialog as we don't want to override them
558 final int themeResId = getThemeResId();
559 if (themeResId != R.style.Theme_DialogWhenLarge &&
560 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
561 setTheme(R.style.Theme_SubSettings);
565 setContentView(mIsShowingDashboard ?
566 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
568 mContent = (ViewGroup) findViewById(mMainContentId);
570 getFragmentManager().addOnBackStackChangedListener(this);
572 if (mIsShowingDashboard) {
573 // Run the Index update only if we have some space
574 if (!Utils.isLowStorage(this)) {
575 long indexStartTime = System.currentTimeMillis();
576 Index.getInstance(getApplicationContext()).update();
577 if (DEBUG_TIMING) Log.d(LOG_TAG, "Index.update() took "
578 + (System.currentTimeMillis() - indexStartTime) + " ms");
580 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
584 if (savedState != null) {
585 // We are restarting from a previous saved state; used that to initialize, instead
586 // of starting fresh.
587 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
588 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
590 setTitleFromIntent(intent);
592 ArrayList<DashboardCategory> categories =
593 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
594 if (categories != null) {
596 mCategories.addAll(categories);
597 setTitleFromBackStack();
600 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
601 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
603 if (!mIsShowingDashboard) {
604 mDisplaySearch = false;
605 // UP will be shown only if it is a sub settings
607 mDisplayHomeAsUpEnabled = isSubSettings;
608 } else if (isSubSettings) {
609 mDisplayHomeAsUpEnabled = true;
611 mDisplayHomeAsUpEnabled = false;
613 setTitleFromIntent(intent);
615 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
616 switchToFragment(initialFragmentName, initialArguments, true, false,
617 mInitialTitleResId, mInitialTitle, false);
619 // No UP affordance if we are displaying the main Dashboard
620 mDisplayHomeAsUpEnabled = false;
621 // Show Search affordance
622 mDisplaySearch = true;
623 mInitialTitleResId = R.string.dashboard_title;
624 switchToFragment(DashboardSummary.class.getName(), null, false, false,
625 mInitialTitleResId, mInitialTitle, false);
629 mActionBar = getActionBar();
630 if (mActionBar != null) {
631 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
632 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
634 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
635 if (mSwitchBar != null) {
636 mSwitchBar.setMetricsTag(getMetricsTag());
639 // see if we should show Back/Next buttons
640 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
642 View buttonBar = findViewById(R.id.button_bar);
643 if (buttonBar != null) {
644 buttonBar.setVisibility(View.VISIBLE);
646 Button backButton = (Button)findViewById(R.id.back_button);
647 backButton.setOnClickListener(new OnClickListener() {
648 public void onClick(View v) {
649 setResult(RESULT_CANCELED, getResultIntentData());
653 Button skipButton = (Button)findViewById(R.id.skip_button);
654 skipButton.setOnClickListener(new OnClickListener() {
655 public void onClick(View v) {
656 setResult(RESULT_OK, getResultIntentData());
660 mNextButton = (Button)findViewById(R.id.next_button);
661 mNextButton.setOnClickListener(new OnClickListener() {
662 public void onClick(View v) {
663 setResult(RESULT_OK, getResultIntentData());
668 // set our various button parameters
669 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
670 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
671 if (TextUtils.isEmpty(buttonText)) {
672 mNextButton.setVisibility(View.GONE);
675 mNextButton.setText(buttonText);
678 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
679 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
680 if (TextUtils.isEmpty(buttonText)) {
681 backButton.setVisibility(View.GONE);
684 backButton.setText(buttonText);
687 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
688 skipButton.setVisibility(View.VISIBLE);
693 if (DEBUG_TIMING) Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
698 * Sets the id of the view continaing the main content. Should be called before calling super's
701 protected void setMainContentId(int contentId) {
702 mMainContentId = contentId;
705 private void setTitleFromIntent(Intent intent) {
706 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
707 if (initialTitleResId > 0) {
708 mInitialTitle = null;
709 mInitialTitleResId = initialTitleResId;
711 final String initialTitleResPackageName = intent.getStringExtra(
712 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
713 if (initialTitleResPackageName != null) {
715 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
716 0 /* flags */, new UserHandle(UserHandle.myUserId()));
717 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
718 setTitle(mInitialTitle);
719 mInitialTitleResId = -1;
721 } catch (NameNotFoundException e) {
722 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
725 setTitle(mInitialTitleResId);
728 mInitialTitleResId = -1;
729 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
730 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
731 setTitle(mInitialTitle);
736 public void onBackStackChanged() {
737 setTitleFromBackStack();
740 private int setTitleFromBackStack() {
741 final int count = getFragmentManager().getBackStackEntryCount();
744 if (mInitialTitleResId > 0) {
745 setTitle(mInitialTitleResId);
747 setTitle(mInitialTitle);
752 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
753 setTitleFromBackStackEntry(bse);
758 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
759 final CharSequence title;
760 final int titleRes = bse.getBreadCrumbTitleRes();
762 title = getText(titleRes);
764 title = bse.getBreadCrumbTitle();
772 protected void onSaveInstanceState(Bundle outState) {
773 super.onSaveInstanceState(outState);
775 if (mCategories.size() > 0) {
776 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
779 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
780 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
782 if (mDisplaySearch) {
783 // The option menus are created if the ActionBar is visible and they are also created
784 // asynchronously. If you launch Settings with an Intent action like
785 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
786 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
787 // menu item and search view are null.
788 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
789 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
791 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
792 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
797 protected void onStart() {
800 if (mNeedToRevertToInitialFragment) {
801 revertToInitialFragment();
804 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
806 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
810 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
811 mDevelopmentPreferencesListener);
813 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
814 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_ADDED));
815 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
817 mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
819 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
820 onQueryTextSubmit(mSearchQuery);
826 protected void onStop() {
828 unregisterReceiver(mBatteryInfoReceiver);
829 unregisterReceiver(mUserAddRemoveReceiver);
830 mDynamicIndexableContentMonitor.unregister();
834 public void onDestroy() {
837 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
838 mDevelopmentPreferencesListener);
839 mDevelopmentPreferencesListener = null;
842 protected boolean isValidFragment(String fragmentName) {
843 // Almost all fragments are wrapped in this,
844 // except for a few that have their own activities.
845 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
846 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
852 public Intent getIntent() {
853 Intent superIntent = super.getIntent();
854 String startingFragment = getStartingFragmentClass(superIntent);
855 // This is called from super.onCreate, isMultiPane() is not yet reliable
856 // Do not use onIsHidingHeaders either, which relies itself on this method
857 if (startingFragment != null) {
858 Intent modIntent = new Intent(superIntent);
859 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
860 Bundle args = superIntent.getExtras();
862 args = new Bundle(args);
866 args.putParcelable("intent", superIntent);
867 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
874 * Checks if the component name in the intent is different from the Settings class and
875 * returns the class name to load as a fragment.
877 private String getStartingFragmentClass(Intent intent) {
878 if (mFragmentClass != null) return mFragmentClass;
880 String intentClass = intent.getComponent().getClassName();
881 if (intentClass.equals(getClass().getName())) return null;
883 if ("com.android.settings.ManageApplications".equals(intentClass)
884 || "com.android.settings.RunningServices".equals(intentClass)
885 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
886 // Old names of manage apps.
887 intentClass = com.android.settings.applications.ManageApplications.class.getName();
894 * Start a new fragment containing a preference panel. If the preferences
895 * are being displayed in multi-pane mode, the given fragment class will
896 * be instantiated and placed in the appropriate pane. If running in
897 * single-pane mode, a new activity will be launched in which to show the
900 * @param fragmentClass Full name of the class implementing the fragment.
901 * @param args Any desired arguments to supply to the fragment.
902 * @param titleRes Optional resource identifier of the title of this
904 * @param titleText Optional text of the title of this fragment.
905 * @param resultTo Optional fragment that result data should be sent to.
906 * If non-null, resultTo.onActivityResult() will be called when this
907 * preference panel is done. The launched panel must use
908 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
909 * @param resultRequestCode If resultTo is non-null, this is the caller's
910 * request code to be received with the result.
912 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
913 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
916 if (titleText != null) {
917 title = titleText.toString();
919 // There not much we can do in that case
923 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
924 titleRes, title, mIsShortcut);
928 * Start a new fragment in a new activity containing a preference panel for a given user. If the
929 * preferences are being displayed in multi-pane mode, the given fragment class will be
930 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
931 * activity will be launched in which to show the fragment.
933 * @param fragmentClass Full name of the class implementing the fragment.
934 * @param args Any desired arguments to supply to the fragment.
935 * @param titleRes Optional resource identifier of the title of this fragment.
936 * @param titleText Optional text of the title of this fragment.
937 * @param userHandle The user for which the panel has to be started.
939 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
940 CharSequence titleText, UserHandle userHandle) {
941 // This is a workaround.
943 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
944 // starting the fragment could cause a native stack corruption. See b/17523189. However,
945 // adding that flag and start the preference panel with the same UserHandler will make it
946 // impossible to use back button to return to the previous screen. See b/20042570.
948 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
949 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
950 // when we're calling it as the same user.
951 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
952 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
956 if (titleText != null) {
957 title = titleText.toString();
959 // There not much we can do in that case
963 Utils.startWithFragmentAsUser(this, fragmentClass, args,
964 titleRes, title, mIsShortcut, userHandle);
969 * Called by a preference panel fragment to finish itself.
971 * @param caller The fragment that is asking to be finished.
972 * @param resultCode Optional result code to send back to the original
973 * launching fragment.
974 * @param resultData Optional result data to send back to the original
975 * launching fragment.
977 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
978 setResult(resultCode, resultData);
983 * Start a new fragment.
985 * @param fragment The fragment to start
986 * @param push If true, the current fragment will be pushed onto the back stack. If false,
987 * the current fragment will be replaced.
989 public void startPreferenceFragment(Fragment fragment, boolean push) {
990 FragmentTransaction transaction = getFragmentManager().beginTransaction();
991 transaction.replace(mMainContentId, fragment);
993 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
994 transaction.addToBackStack(BACK_STACK_PREFS);
996 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
998 transaction.commitAllowingStateLoss();
1002 * Switch to a specific Fragment with taking care of validation, Title and BackStack
1004 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1005 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1006 if (validate && !isValidFragment(fragmentName)) {
1007 throw new IllegalArgumentException("Invalid fragment for this activity: "
1010 Fragment f = Fragment.instantiate(this, fragmentName, args);
1011 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1012 transaction.replace(mMainContentId, f);
1013 if (withTransition) {
1014 TransitionManager.beginDelayedTransition(mContent);
1016 if (addToBackStack) {
1017 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1019 if (titleResId > 0) {
1020 transaction.setBreadCrumbTitle(titleResId);
1021 } else if (title != null) {
1022 transaction.setBreadCrumbTitle(title);
1024 transaction.commitAllowingStateLoss();
1025 getFragmentManager().executePendingTransactions();
1029 private void updateTilesList() {
1030 // Generally the items that are will be changing from these updates will
1031 // not be in the top list of tiles, so run it in the background and the
1032 // SettingsDrawerActivity will pick up on the updates automatically.
1033 AsyncTask.execute(new Runnable() {
1036 doUpdateTilesList();
1041 private void doUpdateTilesList() {
1042 PackageManager pm = getPackageManager();
1043 final UserManager um = UserManager.get(this);
1044 final boolean isAdmin = um.isAdminUser();
1046 String packageName = getPackageName();
1047 setTileEnabled(new ComponentName(packageName, WifiSettingsActivity.class.getName()),
1048 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin, pm);
1050 setTileEnabled(new ComponentName(packageName,
1051 Settings.BluetoothSettingsActivity.class.getName()),
1052 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin, pm);
1054 setTileEnabled(new ComponentName(packageName,
1055 Settings.DataUsageSummaryActivity.class.getName()),
1056 Utils.isBandwidthControlEnabled(), isAdmin, pm);
1058 setTileEnabled(new ComponentName(packageName,
1059 Settings.SimSettingsActivity.class.getName()),
1060 Utils.showSimCardTile(this), isAdmin, pm);
1062 setTileEnabled(new ComponentName(packageName,
1063 Settings.PowerUsageSummaryActivity.class.getName()),
1064 mBatteryPresent, isAdmin, pm);
1066 setTileEnabled(new ComponentName(packageName,
1067 Settings.UserSettingsActivity.class.getName()),
1068 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
1069 && !Utils.isMonkeyRunning(), isAdmin, pm);
1071 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1072 setTileEnabled(new ComponentName(packageName,
1073 Settings.PaymentSettingsActivity.class.getName()),
1074 pm.hasSystemFeature(PackageManager.FEATURE_NFC)
1075 && pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
1076 && adapter != null && adapter.isEnabled(), isAdmin, pm);
1078 setTileEnabled(new ComponentName(packageName,
1079 Settings.PrintSettingsActivity.class.getName()),
1080 pm.hasSystemFeature(PackageManager.FEATURE_PRINTING), isAdmin, pm);
1082 final boolean showDev = mDevelopmentPreferences.getBoolean(
1083 DevelopmentSettings.PREF_SHOW, android.os.Build.TYPE.equals("eng"))
1084 && !um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
1085 setTileEnabled(new ComponentName(packageName,
1086 Settings.DevelopmentSettingsActivity.class.getName()),
1087 showDev, isAdmin, pm);
1089 // Reveal development-only quick settings tiles
1090 DevelopmentTiles.setTilesEnabled(this, showDev);
1092 if (UserHandle.MU_ENABLED && !isAdmin) {
1093 // When on restricted users, disable all extra categories (but only the settings ones).
1094 List<DashboardCategory> categories = getDashboardCategories();
1095 for (DashboardCategory category : categories) {
1096 for (Tile tile : category.tiles) {
1097 ComponentName component = tile.intent.getComponent();
1098 if (packageName.equals(component.getPackageName()) && !ArrayUtils.contains(
1099 SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1100 setTileEnabled(component, false, isAdmin, pm);
1107 private void setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin,
1108 PackageManager pm) {
1109 if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName())
1110 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1113 setTileEnabled(component, enabled);
1116 private void getMetaData() {
1118 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1119 PackageManager.GET_META_DATA);
1120 if (ai == null || ai.metaData == null) return;
1121 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1122 } catch (NameNotFoundException nnfe) {
1124 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1128 // give subclasses access to the Next button
1129 public boolean hasNextButton() {
1130 return mNextButton != null;
1133 public Button getNextButton() {
1138 public boolean shouldUpRecreateTask(Intent targetIntent) {
1139 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1143 public boolean onQueryTextSubmit(String query) {
1144 switchToSearchResultsFragmentIfNeeded();
1145 mSearchQuery = query;
1146 return mSearchResultsFragment.onQueryTextSubmit(query);
1150 public boolean onQueryTextChange(String newText) {
1151 mSearchQuery = newText;
1152 if (mSearchResultsFragment == null) {
1155 return mSearchResultsFragment.onQueryTextChange(newText);
1159 public boolean onClose() {
1164 public boolean onMenuItemActionExpand(MenuItem item) {
1165 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1166 switchToSearchResultsFragmentIfNeeded();
1172 public boolean onMenuItemActionCollapse(MenuItem item) {
1173 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1174 if (mSearchMenuItemExpanded) {
1175 revertToInitialFragment();
1182 protected void onTileClicked(Tile tile) {
1183 if (mIsShowingDashboard) {
1184 // If on dashboard, don't finish so the back comes back to here.
1187 super.onTileClicked(tile);
1192 public void onProfileTileOpen() {
1193 if (!mIsShowingDashboard) {
1198 private void switchToSearchResultsFragmentIfNeeded() {
1199 if (mSearchResultsFragment != null) {
1202 Fragment current = getFragmentManager().findFragmentById(mMainContentId);
1203 if (current != null && current instanceof SearchResultsSummary) {
1204 mSearchResultsFragment = (SearchResultsSummary) current;
1206 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1207 SearchResultsSummary.class.getName(), null, false, true,
1208 R.string.search_results_title, null, true);
1210 mSearchResultsFragment.setSearchView(mSearchView);
1211 mSearchMenuItemExpanded = true;
1214 public void needToRevertToInitialFragment() {
1215 mNeedToRevertToInitialFragment = true;
1218 private void revertToInitialFragment() {
1219 mNeedToRevertToInitialFragment = false;
1220 mSearchResultsFragment = null;
1221 mSearchMenuItemExpanded = false;
1222 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1223 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1224 if (mSearchMenuItem != null) {
1225 mSearchMenuItem.collapseActionView();
1229 public Intent getResultIntentData() {
1230 return mResultIntentData;
1233 public void setResultIntentData(Intent resultIntentData) {
1234 mResultIntentData = resultIntentData;
1237 public void startSuggestion(Intent intent) {
1238 mCurrentSuggestion = intent.getComponent();
1239 startActivityForResult(intent, REQUEST_SUGGESTION);
1243 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1244 if (requestCode == REQUEST_SUGGESTION && mCurrentSuggestion != null
1245 && resultCode != RESULT_CANCELED) {
1246 getPackageManager().setComponentEnabledSetting(mCurrentSuggestion,
1247 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
1249 super.onActivityResult(requestCode, resultCode, data);