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