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 17package com.android.settings; 18 19import com.android.internal.util.ArrayUtils; 20import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; 21import com.android.settings.accounts.AccountSyncSettings; 22import com.android.settings.accounts.AuthenticatorHelper; 23import com.android.settings.accounts.ManageAccountsSettings; 24import com.android.settings.applications.InstalledAppDetails; 25import com.android.settings.applications.ManageApplications; 26import com.android.settings.bluetooth.BluetoothEnabler; 27import com.android.settings.deviceinfo.Memory; 28import com.android.settings.fuelgauge.PowerUsageSummary; 29import com.android.settings.vpn2.VpnSettings; 30import com.android.settings.wifi.WifiEnabler; 31 32import android.accounts.Account; 33import android.accounts.AccountManager; 34import android.accounts.OnAccountsUpdateListener; 35import android.content.ComponentName; 36import android.content.Context; 37import android.content.Intent; 38import android.content.SharedPreferences; 39import android.content.pm.ActivityInfo; 40import android.content.pm.PackageManager; 41import android.content.pm.PackageManager.NameNotFoundException; 42import android.graphics.drawable.Drawable; 43import android.os.Bundle; 44import android.os.INetworkManagementService; 45import android.os.RemoteException; 46import android.os.ServiceManager; 47import android.os.UserHandle; 48import android.os.UserManager; 49import android.preference.Preference; 50import android.preference.PreferenceActivity; 51import android.preference.PreferenceFragment; 52import android.text.TextUtils; 53import android.util.Log; 54import android.view.LayoutInflater; 55import android.view.View; 56import android.view.View.OnClickListener; 57import android.view.ViewGroup; 58import android.widget.ArrayAdapter; 59import android.widget.Button; 60import android.widget.ImageView; 61import android.widget.ListAdapter; 62import android.widget.Switch; 63import android.widget.TextView; 64 65import java.util.ArrayList; 66import java.util.Collections; 67import java.util.Comparator; 68import java.util.HashMap; 69import java.util.List; 70 71/** 72 * Top-level settings activity to handle single pane and double pane UI layout. 73 */ 74public class Settings extends PreferenceActivity 75 implements ButtonBarHandler, OnAccountsUpdateListener { 76 77 private static final String LOG_TAG = "Settings"; 78 79 private static final String META_DATA_KEY_HEADER_ID = 80 "com.android.settings.TOP_LEVEL_HEADER_ID"; 81 private static final String META_DATA_KEY_FRAGMENT_CLASS = 82 "com.android.settings.FRAGMENT_CLASS"; 83 private static final String META_DATA_KEY_PARENT_TITLE = 84 "com.android.settings.PARENT_FRAGMENT_TITLE"; 85 private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS = 86 "com.android.settings.PARENT_FRAGMENT_CLASS"; 87 88 private static final String EXTRA_CLEAR_UI_OPTIONS = "settings:remove_ui_options"; 89 90 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER"; 91 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER"; 92 93 private String mFragmentClass; 94 private int mTopLevelHeaderId; 95 private Header mFirstHeader; 96 private Header mCurrentHeader; 97 private Header mParentHeader; 98 private boolean mInLocalHeaderSwitch; 99 100 // Show only these settings for restricted users 101 private int[] SETTINGS_FOR_RESTRICTED = { 102 R.id.wireless_section, 103 R.id.wifi_settings, 104 R.id.bluetooth_settings, 105 R.id.data_usage_settings, 106 R.id.wireless_settings, 107 R.id.device_section, 108 R.id.sound_settings, 109 R.id.display_settings, 110 R.id.storage_settings, 111 R.id.application_settings, 112 R.id.battery_settings, 113 R.id.personal_section, 114 R.id.location_settings, 115 R.id.security_settings, 116 R.id.language_settings, 117 R.id.user_settings, 118 R.id.account_settings, 119 R.id.account_add, 120 R.id.system_section, 121 R.id.date_time_settings, 122 R.id.about_settings, 123 R.id.accessibility_settings 124 }; 125 126 private SharedPreferences mDevelopmentPreferences; 127 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener; 128 129 // TODO: Update Call Settings based on airplane mode state. 130 131 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>(); 132 133 private AuthenticatorHelper mAuthenticatorHelper; 134 private Header mLastHeader; 135 private boolean mListeningToAccountUpdates; 136 137 @Override 138 protected void onCreate(Bundle savedInstanceState) { 139 if (getIntent().getBooleanExtra(EXTRA_CLEAR_UI_OPTIONS, false)) { 140 getWindow().setUiOptions(0); 141 } 142 143 mAuthenticatorHelper = new AuthenticatorHelper(); 144 mAuthenticatorHelper.updateAuthDescriptions(this); 145 mAuthenticatorHelper.onAccountsUpdated(this, null); 146 147 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, 148 Context.MODE_PRIVATE); 149 150 getMetaData(); 151 mInLocalHeaderSwitch = true; 152 super.onCreate(savedInstanceState); 153 mInLocalHeaderSwitch = false; 154 155 if (!onIsHidingHeaders() && onIsMultiPane()) { 156 highlightHeader(mTopLevelHeaderId); 157 // Force the title so that it doesn't get overridden by a direct launch of 158 // a specific settings screen. 159 setTitle(R.string.settings_label); 160 } 161 162 // Retrieve any saved state 163 if (savedInstanceState != null) { 164 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER); 165 mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER); 166 } 167 168 // If the current header was saved, switch to it 169 if (savedInstanceState != null && mCurrentHeader != null) { 170 //switchToHeaderLocal(mCurrentHeader); 171 showBreadCrumbs(mCurrentHeader.title, null); 172 } 173 174 if (mParentHeader != null) { 175 setParentTitle(mParentHeader.title, null, new OnClickListener() { 176 public void onClick(View v) { 177 switchToParent(mParentHeader.fragment); 178 } 179 }); 180 } 181 182 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs 183 if (onIsMultiPane()) { 184 getActionBar().setDisplayHomeAsUpEnabled(false); 185 getActionBar().setHomeButtonEnabled(false); 186 } 187 } 188 189 @Override 190 protected void onSaveInstanceState(Bundle outState) { 191 super.onSaveInstanceState(outState); 192 193 // Save the current fragment, if it is the same as originally launched 194 if (mCurrentHeader != null) { 195 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader); 196 } 197 if (mParentHeader != null) { 198 outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader); 199 } 200 } 201 202 @Override 203 public void onResume() { 204 super.onResume(); 205 206 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() { 207 @Override 208 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 209 invalidateHeaders(); 210 } 211 }; 212 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener( 213 mDevelopmentPreferencesListener); 214 215 ListAdapter listAdapter = getListAdapter(); 216 if (listAdapter instanceof HeaderAdapter) { 217 ((HeaderAdapter) listAdapter).resume(); 218 } 219 invalidateHeaders(); 220 } 221 222 @Override 223 public void onPause() { 224 super.onPause(); 225 226 ListAdapter listAdapter = getListAdapter(); 227 if (listAdapter instanceof HeaderAdapter) { 228 ((HeaderAdapter) listAdapter).pause(); 229 } 230 231 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener( 232 mDevelopmentPreferencesListener); 233 mDevelopmentPreferencesListener = null; 234 } 235 236 @Override 237 public void onDestroy() { 238 super.onDestroy(); 239 if (mListeningToAccountUpdates) { 240 AccountManager.get(this).removeOnAccountsUpdatedListener(this); 241 } 242 } 243 244 private void switchToHeaderLocal(Header header) { 245 mInLocalHeaderSwitch = true; 246 switchToHeader(header); 247 mInLocalHeaderSwitch = false; 248 } 249 250 @Override 251 public void switchToHeader(Header header) { 252 if (!mInLocalHeaderSwitch) { 253 mCurrentHeader = null; 254 mParentHeader = null; 255 } 256 super.switchToHeader(header); 257 } 258 259 /** 260 * Switch to parent fragment and store the grand parent's info 261 * @param className name of the activity wrapper for the parent fragment. 262 */ 263 private void switchToParent(String className) { 264 final ComponentName cn = new ComponentName(this, className); 265 try { 266 final PackageManager pm = getPackageManager(); 267 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA); 268 269 if (parentInfo != null && parentInfo.metaData != null) { 270 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 271 CharSequence fragmentTitle = parentInfo.loadLabel(pm); 272 Header parentHeader = new Header(); 273 parentHeader.fragment = fragmentClass; 274 parentHeader.title = fragmentTitle; 275 mCurrentHeader = parentHeader; 276 277 switchToHeaderLocal(parentHeader); 278 highlightHeader(mTopLevelHeaderId); 279 280 mParentHeader = new Header(); 281 mParentHeader.fragment 282 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); 283 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE); 284 } 285 } catch (NameNotFoundException nnfe) { 286 Log.w(LOG_TAG, "Could not find parent activity : " + className); 287 } 288 } 289 290 @Override 291 public void onNewIntent(Intent intent) { 292 super.onNewIntent(intent); 293 294 // If it is not launched from history, then reset to top-level 295 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { 296 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) { 297 switchToHeaderLocal(mFirstHeader); 298 } 299 getListView().setSelectionFromTop(0, 0); 300 } 301 } 302 303 private void highlightHeader(int id) { 304 if (id != 0) { 305 Integer index = mHeaderIndexMap.get(id); 306 if (index != null) { 307 getListView().setItemChecked(index, true); 308 if (isMultiPane()) { 309 getListView().smoothScrollToPosition(index); 310 } 311 } 312 } 313 } 314 315 @Override 316 public Intent getIntent() { 317 Intent superIntent = super.getIntent(); 318 String startingFragment = getStartingFragmentClass(superIntent); 319 // This is called from super.onCreate, isMultiPane() is not yet reliable 320 // Do not use onIsHidingHeaders either, which relies itself on this method 321 if (startingFragment != null && !onIsMultiPane()) { 322 Intent modIntent = new Intent(superIntent); 323 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); 324 Bundle args = superIntent.getExtras(); 325 if (args != null) { 326 args = new Bundle(args); 327 } else { 328 args = new Bundle(); 329 } 330 args.putParcelable("intent", superIntent); 331 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras()); 332 return modIntent; 333 } 334 return superIntent; 335 } 336 337 /** 338 * Checks if the component name in the intent is different from the Settings class and 339 * returns the class name to load as a fragment. 340 */ 341 protected String getStartingFragmentClass(Intent intent) { 342 if (mFragmentClass != null) return mFragmentClass; 343 344 String intentClass = intent.getComponent().getClassName(); 345 if (intentClass.equals(getClass().getName())) return null; 346 347 if ("com.android.settings.ManageApplications".equals(intentClass) 348 || "com.android.settings.RunningServices".equals(intentClass) 349 || "com.android.settings.applications.StorageUse".equals(intentClass)) { 350 // Old names of manage apps. 351 intentClass = com.android.settings.applications.ManageApplications.class.getName(); 352 } 353 354 return intentClass; 355 } 356 357 /** 358 * Override initial header when an activity-alias is causing Settings to be launched 359 * for a specific fragment encoded in the android:name parameter. 360 */ 361 @Override 362 public Header onGetInitialHeader() { 363 String fragmentClass = getStartingFragmentClass(super.getIntent()); 364 if (fragmentClass != null) { 365 Header header = new Header(); 366 header.fragment = fragmentClass; 367 header.title = getTitle(); 368 header.fragmentArguments = getIntent().getExtras(); 369 mCurrentHeader = header; 370 return header; 371 } 372 373 return mFirstHeader; 374 } 375 376 @Override 377 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, 378 int titleRes, int shortTitleRes) { 379 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args, 380 titleRes, shortTitleRes); 381 382 // some fragments want to avoid split actionbar 383 if (DataUsageSummary.class.getName().equals(fragmentName) || 384 PowerUsageSummary.class.getName().equals(fragmentName) || 385 AccountSyncSettings.class.getName().equals(fragmentName) || 386 UserDictionarySettings.class.getName().equals(fragmentName) || 387 Memory.class.getName().equals(fragmentName) || 388 ManageApplications.class.getName().equals(fragmentName) || 389 WirelessSettings.class.getName().equals(fragmentName) || 390 SoundSettings.class.getName().equals(fragmentName) || 391 PrivacySettings.class.getName().equals(fragmentName) || 392 ManageAccountsSettings.class.getName().equals(fragmentName) || 393 VpnSettings.class.getName().equals(fragmentName) || 394 SecuritySettings.class.getName().equals(fragmentName) || 395 InstalledAppDetails.class.getName().equals(fragmentName) || 396 ChooseLockGenericFragment.class.getName().equals(fragmentName) || 397 TetherSettings.class.getName().equals(fragmentName) || 398 ApnSettings.class.getName().equals(fragmentName) || 399 LocationSettings.class.getName().equals(fragmentName) || 400 ZonePicker.class.getName().equals(fragmentName)) { 401 intent.putExtra(EXTRA_CLEAR_UI_OPTIONS, true); 402 } 403 404 intent.setClass(this, SubSettings.class); 405 return intent; 406 } 407 408 /** 409 * Populate the activity with the top-level headers. 410 */ 411 @Override 412 public void onBuildHeaders(List<Header> headers) { 413 loadHeadersFromResource(R.xml.settings_headers, headers); 414 415 updateHeaderList(headers); 416 } 417 418 private void updateHeaderList(List<Header> target) { 419 final boolean showDev = mDevelopmentPreferences.getBoolean( 420 DevelopmentSettings.PREF_SHOW, 421 android.os.Build.TYPE.equals("eng")); 422 int i = 0; 423 424 mHeaderIndexMap.clear(); 425 while (i < target.size()) { 426 Header header = target.get(i); 427 // Ids are integers, so downcasting 428 int id = (int) header.id; 429 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { 430 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); 431 } else if (id == R.id.wifi_settings) { 432 // Remove WiFi Settings if WiFi service is not available. 433 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { 434 target.remove(i); 435 } 436 } else if (id == R.id.bluetooth_settings) { 437 // Remove Bluetooth Settings if Bluetooth service is not available. 438 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { 439 target.remove(i); 440 } 441 } else if (id == R.id.data_usage_settings) { 442 // Remove data usage when kernel module not enabled 443 final INetworkManagementService netManager = INetworkManagementService.Stub 444 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); 445 try { 446 if (!netManager.isBandwidthControlEnabled()) { 447 target.remove(i); 448 } 449 } catch (RemoteException e) { 450 // ignored 451 } 452 } else if (id == R.id.account_settings) { 453 int headerIndex = i + 1; 454 i = insertAccountsHeaders(target, headerIndex); 455 } else if (id == R.id.user_settings) { 456 if (!UserHandle.MU_ENABLED 457 || !UserManager.supportsMultipleUsers() 458 || Utils.isMonkeyRunning()) { 459 target.remove(i); 460 } 461 } else if (id == R.id.development_settings) { 462 if (!showDev) { 463 target.remove(i); 464 } 465 } 466 467 if (target.get(i) == header 468 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 469 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { 470 target.remove(i); 471 } 472 473 // Increment if the current one wasn't removed by the Utils code. 474 if (target.get(i) == header) { 475 // Hold on to the first header, when we need to reset to the top-level 476 if (mFirstHeader == null && 477 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) { 478 mFirstHeader = header; 479 } 480 mHeaderIndexMap.put(id, i); 481 i++; 482 } 483 } 484 } 485 486 private int insertAccountsHeaders(List<Header> target, int headerIndex) { 487 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes(); 488 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length); 489 for (String accountType : accountTypes) { 490 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType); 491 if (label == null) { 492 continue; 493 } 494 495 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType); 496 boolean skipToAccount = accounts.length == 1 497 && !mAuthenticatorHelper.hasAccountPreferences(accountType); 498 Header accHeader = new Header(); 499 accHeader.title = label; 500 if (accHeader.extras == null) { 501 accHeader.extras = new Bundle(); 502 } 503 if (skipToAccount) { 504 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title; 505 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title; 506 accHeader.fragment = AccountSyncSettings.class.getName(); 507 accHeader.fragmentArguments = new Bundle(); 508 // Need this for the icon 509 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 510 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); 511 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, 512 accounts[0]); 513 } else { 514 accHeader.breadCrumbTitle = label; 515 accHeader.breadCrumbShortTitle = label; 516 accHeader.fragment = ManageAccountsSettings.class.getName(); 517 accHeader.fragmentArguments = new Bundle(); 518 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 519 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, 520 accountType); 521 if (!isMultiPane()) { 522 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, 523 label.toString()); 524 } 525 } 526 accountHeaders.add(accHeader); 527 } 528 529 // Sort by label 530 Collections.sort(accountHeaders, new Comparator<Header>() { 531 @Override 532 public int compare(Header h1, Header h2) { 533 return h1.title.toString().compareTo(h2.title.toString()); 534 } 535 }); 536 537 for (Header header : accountHeaders) { 538 target.add(headerIndex++, header); 539 } 540 if (!mListeningToAccountUpdates) { 541 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); 542 mListeningToAccountUpdates = true; 543 } 544 return headerIndex; 545 } 546 547 private void getMetaData() { 548 try { 549 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), 550 PackageManager.GET_META_DATA); 551 if (ai == null || ai.metaData == null) return; 552 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); 553 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 554 555 // Check if it has a parent specified and create a Header object 556 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE); 557 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); 558 if (parentFragmentClass != null) { 559 mParentHeader = new Header(); 560 mParentHeader.fragment = parentFragmentClass; 561 if (parentHeaderTitleRes != 0) { 562 mParentHeader.title = getResources().getString(parentHeaderTitleRes); 563 } 564 } 565 } catch (NameNotFoundException nnfe) { 566 // No recovery 567 } 568 } 569 570 @Override 571 public boolean hasNextButton() { 572 return super.hasNextButton(); 573 } 574 575 @Override 576 public Button getNextButton() { 577 return super.getNextButton(); 578 } 579 580 private static class HeaderAdapter extends ArrayAdapter<Header> { 581 static final int HEADER_TYPE_CATEGORY = 0; 582 static final int HEADER_TYPE_NORMAL = 1; 583 static final int HEADER_TYPE_SWITCH = 2; 584 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1; 585 586 private final WifiEnabler mWifiEnabler; 587 private final BluetoothEnabler mBluetoothEnabler; 588 private AuthenticatorHelper mAuthHelper; 589 590 private static class HeaderViewHolder { 591 ImageView icon; 592 TextView title; 593 TextView summary; 594 Switch switch_; 595 } 596 597 private LayoutInflater mInflater; 598 599 static int getHeaderType(Header header) { 600 if (header.fragment == null && header.intent == null) { 601 return HEADER_TYPE_CATEGORY; 602 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) { 603 return HEADER_TYPE_SWITCH; 604 } else { 605 return HEADER_TYPE_NORMAL; 606 } 607 } 608 609 @Override 610 public int getItemViewType(int position) { 611 Header header = getItem(position); 612 return getHeaderType(header); 613 } 614 615 @Override 616 public boolean areAllItemsEnabled() { 617 return false; // because of categories 618 } 619 620 @Override 621 public boolean isEnabled(int position) { 622 return getItemViewType(position) != HEADER_TYPE_CATEGORY; 623 } 624 625 @Override 626 public int getViewTypeCount() { 627 return HEADER_TYPE_COUNT; 628 } 629 630 @Override 631 public boolean hasStableIds() { 632 return true; 633 } 634 635 public HeaderAdapter(Context context, List<Header> objects, 636 AuthenticatorHelper authenticatorHelper) { 637 super(context, 0, objects); 638 639 mAuthHelper = authenticatorHelper; 640 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 641 642 // Temp Switches provided as placeholder until the adapter replaces these with actual 643 // Switches inflated from their layouts. Must be done before adapter is set in super 644 mWifiEnabler = new WifiEnabler(context, new Switch(context)); 645 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context)); 646 } 647 648 @Override 649 public View getView(int position, View convertView, ViewGroup parent) { 650 HeaderViewHolder holder; 651 Header header = getItem(position); 652 int headerType = getHeaderType(header); 653 View view = null; 654 655 if (convertView == null) { 656 holder = new HeaderViewHolder(); 657 switch (headerType) { 658 case HEADER_TYPE_CATEGORY: 659 view = new TextView(getContext(), null, 660 android.R.attr.listSeparatorTextViewStyle); 661 holder.title = (TextView) view; 662 break; 663 664 case HEADER_TYPE_SWITCH: 665 view = mInflater.inflate(R.layout.preference_header_switch_item, parent, 666 false); 667 holder.icon = (ImageView) view.findViewById(R.id.icon); 668 holder.title = (TextView) 669 view.findViewById(com.android.internal.R.id.title); 670 holder.summary = (TextView) 671 view.findViewById(com.android.internal.R.id.summary); 672 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget); 673 break; 674 675 case HEADER_TYPE_NORMAL: 676 view = mInflater.inflate( 677 R.layout.preference_header_item, parent, 678 false); 679 holder.icon = (ImageView) view.findViewById(R.id.icon); 680 holder.title = (TextView) 681 view.findViewById(com.android.internal.R.id.title); 682 holder.summary = (TextView) 683 view.findViewById(com.android.internal.R.id.summary); 684 break; 685 } 686 view.setTag(holder); 687 } else { 688 view = convertView; 689 holder = (HeaderViewHolder) view.getTag(); 690 } 691 692 // All view fields must be updated every time, because the view may be recycled 693 switch (headerType) { 694 case HEADER_TYPE_CATEGORY: 695 holder.title.setText(header.getTitle(getContext().getResources())); 696 break; 697 698 case HEADER_TYPE_SWITCH: 699 // Would need a different treatment if the main menu had more switches 700 if (header.id == R.id.wifi_settings) { 701 mWifiEnabler.setSwitch(holder.switch_); 702 } else { 703 mBluetoothEnabler.setSwitch(holder.switch_); 704 } 705 // No break, fall through on purpose to update common fields 706 707 //$FALL-THROUGH$ 708 case HEADER_TYPE_NORMAL: 709 if (header.extras != null 710 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) { 711 String accType = header.extras.getString( 712 ManageAccountsSettings.KEY_ACCOUNT_TYPE); 713 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams(); 714 lp.width = getContext().getResources().getDimensionPixelSize( 715 R.dimen.header_icon_width); 716 lp.height = lp.width; 717 holder.icon.setLayoutParams(lp); 718 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType); 719 holder.icon.setImageDrawable(icon); 720 } else { 721 holder.icon.setImageResource(header.iconRes); 722 } 723 holder.title.setText(header.getTitle(getContext().getResources())); 724 CharSequence summary = header.getSummary(getContext().getResources()); 725 if (!TextUtils.isEmpty(summary)) { 726 holder.summary.setVisibility(View.VISIBLE); 727 holder.summary.setText(summary); 728 } else { 729 holder.summary.setVisibility(View.GONE); 730 } 731 break; 732 } 733 734 return view; 735 } 736 737 public void resume() { 738 mWifiEnabler.resume(); 739 mBluetoothEnabler.resume(); 740 } 741 742 public void pause() { 743 mWifiEnabler.pause(); 744 mBluetoothEnabler.pause(); 745 } 746 } 747 748 @Override 749 public void onHeaderClick(Header header, int position) { 750 boolean revert = false; 751 if (header.id == R.id.account_add) { 752 revert = true; 753 } 754 755 super.onHeaderClick(header, position); 756 757 if (revert && mLastHeader != null) { 758 highlightHeader((int) mLastHeader.id); 759 } else { 760 mLastHeader = header; 761 } 762 } 763 764 @Override 765 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { 766 // Override the fragment title for Wallpaper settings 767 int titleRes = pref.getTitleRes(); 768 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) { 769 titleRes = R.string.wallpaper_settings_fragment_title; 770 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName()) 771 && UserHandle.myUserId() != UserHandle.USER_OWNER) { 772 titleRes = R.string.user_info_settings_title; 773 } 774 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(), 775 null, 0); 776 return true; 777 } 778 779 public boolean shouldUpRecreateTask(Intent targetIntent) { 780 return super.shouldUpRecreateTask(new Intent(this, Settings.class)); 781 } 782 783 @Override 784 public void setListAdapter(ListAdapter adapter) { 785 if (adapter == null) { 786 super.setListAdapter(null); 787 } else { 788 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper)); 789 } 790 } 791 792 @Override 793 public void onAccountsUpdated(Account[] accounts) { 794 // TODO: watch for package upgrades to invalidate cache; see 7206643 795 mAuthenticatorHelper.updateAuthDescriptions(this); 796 mAuthenticatorHelper.onAccountsUpdated(this, accounts); 797 invalidateHeaders(); 798 } 799 800 /* 801 * Settings subclasses for launching independently. 802 */ 803 public static class BluetoothSettingsActivity extends Settings { /* empty */ } 804 public static class WirelessSettingsActivity extends Settings { /* empty */ } 805 public static class TetherSettingsActivity extends Settings { /* empty */ } 806 public static class VpnSettingsActivity extends Settings { /* empty */ } 807 public static class DateTimeSettingsActivity extends Settings { /* empty */ } 808 public static class StorageSettingsActivity extends Settings { /* empty */ } 809 public static class WifiSettingsActivity extends Settings { /* empty */ } 810 public static class WifiP2pSettingsActivity extends Settings { /* empty */ } 811 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ } 812 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ } 813 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ } 814 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ } 815 public static class LocalePickerActivity extends Settings { /* empty */ } 816 public static class UserDictionarySettingsActivity extends Settings { /* empty */ } 817 public static class SoundSettingsActivity extends Settings { /* empty */ } 818 public static class DisplaySettingsActivity extends Settings { /* empty */ } 819 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ } 820 public static class ApplicationSettingsActivity extends Settings { /* empty */ } 821 public static class ManageApplicationsActivity extends Settings { /* empty */ } 822 public static class StorageUseActivity extends Settings { /* empty */ } 823 public static class DevelopmentSettingsActivity extends Settings { /* empty */ } 824 public static class AccessibilitySettingsActivity extends Settings { /* empty */ } 825 public static class SecuritySettingsActivity extends Settings { /* empty */ } 826 public static class LocationSettingsActivity extends Settings { /* empty */ } 827 public static class PrivacySettingsActivity extends Settings { /* empty */ } 828 public static class RunningServicesActivity extends Settings { /* empty */ } 829 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ } 830 public static class PowerUsageSummaryActivity extends Settings { /* empty */ } 831 public static class AccountSyncSettingsActivity extends Settings { /* empty */ } 832 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ } 833 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ } 834 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ } 835 public static class DataUsageSummaryActivity extends Settings { /* empty */ } 836 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ } 837 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ } 838 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ } 839 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ } 840 public static class DreamSettingsActivity extends Settings { /* empty */ } 841} 842