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