1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings; 18 19 import android.accounts.Account; 20 import android.accounts.AccountManager; 21 import android.accounts.OnAccountsUpdateListener; 22 import android.app.Activity; 23 import android.app.AlertDialog; 24 import android.app.Dialog; 25 import android.app.DialogFragment; 26 import android.app.admin.DevicePolicyManager; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.SharedPreferences; 33 import android.content.pm.ActivityInfo; 34 import android.content.pm.PackageManager; 35 import android.content.pm.PackageManager.NameNotFoundException; 36 import android.content.pm.ResolveInfo; 37 import android.graphics.drawable.Drawable; 38 import android.nfc.NfcAdapter; 39 import android.os.Bundle; 40 import android.os.INetworkManagementService; 41 import android.os.RemoteException; 42 import android.os.ServiceManager; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.preference.Preference; 46 import android.preference.PreferenceActivity; 47 import android.preference.PreferenceFragment; 48 import android.text.TextUtils; 49 import android.util.Log; 50 import android.view.LayoutInflater; 51 import android.view.View; 52 import android.view.View.OnClickListener; 53 import android.view.ViewGroup; 54 import android.widget.ArrayAdapter; 55 import android.widget.Button; 56 import android.widget.ImageButton; 57 import android.widget.ImageView; 58 import android.widget.ListAdapter; 59 import android.widget.Switch; 60 import android.widget.TextView; 61 62 import com.android.internal.util.ArrayUtils; 63 import com.android.settings.accessibility.AccessibilitySettings; 64 import com.android.settings.accessibility.CaptionPropertiesFragment; 65 import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment; 66 import com.android.settings.accounts.AccountSyncSettings; 67 import com.android.settings.accounts.AuthenticatorHelper; 68 import com.android.settings.accounts.ManageAccountsSettings; 69 import com.android.settings.applications.AppOpsSummary; 70 import com.android.settings.applications.ManageApplications; 71 import com.android.settings.applications.ProcessStatsUi; 72 import com.android.settings.bluetooth.BluetoothEnabler; 73 import com.android.settings.bluetooth.BluetoothSettings; 74 import com.android.settings.deviceinfo.Memory; 75 import com.android.settings.deviceinfo.UsbSettings; 76 import com.android.settings.fuelgauge.PowerUsageSummary; 77 import com.android.settings.inputmethod.InputMethodAndLanguageSettings; 78 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; 79 import com.android.settings.inputmethod.SpellCheckersSettings; 80 import com.android.settings.inputmethod.UserDictionaryList; 81 import com.android.settings.location.LocationSettings; 82 import com.android.settings.nfc.AndroidBeam; 83 import com.android.settings.nfc.PaymentSettings; 84 import com.android.settings.print.PrintJobSettingsFragment; 85 import com.android.settings.print.PrintServiceSettingsFragment; 86 import com.android.settings.print.PrintSettingsFragment; 87 import com.android.settings.tts.TextToSpeechSettings; 88 import com.android.settings.users.UserSettings; 89 import com.android.settings.vpn2.VpnSettings; 90 import com.android.settings.wfd.WifiDisplaySettings; 91 import com.android.settings.wifi.AdvancedWifiSettings; 92 import com.android.settings.wifi.WifiEnabler; 93 import com.android.settings.wifi.WifiSettings; 94 import com.android.settings.wifi.p2p.WifiP2pSettings; 95 96 import java.util.ArrayList; 97 import java.util.Collections; 98 import java.util.Comparator; 99 import java.util.HashMap; 100 import java.util.List; 101 102 /** 103 * Top-level settings activity to handle single pane and double pane UI layout. 104 */ 105 public class Settings extends PreferenceActivity 106 implements ButtonBarHandler, OnAccountsUpdateListener { 107 108 private static final String LOG_TAG = "Settings"; 109 110 private static final String META_DATA_KEY_HEADER_ID = 111 "com.android.settings.TOP_LEVEL_HEADER_ID"; 112 private static final String META_DATA_KEY_FRAGMENT_CLASS = 113 "com.android.settings.FRAGMENT_CLASS"; 114 private static final String META_DATA_KEY_PARENT_TITLE = 115 "com.android.settings.PARENT_FRAGMENT_TITLE"; 116 private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS = 117 "com.android.settings.PARENT_FRAGMENT_CLASS"; 118 119 private static final String EXTRA_UI_OPTIONS = "settings:ui_options"; 120 121 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER"; 122 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER"; 123 124 static final int DIALOG_ONLY_ONE_HOME = 1; 125 126 private static boolean sShowNoHomeNotice = false; 127 128 private String mFragmentClass; 129 private int mTopLevelHeaderId; 130 private Header mFirstHeader; 131 private Header mCurrentHeader; 132 private Header mParentHeader; 133 private boolean mInLocalHeaderSwitch; 134 135 // Show only these settings for restricted users 136 private int[] SETTINGS_FOR_RESTRICTED = { 137 R.id.wireless_section, 138 R.id.wifi_settings, 139 R.id.bluetooth_settings, 140 R.id.data_usage_settings, 141 R.id.wireless_settings, 142 R.id.device_section, 143 R.id.sound_settings, 144 R.id.display_settings, 145 R.id.storage_settings, 146 R.id.application_settings, 147 R.id.battery_settings, 148 R.id.personal_section, 149 R.id.location_settings, 150 R.id.security_settings, 151 R.id.language_settings, 152 R.id.user_settings, 153 R.id.account_settings, 154 R.id.account_add, 155 R.id.system_section, 156 R.id.date_time_settings, 157 R.id.about_settings, 158 R.id.accessibility_settings, 159 R.id.print_settings, 160 R.id.nfc_payment_settings, 161 R.id.home_settings 162 }; 163 164 private SharedPreferences mDevelopmentPreferences; 165 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener; 166 167 // TODO: Update Call Settings based on airplane mode state. 168 169 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>(); 170 171 private AuthenticatorHelper mAuthenticatorHelper; 172 private Header mLastHeader; 173 private boolean mListeningToAccountUpdates; 174 175 private boolean mBatteryPresent = true; 176 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() { 177 178 @Override 179 public void onReceive(Context context, Intent intent) { 180 String action = intent.getAction(); 181 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { 182 boolean batteryPresent = Utils.isBatteryPresent(intent); 183 184 if (mBatteryPresent != batteryPresent) { 185 mBatteryPresent = batteryPresent; 186 invalidateHeaders(); 187 } 188 } 189 } 190 }; 191 192 @Override 193 protected void onCreate(Bundle savedInstanceState) { 194 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) { 195 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0)); 196 } 197 198 mAuthenticatorHelper = new AuthenticatorHelper(); 199 mAuthenticatorHelper.updateAuthDescriptions(this); 200 mAuthenticatorHelper.onAccountsUpdated(this, null); 201 202 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, 203 Context.MODE_PRIVATE); 204 205 getMetaData(); 206 mInLocalHeaderSwitch = true; 207 super.onCreate(savedInstanceState); 208 mInLocalHeaderSwitch = false; 209 210 if (!onIsHidingHeaders() && onIsMultiPane()) { 211 highlightHeader(mTopLevelHeaderId); 212 // Force the title so that it doesn't get overridden by a direct launch of 213 // a specific settings screen. 214 setTitle(R.string.settings_label); 215 } 216 217 // Retrieve any saved state 218 if (savedInstanceState != null) { 219 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER); 220 mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER); 221 } 222 223 // If the current header was saved, switch to it 224 if (savedInstanceState != null && mCurrentHeader != null) { 225 //switchToHeaderLocal(mCurrentHeader); 226 showBreadCrumbs(mCurrentHeader.title, null); 227 } 228 229 if (mParentHeader != null) { 230 setParentTitle(mParentHeader.title, null, new OnClickListener() { 231 @Override 232 public void onClick(View v) { 233 switchToParent(mParentHeader.fragment); 234 } 235 }); 236 } 237 238 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs 239 if (onIsMultiPane()) { 240 getActionBar().setDisplayHomeAsUpEnabled(false); 241 getActionBar().setHomeButtonEnabled(false); 242 } 243 } 244 245 @Override 246 protected void onSaveInstanceState(Bundle outState) { 247 super.onSaveInstanceState(outState); 248 249 // Save the current fragment, if it is the same as originally launched 250 if (mCurrentHeader != null) { 251 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader); 252 } 253 if (mParentHeader != null) { 254 outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader); 255 } 256 } 257 258 @Override 259 public void onResume() { 260 super.onResume(); 261 262 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() { 263 @Override 264 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 265 invalidateHeaders(); 266 } 267 }; 268 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener( 269 mDevelopmentPreferencesListener); 270 271 ListAdapter listAdapter = getListAdapter(); 272 if (listAdapter instanceof HeaderAdapter) { 273 ((HeaderAdapter) listAdapter).resume(); 274 } 275 invalidateHeaders(); 276 277 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 278 } 279 280 @Override 281 public void onPause() { 282 super.onPause(); 283 284 unregisterReceiver(mBatteryInfoReceiver); 285 286 ListAdapter listAdapter = getListAdapter(); 287 if (listAdapter instanceof HeaderAdapter) { 288 ((HeaderAdapter) listAdapter).pause(); 289 } 290 291 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener( 292 mDevelopmentPreferencesListener); 293 mDevelopmentPreferencesListener = null; 294 } 295 296 @Override 297 public void onDestroy() { 298 super.onDestroy(); 299 if (mListeningToAccountUpdates) { 300 AccountManager.get(this).removeOnAccountsUpdatedListener(this); 301 } 302 } 303 304 @Override 305 public boolean onIsMultiPane() { 306 return false; 307 } 308 309 private static final String[] ENTRY_FRAGMENTS = { 310 WirelessSettings.class.getName(), 311 WifiSettings.class.getName(), 312 AdvancedWifiSettings.class.getName(), 313 BluetoothSettings.class.getName(), 314 TetherSettings.class.getName(), 315 WifiP2pSettings.class.getName(), 316 VpnSettings.class.getName(), 317 DateTimeSettings.class.getName(), 318 LocalePicker.class.getName(), 319 InputMethodAndLanguageSettings.class.getName(), 320 SpellCheckersSettings.class.getName(), 321 UserDictionaryList.class.getName(), 322 UserDictionarySettings.class.getName(), 323 SoundSettings.class.getName(), 324 DisplaySettings.class.getName(), 325 DeviceInfoSettings.class.getName(), 326 ManageApplications.class.getName(), 327 ProcessStatsUi.class.getName(), 328 NotificationStation.class.getName(), 329 LocationSettings.class.getName(), 330 SecuritySettings.class.getName(), 331 PrivacySettings.class.getName(), 332 DeviceAdminSettings.class.getName(), 333 AccessibilitySettings.class.getName(), 334 CaptionPropertiesFragment.class.getName(), 335 TextToSpeechSettings.class.getName(), 336 Memory.class.getName(), 337 DevelopmentSettings.class.getName(), 338 UsbSettings.class.getName(), 339 AndroidBeam.class.getName(), 340 WifiDisplaySettings.class.getName(), 341 PowerUsageSummary.class.getName(), 342 AccountSyncSettings.class.getName(), 343 CryptKeeperSettings.class.getName(), 344 DataUsageSummary.class.getName(), 345 DreamSettings.class.getName(), 346 UserSettings.class.getName(), 347 NotificationAccessSettings.class.getName(), 348 ManageAccountsSettings.class.getName(), 349 PrintSettingsFragment.class.getName(), 350 PrintJobSettingsFragment.class.getName(), 351 TrustedCredentialsSettings.class.getName(), 352 PaymentSettings.class.getName(), 353 KeyboardLayoutPickerFragment.class.getName() 354 }; 355 356 @Override 357 protected boolean isValidFragment(String fragmentName) { 358 // Almost all fragments are wrapped in this, 359 // except for a few that have their own activities. 360 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) { 361 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true; 362 } 363 return false; 364 } 365 366 private void switchToHeaderLocal(Header header) { 367 mInLocalHeaderSwitch = true; 368 switchToHeader(header); 369 mInLocalHeaderSwitch = false; 370 } 371 372 @Override 373 public void switchToHeader(Header header) { 374 if (!mInLocalHeaderSwitch) { 375 mCurrentHeader = null; 376 mParentHeader = null; 377 } 378 super.switchToHeader(header); 379 } 380 381 /** 382 * Switch to parent fragment and store the grand parent's info 383 * @param className name of the activity wrapper for the parent fragment. 384 */ 385 private void switchToParent(String className) { 386 final ComponentName cn = new ComponentName(this, className); 387 try { 388 final PackageManager pm = getPackageManager(); 389 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA); 390 391 if (parentInfo != null && parentInfo.metaData != null) { 392 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 393 CharSequence fragmentTitle = parentInfo.loadLabel(pm); 394 Header parentHeader = new Header(); 395 parentHeader.fragment = fragmentClass; 396 parentHeader.title = fragmentTitle; 397 mCurrentHeader = parentHeader; 398 399 switchToHeaderLocal(parentHeader); 400 highlightHeader(mTopLevelHeaderId); 401 402 mParentHeader = new Header(); 403 mParentHeader.fragment 404 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); 405 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE); 406 } 407 } catch (NameNotFoundException nnfe) { 408 Log.w(LOG_TAG, "Could not find parent activity : " + className); 409 } 410 } 411 412 @Override 413 public void onNewIntent(Intent intent) { 414 super.onNewIntent(intent); 415 416 // If it is not launched from history, then reset to top-level 417 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { 418 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) { 419 switchToHeaderLocal(mFirstHeader); 420 } 421 getListView().setSelectionFromTop(0, 0); 422 } 423 } 424 425 private void highlightHeader(int id) { 426 if (id != 0) { 427 Integer index = mHeaderIndexMap.get(id); 428 if (index != null) { 429 getListView().setItemChecked(index, true); 430 if (isMultiPane()) { 431 getListView().smoothScrollToPosition(index); 432 } 433 } 434 } 435 } 436 437 @Override 438 public Intent getIntent() { 439 Intent superIntent = super.getIntent(); 440 String startingFragment = getStartingFragmentClass(superIntent); 441 // This is called from super.onCreate, isMultiPane() is not yet reliable 442 // Do not use onIsHidingHeaders either, which relies itself on this method 443 if (startingFragment != null && !onIsMultiPane()) { 444 Intent modIntent = new Intent(superIntent); 445 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); 446 Bundle args = superIntent.getExtras(); 447 if (args != null) { 448 args = new Bundle(args); 449 } else { 450 args = new Bundle(); 451 } 452 args.putParcelable("intent", superIntent); 453 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras()); 454 return modIntent; 455 } 456 return superIntent; 457 } 458 459 /** 460 * Checks if the component name in the intent is different from the Settings class and 461 * returns the class name to load as a fragment. 462 */ 463 protected String getStartingFragmentClass(Intent intent) { 464 if (mFragmentClass != null) return mFragmentClass; 465 466 String intentClass = intent.getComponent().getClassName(); 467 if (intentClass.equals(getClass().getName())) return null; 468 469 if ("com.android.settings.ManageApplications".equals(intentClass) 470 || "com.android.settings.RunningServices".equals(intentClass) 471 || "com.android.settings.applications.StorageUse".equals(intentClass)) { 472 // Old names of manage apps. 473 intentClass = com.android.settings.applications.ManageApplications.class.getName(); 474 } 475 476 return intentClass; 477 } 478 479 /** 480 * Override initial header when an activity-alias is causing Settings to be launched 481 * for a specific fragment encoded in the android:name parameter. 482 */ 483 @Override 484 public Header onGetInitialHeader() { 485 String fragmentClass = getStartingFragmentClass(super.getIntent()); 486 if (fragmentClass != null) { 487 Header header = new Header(); 488 header.fragment = fragmentClass; 489 header.title = getTitle(); 490 header.fragmentArguments = getIntent().getExtras(); 491 mCurrentHeader = header; 492 return header; 493 } 494 495 return mFirstHeader; 496 } 497 498 @Override 499 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, 500 int titleRes, int shortTitleRes) { 501 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args, 502 titleRes, shortTitleRes); 503 504 // Some fragments want split ActionBar; these should stay in sync with 505 // uiOptions for fragments also defined as activities in manifest. 506 if (WifiSettings.class.getName().equals(fragmentName) || 507 WifiP2pSettings.class.getName().equals(fragmentName) || 508 BluetoothSettings.class.getName().equals(fragmentName) || 509 DreamSettings.class.getName().equals(fragmentName) || 510 LocationSettings.class.getName().equals(fragmentName) || 511 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) || 512 PrintSettingsFragment.class.getName().equals(fragmentName) || 513 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) { 514 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW); 515 } 516 517 intent.setClass(this, SubSettings.class); 518 return intent; 519 } 520 521 /** 522 * Populate the activity with the top-level headers. 523 */ 524 @Override 525 public void onBuildHeaders(List<Header> headers) { 526 if (!onIsHidingHeaders()) { 527 loadHeadersFromResource(R.xml.settings_headers, headers); 528 updateHeaderList(headers); 529 } 530 } 531 532 private void updateHeaderList(List<Header> target) { 533 final boolean showDev = mDevelopmentPreferences.getBoolean( 534 DevelopmentSettings.PREF_SHOW, 535 android.os.Build.TYPE.equals("eng")); 536 int i = 0; 537 538 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 539 mHeaderIndexMap.clear(); 540 while (i < target.size()) { 541 Header header = target.get(i); 542 // Ids are integers, so downcasting 543 int id = (int) header.id; 544 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { 545 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); 546 } else if (id == R.id.wifi_settings) { 547 // Remove WiFi Settings if WiFi service is not available. 548 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { 549 target.remove(i); 550 } 551 } else if (id == R.id.bluetooth_settings) { 552 // Remove Bluetooth Settings if Bluetooth service is not available. 553 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { 554 target.remove(i); 555 } 556 } else if (id == R.id.data_usage_settings) { 557 // Remove data usage when kernel module not enabled 558 final INetworkManagementService netManager = INetworkManagementService.Stub 559 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); 560 try { 561 if (!netManager.isBandwidthControlEnabled()) { 562 target.remove(i); 563 } 564 } catch (RemoteException e) { 565 // ignored 566 } 567 } else if (id == R.id.battery_settings) { 568 // Remove battery settings when battery is not available. (e.g. TV) 569 570 if (!mBatteryPresent) { 571 target.remove(i); 572 } 573 } else if (id == R.id.account_settings) { 574 int headerIndex = i + 1; 575 i = insertAccountsHeaders(target, headerIndex); 576 } else if (id == R.id.home_settings) { 577 if (!updateHomeSettingHeaders(header)) { 578 target.remove(i); 579 } 580 } else if (id == R.id.user_settings) { 581 if (!UserHandle.MU_ENABLED 582 || !UserManager.supportsMultipleUsers() 583 || Utils.isMonkeyRunning()) { 584 target.remove(i); 585 } 586 } else if (id == R.id.nfc_payment_settings) { 587 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { 588 target.remove(i); 589 } else { 590 // Only show if NFC is on and we have the HCE feature 591 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); 592 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature( 593 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { 594 target.remove(i); 595 } 596 } 597 } else if (id == R.id.development_settings) { 598 if (!showDev) { 599 target.remove(i); 600 } 601 } else if (id == R.id.account_add) { 602 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 603 target.remove(i); 604 } 605 } 606 607 if (i < target.size() && target.get(i) == header 608 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 609 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { 610 target.remove(i); 611 } 612 613 // Increment if the current one wasn't removed by the Utils code. 614 if (i < target.size() && target.get(i) == header) { 615 // Hold on to the first header, when we need to reset to the top-level 616 if (mFirstHeader == null && 617 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) { 618 mFirstHeader = header; 619 } 620 mHeaderIndexMap.put(id, i); 621 i++; 622 } 623 } 624 } 625 626 private int insertAccountsHeaders(List<Header> target, int headerIndex) { 627 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes(); 628 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length); 629 for (String accountType : accountTypes) { 630 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType); 631 if (label == null) { 632 continue; 633 } 634 635 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType); 636 boolean skipToAccount = accounts.length == 1 637 && !mAuthenticatorHelper.hasAccountPreferences(accountType); 638 Header accHeader = new Header(); 639 accHeader.title = label; 640 if (accHeader.extras == null) { 641 accHeader.extras = new Bundle(); 642 } 643 if (skipToAccount) { 644 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title; 645 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title; 646 accHeader.fragment = AccountSyncSettings.class.getName(); 647 accHeader.fragmentArguments = new Bundle(); 648 // Need this for the icon 649 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 650 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); 651 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, 652 accounts[0]); 653 } else { 654 accHeader.breadCrumbTitle = label; 655 accHeader.breadCrumbShortTitle = label; 656 accHeader.fragment = ManageAccountsSettings.class.getName(); 657 accHeader.fragmentArguments = new Bundle(); 658 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 659 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, 660 accountType); 661 if (!isMultiPane()) { 662 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, 663 label.toString()); 664 } 665 } 666 accountHeaders.add(accHeader); 667 mAuthenticatorHelper.preloadDrawableForType(this, accountType); 668 } 669 670 // Sort by label 671 Collections.sort(accountHeaders, new Comparator<Header>() { 672 @Override 673 public int compare(Header h1, Header h2) { 674 return h1.title.toString().compareTo(h2.title.toString()); 675 } 676 }); 677 678 for (Header header : accountHeaders) { 679 target.add(headerIndex++, header); 680 } 681 if (!mListeningToAccountUpdates) { 682 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); 683 mListeningToAccountUpdates = true; 684 } 685 return headerIndex; 686 } 687 688 private boolean updateHomeSettingHeaders(Header header) { 689 // Once we decide to show Home settings, keep showing it forever 690 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE); 691 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) { 692 return true; 693 } 694 695 try { 696 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>(); 697 getPackageManager().getHomeActivities(homeApps); 698 if (homeApps.size() < 2) { 699 // When there's only one available home app, omit this settings 700 // category entirely at the top level UI. If the user just 701 // uninstalled the penultimate home app candidiate, we also 702 // now tell them about why they aren't seeing 'Home' in the list. 703 if (sShowNoHomeNotice) { 704 sShowNoHomeNotice = false; 705 NoHomeDialogFragment.show(this); 706 } 707 return false; 708 } else { 709 // Okay, we're allowing the Home settings category. Tell it, when 710 // invoked via this front door, that we'll need to be told about the 711 // case when the user uninstalls all but one home app. 712 if (header.fragmentArguments == null) { 713 header.fragmentArguments = new Bundle(); 714 } 715 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true); 716 } 717 } catch (Exception e) { 718 // Can't look up the home activity; bail on configuring the icon 719 Log.w(LOG_TAG, "Problem looking up home activity!", e); 720 } 721 722 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply(); 723 return true; 724 } 725 726 private void getMetaData() { 727 try { 728 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), 729 PackageManager.GET_META_DATA); 730 if (ai == null || ai.metaData == null) return; 731 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); 732 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 733 734 // Check if it has a parent specified and create a Header object 735 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE); 736 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); 737 if (parentFragmentClass != null) { 738 mParentHeader = new Header(); 739 mParentHeader.fragment = parentFragmentClass; 740 if (parentHeaderTitleRes != 0) { 741 mParentHeader.title = getResources().getString(parentHeaderTitleRes); 742 } 743 } 744 } catch (NameNotFoundException nnfe) { 745 // No recovery 746 } 747 } 748 749 @Override 750 public boolean hasNextButton() { 751 return super.hasNextButton(); 752 } 753 754 @Override 755 public Button getNextButton() { 756 return super.getNextButton(); 757 } 758 759 public static class NoHomeDialogFragment extends DialogFragment { 760 public static void show(Activity parent) { 761 final NoHomeDialogFragment dialog = new NoHomeDialogFragment(); 762 dialog.show(parent.getFragmentManager(), null); 763 } 764 765 @Override 766 public Dialog onCreateDialog(Bundle savedInstanceState) { 767 return new AlertDialog.Builder(getActivity()) 768 .setMessage(R.string.only_one_home_message) 769 .setPositiveButton(android.R.string.ok, null) 770 .create(); 771 } 772 } 773 774 private static class HeaderAdapter extends ArrayAdapter<Header> { 775 static final int HEADER_TYPE_CATEGORY = 0; 776 static final int HEADER_TYPE_NORMAL = 1; 777 static final int HEADER_TYPE_SWITCH = 2; 778 static final int HEADER_TYPE_BUTTON = 3; 779 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1; 780 781 private final WifiEnabler mWifiEnabler; 782 private final BluetoothEnabler mBluetoothEnabler; 783 private AuthenticatorHelper mAuthHelper; 784 private DevicePolicyManager mDevicePolicyManager; 785 786 private static class HeaderViewHolder { 787 ImageView icon; 788 TextView title; 789 TextView summary; 790 Switch switch_; 791 ImageButton button_; 792 View divider_; 793 } 794 795 private LayoutInflater mInflater; 796 797 static int getHeaderType(Header header) { 798 if (header.fragment == null && header.intent == null) { 799 return HEADER_TYPE_CATEGORY; 800 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) { 801 return HEADER_TYPE_SWITCH; 802 } else if (header.id == R.id.security_settings) { 803 return HEADER_TYPE_BUTTON; 804 } else { 805 return HEADER_TYPE_NORMAL; 806 } 807 } 808 809 @Override 810 public int getItemViewType(int position) { 811 Header header = getItem(position); 812 return getHeaderType(header); 813 } 814 815 @Override 816 public boolean areAllItemsEnabled() { 817 return false; // because of categories 818 } 819 820 @Override 821 public boolean isEnabled(int position) { 822 return getItemViewType(position) != HEADER_TYPE_CATEGORY; 823 } 824 825 @Override 826 public int getViewTypeCount() { 827 return HEADER_TYPE_COUNT; 828 } 829 830 @Override 831 public boolean hasStableIds() { 832 return true; 833 } 834 835 public HeaderAdapter(Context context, List<Header> objects, 836 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) { 837 super(context, 0, objects); 838 839 mAuthHelper = authenticatorHelper; 840 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 841 842 // Temp Switches provided as placeholder until the adapter replaces these with actual 843 // Switches inflated from their layouts. Must be done before adapter is set in super 844 mWifiEnabler = new WifiEnabler(context, new Switch(context)); 845 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context)); 846 mDevicePolicyManager = dpm; 847 } 848 849 @Override 850 public View getView(int position, View convertView, ViewGroup parent) { 851 HeaderViewHolder holder; 852 Header header = getItem(position); 853 int headerType = getHeaderType(header); 854 View view = null; 855 856 if (convertView == null) { 857 holder = new HeaderViewHolder(); 858 switch (headerType) { 859 case HEADER_TYPE_CATEGORY: 860 view = new TextView(getContext(), null, 861 android.R.attr.listSeparatorTextViewStyle); 862 holder.title = (TextView) view; 863 break; 864 865 case HEADER_TYPE_SWITCH: 866 view = mInflater.inflate(R.layout.preference_header_switch_item, parent, 867 false); 868 holder.icon = (ImageView) view.findViewById(R.id.icon); 869 holder.title = (TextView) 870 view.findViewById(com.android.internal.R.id.title); 871 holder.summary = (TextView) 872 view.findViewById(com.android.internal.R.id.summary); 873 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget); 874 break; 875 876 case HEADER_TYPE_BUTTON: 877 view = mInflater.inflate(R.layout.preference_header_button_item, parent, 878 false); 879 holder.icon = (ImageView) view.findViewById(R.id.icon); 880 holder.title = (TextView) 881 view.findViewById(com.android.internal.R.id.title); 882 holder.summary = (TextView) 883 view.findViewById(com.android.internal.R.id.summary); 884 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget); 885 holder.divider_ = view.findViewById(R.id.divider); 886 break; 887 888 case HEADER_TYPE_NORMAL: 889 view = mInflater.inflate( 890 R.layout.preference_header_item, parent, 891 false); 892 holder.icon = (ImageView) view.findViewById(R.id.icon); 893 holder.title = (TextView) 894 view.findViewById(com.android.internal.R.id.title); 895 holder.summary = (TextView) 896 view.findViewById(com.android.internal.R.id.summary); 897 break; 898 } 899 view.setTag(holder); 900 } else { 901 view = convertView; 902 holder = (HeaderViewHolder) view.getTag(); 903 } 904 905 // All view fields must be updated every time, because the view may be recycled 906 switch (headerType) { 907 case HEADER_TYPE_CATEGORY: 908 holder.title.setText(header.getTitle(getContext().getResources())); 909 break; 910 911 case HEADER_TYPE_SWITCH: 912 // Would need a different treatment if the main menu had more switches 913 if (header.id == R.id.wifi_settings) { 914 mWifiEnabler.setSwitch(holder.switch_); 915 } else { 916 mBluetoothEnabler.setSwitch(holder.switch_); 917 } 918 updateCommonHeaderView(header, holder); 919 break; 920 921 case HEADER_TYPE_BUTTON: 922 if (header.id == R.id.security_settings) { 923 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); 924 if (hasCert) { 925 holder.button_.setVisibility(View.VISIBLE); 926 holder.divider_.setVisibility(View.VISIBLE); 927 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null; 928 if (isManaged) { 929 holder.button_.setImageResource(R.drawable.ic_settings_about); 930 } else { 931 holder.button_.setImageResource( 932 android.R.drawable.stat_notify_error); 933 } 934 holder.button_.setOnClickListener(new OnClickListener() { 935 @Override 936 public void onClick(View v) { 937 Intent intent = new Intent( 938 android.provider.Settings.ACTION_MONITORING_CERT_INFO); 939 getContext().startActivity(intent); 940 } 941 }); 942 } else { 943 holder.button_.setVisibility(View.GONE); 944 holder.divider_.setVisibility(View.GONE); 945 } 946 } 947 updateCommonHeaderView(header, holder); 948 break; 949 950 case HEADER_TYPE_NORMAL: 951 updateCommonHeaderView(header, holder); 952 break; 953 } 954 955 return view; 956 } 957 958 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) { 959 if (header.extras != null 960 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) { 961 String accType = header.extras.getString( 962 ManageAccountsSettings.KEY_ACCOUNT_TYPE); 963 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType); 964 setHeaderIcon(holder, icon); 965 } else { 966 holder.icon.setImageResource(header.iconRes); 967 } 968 holder.title.setText(header.getTitle(getContext().getResources())); 969 CharSequence summary = header.getSummary(getContext().getResources()); 970 if (!TextUtils.isEmpty(summary)) { 971 holder.summary.setVisibility(View.VISIBLE); 972 holder.summary.setText(summary); 973 } else { 974 holder.summary.setVisibility(View.GONE); 975 } 976 } 977 978 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) { 979 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams(); 980 lp.width = getContext().getResources().getDimensionPixelSize( 981 R.dimen.header_icon_width); 982 lp.height = lp.width; 983 holder.icon.setLayoutParams(lp); 984 holder.icon.setImageDrawable(icon); 985 } 986 987 public void resume() { 988 mWifiEnabler.resume(); 989 mBluetoothEnabler.resume(); 990 } 991 992 public void pause() { 993 mWifiEnabler.pause(); 994 mBluetoothEnabler.pause(); 995 } 996 } 997 998 @Override 999 public void onHeaderClick(Header header, int position) { 1000 boolean revert = false; 1001 if (header.id == R.id.account_add) { 1002 revert = true; 1003 } 1004 1005 super.onHeaderClick(header, position); 1006 1007 if (revert && mLastHeader != null) { 1008 highlightHeader((int) mLastHeader.id); 1009 } else { 1010 mLastHeader = header; 1011 } 1012 } 1013 1014 @Override 1015 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { 1016 // Override the fragment title for Wallpaper settings 1017 int titleRes = pref.getTitleRes(); 1018 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) { 1019 titleRes = R.string.wallpaper_settings_fragment_title; 1020 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName()) 1021 && UserHandle.myUserId() != UserHandle.USER_OWNER) { 1022 if (UserManager.get(this).isLinkedUser()) { 1023 titleRes = R.string.profile_info_settings_title; 1024 } else { 1025 titleRes = R.string.user_info_settings_title; 1026 } 1027 } 1028 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(), 1029 null, 0); 1030 return true; 1031 } 1032 1033 @Override 1034 public boolean shouldUpRecreateTask(Intent targetIntent) { 1035 return super.shouldUpRecreateTask(new Intent(this, Settings.class)); 1036 } 1037 1038 @Override 1039 public void setListAdapter(ListAdapter adapter) { 1040 if (adapter == null) { 1041 super.setListAdapter(null); 1042 } else { 1043 DevicePolicyManager dpm = 1044 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 1045 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm)); 1046 } 1047 } 1048 1049 @Override 1050 public void onAccountsUpdated(Account[] accounts) { 1051 // TODO: watch for package upgrades to invalidate cache; see 7206643 1052 mAuthenticatorHelper.updateAuthDescriptions(this); 1053 mAuthenticatorHelper.onAccountsUpdated(this, accounts); 1054 invalidateHeaders(); 1055 } 1056 1057 public static void requestHomeNotice() { 1058 sShowNoHomeNotice = true; 1059 } 1060 1061 /* 1062 * Settings subclasses for launching independently. 1063 */ 1064 public static class BluetoothSettingsActivity extends Settings { /* empty */ } 1065 public static class WirelessSettingsActivity extends Settings { /* empty */ } 1066 public static class TetherSettingsActivity extends Settings { /* empty */ } 1067 public static class VpnSettingsActivity extends Settings { /* empty */ } 1068 public static class DateTimeSettingsActivity extends Settings { /* empty */ } 1069 public static class StorageSettingsActivity extends Settings { /* empty */ } 1070 public static class WifiSettingsActivity extends Settings { /* empty */ } 1071 public static class WifiP2pSettingsActivity extends Settings { /* empty */ } 1072 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ } 1073 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ } 1074 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ } 1075 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ } 1076 public static class LocalePickerActivity extends Settings { /* empty */ } 1077 public static class UserDictionarySettingsActivity extends Settings { /* empty */ } 1078 public static class SoundSettingsActivity extends Settings { /* empty */ } 1079 public static class DisplaySettingsActivity extends Settings { /* empty */ } 1080 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ } 1081 public static class ApplicationSettingsActivity extends Settings { /* empty */ } 1082 public static class ManageApplicationsActivity extends Settings { /* empty */ } 1083 public static class AppOpsSummaryActivity extends Settings { 1084 @Override 1085 public boolean isValidFragment(String className) { 1086 if (AppOpsSummary.class.getName().equals(className)) { 1087 return true; 1088 } 1089 return super.isValidFragment(className); 1090 } 1091 } 1092 public static class StorageUseActivity extends Settings { /* empty */ } 1093 public static class DevelopmentSettingsActivity extends Settings { /* empty */ } 1094 public static class AccessibilitySettingsActivity extends Settings { /* empty */ } 1095 public static class CaptioningSettingsActivity extends Settings { /* empty */ } 1096 public static class SecuritySettingsActivity extends Settings { /* empty */ } 1097 public static class LocationSettingsActivity extends Settings { /* empty */ } 1098 public static class PrivacySettingsActivity extends Settings { /* empty */ } 1099 public static class RunningServicesActivity extends Settings { /* empty */ } 1100 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ } 1101 public static class PowerUsageSummaryActivity extends Settings { /* empty */ } 1102 public static class AccountSyncSettingsActivity extends Settings { /* empty */ } 1103 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ } 1104 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ } 1105 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ } 1106 public static class DataUsageSummaryActivity extends Settings { /* empty */ } 1107 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ } 1108 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ } 1109 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ } 1110 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ } 1111 public static class DreamSettingsActivity extends Settings { /* empty */ } 1112 public static class NotificationStationActivity extends Settings { /* empty */ } 1113 public static class UserSettingsActivity extends Settings { /* empty */ } 1114 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ } 1115 public static class UsbSettingsActivity extends Settings { /* empty */ } 1116 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ } 1117 public static class PaymentSettingsActivity extends Settings { /* empty */ } 1118 public static class PrintSettingsActivity extends Settings { /* empty */ } 1119 public static class PrintJobSettingsActivity extends Settings { /* empty */ } 1120 } 1121