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.applications.AdvancedAppSettings;
60 import com.android.settings.applications.DrawOverlayDetails;
61 import com.android.settings.applications.InstalledAppDetails;
62 import com.android.settings.applications.ManageApplications;
63 import com.android.settings.applications.ManageAssist;
64 import com.android.settings.applications.NotificationApps;
65 import com.android.settings.applications.ProcessStatsSummary;
66 import com.android.settings.applications.ProcessStatsUi;
67 import com.android.settings.applications.UsageAccessDetails;
68 import com.android.settings.applications.WriteSettingsDetails;
69 import com.android.settings.applications.VrListenerSettings;
70 import com.android.settings.bluetooth.BluetoothSettings;
71 import com.android.settings.dashboard.DashboardSummary;
72 import com.android.settings.dashboard.SearchResultsSummary;
73 import com.android.settings.datausage.DataUsageSummary;
74 import com.android.settings.deviceinfo.PrivateVolumeForget;
75 import com.android.settings.deviceinfo.PrivateVolumeSettings;
76 import com.android.settings.deviceinfo.PublicVolumeSettings;
77 import com.android.settings.deviceinfo.StorageSettings;
78 import com.android.settings.fuelgauge.BatterySaverSettings;
79 import com.android.settings.fuelgauge.PowerUsageDetail;
80 import com.android.settings.fuelgauge.PowerUsageSummary;
81 import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
82 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
83 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
84 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment2;
85 import com.android.settings.inputmethod.PhysicalKeyboardFragment;
86 import com.android.settings.inputmethod.SpellCheckersSettings;
87 import com.android.settings.inputmethod.UserDictionaryList;
88 import com.android.settings.localepicker.LocaleListEditor;
89 import com.android.settings.location.LocationSettings;
90 import com.android.settings.nfc.AndroidBeam;
91 import com.android.settings.nfc.PaymentSettings;
92 import com.android.settings.notification.AppNotificationSettings;
93 import com.android.settings.notification.ConfigureNotificationSettings;
94 import com.android.settings.notification.NotificationAccessSettings;
95 import com.android.settings.notification.NotificationStation;
96 import com.android.settings.notification.OtherSoundSettings;
97 import com.android.settings.notification.SoundSettings;
98 import com.android.settings.notification.ZenAccessSettings;
99 import com.android.settings.notification.ZenModeAutomationSettings;
100 import com.android.settings.notification.ZenModeEventRuleSettings;
101 import com.android.settings.notification.ZenModePrioritySettings;
102 import com.android.settings.notification.ZenModeScheduleRuleSettings;
103 import com.android.settings.notification.ZenModeSettings;
104 import com.android.settings.notification.ZenModeVisualInterruptionSettings;
105 import com.android.settings.print.PrintJobSettingsFragment;
106 import com.android.settings.print.PrintSettingsFragment;
107 import com.android.settings.search.DynamicIndexableContentMonitor;
108 import com.android.settings.search.Index;
109 import com.android.settings.sim.SimSettings;
110 import com.android.settings.tts.TextToSpeechSettings;
111 import com.android.settings.users.UserSettings;
112 import com.android.settings.vpn2.VpnSettings;
113 import com.android.settings.wfd.WifiDisplaySettings;
114 import com.android.settings.widget.SwitchBar;
115 import com.android.settings.wifi.AdvancedWifiSettings;
116 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
117 import com.android.settings.wifi.WifiSettings;
118 import com.android.settings.wifi.p2p.WifiP2pSettings;
119 import com.android.settingslib.drawer.DashboardCategory;
120 import com.android.settingslib.drawer.SettingsDrawerActivity;
121 import com.android.settingslib.drawer.Tile;
123 import java.util.ArrayList;
124 import java.util.List;
125 import java.util.Set;
127 public class SettingsActivity extends SettingsDrawerActivity
128 implements PreferenceManager.OnPreferenceTreeClickListener,
129 PreferenceFragment.OnPreferenceStartFragmentCallback,
130 ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
131 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
132 MenuItem.OnActionExpandListener {
134 private static final String LOG_TAG = "Settings";
136 private static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
138 // Constants for state save/restore
139 private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
140 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
141 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
142 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
143 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
144 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
147 * When starting this activity, the invoking Intent can contain this extra
148 * string to specify which fragment should be initially displayed.
149 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
150 * will call isValidFragment() to confirm that the fragment class name is valid for this
153 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
156 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
157 * this extra can also be specified to supply a Bundle of arguments to pass
158 * to that fragment when it is instantiated during the initial creation
161 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
164 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
166 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
168 public static final String BACK_STACK_PREFS = ":settings:prefs";
170 // extras that allow any preference activity to be launched as part of a wizard
172 // show Back and Next buttons? takes boolean parameter
173 // Back will then return RESULT_CANCELED and Next RESULT_OK
174 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
176 // add a Skip button?
177 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
179 // specify custom text for the Back or Next buttons, or cause a button to not appear
180 // at all by setting it to null
181 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
182 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
185 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
186 * those extra can also be specify to supply the title or title res id to be shown for
189 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
191 * The package name used to resolve the title resource id.
193 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
194 ":settings:show_fragment_title_res_package_name";
195 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
196 ":settings:show_fragment_title_resid";
197 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
198 ":settings:show_fragment_as_shortcut";
200 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
201 ":settings:show_fragment_as_subsetting";
203 public static final String EXTRA_HIDE_DRAWER = ":settings:hide_drawer";
205 public static final String META_DATA_KEY_FRAGMENT_CLASS =
206 "com.android.settings.FRAGMENT_CLASS";
208 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
210 private static final String EMPTY_QUERY = "";
212 private static final int REQUEST_SUGGESTION = 42;
214 private String mFragmentClass;
216 private CharSequence mInitialTitle;
217 private int mInitialTitleResId;
219 // Show only these settings for restricted users
220 private String[] SETTINGS_FOR_RESTRICTED = {
222 WifiSettingsActivity.class.getName(),
223 Settings.BluetoothSettingsActivity.class.getName(),
224 Settings.DataUsageSummaryActivity.class.getName(),
225 Settings.SimSettingsActivity.class.getName(),
226 Settings.WirelessSettingsActivity.class.getName(),
228 Settings.HomeSettingsActivity.class.getName(),
229 Settings.SoundSettingsActivity.class.getName(),
230 Settings.DisplaySettingsActivity.class.getName(),
231 Settings.StorageSettingsActivity.class.getName(),
232 Settings.ManageApplicationsActivity.class.getName(),
233 Settings.PowerUsageSummaryActivity.class.getName(),
235 Settings.LocationSettingsActivity.class.getName(),
236 Settings.SecuritySettingsActivity.class.getName(),
237 Settings.InputMethodAndLanguageSettingsActivity.class.getName(),
238 Settings.UserSettingsActivity.class.getName(),
239 Settings.AccountSettingsActivity.class.getName(),
241 Settings.DateTimeSettingsActivity.class.getName(),
242 Settings.DeviceInfoSettingsActivity.class.getName(),
243 Settings.AccessibilitySettingsActivity.class.getName(),
244 Settings.PrintSettingsActivity.class.getName(),
245 Settings.PaymentSettingsActivity.class.getName(),
248 private static final String[] ENTRY_FRAGMENTS = {
249 WirelessSettings.class.getName(),
250 WifiSettings.class.getName(),
251 AdvancedWifiSettings.class.getName(),
252 SavedAccessPointsWifiSettings.class.getName(),
253 BluetoothSettings.class.getName(),
254 SimSettings.class.getName(),
255 TetherSettings.class.getName(),
256 WifiP2pSettings.class.getName(),
257 VpnSettings.class.getName(),
258 DateTimeSettings.class.getName(),
259 LocaleListEditor.class.getName(),
260 InputMethodAndLanguageSettings.class.getName(),
261 AvailableVirtualKeyboardFragment.class.getName(),
262 SpellCheckersSettings.class.getName(),
263 UserDictionaryList.class.getName(),
264 UserDictionarySettings.class.getName(),
265 HomeSettings.class.getName(),
266 DisplaySettings.class.getName(),
267 DeviceInfoSettings.class.getName(),
268 ManageApplications.class.getName(),
269 NotificationApps.class.getName(),
270 ManageAssist.class.getName(),
271 ProcessStatsUi.class.getName(),
272 NotificationStation.class.getName(),
273 LocationSettings.class.getName(),
274 SecuritySettings.class.getName(),
275 UsageAccessDetails.class.getName(),
276 PrivacySettings.class.getName(),
277 DeviceAdminSettings.class.getName(),
278 AccessibilitySettings.class.getName(),
279 AccessibilitySettingsForSetupWizard.class.getName(),
280 CaptionPropertiesFragment.class.getName(),
281 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
282 TextToSpeechSettings.class.getName(),
283 StorageSettings.class.getName(),
284 PrivateVolumeForget.class.getName(),
285 PrivateVolumeSettings.class.getName(),
286 PublicVolumeSettings.class.getName(),
287 DevelopmentSettings.class.getName(),
288 AndroidBeam.class.getName(),
289 WifiDisplaySettings.class.getName(),
290 PowerUsageSummary.class.getName(),
291 AccountSyncSettings.class.getName(),
292 AccountSettings.class.getName(),
293 CryptKeeperSettings.class.getName(),
294 DataUsageSummary.class.getName(),
295 DreamSettings.class.getName(),
296 UserSettings.class.getName(),
297 NotificationAccessSettings.class.getName(),
298 ZenAccessSettings.class.getName(),
299 PrintSettingsFragment.class.getName(),
300 PrintJobSettingsFragment.class.getName(),
301 TrustedCredentialsSettings.class.getName(),
302 PaymentSettings.class.getName(),
303 KeyboardLayoutPickerFragment.class.getName(),
304 KeyboardLayoutPickerFragment2.class.getName(),
305 PhysicalKeyboardFragment.class.getName(),
306 ZenModeSettings.class.getName(),
307 SoundSettings.class.getName(),
308 ConfigureNotificationSettings.class.getName(),
309 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
310 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
311 InstalledAppDetails.class.getName(),
312 BatterySaverSettings.class.getName(),
313 AppNotificationSettings.class.getName(),
314 OtherSoundSettings.class.getName(),
315 ApnSettings.class.getName(),
316 WifiCallingSettings.class.getName(),
317 ZenModePrioritySettings.class.getName(),
318 ZenModeAutomationSettings.class.getName(),
319 ZenModeScheduleRuleSettings.class.getName(),
320 ZenModeEventRuleSettings.class.getName(),
321 ZenModeVisualInterruptionSettings.class.getName(),
322 ProcessStatsUi.class.getName(),
323 PowerUsageDetail.class.getName(),
324 ProcessStatsSummary.class.getName(),
325 DrawOverlayDetails.class.getName(),
326 WriteSettingsDetails.class.getName(),
327 AdvancedAppSettings.class.getName(),
328 WallpaperTypeSettings.class.getName(),
329 VrListenerSettings.class.getName(),
333 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
334 "android.settings.APPLICATION_DETAILS_SETTINGS"
337 private SharedPreferences mDevelopmentPreferences;
338 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
340 private boolean mBatteryPresent = true;
341 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
343 public void onReceive(Context context, Intent intent) {
344 String action = intent.getAction();
345 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
346 boolean batteryPresent = Utils.isBatteryPresent(intent);
348 if (mBatteryPresent != batteryPresent) {
349 mBatteryPresent = batteryPresent;
356 private final BroadcastReceiver mUserAddRemoveReceiver = new BroadcastReceiver() {
358 public void onReceive(Context context, Intent intent) {
359 String action = intent.getAction();
360 if (action.equals(Intent.ACTION_USER_ADDED)
361 || action.equals(Intent.ACTION_USER_REMOVED)) {
362 Index.getInstance(getApplicationContext()).update();
367 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
368 new DynamicIndexableContentMonitor();
370 private ActionBar mActionBar;
371 private SwitchBar mSwitchBar;
373 private Button mNextButton;
375 private boolean mDisplayHomeAsUpEnabled;
376 private boolean mDisplaySearch;
378 private boolean mIsShowingDashboard;
379 private boolean mIsShortcut;
381 private int mMainContentId = R.id.main_content;
382 private ViewGroup mContent;
384 private SearchView mSearchView;
385 private MenuItem mSearchMenuItem;
386 private boolean mSearchMenuItemExpanded = false;
387 private SearchResultsSummary mSearchResultsFragment;
388 private String mSearchQuery;
391 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
393 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
395 private boolean mNeedToRevertToInitialFragment = false;
397 private Intent mResultIntentData;
398 private ComponentName mCurrentSuggestion;
400 public SwitchBar getSwitchBar() {
405 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
406 // Override the fragment title for Wallpaper settings
407 CharSequence title = pref.getTitle();
408 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
409 title = getString(R.string.wallpaper_settings_fragment_title);
411 startPreferencePanel(pref.getFragment(), pref.getExtras(), -1, title,
417 public boolean onPreferenceTreeClick(Preference preference) {
422 public void onConfigurationChanged(Configuration newConfig) {
423 super.onConfigurationChanged(newConfig);
424 Index.getInstance(this).update();
428 protected void onStart() {
431 if (mNeedToRevertToInitialFragment) {
432 revertToInitialFragment();
437 public boolean onCreateOptionsMenu(Menu menu) {
438 if (!mDisplaySearch) {
442 MenuInflater inflater = getMenuInflater();
443 inflater.inflate(R.menu.options_menu, menu);
445 // Cache the search query (can be overriden by the OnQueryTextListener)
446 final String query = mSearchQuery;
448 mSearchMenuItem = menu.findItem(R.id.search);
449 mSearchView = (SearchView) mSearchMenuItem.getActionView();
451 if (mSearchMenuItem == null || mSearchView == null) {
455 if (mSearchResultsFragment != null) {
456 mSearchResultsFragment.setSearchView(mSearchView);
459 mSearchMenuItem.setOnActionExpandListener(this);
460 mSearchView.setOnQueryTextListener(this);
461 mSearchView.setOnCloseListener(this);
463 if (mSearchMenuItemExpanded) {
464 mSearchMenuItem.expandActionView();
466 mSearchView.setQuery(query, true /* submit */);
472 public SharedPreferences getSharedPreferences(String name, int mode) {
473 if (name.equals(getPackageName() + "_preferences")) {
474 return new SharedPreferencesLogger(this, getMetricsTag());
476 return super.getSharedPreferences(name, mode);
479 private String getMetricsTag() {
480 String tag = getClass().getName();
481 if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
482 tag = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
484 if (tag.startsWith("com.android.settings.")) {
485 tag = tag.replace("com.android.settings.", "");
490 private static boolean isShortCutIntent(final Intent intent) {
491 Set<String> categories = intent.getCategories();
492 return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
495 private static boolean isLikeShortCutIntent(final Intent intent) {
496 String action = intent.getAction();
497 if (action == null) {
500 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
501 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
507 protected void onCreate(Bundle savedState) {
508 super.onCreate(savedState);
509 long startTime = System.currentTimeMillis();
511 // Should happen before any call to getIntent()
514 final Intent intent = getIntent();
515 if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
516 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
518 if (intent.getBooleanExtra(EXTRA_HIDE_DRAWER, false)) {
519 setIsDrawerPresent(false);
522 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
523 Context.MODE_PRIVATE);
525 // Getting Intent properties can only be done after the super.onCreate(...)
526 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
528 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
529 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
531 final ComponentName cn = intent.getComponent();
532 final String className = cn.getClassName();
534 mIsShowingDashboard = className.equals(Settings.class.getName())
535 || className.equals(Settings.WirelessSettings.class.getName())
536 || className.equals(Settings.DeviceSettings.class.getName())
537 || className.equals(Settings.PersonalSettings.class.getName())
538 || className.equals(Settings.WirelessSettings.class.getName());
540 // This is a "Sub Settings" when:
541 // - this is a real SubSettings
542 // - or :settings:show_fragment_as_subsetting is passed to the Intent
543 final boolean isSubSettings = this instanceof SubSettings ||
544 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
546 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
548 // Check also that we are not a Theme Dialog as we don't want to override them
549 final int themeResId = getThemeResId();
550 if (themeResId != R.style.Theme_DialogWhenLarge &&
551 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
552 setTheme(R.style.Theme_SubSettings);
556 setContentView(mIsShowingDashboard ?
557 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
559 mContent = (ViewGroup) findViewById(mMainContentId);
561 getFragmentManager().addOnBackStackChangedListener(this);
563 if (mIsShowingDashboard) {
564 // Run the Index update only if we have some space
565 if (!Utils.isLowStorage(this)) {
566 long indexStartTime = System.currentTimeMillis();
567 Index.getInstance(getApplicationContext()).update();
568 if (DEBUG_TIMING) Log.d(LOG_TAG, "Index.update() took "
569 + (System.currentTimeMillis() - indexStartTime) + " ms");
571 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
575 if (savedState != null) {
576 // We are restarting from a previous saved state; used that to initialize, instead
577 // of starting fresh.
578 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
579 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
581 setTitleFromIntent(intent);
583 ArrayList<DashboardCategory> categories =
584 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
585 if (categories != null) {
587 mCategories.addAll(categories);
588 setTitleFromBackStack();
591 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
592 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
594 if (!mIsShowingDashboard) {
595 mDisplaySearch = false;
596 // UP will be shown only if it is a sub settings
598 mDisplayHomeAsUpEnabled = isSubSettings;
599 } else if (isSubSettings) {
600 mDisplayHomeAsUpEnabled = true;
602 mDisplayHomeAsUpEnabled = false;
604 setTitleFromIntent(intent);
606 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
607 switchToFragment(initialFragmentName, initialArguments, true, false,
608 mInitialTitleResId, mInitialTitle, false);
610 // No UP affordance if we are displaying the main Dashboard
611 mDisplayHomeAsUpEnabled = false;
612 // Show Search affordance
613 mDisplaySearch = true;
614 mInitialTitleResId = R.string.dashboard_title;
615 switchToFragment(DashboardSummary.class.getName(), null, false, false,
616 mInitialTitleResId, mInitialTitle, false);
620 mActionBar = getActionBar();
621 if (mActionBar != null) {
622 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
623 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
625 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
626 if (mSwitchBar != null) {
627 mSwitchBar.setMetricsTag(getMetricsTag());
630 // see if we should show Back/Next buttons
631 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
633 View buttonBar = findViewById(R.id.button_bar);
634 if (buttonBar != null) {
635 buttonBar.setVisibility(View.VISIBLE);
637 Button backButton = (Button)findViewById(R.id.back_button);
638 backButton.setOnClickListener(new OnClickListener() {
639 public void onClick(View v) {
640 setResult(RESULT_CANCELED, getResultIntentData());
644 Button skipButton = (Button)findViewById(R.id.skip_button);
645 skipButton.setOnClickListener(new OnClickListener() {
646 public void onClick(View v) {
647 setResult(RESULT_OK, getResultIntentData());
651 mNextButton = (Button)findViewById(R.id.next_button);
652 mNextButton.setOnClickListener(new OnClickListener() {
653 public void onClick(View v) {
654 setResult(RESULT_OK, getResultIntentData());
659 // set our various button parameters
660 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
661 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
662 if (TextUtils.isEmpty(buttonText)) {
663 mNextButton.setVisibility(View.GONE);
666 mNextButton.setText(buttonText);
669 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
670 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
671 if (TextUtils.isEmpty(buttonText)) {
672 backButton.setVisibility(View.GONE);
675 backButton.setText(buttonText);
678 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
679 skipButton.setVisibility(View.VISIBLE);
684 if (DEBUG_TIMING) Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
689 * Sets the id of the view continaing the main content. Should be called before calling super's
692 protected void setMainContentId(int contentId) {
693 mMainContentId = contentId;
696 private void setTitleFromIntent(Intent intent) {
697 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
698 if (initialTitleResId > 0) {
699 mInitialTitle = null;
700 mInitialTitleResId = initialTitleResId;
702 final String initialTitleResPackageName = intent.getStringExtra(
703 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
704 if (initialTitleResPackageName != null) {
706 Context authContext = createPackageContextAsUser(initialTitleResPackageName,
707 0 /* flags */, new UserHandle(UserHandle.myUserId()));
708 mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
709 setTitle(mInitialTitle);
710 mInitialTitleResId = -1;
712 } catch (NameNotFoundException e) {
713 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
716 setTitle(mInitialTitleResId);
719 mInitialTitleResId = -1;
720 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
721 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
722 setTitle(mInitialTitle);
727 public void onBackStackChanged() {
728 setTitleFromBackStack();
731 private int setTitleFromBackStack() {
732 final int count = getFragmentManager().getBackStackEntryCount();
735 if (mInitialTitleResId > 0) {
736 setTitle(mInitialTitleResId);
738 setTitle(mInitialTitle);
743 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
744 setTitleFromBackStackEntry(bse);
749 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
750 final CharSequence title;
751 final int titleRes = bse.getBreadCrumbTitleRes();
753 title = getText(titleRes);
755 title = bse.getBreadCrumbTitle();
763 protected void onSaveInstanceState(Bundle outState) {
764 super.onSaveInstanceState(outState);
766 if (mCategories.size() > 0) {
767 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
770 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
771 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
773 if (mDisplaySearch) {
774 // The option menus are created if the ActionBar is visible and they are also created
775 // asynchronously. If you launch Settings with an Intent action like
776 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
777 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
778 // menu item and search view are null.
779 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
780 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
782 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
783 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
788 protected void onResume() {
791 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
793 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
797 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
798 mDevelopmentPreferencesListener);
800 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
801 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_ADDED));
802 registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
804 mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
806 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
807 onQueryTextSubmit(mSearchQuery);
813 protected void onPause() {
815 unregisterReceiver(mBatteryInfoReceiver);
816 unregisterReceiver(mUserAddRemoveReceiver);
817 mDynamicIndexableContentMonitor.unregister();
821 public void onDestroy() {
824 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
825 mDevelopmentPreferencesListener);
826 mDevelopmentPreferencesListener = null;
829 protected boolean isValidFragment(String fragmentName) {
830 // Almost all fragments are wrapped in this,
831 // except for a few that have their own activities.
832 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
833 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
839 public Intent getIntent() {
840 Intent superIntent = super.getIntent();
841 String startingFragment = getStartingFragmentClass(superIntent);
842 // This is called from super.onCreate, isMultiPane() is not yet reliable
843 // Do not use onIsHidingHeaders either, which relies itself on this method
844 if (startingFragment != null) {
845 Intent modIntent = new Intent(superIntent);
846 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
847 Bundle args = superIntent.getExtras();
849 args = new Bundle(args);
853 args.putParcelable("intent", superIntent);
854 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
861 * Checks if the component name in the intent is different from the Settings class and
862 * returns the class name to load as a fragment.
864 private String getStartingFragmentClass(Intent intent) {
865 if (mFragmentClass != null) return mFragmentClass;
867 String intentClass = intent.getComponent().getClassName();
868 if (intentClass.equals(getClass().getName())) return null;
870 if ("com.android.settings.ManageApplications".equals(intentClass)
871 || "com.android.settings.RunningServices".equals(intentClass)
872 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
873 // Old names of manage apps.
874 intentClass = com.android.settings.applications.ManageApplications.class.getName();
881 * Start a new fragment containing a preference panel. If the preferences
882 * are being displayed in multi-pane mode, the given fragment class will
883 * be instantiated and placed in the appropriate pane. If running in
884 * single-pane mode, a new activity will be launched in which to show the
887 * @param fragmentClass Full name of the class implementing the fragment.
888 * @param args Any desired arguments to supply to the fragment.
889 * @param titleRes Optional resource identifier of the title of this
891 * @param titleText Optional text of the title of this fragment.
892 * @param resultTo Optional fragment that result data should be sent to.
893 * If non-null, resultTo.onActivityResult() will be called when this
894 * preference panel is done. The launched panel must use
895 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
896 * @param resultRequestCode If resultTo is non-null, this is the caller's
897 * request code to be received with the result.
899 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
900 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
903 if (titleText != null) {
904 title = titleText.toString();
906 // There not much we can do in that case
910 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
911 titleRes, title, mIsShortcut);
915 * Start a new fragment in a new activity containing a preference panel for a given user. If the
916 * preferences are being displayed in multi-pane mode, the given fragment class will be
917 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
918 * activity will be launched in which to show the fragment.
920 * @param fragmentClass Full name of the class implementing the fragment.
921 * @param args Any desired arguments to supply to the fragment.
922 * @param titleRes Optional resource identifier of the title of this fragment.
923 * @param titleText Optional text of the title of this fragment.
924 * @param userHandle The user for which the panel has to be started.
926 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
927 CharSequence titleText, UserHandle userHandle) {
928 // This is a workaround.
930 // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
931 // starting the fragment could cause a native stack corruption. See b/17523189. However,
932 // adding that flag and start the preference panel with the same UserHandler will make it
933 // impossible to use back button to return to the previous screen. See b/20042570.
935 // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
936 // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
937 // when we're calling it as the same user.
938 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
939 startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
943 if (titleText != null) {
944 title = titleText.toString();
946 // There not much we can do in that case
950 Utils.startWithFragmentAsUser(this, fragmentClass, args,
951 titleRes, title, mIsShortcut, userHandle);
956 * Called by a preference panel fragment to finish itself.
958 * @param caller The fragment that is asking to be finished.
959 * @param resultCode Optional result code to send back to the original
960 * launching fragment.
961 * @param resultData Optional result data to send back to the original
962 * launching fragment.
964 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
965 setResult(resultCode, resultData);
970 * Start a new fragment.
972 * @param fragment The fragment to start
973 * @param push If true, the current fragment will be pushed onto the back stack. If false,
974 * the current fragment will be replaced.
976 public void startPreferenceFragment(Fragment fragment, boolean push) {
977 FragmentTransaction transaction = getFragmentManager().beginTransaction();
978 transaction.replace(mMainContentId, fragment);
980 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
981 transaction.addToBackStack(BACK_STACK_PREFS);
983 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
985 transaction.commitAllowingStateLoss();
989 * Switch to a specific Fragment with taking care of validation, Title and BackStack
991 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
992 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
993 if (validate && !isValidFragment(fragmentName)) {
994 throw new IllegalArgumentException("Invalid fragment for this activity: "
997 Fragment f = Fragment.instantiate(this, fragmentName, args);
998 FragmentTransaction transaction = getFragmentManager().beginTransaction();
999 transaction.replace(mMainContentId, f);
1000 if (withTransition) {
1001 TransitionManager.beginDelayedTransition(mContent);
1003 if (addToBackStack) {
1004 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1006 if (titleResId > 0) {
1007 transaction.setBreadCrumbTitle(titleResId);
1008 } else if (title != null) {
1009 transaction.setBreadCrumbTitle(title);
1011 transaction.commitAllowingStateLoss();
1012 getFragmentManager().executePendingTransactions();
1016 private void updateTilesList() {
1017 // Generally the items that are will be changing from these updates will
1018 // not be in the top list of tiles, so run it in the background and the
1019 // SettingsDrawerActivity will pick up on the updates automatically.
1020 AsyncTask.execute(new Runnable() {
1023 doUpdateTilesList();
1028 private void doUpdateTilesList() {
1029 PackageManager pm = getPackageManager();
1030 final UserManager um = UserManager.get(this);
1031 final boolean isAdmin = um.isAdminUser();
1033 String packageName = getPackageName();
1034 setTileEnabled(new ComponentName(packageName, WifiSettingsActivity.class.getName()),
1035 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin, pm);
1037 setTileEnabled(new ComponentName(packageName,
1038 Settings.BluetoothSettingsActivity.class.getName()),
1039 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin, pm);
1041 setTileEnabled(new ComponentName(packageName,
1042 Settings.DataUsageSummaryActivity.class.getName()),
1043 Utils.isBandwidthControlEnabled(), isAdmin, pm);
1045 setTileEnabled(new ComponentName(packageName,
1046 Settings.SimSettingsActivity.class.getName()),
1047 Utils.showSimCardTile(this), isAdmin, pm);
1049 setTileEnabled(new ComponentName(packageName,
1050 Settings.PowerUsageSummaryActivity.class.getName()),
1051 mBatteryPresent, isAdmin, pm);
1053 setTileEnabled(new ComponentName(packageName,
1054 Settings.UserSettingsActivity.class.getName()),
1055 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
1056 && !Utils.isMonkeyRunning(), isAdmin, pm);
1058 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1059 setTileEnabled(new ComponentName(packageName,
1060 Settings.PaymentSettingsActivity.class.getName()),
1061 pm.hasSystemFeature(PackageManager.FEATURE_NFC)
1062 && pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
1063 && adapter != null && adapter.isEnabled(), isAdmin, pm);
1065 setTileEnabled(new ComponentName(packageName,
1066 Settings.PrintSettingsActivity.class.getName()),
1067 pm.hasSystemFeature(PackageManager.FEATURE_PRINTING), isAdmin, pm);
1069 final boolean showDev = mDevelopmentPreferences.getBoolean(
1070 DevelopmentSettings.PREF_SHOW,
1071 android.os.Build.TYPE.equals("eng"));
1072 setTileEnabled(new ComponentName(packageName,
1073 Settings.DevelopmentSettingsActivity.class.getName()),
1074 showDev && !um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES),
1077 if (UserHandle.MU_ENABLED && !isAdmin) {
1078 // When on restricted users, disable all extra categories (but only the settings ones).
1079 List<DashboardCategory> categories = getDashboardCategories();
1080 for (DashboardCategory category : categories) {
1081 for (Tile tile : category.tiles) {
1082 ComponentName component = tile.intent.getComponent();
1083 if (packageName.equals(component)&& !ArrayUtils.contains(
1084 SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1085 setTileEnabled(component, false, isAdmin, pm);
1092 private void setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin,
1093 PackageManager pm) {
1094 if (UserHandle.MU_ENABLED && !isAdmin
1095 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, component.getClassName())) {
1098 int state = pm.getComponentEnabledSetting(component);
1099 boolean isEnabled = state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
1100 || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
1101 if (isEnabled != enabled) {
1102 pm.setComponentEnabledSetting(component, enabled
1103 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1104 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
1105 PackageManager.DONT_KILL_APP);
1109 private void getMetaData() {
1111 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1112 PackageManager.GET_META_DATA);
1113 if (ai == null || ai.metaData == null) return;
1114 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1115 } catch (NameNotFoundException nnfe) {
1117 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1121 // give subclasses access to the Next button
1122 public boolean hasNextButton() {
1123 return mNextButton != null;
1126 public Button getNextButton() {
1131 public boolean shouldUpRecreateTask(Intent targetIntent) {
1132 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1136 public boolean onQueryTextSubmit(String query) {
1137 switchToSearchResultsFragmentIfNeeded();
1138 mSearchQuery = query;
1139 return mSearchResultsFragment.onQueryTextSubmit(query);
1143 public boolean onQueryTextChange(String newText) {
1144 mSearchQuery = newText;
1145 if (mSearchResultsFragment == null) {
1148 return mSearchResultsFragment.onQueryTextChange(newText);
1152 public boolean onClose() {
1157 public boolean onMenuItemActionExpand(MenuItem item) {
1158 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1159 switchToSearchResultsFragmentIfNeeded();
1165 public boolean onMenuItemActionCollapse(MenuItem item) {
1166 if (item.getItemId() == mSearchMenuItem.getItemId()) {
1167 if (mSearchMenuItemExpanded) {
1168 revertToInitialFragment();
1175 protected void onTileClicked(Tile tile) {
1176 if (mIsShowingDashboard) {
1177 // If on dashboard, don't finish so the back comes back to here.
1180 super.onTileClicked(tile);
1185 public void onProfileTileOpen() {
1186 if (!mIsShowingDashboard) {
1191 private void switchToSearchResultsFragmentIfNeeded() {
1192 if (mSearchResultsFragment != null) {
1195 Fragment current = getFragmentManager().findFragmentById(mMainContentId);
1196 if (current != null && current instanceof SearchResultsSummary) {
1197 mSearchResultsFragment = (SearchResultsSummary) current;
1199 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1200 SearchResultsSummary.class.getName(), null, false, true,
1201 R.string.search_results_title, null, true);
1203 mSearchResultsFragment.setSearchView(mSearchView);
1204 mSearchMenuItemExpanded = true;
1207 public void needToRevertToInitialFragment() {
1208 mNeedToRevertToInitialFragment = true;
1211 private void revertToInitialFragment() {
1212 mNeedToRevertToInitialFragment = false;
1213 mSearchResultsFragment = null;
1214 mSearchMenuItemExpanded = false;
1215 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1216 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1217 if (mSearchMenuItem != null) {
1218 mSearchMenuItem.collapseActionView();
1222 public Intent getResultIntentData() {
1223 return mResultIntentData;
1226 public void setResultIntentData(Intent resultIntentData) {
1227 mResultIntentData = resultIntentData;
1230 public void startSuggestion(Intent intent) {
1231 mCurrentSuggestion = intent.getComponent();
1232 startActivityForResult(intent, REQUEST_SUGGESTION);
1236 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1237 if (requestCode == REQUEST_SUGGESTION && mCurrentSuggestion != null
1238 && resultCode != RESULT_CANCELED) {
1239 getPackageManager().setComponentEnabledSetting(mCurrentSuggestion,
1240 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
1242 super.onActivityResult(requestCode, resultCode, data);