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