SettingsActivity.java revision 769630c8956fa844545d964166da90cc802fabac
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings; 18 19import android.accounts.Account; 20import android.accounts.AccountManager; 21import android.accounts.OnAccountsUpdateListener; 22import android.app.ActionBar; 23import android.app.Activity; 24import android.app.Fragment; 25import android.app.FragmentManager; 26import android.app.FragmentTransaction; 27import android.content.BroadcastReceiver; 28import android.content.ComponentName; 29import android.content.Context; 30import android.content.Intent; 31import android.content.IntentFilter; 32import android.content.SharedPreferences; 33import android.content.pm.ActivityInfo; 34import android.content.pm.PackageManager; 35import android.content.pm.PackageManager.NameNotFoundException; 36import android.content.pm.ResolveInfo; 37import android.content.res.Configuration; 38import android.content.res.TypedArray; 39import android.content.res.XmlResourceParser; 40import android.nfc.NfcAdapter; 41import android.os.Bundle; 42import android.os.Handler; 43import android.os.INetworkManagementService; 44import android.os.Message; 45import android.os.RemoteException; 46import android.os.ServiceManager; 47import android.os.UserHandle; 48import android.os.UserManager; 49import android.preference.Preference; 50import android.preference.PreferenceFragment; 51import android.preference.PreferenceManager; 52import android.preference.PreferenceScreen; 53import android.text.TextUtils; 54import android.util.AttributeSet; 55import android.util.Log; 56import android.util.TypedValue; 57import android.util.Xml; 58import android.view.Menu; 59import android.view.MenuInflater; 60import android.view.MenuItem; 61import android.view.View; 62import android.view.View.OnClickListener; 63import android.widget.Button; 64 65import android.widget.SearchView; 66import com.android.internal.util.ArrayUtils; 67import com.android.internal.util.XmlUtils; 68import com.android.settings.accessibility.AccessibilitySettings; 69import com.android.settings.accessibility.CaptionPropertiesFragment; 70import com.android.settings.accounts.AccountSyncSettings; 71import com.android.settings.accounts.AuthenticatorHelper; 72import com.android.settings.accounts.ManageAccountsSettings; 73import com.android.settings.applications.InstalledAppDetails; 74import com.android.settings.applications.ManageApplications; 75import com.android.settings.applications.ProcessStatsUi; 76import com.android.settings.bluetooth.BluetoothSettings; 77import com.android.settings.dashboard.DashboardCategory; 78import com.android.settings.dashboard.DashboardSummary; 79import com.android.settings.dashboard.DashboardTile; 80import com.android.settings.dashboard.Header; 81import com.android.settings.dashboard.NoHomeDialogFragment; 82import com.android.settings.dashboard.SearchResultsSummary; 83import com.android.settings.deviceinfo.Memory; 84import com.android.settings.deviceinfo.UsbSettings; 85import com.android.settings.fuelgauge.PowerUsageSummary; 86import com.android.settings.search.DynamicIndexableContentMonitor; 87import com.android.settings.search.Index; 88import com.android.settings.inputmethod.InputMethodAndLanguageSettings; 89import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; 90import com.android.settings.inputmethod.SpellCheckersSettings; 91import com.android.settings.inputmethod.UserDictionaryList; 92import com.android.settings.location.LocationSettings; 93import com.android.settings.nfc.AndroidBeam; 94import com.android.settings.nfc.PaymentSettings; 95import com.android.settings.notification.NotificationAccessSettings; 96import com.android.settings.notification.NotificationSettings; 97import com.android.settings.notification.NotificationStation; 98import com.android.settings.notification.ZenModeSettings; 99import com.android.settings.print.PrintJobSettingsFragment; 100import com.android.settings.print.PrintSettingsFragment; 101import com.android.settings.tts.TextToSpeechSettings; 102import com.android.settings.users.UserSettings; 103import com.android.settings.vpn2.VpnSettings; 104import com.android.settings.wfd.WifiDisplaySettings; 105import com.android.settings.wifi.AdvancedWifiSettings; 106import com.android.settings.wifi.WifiSettings; 107import com.android.settings.wifi.p2p.WifiP2pSettings; 108import org.xmlpull.v1.XmlPullParser; 109import org.xmlpull.v1.XmlPullParserException; 110 111import java.io.IOException; 112import java.util.ArrayList; 113import java.util.Collections; 114import java.util.Comparator; 115import java.util.List; 116 117import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED; 118 119public class SettingsActivity extends Activity 120 implements PreferenceManager.OnPreferenceTreeClickListener, 121 PreferenceFragment.OnPreferenceStartFragmentCallback, 122 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener, 123 SearchView.OnQueryTextListener, SearchView.OnCloseListener, 124 MenuItem.OnActionExpandListener { 125 126 private static final String LOG_TAG = "Settings"; 127 128 // Constants for state save/restore 129 private static final String SAVE_KEY_CATEGORIES = ":settings:categories"; 130 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded"; 131 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query"; 132 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up"; 133 134 /** 135 * When starting this activity, the invoking Intent can contain this extra 136 * string to specify which fragment should be initially displayed. 137 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity 138 * will call isValidFragment() to confirm that the fragment class name is valid for this 139 * activity. 140 */ 141 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment"; 142 143 /** 144 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, 145 * this extra can also be specified to supply a Bundle of arguments to pass 146 * to that fragment when it is instantiated during the initial creation 147 * of the activity. 148 */ 149 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"; 150 151 /** 152 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS} 153 */ 154 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; 155 156 /** 157 * When starting this activity, the invoking Intent can contain this extra 158 * boolean that the header list should not be displayed. This is most often 159 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch 160 * the activity to display a specific fragment that the user has navigated 161 * to. 162 */ 163 public static final String EXTRA_NO_HEADERS = ":settings:no_headers"; 164 165 public static final String BACK_STACK_PREFS = ":settings:prefs"; 166 167 // extras that allow any preference activity to be launched as part of a wizard 168 169 // show Back and Next buttons? takes boolean parameter 170 // Back will then return RESULT_CANCELED and Next RESULT_OK 171 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar"; 172 173 // add a Skip button? 174 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip"; 175 176 // specify custom text for the Back or Next buttons, or cause a button to not appear 177 // at all by setting it to null 178 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text"; 179 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text"; 180 181 /** 182 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, 183 * this extra can also be specify to supply the title to be shown for 184 * that fragment. 185 */ 186 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title"; 187 188 private static final String META_DATA_KEY_FRAGMENT_CLASS = 189 "com.android.settings.FRAGMENT_CLASS"; 190 191 private static final String EXTRA_UI_OPTIONS = "settings:ui_options"; 192 193 private static final String EMPTY_QUERY = ""; 194 195 private static boolean sShowNoHomeNotice = false; 196 197 private String mFragmentClass; 198 199 private CharSequence mInitialTitle; 200 201 // Show only these settings for restricted users 202 private int[] SETTINGS_FOR_RESTRICTED = { 203 R.id.wireless_section, 204 R.id.wifi_settings, 205 R.id.bluetooth_settings, 206 R.id.data_usage_settings, 207 R.id.wireless_settings, 208 R.id.device_section, 209 R.id.sound_settings, 210 R.id.display_settings, 211 R.id.storage_settings, 212 R.id.application_settings, 213 R.id.battery_settings, 214 R.id.personal_section, 215 R.id.location_settings, 216 R.id.security_settings, 217 R.id.language_settings, 218 R.id.user_settings, 219 R.id.account_settings, 220 R.id.account_add, 221 R.id.system_section, 222 R.id.date_time_settings, 223 R.id.about_settings, 224 R.id.accessibility_settings, 225 R.id.print_settings, 226 R.id.nfc_payment_settings, 227 R.id.home_settings, 228 R.id.dashboard 229 }; 230 231 private static final String[] ENTRY_FRAGMENTS = { 232 WirelessSettings.class.getName(), 233 WifiSettings.class.getName(), 234 AdvancedWifiSettings.class.getName(), 235 BluetoothSettings.class.getName(), 236 TetherSettings.class.getName(), 237 WifiP2pSettings.class.getName(), 238 VpnSettings.class.getName(), 239 DateTimeSettings.class.getName(), 240 LocalePicker.class.getName(), 241 InputMethodAndLanguageSettings.class.getName(), 242 SpellCheckersSettings.class.getName(), 243 UserDictionaryList.class.getName(), 244 UserDictionarySettings.class.getName(), 245 SoundSettings.class.getName(), 246 DisplaySettings.class.getName(), 247 DeviceInfoSettings.class.getName(), 248 ManageApplications.class.getName(), 249 ProcessStatsUi.class.getName(), 250 NotificationStation.class.getName(), 251 LocationSettings.class.getName(), 252 SecuritySettings.class.getName(), 253 PrivacySettings.class.getName(), 254 DeviceAdminSettings.class.getName(), 255 AccessibilitySettings.class.getName(), 256 CaptionPropertiesFragment.class.getName(), 257 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(), 258 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(), 259 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(), 260 TextToSpeechSettings.class.getName(), 261 Memory.class.getName(), 262 DevelopmentSettings.class.getName(), 263 UsbSettings.class.getName(), 264 AndroidBeam.class.getName(), 265 WifiDisplaySettings.class.getName(), 266 PowerUsageSummary.class.getName(), 267 AccountSyncSettings.class.getName(), 268 CryptKeeperSettings.class.getName(), 269 DataUsageSummary.class.getName(), 270 DreamSettings.class.getName(), 271 UserSettings.class.getName(), 272 NotificationAccessSettings.class.getName(), 273 ManageAccountsSettings.class.getName(), 274 PrintSettingsFragment.class.getName(), 275 PrintJobSettingsFragment.class.getName(), 276 TrustedCredentialsSettings.class.getName(), 277 PaymentSettings.class.getName(), 278 KeyboardLayoutPickerFragment.class.getName(), 279 ZenModeSettings.class.getName(), 280 NotificationSettings.class.getName(), 281 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(), 282 ChooseLockPattern.ChooseLockPatternFragment.class.getName(), 283 InstalledAppDetails.class.getName() 284 }; 285 286 private SharedPreferences mDevelopmentPreferences; 287 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener; 288 289 private AuthenticatorHelper mAuthenticatorHelper; 290 private boolean mListeningToAccountUpdates; 291 292 private boolean mBatteryPresent = true; 293 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() { 294 295 @Override 296 public void onReceive(Context context, Intent intent) { 297 String action = intent.getAction(); 298 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { 299 boolean batteryPresent = Utils.isBatteryPresent(intent); 300 301 if (mBatteryPresent != batteryPresent) { 302 mBatteryPresent = batteryPresent; 303 invalidateCategories(); 304 } 305 } 306 } 307 }; 308 309 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor = 310 new DynamicIndexableContentMonitor(); 311 312 private Button mNextButton; 313 private ActionBar mActionBar; 314 private boolean mDisplayHomeAsUpEnabled; 315 316 private SearchView mSearchView; 317 private MenuItem mSearchMenuItem; 318 private boolean mSearchMenuItemExpanded = false; 319 private SearchResultsSummary mSearchResultsFragment; 320 private String mSearchQuery; 321 322 // Categories 323 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>(); 324 private boolean mNeedToRebuildCategories; 325 326 private static final int MSG_BUILD_CATEGORIES = 1; 327 private Handler mHandler = new Handler() { 328 @Override 329 public void handleMessage(Message msg) { 330 switch (msg.what) { 331 case MSG_BUILD_CATEGORIES: { 332 buildDashboardCategories(mCategories); 333 } break; 334 } 335 } 336 }; 337 338 private boolean mNeedToRevertToInitialFragment = false; 339 340 public AuthenticatorHelper getAuthenticatorHelper() { 341 return mAuthenticatorHelper; 342 } 343 344 public List<DashboardCategory> getDashboardCategories() { 345 if (mNeedToRebuildCategories) { 346 buildDashboardCategories(mCategories); 347 mNeedToRebuildCategories = false; 348 } 349 return mCategories; 350 } 351 352 @Override 353 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { 354 // Override the fragment title for Wallpaper settings 355 int titleRes = pref.getTitleRes(); 356 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) { 357 titleRes = R.string.wallpaper_settings_fragment_title; 358 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName()) 359 && UserHandle.myUserId() != UserHandle.USER_OWNER) { 360 if (UserManager.get(this).isLinkedUser()) { 361 titleRes = R.string.profile_info_settings_title; 362 } else { 363 titleRes = R.string.user_info_settings_title; 364 } 365 } 366 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(), 367 null, 0); 368 return true; 369 } 370 371 @Override 372 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 373 return false; 374 } 375 376 private void invalidateCategories() { 377 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) { 378 mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES); 379 } 380 } 381 382 @Override 383 public void onConfigurationChanged(Configuration newConfig) { 384 super.onConfigurationChanged(newConfig); 385 Index.getInstance(this).update(); 386 } 387 388 @Override 389 protected void onStart() { 390 super.onStart(); 391 392 if (mNeedToRevertToInitialFragment) { 393 revertToInitialFragment(); 394 } 395 } 396 397 @Override 398 public boolean onCreateOptionsMenu(Menu menu) { 399 MenuInflater inflater = getMenuInflater(); 400 inflater.inflate(R.menu.options_menu, menu); 401 402 // Cache the search query (can be overriden by the OnQueryTextListener) 403 final String query = mSearchQuery; 404 405 mSearchMenuItem = menu.findItem(R.id.search); 406 mSearchView = (SearchView) mSearchMenuItem.getActionView(); 407 408 if (mSearchMenuItem == null || mSearchView == null) { 409 return false; 410 } 411 412 mSearchMenuItem.setOnActionExpandListener(this); 413 mSearchView.setOnQueryTextListener(this); 414 mSearchView.setOnCloseListener(this); 415 416 if (mSearchMenuItemExpanded) { 417 mSearchMenuItem.expandActionView(); 418 } 419 mSearchView.setQuery(query, true /* submit */); 420 421 return true; 422 } 423 424 @Override 425 protected void onCreate(Bundle savedState) { 426 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) { 427 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0)); 428 } 429 430 mAuthenticatorHelper = new AuthenticatorHelper(); 431 mAuthenticatorHelper.updateAuthDescriptions(this); 432 mAuthenticatorHelper.onAccountsUpdated(this, null); 433 434 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, 435 Context.MODE_PRIVATE); 436 437 getMetaData(); 438 439 super.onCreate(savedState); 440 441 setContentView(R.layout.settings_main); 442 443 getFragmentManager().addOnBackStackChangedListener(this); 444 445 mDisplayHomeAsUpEnabled = true; 446 447 // Getting Intent properties can only be done after the super.onCreate(...) 448 final String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT); 449 450 if (initialFragmentName == null) { 451 Index.getInstance(this).update(); 452 } 453 454 if (savedState != null) { 455 // We are restarting from a previous saved state; used that to initialize, instead 456 // of starting fresh. 457 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED); 458 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY); 459 460 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE); 461 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle(); 462 setTitle(mInitialTitle); 463 464 ArrayList<DashboardCategory> categories = 465 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES); 466 if (categories != null) { 467 mCategories.addAll(categories); 468 setTitleFromBackStack(); 469 } 470 471 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP); 472 } else { 473 // We need to build the Categories in all cases 474 buildDashboardCategories(mCategories); 475 476 if (initialFragmentName != null) { 477 final ComponentName cn = getIntent().getComponent(); 478 // No UP is we are launched thru a Settings shortcut 479 if (!cn.getClassName().equals(SubSettings.class.getName())) { 480 mDisplayHomeAsUpEnabled = false; 481 } 482 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE); 483 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle(); 484 setTitle(mInitialTitle); 485 486 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 487 switchToFragment( initialFragmentName, initialArguments, true, false, 488 mInitialTitle, false); 489 } else { 490 // No UP if we are displaying the Headers 491 mDisplayHomeAsUpEnabled = false; 492 if (mCategories.size() > 0) { 493 mInitialTitle = getText(R.string.dashboard_title); 494 switchToFragment(DashboardSummary.class.getName(), null, false, false, 495 mInitialTitle, false); 496 } 497 } 498 } 499 500 mActionBar = getActionBar(); 501 mActionBar.setHomeButtonEnabled(true); 502 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled); 503 504 // see if we should show Back/Next buttons 505 Intent intent = getIntent(); 506 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) { 507 508 View buttonBar = findViewById(com.android.internal.R.id.button_bar); 509 if (buttonBar != null) { 510 buttonBar.setVisibility(View.VISIBLE); 511 512 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button); 513 backButton.setOnClickListener(new OnClickListener() { 514 public void onClick(View v) { 515 setResult(RESULT_CANCELED); 516 finish(); 517 } 518 }); 519 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button); 520 skipButton.setOnClickListener(new OnClickListener() { 521 public void onClick(View v) { 522 setResult(RESULT_OK); 523 finish(); 524 } 525 }); 526 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button); 527 mNextButton.setOnClickListener(new OnClickListener() { 528 public void onClick(View v) { 529 setResult(RESULT_OK); 530 finish(); 531 } 532 }); 533 534 // set our various button parameters 535 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) { 536 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT); 537 if (TextUtils.isEmpty(buttonText)) { 538 mNextButton.setVisibility(View.GONE); 539 } 540 else { 541 mNextButton.setText(buttonText); 542 } 543 } 544 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) { 545 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT); 546 if (TextUtils.isEmpty(buttonText)) { 547 backButton.setVisibility(View.GONE); 548 } 549 else { 550 backButton.setText(buttonText); 551 } 552 } 553 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) { 554 skipButton.setVisibility(View.VISIBLE); 555 } 556 } 557 } 558 } 559 560 @Override 561 public void onBackStackChanged() { 562 setTitleFromBackStack(); 563 } 564 565 private int setTitleFromBackStack() { 566 final int count = getFragmentManager().getBackStackEntryCount(); 567 568 if (count == 0) { 569 setTitle(mInitialTitle); 570 return 0; 571 } 572 573 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1); 574 setTitleFromBackStackEntry(bse); 575 576 return count; 577 } 578 579 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) { 580 final CharSequence title; 581 final int titleRes = bse.getBreadCrumbTitleRes(); 582 if (titleRes > 0) { 583 title = getText(titleRes); 584 } else { 585 title = bse.getBreadCrumbTitle(); 586 } 587 if (title != null) { 588 setTitle(title); 589 } 590 } 591 592 @Override 593 protected void onSaveInstanceState(Bundle outState) { 594 super.onSaveInstanceState(outState); 595 596 if (mCategories.size() > 0) { 597 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories); 598 } 599 600 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled); 601 602 // The option menus are created if the ActionBar is visible and they are also created 603 // asynchronously. If you launch Settings with an Intent action like 604 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked 605 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search 606 // menu item and search view are null. 607 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded(); 608 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded); 609 610 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY; 611 outState.putString(SAVE_KEY_SEARCH_QUERY, query); 612 } 613 614 @Override 615 public void onResume() { 616 super.onResume(); 617 618 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() { 619 @Override 620 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 621 invalidateCategories(); 622 } 623 }; 624 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener( 625 mDevelopmentPreferencesListener); 626 627 invalidateCategories(); 628 629 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 630 631 mDynamicIndexableContentMonitor.register(this); 632 633 if(!TextUtils.isEmpty(mSearchQuery)) { 634 onQueryTextSubmit(mSearchQuery); 635 } 636 } 637 638 @Override 639 public void onPause() { 640 super.onPause(); 641 642 unregisterReceiver(mBatteryInfoReceiver); 643 644 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener( 645 mDevelopmentPreferencesListener); 646 647 mDevelopmentPreferencesListener = null; 648 649 mDynamicIndexableContentMonitor.unregister(); 650 } 651 652 @Override 653 public void onDestroy() { 654 super.onDestroy(); 655 if (mListeningToAccountUpdates) { 656 AccountManager.get(this).removeOnAccountsUpdatedListener(this); 657 } 658 } 659 660 protected boolean isValidFragment(String fragmentName) { 661 // Almost all fragments are wrapped in this, 662 // except for a few that have their own activities. 663 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) { 664 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true; 665 } 666 return false; 667 } 668 669 /** 670 * When in two-pane mode, switch to the fragment pane to show the given 671 * preference fragment. 672 * 673 * @param header The new header to display. 674 * @param position The position of the Header in the list. 675 */ 676 private void onHeaderClick(Header header, int position) { 677 if (header == null) { 678 return; 679 } 680 if (header.fragment != null) { 681 Utils.startWithFragment(this, header.fragment, header.fragmentArguments, null, 0, 682 header.getTitle(getResources())); 683 } else if (header.intent != null) { 684 startActivity(header.intent); 685 } 686 } 687 688 /** 689 * Called to determine whether the header list should be hidden. 690 * The default implementation returns the 691 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied. 692 * This is set to false, for example, when the activity is being re-launched 693 * to show a particular preference activity. 694 */ 695 public boolean onIsHidingHeaders() { 696 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false); 697 } 698 699 @Override 700 public Intent getIntent() { 701 Intent superIntent = super.getIntent(); 702 String startingFragment = getStartingFragmentClass(superIntent); 703 // This is called from super.onCreate, isMultiPane() is not yet reliable 704 // Do not use onIsHidingHeaders either, which relies itself on this method 705 if (startingFragment != null) { 706 Intent modIntent = new Intent(superIntent); 707 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); 708 Bundle args = superIntent.getExtras(); 709 if (args != null) { 710 args = new Bundle(args); 711 } else { 712 args = new Bundle(); 713 } 714 args.putParcelable("intent", superIntent); 715 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras()); 716 return modIntent; 717 } 718 return superIntent; 719 } 720 721 /** 722 * Checks if the component name in the intent is different from the Settings class and 723 * returns the class name to load as a fragment. 724 */ 725 private String getStartingFragmentClass(Intent intent) { 726 if (mFragmentClass != null) return mFragmentClass; 727 728 String intentClass = intent.getComponent().getClassName(); 729 if (intentClass.equals(getClass().getName())) return null; 730 731 if ("com.android.settings.ManageApplications".equals(intentClass) 732 || "com.android.settings.RunningServices".equals(intentClass) 733 || "com.android.settings.applications.StorageUse".equals(intentClass)) { 734 // Old names of manage apps. 735 intentClass = com.android.settings.applications.ManageApplications.class.getName(); 736 } 737 738 return intentClass; 739 } 740 741 /** 742 * Start a new fragment containing a preference panel. If the preferences 743 * are being displayed in multi-pane mode, the given fragment class will 744 * be instantiated and placed in the appropriate pane. If running in 745 * single-pane mode, a new activity will be launched in which to show the 746 * fragment. 747 * 748 * @param fragmentClass Full name of the class implementing the fragment. 749 * @param args Any desired arguments to supply to the fragment. 750 * @param titleRes Optional resource identifier of the title of this 751 * fragment. 752 * @param titleText Optional text of the title of this fragment. 753 * @param resultTo Optional fragment that result data should be sent to. 754 * If non-null, resultTo.onActivityResult() will be called when this 755 * preference panel is done. The launched panel must use 756 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done. 757 * @param resultRequestCode If resultTo is non-null, this is the caller's 758 * request code to be received with the resut. 759 */ 760 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes, 761 CharSequence titleText, Fragment resultTo, int resultRequestCode) { 762 String title; 763 if (titleRes > 0) { 764 title = getString(titleRes); 765 } else if (titleText != null) { 766 title = titleText.toString(); 767 } else { 768 // There not much we can do in that case 769 title = ""; 770 } 771 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title); 772 } 773 774 /** 775 * Called by a preference panel fragment to finish itself. 776 * 777 * @param caller The fragment that is asking to be finished. 778 * @param resultCode Optional result code to send back to the original 779 * launching fragment. 780 * @param resultData Optional result data to send back to the original 781 * launching fragment. 782 */ 783 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) { 784 setResult(resultCode, resultData); 785 } 786 787 /** 788 * Start a new fragment. 789 * 790 * @param fragment The fragment to start 791 * @param push If true, the current fragment will be pushed onto the back stack. If false, 792 * the current fragment will be replaced. 793 */ 794 public void startPreferenceFragment(Fragment fragment, boolean push) { 795 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 796 transaction.replace(R.id.prefs, fragment); 797 if (push) { 798 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 799 transaction.addToBackStack(BACK_STACK_PREFS); 800 } else { 801 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); 802 } 803 transaction.commitAllowingStateLoss(); 804 } 805 806 /** 807 * Switch to a specific Fragment with taking care of validation, Title and BackStack 808 */ 809 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate, 810 boolean addToBackStack, CharSequence title, boolean withTransition) { 811 if (validate && !isValidFragment(fragmentName)) { 812 throw new IllegalArgumentException("Invalid fragment for this activity: " 813 + fragmentName); 814 } 815 Fragment f = Fragment.instantiate(this, fragmentName, args); 816 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 817 transaction.replace(R.id.prefs, f); 818 if (withTransition) { 819 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 820 } 821 if (addToBackStack) { 822 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS); 823 } 824 if (title != null) { 825 transaction.setBreadCrumbTitle(title); 826 } 827 transaction.commitAllowingStateLoss(); 828 return f; 829 } 830 831 public void setNeedToRebuildCategories(boolean need) { 832 mNeedToRebuildCategories = need; 833 } 834 835 /** 836 * Called when the activity needs its list of categories/tiles built. 837 * 838 * @param categories The list in which to place the tiles categories. 839 */ 840 private void buildDashboardCategories(List<DashboardCategory> categories) { 841 mCategories.clear(); 842 loadCategoriesFromResource(R.xml.dashboard_categories, categories); 843 updateTilesList(categories); 844 } 845 846 /** 847 * Parse the given XML file as a categories description, adding each 848 * parsed categories and tiles into the target list. 849 * 850 * @param resid The XML resource to load and parse. 851 * @param target The list in which the parsed categories and tiles should be placed. 852 */ 853 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) { 854 XmlResourceParser parser = null; 855 try { 856 parser = getResources().getXml(resid); 857 AttributeSet attrs = Xml.asAttributeSet(parser); 858 859 int type; 860 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 861 && type != XmlPullParser.START_TAG) { 862 // Parse next until start tag is found 863 } 864 865 String nodeName = parser.getName(); 866 if (!"dashboard-categories".equals(nodeName)) { 867 throw new RuntimeException( 868 "XML document must start with <preference-categories> tag; found" 869 + nodeName + " at " + parser.getPositionDescription()); 870 } 871 872 Bundle curBundle = null; 873 874 final int outerDepth = parser.getDepth(); 875 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 876 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 877 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 878 continue; 879 } 880 881 nodeName = parser.getName(); 882 if ("dashboard-category".equals(nodeName)) { 883 DashboardCategory category = new DashboardCategory(); 884 885 TypedArray sa = obtainStyledAttributes( 886 attrs, com.android.internal.R.styleable.PreferenceHeader); 887 category.id = sa.getResourceId( 888 com.android.internal.R.styleable.PreferenceHeader_id, 889 (int)DashboardCategory.CAT_ID_UNDEFINED); 890 891 TypedValue tv = sa.peekValue( 892 com.android.internal.R.styleable.PreferenceHeader_title); 893 if (tv != null && tv.type == TypedValue.TYPE_STRING) { 894 if (tv.resourceId != 0) { 895 category.titleRes = tv.resourceId; 896 } else { 897 category.title = tv.string; 898 } 899 } 900 sa.recycle(); 901 902 final int innerDepth = parser.getDepth(); 903 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 904 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 905 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 906 continue; 907 } 908 909 String innerNodeName = parser.getName(); 910 if (innerNodeName.equals("dashboard-tile")) { 911 DashboardTile tile = new DashboardTile(); 912 913 sa = obtainStyledAttributes( 914 attrs, com.android.internal.R.styleable.PreferenceHeader); 915 tile.id = sa.getResourceId( 916 com.android.internal.R.styleable.PreferenceHeader_id, 917 (int)HEADER_ID_UNDEFINED); 918 tv = sa.peekValue( 919 com.android.internal.R.styleable.PreferenceHeader_title); 920 if (tv != null && tv.type == TypedValue.TYPE_STRING) { 921 if (tv.resourceId != 0) { 922 tile.titleRes = tv.resourceId; 923 } else { 924 tile.title = tv.string; 925 } 926 } 927 tv = sa.peekValue( 928 com.android.internal.R.styleable.PreferenceHeader_summary); 929 if (tv != null && tv.type == TypedValue.TYPE_STRING) { 930 if (tv.resourceId != 0) { 931 tile.summaryRes = tv.resourceId; 932 } else { 933 tile.summary = tv.string; 934 } 935 } 936 tile.iconRes = sa.getResourceId( 937 com.android.internal.R.styleable.PreferenceHeader_icon, 0); 938 tile.fragment = sa.getString( 939 com.android.internal.R.styleable.PreferenceHeader_fragment); 940 sa.recycle(); 941 942 if (curBundle == null) { 943 curBundle = new Bundle(); 944 } 945 946 final int innerDepth2 = parser.getDepth(); 947 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 948 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) { 949 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 950 continue; 951 } 952 953 String innerNodeName2 = parser.getName(); 954 if (innerNodeName2.equals("extra")) { 955 getResources().parseBundleExtra("extra", attrs, curBundle); 956 XmlUtils.skipCurrentTag(parser); 957 958 } else if (innerNodeName2.equals("intent")) { 959 tile.intent = Intent.parseIntent(getResources(), parser, attrs); 960 961 } else { 962 XmlUtils.skipCurrentTag(parser); 963 } 964 } 965 966 if (curBundle.size() > 0) { 967 tile.fragmentArguments = curBundle; 968 curBundle = null; 969 } 970 971 category.addTile(tile); 972 973 } else { 974 XmlUtils.skipCurrentTag(parser); 975 } 976 } 977 978 target.add(category); 979 } else { 980 XmlUtils.skipCurrentTag(parser); 981 } 982 } 983 984 } catch (XmlPullParserException e) { 985 throw new RuntimeException("Error parsing categories", e); 986 } catch (IOException e) { 987 throw new RuntimeException("Error parsing categories", e); 988 } finally { 989 if (parser != null) parser.close(); 990 } 991 } 992 993 private void updateTilesList(List<DashboardCategory> target) { 994 final boolean showDev = mDevelopmentPreferences.getBoolean( 995 DevelopmentSettings.PREF_SHOW, 996 android.os.Build.TYPE.equals("eng")); 997 998 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 999 1000 final int size = target.size(); 1001 for (int i = 0; i < size; i++) { 1002 1003 DashboardCategory category = target.get(i); 1004 1005 // Ids are integers, so downcasting is ok 1006 int id = (int) category.id; 1007 if (id == R.id.account_settings) { 1008 insertAccountsTiles(category); 1009 continue; 1010 } 1011 int n = category.getTilesCount() - 1; 1012 while (n >= 0) { 1013 1014 DashboardTile tile = category.getTile(n); 1015 1016 id = (int) tile.id; 1017 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { 1018 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, category, tile); 1019 } else if (id == R.id.wifi_settings) { 1020 // Remove WiFi Settings if WiFi service is not available. 1021 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { 1022 category.removeTile(n); 1023 } 1024 } else if (id == R.id.bluetooth_settings) { 1025 // Remove Bluetooth Settings if Bluetooth service is not available. 1026 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { 1027 category.removeTile(n); 1028 } 1029 } else if (id == R.id.data_usage_settings) { 1030 // Remove data usage when kernel module not enabled 1031 final INetworkManagementService netManager = INetworkManagementService.Stub 1032 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); 1033 try { 1034 if (!netManager.isBandwidthControlEnabled()) { 1035 category.removeTile(n); 1036 } 1037 } catch (RemoteException e) { 1038 // ignored 1039 } 1040 } else if (id == R.id.battery_settings) { 1041 // Remove battery settings when battery is not available. (e.g. TV) 1042 1043 if (!mBatteryPresent) { 1044 category.removeTile(n); 1045 } 1046 } else if (id == R.id.home_settings) { 1047 if (!updateHomeSettingTiles(tile)) { 1048 category.removeTile(n); 1049 } 1050 } else if (id == R.id.user_settings) { 1051 if (!UserHandle.MU_ENABLED 1052 || !UserManager.supportsMultipleUsers() 1053 || Utils.isMonkeyRunning()) { 1054 category.removeTile(n); 1055 } 1056 } else if (id == R.id.nfc_payment_settings) { 1057 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { 1058 category.removeTile(n); 1059 } else { 1060 // Only show if NFC is on and we have the HCE feature 1061 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); 1062 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature( 1063 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { 1064 category.removeTile(n); 1065 } 1066 } 1067 } else if (id == R.id.development_settings) { 1068 if (!showDev) { 1069 category.removeTile(n); 1070 } 1071 } else if (id == R.id.account_add) { 1072 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 1073 category.removeTile(n); 1074 } 1075 } 1076 1077 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 1078 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { 1079 category.removeTile(n); 1080 } 1081 1082 n--; 1083 } 1084 } 1085 } 1086 1087 private boolean updateHomeSettingTiles(DashboardTile tile) { 1088 // Once we decide to show Home settings, keep showing it forever 1089 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE); 1090 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) { 1091 return true; 1092 } 1093 1094 try { 1095 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>(); 1096 getPackageManager().getHomeActivities(homeApps); 1097 if (homeApps.size() < 2) { 1098 // When there's only one available home app, omit this settings 1099 // category entirely at the top level UI. If the user just 1100 // uninstalled the penultimate home app candidiate, we also 1101 // now tell them about why they aren't seeing 'Home' in the list. 1102 if (sShowNoHomeNotice) { 1103 sShowNoHomeNotice = false; 1104 NoHomeDialogFragment.show(this); 1105 } 1106 return false; 1107 } else { 1108 // Okay, we're allowing the Home settings category. Tell it, when 1109 // invoked via this front door, that we'll need to be told about the 1110 // case when the user uninstalls all but one home app. 1111 if (tile.fragmentArguments == null) { 1112 tile.fragmentArguments = new Bundle(); 1113 } 1114 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true); 1115 } 1116 } catch (Exception e) { 1117 // Can't look up the home activity; bail on configuring the icon 1118 Log.w(LOG_TAG, "Problem looking up home activity!", e); 1119 } 1120 1121 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply(); 1122 return true; 1123 } 1124 1125 private void insertAccountsTiles(DashboardCategory target) { 1126 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes(); 1127 List<DashboardTile> dashboardTiles = new ArrayList<DashboardTile>(accountTypes.length); 1128 for (String accountType : accountTypes) { 1129 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType); 1130 if (label == null) { 1131 continue; 1132 } 1133 1134 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType); 1135 boolean skipToAccount = accounts.length == 1 1136 && !mAuthenticatorHelper.hasAccountPreferences(accountType); 1137 DashboardTile accountTile = new DashboardTile(); 1138 accountTile.title = label; 1139 if (accountTile.extras == null) { 1140 accountTile.extras = new Bundle(); 1141 } 1142 if (skipToAccount) { 1143 accountTile.fragment = AccountSyncSettings.class.getName(); 1144 accountTile.fragmentArguments = new Bundle(); 1145 // Need this for the icon 1146 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 1147 accountTile.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); 1148 accountTile.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, 1149 accounts[0]); 1150 } else { 1151 accountTile.fragment = ManageAccountsSettings.class.getName(); 1152 accountTile.fragmentArguments = new Bundle(); 1153 accountTile.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 1154 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, 1155 accountType); 1156 accountTile.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, 1157 label.toString()); 1158 } 1159 dashboardTiles.add(accountTile); 1160 mAuthenticatorHelper.preloadDrawableForType(this, accountType); 1161 } 1162 1163 // Sort by label 1164 Collections.sort(dashboardTiles, new Comparator<DashboardTile>() { 1165 @Override 1166 public int compare(DashboardTile t1, DashboardTile t2) { 1167 return t1.title.toString().compareTo(t2.title.toString()); 1168 } 1169 }); 1170 int index = 0; 1171 for (DashboardTile tile : dashboardTiles) { 1172 target.addTile(index, tile); 1173 index++; 1174 } 1175 if (!mListeningToAccountUpdates) { 1176 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); 1177 mListeningToAccountUpdates = true; 1178 } 1179 } 1180 1181 private void getMetaData() { 1182 try { 1183 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), 1184 PackageManager.GET_META_DATA); 1185 if (ai == null || ai.metaData == null) return; 1186 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 1187 } catch (NameNotFoundException nnfe) { 1188 // No recovery 1189 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString()); 1190 } 1191 } 1192 1193 // give subclasses access to the Next button 1194 public boolean hasNextButton() { 1195 return mNextButton != null; 1196 } 1197 1198 public Button getNextButton() { 1199 return mNextButton; 1200 } 1201 1202 @Override 1203 public boolean shouldUpRecreateTask(Intent targetIntent) { 1204 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class)); 1205 } 1206 1207 @Override 1208 public void onAccountsUpdated(Account[] accounts) { 1209 // TODO: watch for package upgrades to invalidate cache; see 7206643 1210 mAuthenticatorHelper.updateAuthDescriptions(this); 1211 mAuthenticatorHelper.onAccountsUpdated(this, accounts); 1212 invalidateCategories(); 1213 } 1214 1215 public static void requestHomeNotice() { 1216 sShowNoHomeNotice = true; 1217 } 1218 1219 @Override 1220 public boolean onQueryTextSubmit(String query) { 1221 switchToSearchResultsFragmentIfNeeded(); 1222 mSearchQuery = query; 1223 return mSearchResultsFragment.onQueryTextSubmit(query); 1224 } 1225 1226 @Override 1227 public boolean onQueryTextChange(String newText) { 1228 mSearchQuery = newText; 1229 if (TextUtils.isEmpty(newText) && mSearchResultsFragment == null) { 1230 return false; 1231 } 1232 return mSearchResultsFragment.onQueryTextChange(newText); 1233 } 1234 1235 @Override 1236 public boolean onClose() { 1237 return false; 1238 } 1239 1240 @Override 1241 public boolean onMenuItemActionExpand(MenuItem item) { 1242 if (item.getItemId() == mSearchMenuItem.getItemId()) { 1243 switchToSearchResultsFragmentIfNeeded(); 1244 } 1245 return true; 1246 } 1247 1248 @Override 1249 public boolean onMenuItemActionCollapse(MenuItem item) { 1250 if (item.getItemId() == mSearchMenuItem.getItemId()) { 1251 if (mSearchMenuItemExpanded) { 1252 revertToInitialFragment(); 1253 } 1254 } 1255 return true; 1256 } 1257 1258 private void switchToSearchResultsFragmentIfNeeded() { 1259 if (mSearchResultsFragment != null) { 1260 return; 1261 } 1262 Fragment current = getFragmentManager().findFragmentById(R.id.prefs); 1263 if (current != null && current instanceof SearchResultsSummary) { 1264 mSearchResultsFragment = (SearchResultsSummary) current; 1265 } else { 1266 String title = getString(R.string.search_results_title); 1267 mSearchResultsFragment = (SearchResultsSummary) switchToFragment( 1268 SearchResultsSummary.class.getName(), null, false, true, title, true); 1269 } 1270 mSearchResultsFragment.setSearchView(mSearchView); 1271 mSearchMenuItemExpanded = true; 1272 } 1273 1274 public void needToRevertToInitialFragment() { 1275 mNeedToRevertToInitialFragment = true; 1276 } 1277 1278 private void revertToInitialFragment() { 1279 mNeedToRevertToInitialFragment = false; 1280 mSearchResultsFragment = null; 1281 mSearchMenuItemExpanded = false; 1282 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS, 1283 FragmentManager.POP_BACK_STACK_INCLUSIVE); 1284 if (mSearchMenuItem != null) { 1285 mSearchMenuItem.collapseActionView(); 1286 } 1287 } 1288} 1289