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