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.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.DashboardContainerFragment;
74 import com.android.settings.dashboard.SearchResultsSummary;
75 import com.android.settings.datausage.DataUsageSummary;
76 import com.android.settings.deviceinfo.PrivateVolumeForget;
77 import com.android.settings.deviceinfo.PrivateVolumeSettings;
78 import com.android.settings.deviceinfo.PublicVolumeSettings;
79 import com.android.settings.deviceinfo.StorageSettings;
80 import com.android.settings.fuelgauge.BatterySaverSettings;
81 import com.android.settings.fuelgauge.PowerUsageDetail;
82 import com.android.settings.fuelgauge.PowerUsageSummary;
83 import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
84 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
85 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
86 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment2;
87 import com.android.settings.inputmethod.PhysicalKeyboardFragment;
88 import com.android.settings.inputmethod.SpellCheckersSettings;
89 import com.android.settings.inputmethod.UserDictionaryList;
90 import com.android.settings.localepicker.LocaleListEditor;
91 import com.android.settings.location.LocationSettings;
92 import com.android.settings.nfc.AndroidBeam;
93 import com.android.settings.nfc.PaymentSettings;
94 import com.android.settings.notification.AppNotificationSettings;
95 import com.android.settings.notification.ConfigureNotificationSettings;
96 import com.android.settings.notification.NotificationAccessSettings;
97 import com.android.settings.notification.NotificationStation;
98 import com.android.settings.notification.OtherSoundSettings;
99 import com.android.settings.notification.SoundSettings;
100 import com.android.settings.notification.ZenAccessSettings;
101 import com.android.settings.notification.ZenModeAutomationSettings;
102 import com.android.settings.notification.ZenModeEventRuleSettings;
103 import com.android.settings.notification.ZenModePrioritySettings;
104 import com.android.settings.notification.ZenModeScheduleRuleSettings;
105 import com.android.settings.notification.ZenModeSettings;
106 import com.android.settings.notification.ZenModeVisualInterruptionSettings;
107 import com.android.settings.overlay.FeatureFactory;
108 import com.android.settings.print.PrintJobSettingsFragment;
109 import com.android.settings.print.PrintSettingsFragment;
110 import com.android.settings.qstile.DevelopmentTiles;
111 import com.android.settings.search.DynamicIndexableContentMonitor;
112 import com.android.settings.search.Index;
113 import com.android.settings.sim.SimSettings;
114 import com.android.settings.tts.TextToSpeechSettings;
115 import com.android.settings.users.UserSettings;
116 import com.android.settings.vpn2.VpnSettings;
117 import com.android.settings.wfd.WifiDisplaySettings;
118 import com.android.settings.widget.SwitchBar;
119 import com.android.settings.wifi.AdvancedWifiSettings;
120 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
121 import com.android.settings.wifi.WifiSettings;
122 import com.android.settings.wifi.p2p.WifiP2pSettings;
123 import com.android.settingslib.drawer.DashboardCategory;
124 import com.android.settingslib.drawer.SettingsDrawerActivity;
125 import com.android.settingslib.drawer.Tile;
127 import java.util.ArrayList;
128 import java.util.List;
129 import java.util.Set;
131 public class SettingsActivity extends SettingsDrawerActivity
132 implements PreferenceManager.OnPreferenceTreeClickListener,
133 PreferenceFragment.OnPreferenceStartFragmentCallback,
134 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
135 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
136 MenuItem.OnActionExpandListener {
138 private static final String LOG_TAG = "Settings";
140 private static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
142 // Constants for state save/restore
143 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
144 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
145 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
146 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
147 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
148 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
151 * When starting this activity, the invoking Intent can contain this extra
152 * string to specify which fragment should be initially displayed.
153 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
154 * will call isValidFragment() to confirm that the fragment class name is valid for this
157 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
160 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
161 * this extra can also be specified to supply a Bundle of arguments to pass
162 * to that fragment when it is instantiated during the initial creation
165 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
168 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
170 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
172 public static final String BACK_STACK_PREFS = ":settings:prefs";
174 // extras that allow any preference activity to be launched as part of a wizard
176 // show Back and Next buttons? takes boolean parameter
177 // Back will then return RESULT_CANCELED and Next RESULT_OK
178 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
180 // add a Skip button?
181 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
183 // specify custom text for the Back or Next buttons, or cause a button to not appear
184 // at all by setting it to null
185 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
186 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
189 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
190 * those extra can also be specify to supply the title or title res id to be shown for
193 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
195 * The package name used to resolve the title resource id.
197 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
198 ":settings:show_fragment_title_res_package_name";
199 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
200 ":settings:show_fragment_title_resid";
201 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
202 ":settings:show_fragment_as_shortcut";
204 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
205 ":settings:show_fragment_as_subsetting";
207 public static final String EXTRA_HIDE_DRAWER = ":settings:hide_drawer";
209 public static final String META_DATA_KEY_FRAGMENT_CLASS =
210 "com.android.settings.FRAGMENT_CLASS";
212 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
214 private static final String EMPTY_QUERY = "";
216 private static final int REQUEST_SUGGESTION = 42;
218 private String mFragmentClass;
220 private CharSequence mInitialTitle;
221 private int mInitialTitleResId;
223 // Show only these settings for restricted users
224 private String[] SETTINGS_FOR_RESTRICTED = {
226 WifiSettingsActivity.class.getName(),
227 Settings.BluetoothSettingsActivity.class.getName(),
228 Settings.DataUsageSummaryActivity.class.getName(),
229 Settings.SimSettingsActivity.class.getName(),
230 Settings.WirelessSettingsActivity.class.getName(),
232 Settings.HomeSettingsActivity.class.getName(),
233 Settings.SoundSettingsActivity.class.getName(),
234 Settings.DisplaySettingsActivity.class.getName(),
235 Settings.StorageSettingsActivity.class.getName(),
236 Settings.ManageApplicationsActivity.class.getName(),
237 Settings.PowerUsageSummaryActivity.class.getName(),
239 Settings.LocationSettingsActivity.class.getName(),
240 Settings.SecuritySettingsActivity.class.getName(),
241 Settings.InputMethodAndLanguageSettingsActivity.class.getName(),
242 Settings.UserSettingsActivity.class.getName(),
243 Settings.AccountSettingsActivity.class.getName(),
245 Settings.DateTimeSettingsActivity.class.getName(),
246 Settings.DeviceInfoSettingsActivity.class.getName(),
247 Settings.AccessibilitySettingsActivity.class.getName(),
248 Settings.PrintSettingsActivity.class.getName(),
249 Settings.PaymentSettingsActivity.class.getName(),
252 private static final String[] ENTRY_FRAGMENTS = {
253 WirelessSettings.class.getName(),
254 WifiSettings.class.getName(),
255 AdvancedWifiSettings.class.getName(),
256 SavedAccessPointsWifiSettings.class.getName(),
257 BluetoothSettings.class.getName(),
258 SimSettings.class.getName(),
259 TetherSettings.class.getName(),
260 WifiP2pSettings.class.getName(),
261 VpnSettings.class.getName(),
262 DateTimeSettings.class.getName(),
263 LocaleListEditor.class.getName(),
264 InputMethodAndLanguageSettings.class.getName(),
265 AvailableVirtualKeyboardFragment.class.getName(),
266 SpellCheckersSettings.class.getName(),
267 UserDictionaryList.class.getName(),
268 UserDictionarySettings.class.getName(),
269 HomeSettings.class.getName(),
270 DisplaySettings.class.getName(),
271 DeviceInfoSettings.class.getName(),
272 ManageApplications.class.getName(),
273 NotificationApps.class.getName(),
274 ManageAssist.class.getName(),
275 ProcessStatsUi.class.getName(),
276 NotificationStation.class.getName(),
277 LocationSettings.class.getName(),
278 SecuritySettings.class.getName(),
279 UsageAccessDetails.class.getName(),
280 PrivacySettings.class.getName(),
281 DeviceAdminSettings.class.getName(),
282 AccessibilitySettings.class.getName(),
283 AccessibilitySettingsForSetupWizard.class.getName(),
284 CaptionPropertiesFragment.class.getName(),
285 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
286 TextToSpeechSettings.class.getName(),
287 StorageSettings.class.getName(),
288 PrivateVolumeForget.class.getName(),
289 PrivateVolumeSettings.class.getName(),
290 PublicVolumeSettings.class.getName(),
291 DevelopmentSettings.class.getName(),
292 AndroidBeam.class.getName(),
293 WifiDisplaySettings.class.getName(),
294 PowerUsageSummary.class.getName(),
295 AccountSyncSettings.class.getName(),
296 AccountSettings.class.getName(),
297 CryptKeeperSettings.class.getName(),
298 DataUsageSummary.class.getName(),
299 DreamSettings.class.getName(),
300 UserSettings.class.getName(),
301 NotificationAccessSettings.class.getName(),
302 ZenAccessSettings.class.getName(),
303 PrintSettingsFragment.class.getName(),
304 PrintJobSettingsFragment.class.getName(),
305 TrustedCredentialsSettings.class.getName(),
306 PaymentSettings.class.getName(),
307 KeyboardLayoutPickerFragment.class.getName(),
308 KeyboardLayoutPickerFragment2.class.getName(),
309 PhysicalKeyboardFragment.class.getName(),
310 ZenModeSettings.class.getName(),
311 SoundSettings.class.getName(),
312 ConfigureNotificationSettings.class.getName(),
313 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
314 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
315 InstalledAppDetails.class.getName(),
316 BatterySaverSettings.class.getName(),
317 AppNotificationSettings.class.getName(),
318 OtherSoundSettings.class.getName(),
319 ApnSettings.class.getName(),
320 WifiCallingSettings.class.getName(),
321 ZenModePrioritySettings.class.getName(),
322 ZenModeAutomationSettings.class.getName(),
323 ZenModeScheduleRuleSettings.class.getName(),
324 ZenModeEventRuleSettings.class.getName(),
325 ZenModeVisualInterruptionSettings.class.getName(),
326 ProcessStatsUi.class.getName(),
327 PowerUsageDetail.class.getName(),
328 ProcessStatsSummary.class.getName(),
329 DrawOverlayDetails.class.getName(),
330 WriteSettingsDetails.class.getName(),
331 AdvancedAppSettings.class.getName(),
332 WallpaperTypeSettings.class.getName(),
333 VrListenerSettings.class.getName(),
334 ManagedProfileSettings.class.getName(),
338 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
339 "android.settings.APPLICATION_DETAILS_SETTINGS"
342 private SharedPreferences mDevelopmentPreferences;
343 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
345 private boolean mBatteryPresent = true;
346 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
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);
353 if (mBatteryPresent != batteryPresent) {
354 mBatteryPresent = batteryPresent;
361 private final BroadcastReceiver mUserAddRemoveReceiver = new BroadcastReceiver() {
363 public void onReceive(Context context, Intent intent) {
364 String action = intent.getAction();
365 if (action.equals(Intent.ACTION_USER_ADDED)
366 || action.equals(Intent.ACTION_USER_REMOVED)) {
367 Index.getInstance(getApplicationContext()).update();
372 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
373 new DynamicIndexableContentMonitor();
375 private ActionBar mActionBar;
376 private SwitchBar mSwitchBar;
378 private Button mNextButton;
380 private boolean mDisplayHomeAsUpEnabled;
381 private boolean mDisplaySearch;
383 private boolean mIsShowingDashboard;
384 private boolean mIsShortcut;
386 private int mMainContentId = R.id.main_content;
387 private ViewGroup mContent;
389 private SearchView mSearchView;
390 private MenuItem mSearchMenuItem;
391 private boolean mSearchMenuItemExpanded = false;
392 private SearchResultsSummary mSearchResultsFragment;
393 private String mSearchQuery;
396 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
398 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
400 private boolean mNeedToRevertToInitialFragment = false;
402 private Intent mResultIntentData;
403 private ComponentName mCurrentSuggestion;
405 public SwitchBar getSwitchBar() {
410 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
411 // Override the fragment title for Wallpaper settings
412 CharSequence title = pref.getTitle();
413 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
414 title = getString(R.string.wallpaper_settings_fragment_title);
416 startPreferencePanel(pref.getFragment(), pref.getExtras(), -1, title,
422 public boolean onPreferenceTreeClick(Preference preference) {
427 public void onConfigurationChanged(Configuration newConfig) {
428 super.onConfigurationChanged(newConfig);
429 Index.getInstance(this).update();
433 protected void onStart() {
436 if (mNeedToRevertToInitialFragment) {
437 revertToInitialFragment();
442 public boolean onCreateOptionsMenu(Menu menu) {
443 if (!mDisplaySearch) {
447 MenuInflater inflater = getMenuInflater();
448 inflater.inflate(R.menu.options_menu, menu);
450 // Cache the search query (can be overriden by the OnQueryTextListener)
451 final String query = mSearchQuery;
453 mSearchMenuItem = menu.findItem(R.id.search);
454 mSearchView = (SearchView) mSearchMenuItem.getActionView();
456 if (mSearchMenuItem == null || mSearchView == null) {
460 if (mSearchResultsFragment != null) {
461 mSearchResultsFragment.setSearchView(mSearchView);
464 mSearchMenuItem.setOnActionExpandListener(this);
465 mSearchView.setOnQueryTextListener(this);
466 mSearchView.setOnCloseListener(this);
468 if (mSearchMenuItemExpanded) {
469 mSearchMenuItem.expandActionView();
471 mSearchView.setQuery(query, true /* submit */);
477 public SharedPreferences getSharedPreferences(String name, int mode) {
478 if (name.equals(getPackageName() + "_preferences")) {
479 return new SharedPreferencesLogger(this, getMetricsTag());
481 return super.getSharedPreferences(name, mode);
484 private String getMetricsTag() {
485 String tag = getClass().getName();
486 if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
487 tag = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
489 if (tag.startsWith("com.android.settings.")) {
490 tag = tag.replace("com.android.settings.", "");
495 private static boolean isShortCutIntent(final Intent intent) {
496 Set<String> categories = intent.getCategories();
497 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
500 private static boolean isLikeShortCutIntent(final Intent intent) {
501 String action = intent.getAction();
502 if (action == null) {
505 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
506 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
512 protected void onCreate(Bundle savedState) {
513 super.onCreate(savedState);
514 long startTime = System.currentTimeMillis();
516 // Should happen before any call to getIntent()
519 final Intent intent = getIntent();
520 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
521 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
523 if (intent.getBooleanExtra(EXTRA_HIDE_DRAWER, false)) {
524 setIsDrawerPresent(false);
527 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
528 Context.MODE_PRIVATE);
530 // Getting Intent properties can only be done after the super.onCreate(...)
531 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
533 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
534 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
536 final ComponentName cn = intent.getComponent();
537 final String className = cn.getClassName();
539 mIsShowingDashboard = className.equals(Settings.class.getName())
540 || className.equals(Settings.WirelessSettings.class.getName())
541 || className.equals(Settings.DeviceSettings.class.getName())
542 || className.equals(Settings.PersonalSettings.class.getName())
543 || className.equals(Settings.WirelessSettings.class.getName());
545 // This is a "Sub Settings" when:
546 // - this is a real SubSettings
547 // - or :settings:show_fragment_as_subsetting is passed to the Intent
548 final boolean isSubSettings = this instanceof SubSettings ||
549 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
551 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
553 // Check also that we are not a Theme Dialog as we don't want to override them
554 final int themeResId = getThemeResId();
555 if (themeResId != R.style.Theme_DialogWhenLarge &&
556 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
557 setTheme(R.style.Theme_SubSettings);
561 setContentView(mIsShowingDashboard ?
562 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
564 mContent = (ViewGroup) findViewById(mMainContentId);
566 getFragmentManager().addOnBackStackChangedListener(this);
568 if (mIsShowingDashboard) {
569 // Run the Index update only if we have some space
570 if (!Utils.isLowStorage(this)) {
571 long indexStartTime = System.currentTimeMillis();
572 Index.getInstance(getApplicationContext()).update();
573 if (DEBUG_TIMING) Log.d(LOG_TAG, "Index.update() took "
574 + (System.currentTimeMillis() - indexStartTime) + " ms");
576 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
580 if (savedState != null) {
581 // We are restarting from a previous saved state; used that to initialize, instead
582 // of starting fresh.
583 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
584 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
586 setTitleFromIntent(intent);
588 ArrayList<DashboardCategory> categories =
589 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
590 if (categories != null) {
592 mCategories.addAll(categories);
593 setTitleFromBackStack();
596 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
597 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
599 if (!mIsShowingDashboard) {
600 mDisplaySearch = false;
601 // UP will be shown only if it is a sub settings
603 mDisplayHomeAsUpEnabled = isSubSettings;
604 } else if (isSubSettings) {
605 mDisplayHomeAsUpEnabled = true;
607 mDisplayHomeAsUpEnabled = false;
609 setTitleFromIntent(intent);
611 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
612 switchToFragment(initialFragmentName, initialArguments, true, false,
613 mInitialTitleResId, mInitialTitle, false);
615 // No UP affordance if we are displaying the main Dashboard
616 mDisplayHomeAsUpEnabled = false;
617 // Show Search affordance
618 mDisplaySearch = true;
619 mInitialTitleResId = R.string.dashboard_title;
620 switchToFragment(DashboardContainerFragment.class.getName(), null, false, false,
621 mInitialTitleResId, mInitialTitle, false);
625 mActionBar = getActionBar();
626 if (mActionBar != null) {
627 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
628 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
630 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
631 if (mSwitchBar != null) {
632 mSwitchBar.setMetricsTag(getMetricsTag());
635 // see if we should show Back/Next buttons
636 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
638 View buttonBar = findViewById(R.id.button_bar);
639 if (buttonBar != null) {
640 buttonBar.setVisibility(View.VISIBLE);
642 Button backButton = (Button)findViewById(R.id.back_button);
643 backButton.setOnClickListener(new OnClickListener() {
644 public void onClick(View v) {
645 setResult(RESULT_CANCELED, getResultIntentData());
649 Button skipButton = (Button)findViewById(R.id.skip_button);
650 skipButton.setOnClickListener(new OnClickListener() {
651 public void onClick(View v) {
652 setResult(RESULT_OK, getResultIntentData());
656 mNextButton = (Button)findViewById(R.id.next_button);
657 mNextButton.setOnClickListener(new OnClickListener() {
658 public void onClick(View v) {
659 setResult(RESULT_OK, getResultIntentData());
664 // set our various button parameters
665 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
666 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
667 if (TextUtils.isEmpty(buttonText)) {
668 mNextButton.setVisibility(View.GONE);
671 mNextButton.setText(buttonText);
674 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
675 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
676 if (TextUtils.isEmpty(buttonText)) {
677 backButton.setVisibility(View.GONE);
680 backButton.setText(buttonText);
683 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
684 skipButton.setVisibility(View.VISIBLE);
689 if (DEBUG_TIMING) Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
694 * Sets the id of the view containing the main content. Should be called before calling super's
697 protected void setMainContentId(int contentId) {
698 mMainContentId = contentId;
701 private void setTitleFromIntent(Intent intent) {
702 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
703 if (initialTitleResId > 0) {
704 mInitialTitle = null;
705 mInitialTitleResId = initialTitleResId;
707 final String initialTitleResPackageName = intent.getStringExtra(
708 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
709 if (initialTitleResPackageName != null) {
711 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
712 0 /* flags */, new UserHandle(UserHandle.myUserId()));
713 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
714 setTitle(mInitialTitle);
715 mInitialTitleResId = -1;
717 } catch (NameNotFoundException e) {
718 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
721 setTitle(mInitialTitleResId);
724 mInitialTitleResId = -1;
725 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
726 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
727 setTitle(mInitialTitle);
732 public void onBackStackChanged() {
733 setTitleFromBackStack();
736 private void setTitleFromBackStack() {
737 final int count = getFragmentManager().getBackStackEntryCount();
740 if (mInitialTitleResId > 0) {
741 setTitle(mInitialTitleResId);
743 setTitle(mInitialTitle);
748 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
749 setTitleFromBackStackEntry(bse);
752 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
753 final CharSequence title;
754 final int titleRes = bse.getBreadCrumbTitleRes();
756 title = getText(titleRes);
758 title = bse.getBreadCrumbTitle();
766 protected void onSaveInstanceState(Bundle outState) {
767 super.onSaveInstanceState(outState);
769 if (mCategories.size() > 0) {
770 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
773 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
774 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
776 if (mDisplaySearch) {
777 // The option menus are created if the ActionBar is visible and they are also created
778 // asynchronously. If you launch Settings with an Intent action like
779 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
780 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
781 // menu item and search view are null.
782 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
783 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
785 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
786 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
791 protected void onResume() {
794 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
796 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
800 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
801 mDevelopmentPreferencesListener);
803 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
804 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_ADDED));
805 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
807 mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
809 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
810 onQueryTextSubmit(mSearchQuery);
816 protected void onPause() {
818 unregisterReceiver(mBatteryInfoReceiver);
819 unregisterReceiver(mUserAddRemoveReceiver);
820 mDynamicIndexableContentMonitor.unregister();
824 public void onDestroy() {
827 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
828 mDevelopmentPreferencesListener);
829 mDevelopmentPreferencesListener = null;
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;
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();
852 args = new Bundle(args);
856 args.putParcelable("intent", superIntent);
857 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
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.
867 private String getStartingFragmentClass(Intent intent) {
868 if (mFragmentClass != null) return mFragmentClass;
870 String intentClass = intent.getComponent().getClassName();
871 if (intentClass.equals(getClass().getName())) return null;
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();
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
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
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.
902 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
903 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
906 if (titleText != null) {
907 title = titleText.toString();
909 // There not much we can do in that case
913 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
914 titleRes, title, mIsShortcut);
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.
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.
929 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
930 CharSequence titleText, UserHandle userHandle) {
931 // This is a workaround.
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.
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);
946 if (titleText != null) {
947 title = titleText.toString();
949 // There not much we can do in that case
953 Utils.startWithFragmentAsUser(this, fragmentClass, args,
954 titleRes, title, mIsShortcut, userHandle);
959 * Called by a preference panel fragment to finish itself.
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.
967 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
968 setResult(resultCode, resultData);
973 * Start a new fragment.
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.
979 public void startPreferenceFragment(Fragment fragment, boolean push) {
980 FragmentTransaction transaction = getFragmentManager().beginTransaction();
981 transaction.replace(mMainContentId, fragment);
983 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
984 transaction.addToBackStack(BACK_STACK_PREFS);
986 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
988 transaction.commitAllowingStateLoss();
992 * Switch to a specific Fragment with taking care of validation, Title and BackStack
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: "
1000 Fragment f = Fragment.instantiate(this, fragmentName, args);
1001 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1002 transaction.replace(mMainContentId, f);
1003 if (withTransition) {
1004 TransitionManager.beginDelayedTransition(mContent);
1006 if (addToBackStack) {
1007 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1009 if (titleResId > 0) {
1010 transaction.setBreadCrumbTitle(titleResId);
1011 } else if (title != null) {
1012 transaction.setBreadCrumbTitle(title);
1014 transaction.commitAllowingStateLoss();
1015 getFragmentManager().executePendingTransactions();
1019 private void updateTilesList() {
1020 // Generally the items that are will be changing from these updates will
1021 // not be in the top list of tiles, so run it in the background and the
1022 // SettingsDrawerActivity will pick up on the updates automatically.
1023 AsyncTask.execute(new Runnable() {
1026 doUpdateTilesList();
1031 private void doUpdateTilesList() {
1032 PackageManager pm = getPackageManager();
1033 final UserManager um = UserManager.get(this);
1034 final boolean isAdmin = um.isAdminUser();
1036 String packageName = getPackageName();
1037 setTileEnabled(new ComponentName(packageName, WifiSettingsActivity.class.getName()),
1038 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin, pm);
1040 setTileEnabled(new ComponentName(packageName,
1041 Settings.BluetoothSettingsActivity.class.getName()),
1042 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin, pm);
1044 setTileEnabled(new ComponentName(packageName,
1045 Settings.DataUsageSummaryActivity.class.getName()),
1046 Utils.isBandwidthControlEnabled(), isAdmin, pm);
1048 setTileEnabled(new ComponentName(packageName,
1049 Settings.SimSettingsActivity.class.getName()),
1050 Utils.showSimCardTile(this), isAdmin, pm);
1052 setTileEnabled(new ComponentName(packageName,
1053 Settings.PowerUsageSummaryActivity.class.getName()),
1054 mBatteryPresent, isAdmin, pm);
1056 setTileEnabled(new ComponentName(packageName,
1057 Settings.UserSettingsActivity.class.getName()),
1058 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
1059 && !Utils.isMonkeyRunning(), isAdmin, pm);
1061 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1062 setTileEnabled(new ComponentName(packageName,
1063 Settings.PaymentSettingsActivity.class.getName()),
1064 pm.hasSystemFeature(PackageManager.FEATURE_NFC)
1065 && pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
1066 && adapter != null && adapter.isEnabled(), isAdmin, pm);
1068 setTileEnabled(new ComponentName(packageName,
1069 Settings.PrintSettingsActivity.class.getName()),
1070 pm.hasSystemFeature(PackageManager.FEATURE_PRINTING), isAdmin, pm);
1072 final boolean showDev = mDevelopmentPreferences.getBoolean(
1073 DevelopmentSettings.PREF_SHOW, android.os.Build.TYPE.equals("eng"))
1074 && !um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
1075 setTileEnabled(new ComponentName(packageName,
1076 Settings.DevelopmentSettingsActivity.class.getName()),
1077 showDev, isAdmin, pm);
1079 // Reveal development-only quick settings tiles
1080 DevelopmentTiles.setTilesEnabled(this, showDev);
1082 if (UserHandle.MU_ENABLED && !isAdmin) {
1083 // When on restricted users, disable all extra categories (but only the settings ones).
1084 List<DashboardCategory> categories = getDashboardCategories();
1085 for (DashboardCategory category : categories) {
1086 for (Tile tile : category.tiles) {
1087 ComponentName component = tile.intent.getComponent();
1088 if (packageName.equals(component)&& !ArrayUtils.contains(
1089 SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1090 setTileEnabled(component, false, isAdmin, pm);
1097 private void setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin,
1098 PackageManager pm) {
1099 if (UserHandle.MU_ENABLED && !isAdmin
1100 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1103 int state = pm.getComponentEnabledSetting(component);
1104 boolean isEnabled = state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
1105 if (isEnabled != enabled) {
1106 pm.setComponentEnabledSetting(component, enabled
1107 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1108 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
1109 PackageManager.DONT_KILL_APP);
1113 private void getMetaData() {
1115 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1116 PackageManager.GET_META_DATA);
1117 if (ai == null || ai.metaData == null) return;
1118 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1119 } catch (NameNotFoundException nnfe) {
1121 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1125 // give subclasses access to the Next button
1126 public boolean hasNextButton() {
1127 return mNextButton != null;
1130 public Button getNextButton() {
1135 public boolean shouldUpRecreateTask(Intent targetIntent) {
1136 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1140 public boolean onQueryTextSubmit(String query) {
1141 switchToSearchResultsFragmentIfNeeded();
1142 mSearchQuery = query;
1143 return mSearchResultsFragment.onQueryTextSubmit(query);
1147 public boolean onQueryTextChange(String newText) {
1148 mSearchQuery = newText;
1149 if (mSearchResultsFragment == null) {
1152 return mSearchResultsFragment.onQueryTextChange(newText);
1156 public boolean onClose() {
1161 public boolean onMenuItemActionExpand(MenuItem item) {
1162 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1163 switchToSearchResultsFragmentIfNeeded();
1169 public boolean onMenuItemActionCollapse(MenuItem item) {
1170 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1171 if (mSearchMenuItemExpanded) {
1172 revertToInitialFragment();
1179 protected void onTileClicked(Tile tile) {
1180 if (mIsShowingDashboard) {
1181 // If on dashboard, don't finish so the back comes back to here.
1184 super.onTileClicked(tile);
1189 public void onProfileTileOpen() {
1190 if (!mIsShowingDashboard) {
1195 private void switchToSearchResultsFragmentIfNeeded() {
1196 if (mSearchResultsFragment != null) {
1199 Fragment current = getFragmentManager().findFragmentById(mMainContentId);
1200 if (current != null && current instanceof SearchResultsSummary) {
1201 mSearchResultsFragment = (SearchResultsSummary) current;
1203 setContentHeaderView(null);
1204 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1205 SearchResultsSummary.class.getName(), null, false, true,
1206 R.string.search_results_title, null, true);
1208 mSearchResultsFragment.setSearchView(mSearchView);
1209 mSearchMenuItemExpanded = true;
1212 public void needToRevertToInitialFragment() {
1213 mNeedToRevertToInitialFragment = true;
1216 private void revertToInitialFragment() {
1217 mNeedToRevertToInitialFragment = false;
1218 mSearchResultsFragment = null;
1219 mSearchMenuItemExpanded = false;
1220 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1221 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1222 if (mSearchMenuItem != null) {
1223 mSearchMenuItem.collapseActionView();
1227 public Intent getResultIntentData() {
1228 return mResultIntentData;
1231 public void setResultIntentData(Intent resultIntentData) {
1232 mResultIntentData = resultIntentData;
1235 public void startSuggestion(Intent intent) {
1236 mCurrentSuggestion = intent.getComponent();
1237 startActivityForResult(intent, REQUEST_SUGGESTION);
1241 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1242 if (requestCode == REQUEST_SUGGESTION && mCurrentSuggestion != null
1243 && resultCode != RESULT_CANCELED) {
1244 getPackageManager().setComponentEnabledSetting(mCurrentSuggestion,
1245 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
1247 super.onActivityResult(requestCode, resultCode, data);