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.ToggleAccessibilityServicePreferenceFragment; 65 import com.android.settings.accessibility.ToggleCaptioningPreferenceFragment; 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 AppOpsSummary.class.getName(), 330 LocationSettings.class.getName(), 331 SecuritySettings.class.getName(), 332 PrivacySettings.class.getName(), 333 DeviceAdminSettings.class.getName(), 334 AccessibilitySettings.class.getName(), 335 ToggleCaptioningPreferenceFragment.class.getName(), 336 TextToSpeechSettings.class.getName(), 337 Memory.class.getName(), 338 DevelopmentSettings.class.getName(), 339 UsbSettings.class.getName(), 340 AndroidBeam.class.getName(), 341 WifiDisplaySettings.class.getName(), 342 PowerUsageSummary.class.getName(), 343 AccountSyncSettings.class.getName(), 344 CryptKeeperSettings.class.getName(), 345 DataUsageSummary.class.getName(), 346 DreamSettings.class.getName(), 347 UserSettings.class.getName(), 348 NotificationAccessSettings.class.getName(), 349 ManageAccountsSettings.class.getName(), 350 PrintSettingsFragment.class.getName(), 351 PrintJobSettingsFragment.class.getName(), 352 TrustedCredentialsSettings.class.getName(), 353 PaymentSettings.class.getName(), 354 KeyboardLayoutPickerFragment.class.getName() 355 }; 356 357 @Override 358 protected boolean isValidFragment(String fragmentName) { 359 // Almost all fragments are wrapped in this, 360 // except for a few that have their own activities. 361 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) { 362 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true; 363 } 364 return false; 365 } 366 367 private void switchToHeaderLocal(Header header) { 368 mInLocalHeaderSwitch = true; 369 switchToHeader(header); 370 mInLocalHeaderSwitch = false; 371 } 372 373 @Override 374 public void switchToHeader(Header header) { 375 if (!mInLocalHeaderSwitch) { 376 mCurrentHeader = null; 377 mParentHeader = null; 378 } 379 super.switchToHeader(header); 380 } 381 382 /** 383 * Switch to parent fragment and store the grand parent's info 384 * @param className name of the activity wrapper for the parent fragment. 385 */ 386 private void switchToParent(String className) { 387 final ComponentName cn = new ComponentName(this, className); 388 try { 389 final PackageManager pm = getPackageManager(); 390 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA); 391 392 if (parentInfo != null && parentInfo.metaData != null) { 393 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 394 CharSequence fragmentTitle = parentInfo.loadLabel(pm); 395 Header parentHeader = new Header(); 396 parentHeader.fragment = fragmentClass; 397 parentHeader.title = fragmentTitle; 398 mCurrentHeader = parentHeader; 399 400 switchToHeaderLocal(parentHeader); 401 highlightHeader(mTopLevelHeaderId); 402 403 mParentHeader = new Header(); 404 mParentHeader.fragment 405 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); 406 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE); 407 } 408 } catch (NameNotFoundException nnfe) { 409 Log.w(LOG_TAG, "Could not find parent activity : " + className); 410 } 411 } 412 413 @Override 414 public void onNewIntent(Intent intent) { 415 super.onNewIntent(intent); 416 417 // If it is not launched from history, then reset to top-level 418 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { 419 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) { 420 switchToHeaderLocal(mFirstHeader); 421 } 422 getListView().setSelectionFromTop(0, 0); 423 } 424 } 425 426 private void highlightHeader(int id) { 427 if (id != 0) { 428 Integer index = mHeaderIndexMap.get(id); 429 if (index != null) { 430 getListView().setItemChecked(index, true); 431 if (isMultiPane()) { 432 getListView().smoothScrollToPosition(index); 433 } 434 } 435 } 436 } 437 438 @Override 439 public Intent getIntent() { 440 Intent superIntent = super.getIntent(); 441 String startingFragment = getStartingFragmentClass(superIntent); 442 // This is called from super.onCreate, isMultiPane() is not yet reliable 443 // Do not use onIsHidingHeaders either, which relies itself on this method 444 if (startingFragment != null && !onIsMultiPane()) { 445 Intent modIntent = new Intent(superIntent); 446 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); 447 Bundle args = superIntent.getExtras(); 448 if (args != null) { 449 args = new Bundle(args); 450 } else { 451 args = new Bundle(); 452 } 453 args.putParcelable("intent", superIntent); 454 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras()); 455 return modIntent; 456 } 457 return superIntent; 458 } 459 460 /** 461 * Checks if the component name in the intent is different from the Settings class and 462 * returns the class name to load as a fragment. 463 */ 464 protected String getStartingFragmentClass(Intent intent) { 465 if (mFragmentClass != null) return mFragmentClass; 466 467 String intentClass = intent.getComponent().getClassName(); 468 if (intentClass.equals(getClass().getName())) return null; 469 470 if ("com.android.settings.ManageApplications".equals(intentClass) 471 || "com.android.settings.RunningServices".equals(intentClass) 472 || "com.android.settings.applications.StorageUse".equals(intentClass)) { 473 // Old names of manage apps. 474 intentClass = com.android.settings.applications.ManageApplications.class.getName(); 475 } 476 477 return intentClass; 478 } 479 480 /** 481 * Override initial header when an activity-alias is causing Settings to be launched 482 * for a specific fragment encoded in the android:name parameter. 483 */ 484 @Override 485 public Header onGetInitialHeader() { 486 String fragmentClass = getStartingFragmentClass(super.getIntent()); 487 if (fragmentClass != null) { 488 Header header = new Header(); 489 header.fragment = fragmentClass; 490 header.title = getTitle(); 491 header.fragmentArguments = getIntent().getExtras(); 492 mCurrentHeader = header; 493 return header; 494 } 495 496 return mFirstHeader; 497 } 498 499 @Override 500 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, 501 int titleRes, int shortTitleRes) { 502 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args, 503 titleRes, shortTitleRes); 504 505 // Some fragments want split ActionBar; these should stay in sync with 506 // uiOptions for fragments also defined as activities in manifest. 507 if (WifiSettings.class.getName().equals(fragmentName) || 508 WifiP2pSettings.class.getName().equals(fragmentName) || 509 WifiDisplaySettings.class.getName().equals(fragmentName) || 510 BluetoothSettings.class.getName().equals(fragmentName) || 511 DreamSettings.class.getName().equals(fragmentName) || 512 LocationSettings.class.getName().equals(fragmentName) || 513 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) || 514 PrintSettingsFragment.class.getName().equals(fragmentName) || 515 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) { 516 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW); 517 } 518 519 intent.setClass(this, SubSettings.class); 520 return intent; 521 } 522 523 /** 524 * Populate the activity with the top-level headers. 525 */ 526 @Override 527 public void onBuildHeaders(List<Header> headers) { 528 if (!onIsHidingHeaders()) { 529 loadHeadersFromResource(R.xml.settings_headers, headers); 530 updateHeaderList(headers); 531 } 532 } 533 534 private void updateHeaderList(List<Header> target) { 535 final boolean showDev = mDevelopmentPreferences.getBoolean( 536 DevelopmentSettings.PREF_SHOW, 537 android.os.Build.TYPE.equals("eng")); 538 int i = 0; 539 540 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 541 mHeaderIndexMap.clear(); 542 while (i < target.size()) { 543 Header header = target.get(i); 544 // Ids are integers, so downcasting 545 int id = (int) header.id; 546 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { 547 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); 548 } else if (id == R.id.wifi_settings) { 549 // Remove WiFi Settings if WiFi service is not available. 550 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { 551 target.remove(i); 552 } 553 } else if (id == R.id.bluetooth_settings) { 554 // Remove Bluetooth Settings if Bluetooth service is not available. 555 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { 556 target.remove(i); 557 } 558 } else if (id == R.id.data_usage_settings) { 559 // Remove data usage when kernel module not enabled 560 final INetworkManagementService netManager = INetworkManagementService.Stub 561 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); 562 try { 563 if (!netManager.isBandwidthControlEnabled()) { 564 target.remove(i); 565 } 566 } catch (RemoteException e) { 567 // ignored 568 } 569 } else if (id == R.id.battery_settings) { 570 // Remove battery settings when battery is not available. (e.g. TV) 571 572 if (!mBatteryPresent) { 573 target.remove(i); 574 } 575 } else if (id == R.id.account_settings) { 576 int headerIndex = i + 1; 577 i = insertAccountsHeaders(target, headerIndex); 578 } else if (id == R.id.home_settings) { 579 if (!updateHomeSettingHeaders(header)) { 580 target.remove(i); 581 } 582 } else if (id == R.id.user_settings) { 583 if (!UserHandle.MU_ENABLED 584 || !UserManager.supportsMultipleUsers() 585 || Utils.isMonkeyRunning()) { 586 target.remove(i); 587 } 588 } else if (id == R.id.nfc_payment_settings) { 589 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { 590 target.remove(i); 591 } else { 592 // Only show if NFC is on and we have the HCE feature 593 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); 594 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature( 595 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { 596 target.remove(i); 597 } 598 } 599 } else if (id == R.id.development_settings) { 600 if (!showDev) { 601 target.remove(i); 602 } 603 } else if (id == R.id.account_add) { 604 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 605 target.remove(i); 606 } 607 } 608 609 if (i < target.size() && target.get(i) == header 610 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 611 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { 612 target.remove(i); 613 } 614 615 // Increment if the current one wasn't removed by the Utils code. 616 if (i < target.size() && target.get(i) == header) { 617 // Hold on to the first header, when we need to reset to the top-level 618 if (mFirstHeader == null && 619 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) { 620 mFirstHeader = header; 621 } 622 mHeaderIndexMap.put(id, i); 623 i++; 624 } 625 } 626 } 627 628 private int insertAccountsHeaders(List<Header> target, int headerIndex) { 629 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes(); 630 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length); 631 for (String accountType : accountTypes) { 632 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType); 633 if (label == null) { 634 continue; 635 } 636 637 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType); 638 boolean skipToAccount = accounts.length == 1 639 && !mAuthenticatorHelper.hasAccountPreferences(accountType); 640 Header accHeader = new Header(); 641 accHeader.title = label; 642 if (accHeader.extras == null) { 643 accHeader.extras = new Bundle(); 644 } 645 if (skipToAccount) { 646 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title; 647 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title; 648 accHeader.fragment = AccountSyncSettings.class.getName(); 649 accHeader.fragmentArguments = new Bundle(); 650 // Need this for the icon 651 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 652 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); 653 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, 654 accounts[0]); 655 } else { 656 accHeader.breadCrumbTitle = label; 657 accHeader.breadCrumbShortTitle = label; 658 accHeader.fragment = ManageAccountsSettings.class.getName(); 659 accHeader.fragmentArguments = new Bundle(); 660 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 661 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, 662 accountType); 663 if (!isMultiPane()) { 664 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, 665 label.toString()); 666 } 667 } 668 accountHeaders.add(accHeader); 669 mAuthenticatorHelper.preloadDrawableForType(this, accountType); 670 } 671 672 // Sort by label 673 Collections.sort(accountHeaders, new Comparator<Header>() { 674 @Override 675 public int compare(Header h1, Header h2) { 676 return h1.title.toString().compareTo(h2.title.toString()); 677 } 678 }); 679 680 for (Header header : accountHeaders) { 681 target.add(headerIndex++, header); 682 } 683 if (!mListeningToAccountUpdates) { 684 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); 685 mListeningToAccountUpdates = true; 686 } 687 return headerIndex; 688 } 689 690 private boolean updateHomeSettingHeaders(Header header) { 691 // Once we decide to show Home settings, keep showing it forever 692 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE); 693 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) { 694 return true; 695 } 696 697 try { 698 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>(); 699 getPackageManager().getHomeActivities(homeApps); 700 if (homeApps.size() < 2) { 701 // When there's only one available home app, omit this settings 702 // category entirely at the top level UI. If the user just 703 // uninstalled the penultimate home app candidiate, we also 704 // now tell them about why they aren't seeing 'Home' in the list. 705 if (sShowNoHomeNotice) { 706 sShowNoHomeNotice = false; 707 NoHomeDialogFragment.show(this); 708 } 709 return false; 710 } else { 711 // Okay, we're allowing the Home settings category. Tell it, when 712 // invoked via this front door, that we'll need to be told about the 713 // case when the user uninstalls all but one home app. 714 if (header.fragmentArguments == null) { 715 header.fragmentArguments = new Bundle(); 716 } 717 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true); 718 } 719 } catch (Exception e) { 720 // Can't look up the home activity; bail on configuring the icon 721 Log.w(LOG_TAG, "Problem looking up home activity!", e); 722 } 723 724 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply(); 725 return true; 726 } 727 728 private void getMetaData() { 729 try { 730 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), 731 PackageManager.GET_META_DATA); 732 if (ai == null || ai.metaData == null) return; 733 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); 734 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 735 736 // Check if it has a parent specified and create a Header object 737 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE); 738 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); 739 if (parentFragmentClass != null) { 740 mParentHeader = new Header(); 741 mParentHeader.fragment = parentFragmentClass; 742 if (parentHeaderTitleRes != 0) { 743 mParentHeader.title = getResources().getString(parentHeaderTitleRes); 744 } 745 } 746 } catch (NameNotFoundException nnfe) { 747 // No recovery 748 } 749 } 750 751 @Override 752 public boolean hasNextButton() { 753 return super.hasNextButton(); 754 } 755 756 @Override 757 public Button getNextButton() { 758 return super.getNextButton(); 759 } 760 761 public static class NoHomeDialogFragment extends DialogFragment { 762 public static void show(Activity parent) { 763 final NoHomeDialogFragment dialog = new NoHomeDialogFragment(); 764 dialog.show(parent.getFragmentManager(), null); 765 } 766 767 @Override 768 public Dialog onCreateDialog(Bundle savedInstanceState) { 769 return new AlertDialog.Builder(getActivity()) 770 .setMessage(R.string.only_one_home_message) 771 .setPositiveButton(android.R.string.ok, null) 772 .create(); 773 } 774 } 775 776 private static class HeaderAdapter extends ArrayAdapter<Header> { 777 static final int HEADER_TYPE_CATEGORY = 0; 778 static final int HEADER_TYPE_NORMAL = 1; 779 static final int HEADER_TYPE_SWITCH = 2; 780 static final int HEADER_TYPE_BUTTON = 3; 781 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1; 782 783 private final WifiEnabler mWifiEnabler; 784 private final BluetoothEnabler mBluetoothEnabler; 785 private AuthenticatorHelper mAuthHelper; 786 private DevicePolicyManager mDevicePolicyManager; 787 788 private static class HeaderViewHolder { 789 ImageView icon; 790 TextView title; 791 TextView summary; 792 Switch switch_; 793 ImageButton button_; 794 View divider_; 795 } 796 797 private LayoutInflater mInflater; 798 799 static int getHeaderType(Header header) { 800 if (header.fragment == null && header.intent == null) { 801 return HEADER_TYPE_CATEGORY; 802 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) { 803 return HEADER_TYPE_SWITCH; 804 } else if (header.id == R.id.security_settings) { 805 return HEADER_TYPE_BUTTON; 806 } else { 807 return HEADER_TYPE_NORMAL; 808 } 809 } 810 811 @Override 812 public int getItemViewType(int position) { 813 Header header = getItem(position); 814 return getHeaderType(header); 815 } 816 817 @Override 818 public boolean areAllItemsEnabled() { 819 return false; // because of categories 820 } 821 822 @Override 823 public boolean isEnabled(int position) { 824 return getItemViewType(position) != HEADER_TYPE_CATEGORY; 825 } 826 827 @Override 828 public int getViewTypeCount() { 829 return HEADER_TYPE_COUNT; 830 } 831 832 @Override 833 public boolean hasStableIds() { 834 return true; 835 } 836 837 public HeaderAdapter(Context context, List<Header> objects, 838 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) { 839 super(context, 0, objects); 840 841 mAuthHelper = authenticatorHelper; 842 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 843 844 // Temp Switches provided as placeholder until the adapter replaces these with actual 845 // Switches inflated from their layouts. Must be done before adapter is set in super 846 mWifiEnabler = new WifiEnabler(context, new Switch(context)); 847 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context)); 848 mDevicePolicyManager = dpm; 849 } 850 851 @Override 852 public View getView(int position, View convertView, ViewGroup parent) { 853 HeaderViewHolder holder; 854 Header header = getItem(position); 855 int headerType = getHeaderType(header); 856 View view = null; 857 858 if (convertView == null) { 859 holder = new HeaderViewHolder(); 860 switch (headerType) { 861 case HEADER_TYPE_CATEGORY: 862 view = new TextView(getContext(), null, 863 android.R.attr.listSeparatorTextViewStyle); 864 holder.title = (TextView) view; 865 break; 866 867 case HEADER_TYPE_SWITCH: 868 view = mInflater.inflate(R.layout.preference_header_switch_item, parent, 869 false); 870 holder.icon = (ImageView) view.findViewById(R.id.icon); 871 holder.title = (TextView) 872 view.findViewById(com.android.internal.R.id.title); 873 holder.summary = (TextView) 874 view.findViewById(com.android.internal.R.id.summary); 875 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget); 876 break; 877 878 case HEADER_TYPE_BUTTON: 879 view = mInflater.inflate(R.layout.preference_header_button_item, parent, 880 false); 881 holder.icon = (ImageView) view.findViewById(R.id.icon); 882 holder.title = (TextView) 883 view.findViewById(com.android.internal.R.id.title); 884 holder.summary = (TextView) 885 view.findViewById(com.android.internal.R.id.summary); 886 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget); 887 holder.divider_ = view.findViewById(R.id.divider); 888 break; 889 890 case HEADER_TYPE_NORMAL: 891 view = mInflater.inflate( 892 R.layout.preference_header_item, parent, 893 false); 894 holder.icon = (ImageView) view.findViewById(R.id.icon); 895 holder.title = (TextView) 896 view.findViewById(com.android.internal.R.id.title); 897 holder.summary = (TextView) 898 view.findViewById(com.android.internal.R.id.summary); 899 break; 900 } 901 view.setTag(holder); 902 } else { 903 view = convertView; 904 holder = (HeaderViewHolder) view.getTag(); 905 } 906 907 // All view fields must be updated every time, because the view may be recycled 908 switch (headerType) { 909 case HEADER_TYPE_CATEGORY: 910 holder.title.setText(header.getTitle(getContext().getResources())); 911 break; 912 913 case HEADER_TYPE_SWITCH: 914 // Would need a different treatment if the main menu had more switches 915 if (header.id == R.id.wifi_settings) { 916 mWifiEnabler.setSwitch(holder.switch_); 917 } else { 918 mBluetoothEnabler.setSwitch(holder.switch_); 919 } 920 updateCommonHeaderView(header, holder); 921 break; 922 923 case HEADER_TYPE_BUTTON: 924 if (header.id == R.id.security_settings) { 925 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); 926 if (hasCert) { 927 holder.button_.setVisibility(View.VISIBLE); 928 holder.divider_.setVisibility(View.VISIBLE); 929 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null; 930 if (isManaged) { 931 holder.button_.setImageResource(R.drawable.ic_settings_about); 932 } else { 933 holder.button_.setImageResource( 934 android.R.drawable.stat_notify_error); 935 } 936 holder.button_.setOnClickListener(new OnClickListener() { 937 @Override 938 public void onClick(View v) { 939 Intent intent = new Intent( 940 android.provider.Settings.ACTION_MONITORING_CERT_INFO); 941 getContext().startActivity(intent); 942 } 943 }); 944 } else { 945 holder.button_.setVisibility(View.GONE); 946 holder.divider_.setVisibility(View.GONE); 947 } 948 } 949 updateCommonHeaderView(header, holder); 950 break; 951 952 case HEADER_TYPE_NORMAL: 953 updateCommonHeaderView(header, holder); 954 break; 955 } 956 957 return view; 958 } 959 960 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) { 961 if (header.extras != null 962 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) { 963 String accType = header.extras.getString( 964 ManageAccountsSettings.KEY_ACCOUNT_TYPE); 965 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType); 966 setHeaderIcon(holder, icon); 967 } else { 968 holder.icon.setImageResource(header.iconRes); 969 } 970 holder.title.setText(header.getTitle(getContext().getResources())); 971 CharSequence summary = header.getSummary(getContext().getResources()); 972 if (!TextUtils.isEmpty(summary)) { 973 holder.summary.setVisibility(View.VISIBLE); 974 holder.summary.setText(summary); 975 } else { 976 holder.summary.setVisibility(View.GONE); 977 } 978 } 979 980 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) { 981 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams(); 982 lp.width = getContext().getResources().getDimensionPixelSize( 983 R.dimen.header_icon_width); 984 lp.height = lp.width; 985 holder.icon.setLayoutParams(lp); 986 holder.icon.setImageDrawable(icon); 987 } 988 989 public void resume() { 990 mWifiEnabler.resume(); 991 mBluetoothEnabler.resume(); 992 } 993 994 public void pause() { 995 mWifiEnabler.pause(); 996 mBluetoothEnabler.pause(); 997 } 998 } 999 1000 @Override 1001 public void onHeaderClick(Header header, int position) { 1002 boolean revert = false; 1003 if (header.id == R.id.account_add) { 1004 revert = true; 1005 } 1006 1007 super.onHeaderClick(header, position); 1008 1009 if (revert && mLastHeader != null) { 1010 highlightHeader((int) mLastHeader.id); 1011 } else { 1012 mLastHeader = header; 1013 } 1014 } 1015 1016 @Override 1017 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { 1018 // Override the fragment title for Wallpaper settings 1019 int titleRes = pref.getTitleRes(); 1020 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) { 1021 titleRes = R.string.wallpaper_settings_fragment_title; 1022 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName()) 1023 && UserHandle.myUserId() != UserHandle.USER_OWNER) { 1024 if (UserManager.get(this).isLinkedUser()) { 1025 titleRes = R.string.profile_info_settings_title; 1026 } else { 1027 titleRes = R.string.user_info_settings_title; 1028 } 1029 } 1030 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(), 1031 null, 0); 1032 return true; 1033 } 1034 1035 @Override 1036 public boolean shouldUpRecreateTask(Intent targetIntent) { 1037 return super.shouldUpRecreateTask(new Intent(this, Settings.class)); 1038 } 1039 1040 @Override 1041 public void setListAdapter(ListAdapter adapter) { 1042 if (adapter == null) { 1043 super.setListAdapter(null); 1044 } else { 1045 DevicePolicyManager dpm = 1046 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 1047 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm)); 1048 } 1049 } 1050 1051 @Override 1052 public void onAccountsUpdated(Account[] accounts) { 1053 // TODO: watch for package upgrades to invalidate cache; see 7206643 1054 mAuthenticatorHelper.updateAuthDescriptions(this); 1055 mAuthenticatorHelper.onAccountsUpdated(this, accounts); 1056 invalidateHeaders(); 1057 } 1058 1059 public static void requestHomeNotice() { 1060 sShowNoHomeNotice = true; 1061 } 1062 1063 /* 1064 * Settings subclasses for launching independently. 1065 */ 1066 public static class BluetoothSettingsActivity extends Settings { /* empty */ } 1067 public static class WirelessSettingsActivity extends Settings { /* empty */ } 1068 public static class TetherSettingsActivity extends Settings { /* empty */ } 1069 public static class VpnSettingsActivity extends Settings { /* empty */ } 1070 public static class DateTimeSettingsActivity extends Settings { /* empty */ } 1071 public static class StorageSettingsActivity extends Settings { /* empty */ } 1072 public static class WifiSettingsActivity extends Settings { /* empty */ } 1073 public static class WifiP2pSettingsActivity extends Settings { /* empty */ } 1074 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ } 1075 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ } 1076 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ } 1077 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ } 1078 public static class LocalePickerActivity extends Settings { /* empty */ } 1079 public static class UserDictionarySettingsActivity extends Settings { /* empty */ } 1080 public static class SoundSettingsActivity extends Settings { /* empty */ } 1081 public static class DisplaySettingsActivity extends Settings { /* empty */ } 1082 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ } 1083 public static class ApplicationSettingsActivity extends Settings { /* empty */ } 1084 public static class ManageApplicationsActivity extends Settings { /* empty */ } 1085 public static class AppOpsSummaryActivity extends Settings { /* empty */ } 1086 public static class StorageUseActivity extends Settings { /* empty */ } 1087 public static class DevelopmentSettingsActivity extends Settings { /* empty */ } 1088 public static class AccessibilitySettingsActivity extends Settings { /* empty */ } 1089 public static class CaptioningSettingsActivity extends Settings { /* empty */ } 1090 public static class SecuritySettingsActivity extends Settings { /* empty */ } 1091 public static class LocationSettingsActivity extends Settings { /* empty */ } 1092 public static class PrivacySettingsActivity extends Settings { /* empty */ } 1093 public static class RunningServicesActivity extends Settings { /* empty */ } 1094 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ } 1095 public static class PowerUsageSummaryActivity extends Settings { /* empty */ } 1096 public static class AccountSyncSettingsActivity extends Settings { /* empty */ } 1097 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ } 1098 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ } 1099 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ } 1100 public static class DataUsageSummaryActivity extends Settings { /* empty */ } 1101 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ } 1102 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ } 1103 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ } 1104 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ } 1105 public static class DreamSettingsActivity extends Settings { /* empty */ } 1106 public static class NotificationStationActivity extends Settings { /* empty */ } 1107 public static class UserSettingsActivity extends Settings { /* empty */ } 1108 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ } 1109 public static class UsbSettingsActivity extends Settings { /* empty */ } 1110 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ } 1111 public static class PaymentSettingsActivity extends Settings { /* empty */ } 1112 public static class PrintSettingsActivity extends Settings { /* empty */ } 1113 public static class PrintJobSettingsActivity extends Settings { /* empty */ } 1114 } 1115