2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.accounts.OnAccountsUpdateListener;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.SharedPreferences;
29 import android.content.pm.ActivityInfo;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.PackageManager.NameNotFoundException;
33 import android.content.pm.ResolveInfo;
34 import android.graphics.drawable.Drawable;
35 import android.nfc.NfcAdapter;
36 import android.os.Bundle;
37 import android.os.INetworkManagementService;
38 import android.os.RemoteException;
39 import android.os.ServiceManager;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.preference.Preference;
43 import android.preference.PreferenceActivity;
44 import android.preference.PreferenceFragment;
45 import android.text.TextUtils;
46 import android.util.Log;
47 import android.view.LayoutInflater;
48 import android.view.View;
49 import android.view.View.OnClickListener;
50 import android.view.ViewGroup;
51 import android.widget.ArrayAdapter;
52 import android.widget.Button;
53 import android.widget.ImageButton;
54 import android.widget.ImageView;
55 import android.widget.ListAdapter;
56 import android.widget.Switch;
57 import android.widget.TextView;
59 import com.android.internal.util.ArrayUtils;
60 import com.android.settings.accessibility.AccessibilitySettings;
61 import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
62 import com.android.settings.accessibility.ToggleCaptioningPreferenceFragment;
63 import com.android.settings.accounts.AccountSyncSettings;
64 import com.android.settings.accounts.AuthenticatorHelper;
65 import com.android.settings.accounts.ManageAccountsSettings;
66 import com.android.settings.applications.AppOpsSummary;
67 import com.android.settings.applications.ManageApplications;
68 import com.android.settings.applications.ProcessStatsUi;
69 import com.android.settings.bluetooth.BluetoothEnabler;
70 import com.android.settings.bluetooth.BluetoothSettings;
71 import com.android.settings.deviceinfo.Memory;
72 import com.android.settings.deviceinfo.UsbSettings;
73 import com.android.settings.fuelgauge.PowerUsageSummary;
74 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
75 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
76 import com.android.settings.inputmethod.SpellCheckersSettings;
77 import com.android.settings.inputmethod.UserDictionaryList;
78 import com.android.settings.location.LocationSettings;
79 import com.android.settings.nfc.AndroidBeam;
80 import com.android.settings.nfc.PaymentSettings;
81 import com.android.settings.print.PrintServiceSettingsFragment;
82 import com.android.settings.print.PrintSettingsFragment;
83 import com.android.settings.tts.TextToSpeechSettings;
84 import com.android.settings.users.UserSettings;
85 import com.android.settings.vpn2.VpnSettings;
86 import com.android.settings.wfd.WifiDisplaySettings;
87 import com.android.settings.wifi.AdvancedWifiSettings;
88 import com.android.settings.wifi.WifiEnabler;
89 import com.android.settings.wifi.WifiSettings;
90 import com.android.settings.wifi.p2p.WifiP2pSettings;
92 import java.util.ArrayList;
93 import java.util.Collections;
94 import java.util.Comparator;
95 import java.util.HashMap;
96 import java.util.List;
99 * Top-level settings activity to handle single pane and double pane UI layout.
101 public class Settings extends PreferenceActivity
102 implements ButtonBarHandler, OnAccountsUpdateListener {
104 private static final String LOG_TAG = "Settings";
106 private static final String META_DATA_KEY_HEADER_ID =
107 "com.android.settings.TOP_LEVEL_HEADER_ID";
108 private static final String META_DATA_KEY_FRAGMENT_CLASS =
109 "com.android.settings.FRAGMENT_CLASS";
110 private static final String META_DATA_KEY_PARENT_TITLE =
111 "com.android.settings.PARENT_FRAGMENT_TITLE";
112 private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
113 "com.android.settings.PARENT_FRAGMENT_CLASS";
115 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
117 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
118 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
120 private String mFragmentClass;
121 private int mTopLevelHeaderId;
122 private Header mFirstHeader;
123 private Header mCurrentHeader;
124 private Header mParentHeader;
125 private boolean mInLocalHeaderSwitch;
127 // Show only these settings for restricted users
128 private int[] SETTINGS_FOR_RESTRICTED = {
129 R.id.wireless_section,
131 R.id.bluetooth_settings,
132 R.id.data_usage_settings,
133 R.id.wireless_settings,
136 R.id.display_settings,
137 R.id.storage_settings,
138 R.id.application_settings,
139 R.id.battery_settings,
140 R.id.personal_section,
141 R.id.location_settings,
142 R.id.security_settings,
143 R.id.language_settings,
145 R.id.account_settings,
148 R.id.date_time_settings,
150 R.id.accessibility_settings,
152 R.id.nfc_payment_settings,
156 private SharedPreferences mDevelopmentPreferences;
157 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
159 // TODO: Update Call Settings based on airplane mode state.
161 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
163 private AuthenticatorHelper mAuthenticatorHelper;
164 private Header mLastHeader;
165 private boolean mListeningToAccountUpdates;
167 private boolean mBatteryPresent = true;
168 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
171 public void onReceive(Context context, Intent intent) {
172 String action = intent.getAction();
173 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
174 boolean batteryPresent = Utils.isBatteryPresent(intent);
176 if (mBatteryPresent != batteryPresent) {
177 mBatteryPresent = batteryPresent;
185 protected void onCreate(Bundle savedInstanceState) {
186 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
187 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
190 mAuthenticatorHelper = new AuthenticatorHelper();
191 mAuthenticatorHelper.updateAuthDescriptions(this);
192 mAuthenticatorHelper.onAccountsUpdated(this, null);
194 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
195 Context.MODE_PRIVATE);
198 mInLocalHeaderSwitch = true;
199 super.onCreate(savedInstanceState);
200 mInLocalHeaderSwitch = false;
202 if (!onIsHidingHeaders() && onIsMultiPane()) {
203 highlightHeader(mTopLevelHeaderId);
204 // Force the title so that it doesn't get overridden by a direct launch of
205 // a specific settings screen.
206 setTitle(R.string.settings_label);
209 // Retrieve any saved state
210 if (savedInstanceState != null) {
211 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
212 mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
215 // If the current header was saved, switch to it
216 if (savedInstanceState != null && mCurrentHeader != null) {
217 //switchToHeaderLocal(mCurrentHeader);
218 showBreadCrumbs(mCurrentHeader.title, null);
221 if (mParentHeader != null) {
222 setParentTitle(mParentHeader.title, null, new OnClickListener() {
224 public void onClick(View v) {
225 switchToParent(mParentHeader.fragment);
230 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
231 if (onIsMultiPane()) {
232 getActionBar().setDisplayHomeAsUpEnabled(false);
233 getActionBar().setHomeButtonEnabled(false);
238 protected void onSaveInstanceState(Bundle outState) {
239 super.onSaveInstanceState(outState);
241 // Save the current fragment, if it is the same as originally launched
242 if (mCurrentHeader != null) {
243 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
245 if (mParentHeader != null) {
246 outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
251 public void onResume() {
254 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
256 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
260 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
261 mDevelopmentPreferencesListener);
263 ListAdapter listAdapter = getListAdapter();
264 if (listAdapter instanceof HeaderAdapter) {
265 ((HeaderAdapter) listAdapter).resume();
269 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
273 public void onPause() {
276 unregisterReceiver(mBatteryInfoReceiver);
278 ListAdapter listAdapter = getListAdapter();
279 if (listAdapter instanceof HeaderAdapter) {
280 ((HeaderAdapter) listAdapter).pause();
283 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
284 mDevelopmentPreferencesListener);
285 mDevelopmentPreferencesListener = null;
289 public void onDestroy() {
291 if (mListeningToAccountUpdates) {
292 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
297 public boolean onIsMultiPane() {
301 private static final String[] ENTRY_FRAGMENTS = {
302 WirelessSettings.class.getName(),
303 WifiSettings.class.getName(),
304 AdvancedWifiSettings.class.getName(),
305 BluetoothSettings.class.getName(),
306 TetherSettings.class.getName(),
307 WifiP2pSettings.class.getName(),
308 VpnSettings.class.getName(),
309 DateTimeSettings.class.getName(),
310 LocalePicker.class.getName(),
311 InputMethodAndLanguageSettings.class.getName(),
312 SpellCheckersSettings.class.getName(),
313 UserDictionaryList.class.getName(),
314 UserDictionarySettings.class.getName(),
315 SoundSettings.class.getName(),
316 DisplaySettings.class.getName(),
317 DeviceInfoSettings.class.getName(),
318 ManageApplications.class.getName(),
319 ProcessStatsUi.class.getName(),
320 NotificationStation.class.getName(),
321 AppOpsSummary.class.getName(),
322 LocationSettings.class.getName(),
323 SecuritySettings.class.getName(),
324 PrivacySettings.class.getName(),
325 DeviceAdminSettings.class.getName(),
326 AccessibilitySettings.class.getName(),
327 ToggleCaptioningPreferenceFragment.class.getName(),
328 TextToSpeechSettings.class.getName(),
329 Memory.class.getName(),
330 DevelopmentSettings.class.getName(),
331 UsbSettings.class.getName(),
332 AndroidBeam.class.getName(),
333 WifiDisplaySettings.class.getName(),
334 PowerUsageSummary.class.getName(),
335 AccountSyncSettings.class.getName(),
336 CryptKeeperSettings.class.getName(),
337 DataUsageSummary.class.getName(),
338 DreamSettings.class.getName(),
339 UserSettings.class.getName(),
340 NotificationAccessSettings.class.getName(),
341 ManageAccountsSettings.class.getName(),
342 PrintSettingsFragment.class.getName(),
343 TrustedCredentialsSettings.class.getName(),
344 PaymentSettings.class.getName(),
345 KeyboardLayoutPickerFragment.class.getName()
349 protected boolean isValidFragment(String fragmentName) {
350 // Almost all fragments are wrapped in this,
351 // except for a few that have their own activities.
352 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
353 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
358 private void switchToHeaderLocal(Header header) {
359 mInLocalHeaderSwitch = true;
360 switchToHeader(header);
361 mInLocalHeaderSwitch = false;
365 public void switchToHeader(Header header) {
366 if (!mInLocalHeaderSwitch) {
367 mCurrentHeader = null;
368 mParentHeader = null;
370 super.switchToHeader(header);
374 * Switch to parent fragment and store the grand parent's info
375 * @param className name of the activity wrapper for the parent fragment.
377 private void switchToParent(String className) {
378 final ComponentName cn = new ComponentName(this, className);
380 final PackageManager pm = getPackageManager();
381 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
383 if (parentInfo != null && parentInfo.metaData != null) {
384 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
385 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
386 Header parentHeader = new Header();
387 parentHeader.fragment = fragmentClass;
388 parentHeader.title = fragmentTitle;
389 mCurrentHeader = parentHeader;
391 switchToHeaderLocal(parentHeader);
392 highlightHeader(mTopLevelHeaderId);
394 mParentHeader = new Header();
395 mParentHeader.fragment
396 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
397 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
399 } catch (NameNotFoundException nnfe) {
400 Log.w(LOG_TAG, "Could not find parent activity : " + className);
405 public void onNewIntent(Intent intent) {
406 super.onNewIntent(intent);
408 // If it is not launched from history, then reset to top-level
409 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
410 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
411 switchToHeaderLocal(mFirstHeader);
413 getListView().setSelectionFromTop(0, 0);
417 private void highlightHeader(int id) {
419 Integer index = mHeaderIndexMap.get(id);
421 getListView().setItemChecked(index, true);
423 getListView().smoothScrollToPosition(index);
430 public Intent getIntent() {
431 Intent superIntent = super.getIntent();
432 String startingFragment = getStartingFragmentClass(superIntent);
433 // This is called from super.onCreate, isMultiPane() is not yet reliable
434 // Do not use onIsHidingHeaders either, which relies itself on this method
435 if (startingFragment != null && !onIsMultiPane()) {
436 Intent modIntent = new Intent(superIntent);
437 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
438 Bundle args = superIntent.getExtras();
440 args = new Bundle(args);
444 args.putParcelable("intent", superIntent);
445 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
452 * Checks if the component name in the intent is different from the Settings class and
453 * returns the class name to load as a fragment.
455 protected String getStartingFragmentClass(Intent intent) {
456 if (mFragmentClass != null) return mFragmentClass;
458 String intentClass = intent.getComponent().getClassName();
459 if (intentClass.equals(getClass().getName())) return null;
461 if ("com.android.settings.ManageApplications".equals(intentClass)
462 || "com.android.settings.RunningServices".equals(intentClass)
463 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
464 // Old names of manage apps.
465 intentClass = com.android.settings.applications.ManageApplications.class.getName();
472 * Override initial header when an activity-alias is causing Settings to be launched
473 * for a specific fragment encoded in the android:name parameter.
476 public Header onGetInitialHeader() {
477 String fragmentClass = getStartingFragmentClass(super.getIntent());
478 if (fragmentClass != null) {
479 Header header = new Header();
480 header.fragment = fragmentClass;
481 header.title = getTitle();
482 header.fragmentArguments = getIntent().getExtras();
483 mCurrentHeader = header;
491 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
492 int titleRes, int shortTitleRes) {
493 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
494 titleRes, shortTitleRes);
496 // Some fragments want split ActionBar; these should stay in sync with
497 // uiOptions for fragments also defined as activities in manifest.
498 if (WifiSettings.class.getName().equals(fragmentName) ||
499 WifiP2pSettings.class.getName().equals(fragmentName) ||
500 WifiDisplaySettings.class.getName().equals(fragmentName) ||
501 BluetoothSettings.class.getName().equals(fragmentName) ||
502 DreamSettings.class.getName().equals(fragmentName) ||
503 LocationSettings.class.getName().equals(fragmentName) ||
504 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
505 PrintSettingsFragment.class.getName().equals(fragmentName) ||
506 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
507 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
510 intent.setClass(this, SubSettings.class);
515 * Populate the activity with the top-level headers.
518 public void onBuildHeaders(List<Header> headers) {
519 loadHeadersFromResource(R.xml.settings_headers, headers);
520 updateHeaderList(headers);
523 private void updateHeaderList(List<Header> target) {
524 final boolean showDev = mDevelopmentPreferences.getBoolean(
525 DevelopmentSettings.PREF_SHOW,
526 android.os.Build.TYPE.equals("eng"));
529 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
530 mHeaderIndexMap.clear();
531 while (i < target.size()) {
532 Header header = target.get(i);
533 // Ids are integers, so downcasting
534 int id = (int) header.id;
535 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
536 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
537 } else if (id == R.id.wifi_settings) {
538 // Remove WiFi Settings if WiFi service is not available.
539 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
542 } else if (id == R.id.bluetooth_settings) {
543 // Remove Bluetooth Settings if Bluetooth service is not available.
544 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
547 } else if (id == R.id.data_usage_settings) {
548 // Remove data usage when kernel module not enabled
549 final INetworkManagementService netManager = INetworkManagementService.Stub
550 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
552 if (!netManager.isBandwidthControlEnabled()) {
555 } catch (RemoteException e) {
558 } else if (id == R.id.battery_settings) {
559 // Remove battery settings when battery is not available. (e.g. TV)
561 if (!mBatteryPresent) {
564 } else if (id == R.id.account_settings) {
565 int headerIndex = i + 1;
566 i = insertAccountsHeaders(target, headerIndex);
567 } else if (id == R.id.home_settings) {
568 if (!updateHomeSettingHeaders(header)) {
571 } else if (id == R.id.user_settings) {
572 if (!UserHandle.MU_ENABLED
573 || !UserManager.supportsMultipleUsers()
574 || Utils.isMonkeyRunning()) {
577 } else if (id == R.id.nfc_payment_settings) {
578 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
581 // Only show if NFC is on and we have the HCE feature
582 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
583 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
584 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
588 } else if (id == R.id.development_settings) {
592 } else if (id == R.id.account_add) {
593 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
598 if (i < target.size() && target.get(i) == header
599 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
600 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
604 // Increment if the current one wasn't removed by the Utils code.
605 if (i < target.size() && target.get(i) == header) {
606 // Hold on to the first header, when we need to reset to the top-level
607 if (mFirstHeader == null &&
608 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
609 mFirstHeader = header;
611 mHeaderIndexMap.put(id, i);
617 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
618 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
619 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
620 for (String accountType : accountTypes) {
621 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
626 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
627 boolean skipToAccount = accounts.length == 1
628 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
629 Header accHeader = new Header();
630 accHeader.title = label;
631 if (accHeader.extras == null) {
632 accHeader.extras = new Bundle();
635 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
636 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
637 accHeader.fragment = AccountSyncSettings.class.getName();
638 accHeader.fragmentArguments = new Bundle();
639 // Need this for the icon
640 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
641 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
642 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
645 accHeader.breadCrumbTitle = label;
646 accHeader.breadCrumbShortTitle = label;
647 accHeader.fragment = ManageAccountsSettings.class.getName();
648 accHeader.fragmentArguments = new Bundle();
649 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
650 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
652 if (!isMultiPane()) {
653 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
657 accountHeaders.add(accHeader);
661 Collections.sort(accountHeaders, new Comparator<Header>() {
663 public int compare(Header h1, Header h2) {
664 return h1.title.toString().compareTo(h2.title.toString());
668 for (Header header : accountHeaders) {
669 target.add(headerIndex++, header);
671 if (!mListeningToAccountUpdates) {
672 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
673 mListeningToAccountUpdates = true;
678 private boolean updateHomeSettingHeaders(Header header) {
680 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
681 getPackageManager().getHomeActivities(homeApps);
682 if (homeApps.size() < 2) {
683 // When there's only one available home app, omit this settings
684 // category entirely at the top level UI.
687 } catch (Exception e) {
688 // Can't look up the home activity; bail on configuring the icon
689 Log.w(LOG_TAG, "Problem looking up home activity!", e);
694 private void getMetaData() {
696 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
697 PackageManager.GET_META_DATA);
698 if (ai == null || ai.metaData == null) return;
699 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
700 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
702 // Check if it has a parent specified and create a Header object
703 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
704 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
705 if (parentFragmentClass != null) {
706 mParentHeader = new Header();
707 mParentHeader.fragment = parentFragmentClass;
708 if (parentHeaderTitleRes != 0) {
709 mParentHeader.title = getResources().getString(parentHeaderTitleRes);
712 } catch (NameNotFoundException nnfe) {
718 public boolean hasNextButton() {
719 return super.hasNextButton();
723 public Button getNextButton() {
724 return super.getNextButton();
727 private static class HeaderAdapter extends ArrayAdapter<Header> {
728 static final int HEADER_TYPE_CATEGORY = 0;
729 static final int HEADER_TYPE_NORMAL = 1;
730 static final int HEADER_TYPE_SWITCH = 2;
731 static final int HEADER_TYPE_BUTTON = 3;
732 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
734 private final WifiEnabler mWifiEnabler;
735 private final BluetoothEnabler mBluetoothEnabler;
736 private AuthenticatorHelper mAuthHelper;
737 private DevicePolicyManager mDevicePolicyManager;
739 private static class HeaderViewHolder {
748 private LayoutInflater mInflater;
750 static int getHeaderType(Header header) {
751 if (header.fragment == null && header.intent == null) {
752 return HEADER_TYPE_CATEGORY;
753 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
754 return HEADER_TYPE_SWITCH;
755 } else if (header.id == R.id.security_settings) {
756 return HEADER_TYPE_BUTTON;
758 return HEADER_TYPE_NORMAL;
763 public int getItemViewType(int position) {
764 Header header = getItem(position);
765 return getHeaderType(header);
769 public boolean areAllItemsEnabled() {
770 return false; // because of categories
774 public boolean isEnabled(int position) {
775 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
779 public int getViewTypeCount() {
780 return HEADER_TYPE_COUNT;
784 public boolean hasStableIds() {
788 public HeaderAdapter(Context context, List<Header> objects,
789 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
790 super(context, 0, objects);
792 mAuthHelper = authenticatorHelper;
793 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
795 // Temp Switches provided as placeholder until the adapter replaces these with actual
796 // Switches inflated from their layouts. Must be done before adapter is set in super
797 mWifiEnabler = new WifiEnabler(context, new Switch(context));
798 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
799 mDevicePolicyManager = dpm;
803 public View getView(int position, View convertView, ViewGroup parent) {
804 HeaderViewHolder holder;
805 Header header = getItem(position);
806 int headerType = getHeaderType(header);
809 if (convertView == null) {
810 holder = new HeaderViewHolder();
811 switch (headerType) {
812 case HEADER_TYPE_CATEGORY:
813 view = new TextView(getContext(), null,
814 android.R.attr.listSeparatorTextViewStyle);
815 holder.title = (TextView) view;
818 case HEADER_TYPE_SWITCH:
819 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
821 holder.icon = (ImageView) view.findViewById(R.id.icon);
822 holder.title = (TextView)
823 view.findViewById(com.android.internal.R.id.title);
824 holder.summary = (TextView)
825 view.findViewById(com.android.internal.R.id.summary);
826 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
829 case HEADER_TYPE_BUTTON:
830 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
832 holder.icon = (ImageView) view.findViewById(R.id.icon);
833 holder.title = (TextView)
834 view.findViewById(com.android.internal.R.id.title);
835 holder.summary = (TextView)
836 view.findViewById(com.android.internal.R.id.summary);
837 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
838 holder.divider_ = view.findViewById(R.id.divider);
841 case HEADER_TYPE_NORMAL:
842 view = mInflater.inflate(
843 R.layout.preference_header_item, parent,
845 holder.icon = (ImageView) view.findViewById(R.id.icon);
846 holder.title = (TextView)
847 view.findViewById(com.android.internal.R.id.title);
848 holder.summary = (TextView)
849 view.findViewById(com.android.internal.R.id.summary);
855 holder = (HeaderViewHolder) view.getTag();
858 // All view fields must be updated every time, because the view may be recycled
859 switch (headerType) {
860 case HEADER_TYPE_CATEGORY:
861 holder.title.setText(header.getTitle(getContext().getResources()));
864 case HEADER_TYPE_SWITCH:
865 // Would need a different treatment if the main menu had more switches
866 if (header.id == R.id.wifi_settings) {
867 mWifiEnabler.setSwitch(holder.switch_);
869 mBluetoothEnabler.setSwitch(holder.switch_);
871 updateCommonHeaderView(header, holder);
874 case HEADER_TYPE_BUTTON:
875 if (header.id == R.id.security_settings) {
876 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
878 holder.button_.setVisibility(View.VISIBLE);
879 holder.divider_.setVisibility(View.VISIBLE);
880 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
882 holder.button_.setImageResource(R.drawable.ic_settings_about);
884 holder.button_.setImageResource(
885 android.R.drawable.stat_notify_error);
887 holder.button_.setOnClickListener(new OnClickListener() {
889 public void onClick(View v) {
890 Intent intent = new Intent(
891 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
892 getContext().startActivity(intent);
896 holder.button_.setVisibility(View.GONE);
897 holder.divider_.setVisibility(View.GONE);
900 updateCommonHeaderView(header, holder);
903 case HEADER_TYPE_NORMAL:
904 updateCommonHeaderView(header, holder);
911 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
912 if (header.extras != null
913 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
914 String accType = header.extras.getString(
915 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
916 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
917 setHeaderIcon(holder, icon);
918 } else if (header.extras != null &&
919 header.extras.containsKey(HomeSettings.CURRENT_HOME)) {
920 ActivityInfo ai = header.extras.getParcelable(HomeSettings.CURRENT_HOME);
921 Drawable icon = ai.loadIcon(getContext().getPackageManager());
922 setHeaderIcon(holder, icon);
924 holder.icon.setImageResource(header.iconRes);
926 holder.title.setText(header.getTitle(getContext().getResources()));
927 CharSequence summary = header.getSummary(getContext().getResources());
928 if (!TextUtils.isEmpty(summary)) {
929 holder.summary.setVisibility(View.VISIBLE);
930 holder.summary.setText(summary);
932 holder.summary.setVisibility(View.GONE);
936 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
937 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
938 lp.width = getContext().getResources().getDimensionPixelSize(
939 R.dimen.header_icon_width);
940 lp.height = lp.width;
941 holder.icon.setLayoutParams(lp);
942 holder.icon.setImageDrawable(icon);
945 public void resume() {
946 mWifiEnabler.resume();
947 mBluetoothEnabler.resume();
950 public void pause() {
951 mWifiEnabler.pause();
952 mBluetoothEnabler.pause();
957 public void onHeaderClick(Header header, int position) {
958 boolean revert = false;
959 if (header.id == R.id.account_add) {
963 super.onHeaderClick(header, position);
965 if (revert && mLastHeader != null) {
966 highlightHeader((int) mLastHeader.id);
968 mLastHeader = header;
973 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
974 // Override the fragment title for Wallpaper settings
975 int titleRes = pref.getTitleRes();
976 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
977 titleRes = R.string.wallpaper_settings_fragment_title;
978 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
979 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
980 if (UserManager.get(this).isLinkedUser()) {
981 titleRes = R.string.profile_info_settings_title;
983 titleRes = R.string.user_info_settings_title;
986 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
992 public boolean shouldUpRecreateTask(Intent targetIntent) {
993 return super.shouldUpRecreateTask(new Intent(this, Settings.class));
997 public void setListAdapter(ListAdapter adapter) {
998 if (adapter == null) {
999 super.setListAdapter(null);
1001 DevicePolicyManager dpm =
1002 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1003 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
1008 public void onAccountsUpdated(Account[] accounts) {
1009 // TODO: watch for package upgrades to invalidate cache; see 7206643
1010 mAuthenticatorHelper.updateAuthDescriptions(this);
1011 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1012 invalidateHeaders();
1016 * Settings subclasses for launching independently.
1018 public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1019 public static class WirelessSettingsActivity extends Settings { /* empty */ }
1020 public static class TetherSettingsActivity extends Settings { /* empty */ }
1021 public static class VpnSettingsActivity extends Settings { /* empty */ }
1022 public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1023 public static class StorageSettingsActivity extends Settings { /* empty */ }
1024 public static class WifiSettingsActivity extends Settings { /* empty */ }
1025 public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
1026 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
1027 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
1028 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
1029 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
1030 public static class LocalePickerActivity extends Settings { /* empty */ }
1031 public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1032 public static class SoundSettingsActivity extends Settings { /* empty */ }
1033 public static class DisplaySettingsActivity extends Settings { /* empty */ }
1034 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1035 public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1036 public static class ManageApplicationsActivity extends Settings { /* empty */ }
1037 public static class AppOpsSummaryActivity extends Settings { /* empty */ }
1038 public static class StorageUseActivity extends Settings { /* empty */ }
1039 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1040 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
1041 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
1042 public static class SecuritySettingsActivity extends Settings { /* empty */ }
1043 public static class LocationSettingsActivity extends Settings { /* empty */ }
1044 public static class PrivacySettingsActivity extends Settings { /* empty */ }
1045 public static class RunningServicesActivity extends Settings { /* empty */ }
1046 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1047 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1048 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1049 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1050 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1051 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1052 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
1053 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
1054 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
1055 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
1056 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
1057 public static class DreamSettingsActivity extends Settings { /* empty */ }
1058 public static class NotificationStationActivity extends Settings { /* empty */ }
1059 public static class UserSettingsActivity extends Settings { /* empty */ }
1060 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
1061 public static class UsbSettingsActivity extends Settings { /* empty */ }
1062 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
1063 public static class PaymentSettingsActivity extends Settings { /* empty */ }
1064 public static class PrintSettingsActivity extends Settings { /* empty */ }