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