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.os.Bundle;
36 import android.os.INetworkManagementService;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.UserHandle;
40 import android.os.UserManager;
41 import android.preference.Preference;
42 import android.preference.PreferenceActivity;
43 import android.preference.PreferenceFragment;
44 import android.text.TextUtils;
45 import android.util.Log;
46 import android.view.LayoutInflater;
47 import android.view.View;
48 import android.view.View.OnClickListener;
49 import android.view.ViewGroup;
50 import android.widget.ArrayAdapter;
51 import android.widget.Button;
52 import android.widget.ImageButton;
53 import android.widget.ImageView;
54 import android.widget.ListAdapter;
55 import android.widget.Switch;
56 import android.widget.TextView;
58 import com.android.internal.util.ArrayUtils;
59 import com.android.settings.accessibility.AccessibilitySettings;
60 import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
61 import com.android.settings.accessibility.ToggleCaptioningPreferenceFragment;
62 import com.android.settings.accounts.AccountSyncSettings;
63 import com.android.settings.accounts.AuthenticatorHelper;
64 import com.android.settings.accounts.ManageAccountsSettings;
65 import com.android.settings.applications.AppOpsSummary;
66 import com.android.settings.applications.ManageApplications;
67 import com.android.settings.applications.ProcessStatsUi;
68 import com.android.settings.bluetooth.BluetoothEnabler;
69 import com.android.settings.bluetooth.BluetoothSettings;
70 import com.android.settings.deviceinfo.Memory;
71 import com.android.settings.deviceinfo.UsbSettings;
72 import com.android.settings.fuelgauge.PowerUsageSummary;
73 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
74 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
75 import com.android.settings.inputmethod.SpellCheckersSettings;
76 import com.android.settings.inputmethod.UserDictionaryList;
77 import com.android.settings.location.LocationSettings;
78 import com.android.settings.nfc.AndroidBeam;
79 import com.android.settings.nfc.PaymentSettings;
80 import com.android.settings.print.PrintServiceSettingsFragment;
81 import com.android.settings.print.PrintSettingsFragment;
82 import com.android.settings.tts.TextToSpeechSettings;
83 import com.android.settings.users.UserSettings;
84 import com.android.settings.vpn2.VpnSettings;
85 import com.android.settings.wfd.WifiDisplaySettings;
86 import com.android.settings.wifi.AdvancedWifiSettings;
87 import com.android.settings.wifi.WifiEnabler;
88 import com.android.settings.wifi.WifiSettings;
89 import com.android.settings.wifi.p2p.WifiP2pSettings;
91 import java.util.ArrayList;
92 import java.util.Collections;
93 import java.util.Comparator;
94 import java.util.HashMap;
95 import java.util.List;
98 * Top-level settings activity to handle single pane and double pane UI layout.
100 public class Settings extends PreferenceActivity
101 implements ButtonBarHandler, OnAccountsUpdateListener {
103 private static final String LOG_TAG = "Settings";
105 private static final String META_DATA_KEY_HEADER_ID =
106 "com.android.settings.TOP_LEVEL_HEADER_ID";
107 private static final String META_DATA_KEY_FRAGMENT_CLASS =
108 "com.android.settings.FRAGMENT_CLASS";
109 private static final String META_DATA_KEY_PARENT_TITLE =
110 "com.android.settings.PARENT_FRAGMENT_TITLE";
111 private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
112 "com.android.settings.PARENT_FRAGMENT_CLASS";
114 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
116 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
117 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
119 private String mFragmentClass;
120 private int mTopLevelHeaderId;
121 private Header mFirstHeader;
122 private Header mCurrentHeader;
123 private Header mParentHeader;
124 private boolean mInLocalHeaderSwitch;
126 // Show only these settings for restricted users
127 private int[] SETTINGS_FOR_RESTRICTED = {
128 R.id.wireless_section,
130 R.id.bluetooth_settings,
131 R.id.data_usage_settings,
132 R.id.wireless_settings,
135 R.id.display_settings,
136 R.id.storage_settings,
137 R.id.application_settings,
138 R.id.battery_settings,
139 R.id.personal_section,
140 R.id.location_settings,
141 R.id.security_settings,
142 R.id.language_settings,
144 R.id.account_settings,
147 R.id.date_time_settings,
149 R.id.accessibility_settings,
151 R.id.nfc_payment_settings,
155 private SharedPreferences mDevelopmentPreferences;
156 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
158 // TODO: Update Call Settings based on airplane mode state.
160 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
162 private AuthenticatorHelper mAuthenticatorHelper;
163 private Header mLastHeader;
164 private boolean mListeningToAccountUpdates;
166 private boolean mBatteryPresent = true;
167 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
170 public void onReceive(Context context, Intent intent) {
171 String action = intent.getAction();
172 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
173 boolean batteryPresent = Utils.isBatteryPresent(intent);
175 if (mBatteryPresent != batteryPresent) {
176 mBatteryPresent = batteryPresent;
184 protected void onCreate(Bundle savedInstanceState) {
185 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
186 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
189 mAuthenticatorHelper = new AuthenticatorHelper();
190 mAuthenticatorHelper.updateAuthDescriptions(this);
191 mAuthenticatorHelper.onAccountsUpdated(this, null);
193 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
194 Context.MODE_PRIVATE);
197 mInLocalHeaderSwitch = true;
198 super.onCreate(savedInstanceState);
199 mInLocalHeaderSwitch = false;
201 if (!onIsHidingHeaders() && onIsMultiPane()) {
202 highlightHeader(mTopLevelHeaderId);
203 // Force the title so that it doesn't get overridden by a direct launch of
204 // a specific settings screen.
205 setTitle(R.string.settings_label);
208 // Retrieve any saved state
209 if (savedInstanceState != null) {
210 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
211 mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
214 // If the current header was saved, switch to it
215 if (savedInstanceState != null && mCurrentHeader != null) {
216 //switchToHeaderLocal(mCurrentHeader);
217 showBreadCrumbs(mCurrentHeader.title, null);
220 if (mParentHeader != null) {
221 setParentTitle(mParentHeader.title, null, new OnClickListener() {
223 public void onClick(View v) {
224 switchToParent(mParentHeader.fragment);
229 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
230 if (onIsMultiPane()) {
231 getActionBar().setDisplayHomeAsUpEnabled(false);
232 getActionBar().setHomeButtonEnabled(false);
237 protected void onSaveInstanceState(Bundle outState) {
238 super.onSaveInstanceState(outState);
240 // Save the current fragment, if it is the same as originally launched
241 if (mCurrentHeader != null) {
242 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
244 if (mParentHeader != null) {
245 outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
250 public void onResume() {
253 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
255 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
259 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
260 mDevelopmentPreferencesListener);
262 ListAdapter listAdapter = getListAdapter();
263 if (listAdapter instanceof HeaderAdapter) {
264 ((HeaderAdapter) listAdapter).resume();
268 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
272 public void onPause() {
275 unregisterReceiver(mBatteryInfoReceiver);
277 ListAdapter listAdapter = getListAdapter();
278 if (listAdapter instanceof HeaderAdapter) {
279 ((HeaderAdapter) listAdapter).pause();
282 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
283 mDevelopmentPreferencesListener);
284 mDevelopmentPreferencesListener = null;
288 public void onDestroy() {
290 if (mListeningToAccountUpdates) {
291 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
296 public boolean onIsMultiPane() {
300 private static final String[] ENTRY_FRAGMENTS = {
301 WirelessSettings.class.getName(),
302 WifiSettings.class.getName(),
303 AdvancedWifiSettings.class.getName(),
304 BluetoothSettings.class.getName(),
305 TetherSettings.class.getName(),
306 WifiP2pSettings.class.getName(),
307 VpnSettings.class.getName(),
308 DateTimeSettings.class.getName(),
309 LocalePicker.class.getName(),
310 InputMethodAndLanguageSettings.class.getName(),
311 SpellCheckersSettings.class.getName(),
312 UserDictionaryList.class.getName(),
313 UserDictionarySettings.class.getName(),
314 SoundSettings.class.getName(),
315 DisplaySettings.class.getName(),
316 DeviceInfoSettings.class.getName(),
317 ManageApplications.class.getName(),
318 ProcessStatsUi.class.getName(),
319 NotificationStation.class.getName(),
320 AppOpsSummary.class.getName(),
321 LocationSettings.class.getName(),
322 SecuritySettings.class.getName(),
323 PrivacySettings.class.getName(),
324 DeviceAdminSettings.class.getName(),
325 AccessibilitySettings.class.getName(),
326 ToggleCaptioningPreferenceFragment.class.getName(),
327 TextToSpeechSettings.class.getName(),
328 Memory.class.getName(),
329 DevelopmentSettings.class.getName(),
330 UsbSettings.class.getName(),
331 AndroidBeam.class.getName(),
332 WifiDisplaySettings.class.getName(),
333 PowerUsageSummary.class.getName(),
334 AccountSyncSettings.class.getName(),
335 CryptKeeperSettings.class.getName(),
336 DataUsageSummary.class.getName(),
337 DreamSettings.class.getName(),
338 UserSettings.class.getName(),
339 NotificationAccessSettings.class.getName(),
340 ManageAccountsSettings.class.getName(),
341 PrintSettingsFragment.class.getName(),
342 TrustedCredentialsSettings.class.getName(),
343 PaymentSettings.class.getName(),
344 KeyboardLayoutPickerFragment.class.getName()
348 protected boolean isValidFragment(String fragmentName) {
349 // Almost all fragments are wrapped in this,
350 // except for a few that have their own activities.
351 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
352 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
357 private void switchToHeaderLocal(Header header) {
358 mInLocalHeaderSwitch = true;
359 switchToHeader(header);
360 mInLocalHeaderSwitch = false;
364 public void switchToHeader(Header header) {
365 if (!mInLocalHeaderSwitch) {
366 mCurrentHeader = null;
367 mParentHeader = null;
369 super.switchToHeader(header);
373 * Switch to parent fragment and store the grand parent's info
374 * @param className name of the activity wrapper for the parent fragment.
376 private void switchToParent(String className) {
377 final ComponentName cn = new ComponentName(this, className);
379 final PackageManager pm = getPackageManager();
380 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
382 if (parentInfo != null && parentInfo.metaData != null) {
383 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
384 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
385 Header parentHeader = new Header();
386 parentHeader.fragment = fragmentClass;
387 parentHeader.title = fragmentTitle;
388 mCurrentHeader = parentHeader;
390 switchToHeaderLocal(parentHeader);
391 highlightHeader(mTopLevelHeaderId);
393 mParentHeader = new Header();
394 mParentHeader.fragment
395 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
396 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
398 } catch (NameNotFoundException nnfe) {
399 Log.w(LOG_TAG, "Could not find parent activity : " + className);
404 public void onNewIntent(Intent intent) {
405 super.onNewIntent(intent);
407 // If it is not launched from history, then reset to top-level
408 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
409 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
410 switchToHeaderLocal(mFirstHeader);
412 getListView().setSelectionFromTop(0, 0);
416 private void highlightHeader(int id) {
418 Integer index = mHeaderIndexMap.get(id);
420 getListView().setItemChecked(index, true);
422 getListView().smoothScrollToPosition(index);
429 public Intent getIntent() {
430 Intent superIntent = super.getIntent();
431 String startingFragment = getStartingFragmentClass(superIntent);
432 // This is called from super.onCreate, isMultiPane() is not yet reliable
433 // Do not use onIsHidingHeaders either, which relies itself on this method
434 if (startingFragment != null && !onIsMultiPane()) {
435 Intent modIntent = new Intent(superIntent);
436 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
437 Bundle args = superIntent.getExtras();
439 args = new Bundle(args);
443 args.putParcelable("intent", superIntent);
444 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
451 * Checks if the component name in the intent is different from the Settings class and
452 * returns the class name to load as a fragment.
454 protected String getStartingFragmentClass(Intent intent) {
455 if (mFragmentClass != null) return mFragmentClass;
457 String intentClass = intent.getComponent().getClassName();
458 if (intentClass.equals(getClass().getName())) return null;
460 if ("com.android.settings.ManageApplications".equals(intentClass)
461 || "com.android.settings.RunningServices".equals(intentClass)
462 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
463 // Old names of manage apps.
464 intentClass = com.android.settings.applications.ManageApplications.class.getName();
471 * Override initial header when an activity-alias is causing Settings to be launched
472 * for a specific fragment encoded in the android:name parameter.
475 public Header onGetInitialHeader() {
476 String fragmentClass = getStartingFragmentClass(super.getIntent());
477 if (fragmentClass != null) {
478 Header header = new Header();
479 header.fragment = fragmentClass;
480 header.title = getTitle();
481 header.fragmentArguments = getIntent().getExtras();
482 mCurrentHeader = header;
490 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
491 int titleRes, int shortTitleRes) {
492 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
493 titleRes, shortTitleRes);
495 // Some fragments want split ActionBar; these should stay in sync with
496 // uiOptions for fragments also defined as activities in manifest.
497 if (WifiSettings.class.getName().equals(fragmentName) ||
498 WifiP2pSettings.class.getName().equals(fragmentName) ||
499 WifiDisplaySettings.class.getName().equals(fragmentName) ||
500 BluetoothSettings.class.getName().equals(fragmentName) ||
501 DreamSettings.class.getName().equals(fragmentName) ||
502 LocationSettings.class.getName().equals(fragmentName) ||
503 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
504 PrintSettingsFragment.class.getName().equals(fragmentName) ||
505 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
506 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
509 intent.setClass(this, SubSettings.class);
514 * Populate the activity with the top-level headers.
517 public void onBuildHeaders(List<Header> headers) {
518 loadHeadersFromResource(R.xml.settings_headers, headers);
519 updateHeaderList(headers);
522 private void updateHeaderList(List<Header> target) {
523 final boolean showDev = mDevelopmentPreferences.getBoolean(
524 DevelopmentSettings.PREF_SHOW,
525 android.os.Build.TYPE.equals("eng"));
528 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
529 mHeaderIndexMap.clear();
530 while (i < target.size()) {
531 Header header = target.get(i);
532 // Ids are integers, so downcasting
533 int id = (int) header.id;
534 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
535 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
536 } else if (id == R.id.wifi_settings) {
537 // Remove WiFi Settings if WiFi service is not available.
538 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
541 } else if (id == R.id.bluetooth_settings) {
542 // Remove Bluetooth Settings if Bluetooth service is not available.
543 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
546 } else if (id == R.id.data_usage_settings) {
547 // Remove data usage when kernel module not enabled
548 final INetworkManagementService netManager = INetworkManagementService.Stub
549 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
551 if (!netManager.isBandwidthControlEnabled()) {
554 } catch (RemoteException e) {
557 } else if (id == R.id.battery_settings) {
558 // Remove battery settings when battery is not available. (e.g. TV)
560 if (!mBatteryPresent) {
563 } else if (id == R.id.account_settings) {
564 int headerIndex = i + 1;
565 i = insertAccountsHeaders(target, headerIndex);
566 } else if (id == R.id.home_settings) {
567 if (!updateHomeSettingHeaders(header)) {
570 } else if (id == R.id.user_settings) {
571 if (!UserHandle.MU_ENABLED
572 || !UserManager.supportsMultipleUsers()
573 || Utils.isMonkeyRunning()) {
576 } else if (id == R.id.nfc_payment_settings) {
577 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
580 } else if (id == R.id.development_settings) {
584 } else if (id == R.id.account_add) {
585 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
590 if (i < target.size() && target.get(i) == header
591 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
592 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
596 // Increment if the current one wasn't removed by the Utils code.
597 if (i < target.size() && target.get(i) == header) {
598 // Hold on to the first header, when we need to reset to the top-level
599 if (mFirstHeader == null &&
600 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
601 mFirstHeader = header;
603 mHeaderIndexMap.put(id, i);
609 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
610 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
611 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
612 for (String accountType : accountTypes) {
613 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
618 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
619 boolean skipToAccount = accounts.length == 1
620 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
621 Header accHeader = new Header();
622 accHeader.title = label;
623 if (accHeader.extras == null) {
624 accHeader.extras = new Bundle();
627 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
628 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
629 accHeader.fragment = AccountSyncSettings.class.getName();
630 accHeader.fragmentArguments = new Bundle();
631 // Need this for the icon
632 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
633 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
634 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
637 accHeader.breadCrumbTitle = label;
638 accHeader.breadCrumbShortTitle = label;
639 accHeader.fragment = ManageAccountsSettings.class.getName();
640 accHeader.fragmentArguments = new Bundle();
641 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
642 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
644 if (!isMultiPane()) {
645 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
649 accountHeaders.add(accHeader);
653 Collections.sort(accountHeaders, new Comparator<Header>() {
655 public int compare(Header h1, Header h2) {
656 return h1.title.toString().compareTo(h2.title.toString());
660 for (Header header : accountHeaders) {
661 target.add(headerIndex++, header);
663 if (!mListeningToAccountUpdates) {
664 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
665 mListeningToAccountUpdates = true;
670 private boolean updateHomeSettingHeaders(Header header) {
672 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
673 getPackageManager().getHomeActivities(homeApps);
674 if (homeApps.size() < 2) {
675 // When there's only one available home app, omit this settings
676 // category entirely at the top level UI.
679 } catch (Exception e) {
680 // Can't look up the home activity; bail on configuring the icon
681 Log.w(LOG_TAG, "Problem looking up home activity!", e);
686 private void getMetaData() {
688 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
689 PackageManager.GET_META_DATA);
690 if (ai == null || ai.metaData == null) return;
691 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
692 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
694 // Check if it has a parent specified and create a Header object
695 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
696 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
697 if (parentFragmentClass != null) {
698 mParentHeader = new Header();
699 mParentHeader.fragment = parentFragmentClass;
700 if (parentHeaderTitleRes != 0) {
701 mParentHeader.title = getResources().getString(parentHeaderTitleRes);
704 } catch (NameNotFoundException nnfe) {
710 public boolean hasNextButton() {
711 return super.hasNextButton();
715 public Button getNextButton() {
716 return super.getNextButton();
719 private static class HeaderAdapter extends ArrayAdapter<Header> {
720 static final int HEADER_TYPE_CATEGORY = 0;
721 static final int HEADER_TYPE_NORMAL = 1;
722 static final int HEADER_TYPE_SWITCH = 2;
723 static final int HEADER_TYPE_BUTTON = 3;
724 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
726 private final WifiEnabler mWifiEnabler;
727 private final BluetoothEnabler mBluetoothEnabler;
728 private AuthenticatorHelper mAuthHelper;
729 private DevicePolicyManager mDevicePolicyManager;
731 private static class HeaderViewHolder {
740 private LayoutInflater mInflater;
742 static int getHeaderType(Header header) {
743 if (header.fragment == null && header.intent == null) {
744 return HEADER_TYPE_CATEGORY;
745 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
746 return HEADER_TYPE_SWITCH;
747 } else if (header.id == R.id.security_settings) {
748 return HEADER_TYPE_BUTTON;
750 return HEADER_TYPE_NORMAL;
755 public int getItemViewType(int position) {
756 Header header = getItem(position);
757 return getHeaderType(header);
761 public boolean areAllItemsEnabled() {
762 return false; // because of categories
766 public boolean isEnabled(int position) {
767 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
771 public int getViewTypeCount() {
772 return HEADER_TYPE_COUNT;
776 public boolean hasStableIds() {
780 public HeaderAdapter(Context context, List<Header> objects,
781 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
782 super(context, 0, objects);
784 mAuthHelper = authenticatorHelper;
785 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
787 // Temp Switches provided as placeholder until the adapter replaces these with actual
788 // Switches inflated from their layouts. Must be done before adapter is set in super
789 mWifiEnabler = new WifiEnabler(context, new Switch(context));
790 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
791 mDevicePolicyManager = dpm;
795 public View getView(int position, View convertView, ViewGroup parent) {
796 HeaderViewHolder holder;
797 Header header = getItem(position);
798 int headerType = getHeaderType(header);
801 if (convertView == null) {
802 holder = new HeaderViewHolder();
803 switch (headerType) {
804 case HEADER_TYPE_CATEGORY:
805 view = new TextView(getContext(), null,
806 android.R.attr.listSeparatorTextViewStyle);
807 holder.title = (TextView) view;
810 case HEADER_TYPE_SWITCH:
811 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
813 holder.icon = (ImageView) view.findViewById(R.id.icon);
814 holder.title = (TextView)
815 view.findViewById(com.android.internal.R.id.title);
816 holder.summary = (TextView)
817 view.findViewById(com.android.internal.R.id.summary);
818 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
821 case HEADER_TYPE_BUTTON:
822 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
824 holder.icon = (ImageView) view.findViewById(R.id.icon);
825 holder.title = (TextView)
826 view.findViewById(com.android.internal.R.id.title);
827 holder.summary = (TextView)
828 view.findViewById(com.android.internal.R.id.summary);
829 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
830 holder.divider_ = view.findViewById(R.id.divider);
833 case HEADER_TYPE_NORMAL:
834 view = mInflater.inflate(
835 R.layout.preference_header_item, parent,
837 holder.icon = (ImageView) view.findViewById(R.id.icon);
838 holder.title = (TextView)
839 view.findViewById(com.android.internal.R.id.title);
840 holder.summary = (TextView)
841 view.findViewById(com.android.internal.R.id.summary);
847 holder = (HeaderViewHolder) view.getTag();
850 // All view fields must be updated every time, because the view may be recycled
851 switch (headerType) {
852 case HEADER_TYPE_CATEGORY:
853 holder.title.setText(header.getTitle(getContext().getResources()));
856 case HEADER_TYPE_SWITCH:
857 // Would need a different treatment if the main menu had more switches
858 if (header.id == R.id.wifi_settings) {
859 mWifiEnabler.setSwitch(holder.switch_);
861 mBluetoothEnabler.setSwitch(holder.switch_);
863 updateCommonHeaderView(header, holder);
866 case HEADER_TYPE_BUTTON:
867 if (header.id == R.id.security_settings) {
868 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
870 holder.button_.setVisibility(View.VISIBLE);
871 holder.divider_.setVisibility(View.VISIBLE);
872 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
874 holder.button_.setImageResource(R.drawable.ic_qs_certificate_info);
876 holder.button_.setImageResource(
877 android.R.drawable.stat_notify_error);
879 holder.button_.setOnClickListener(new OnClickListener() {
881 public void onClick(View v) {
882 Intent intent = new Intent(
883 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
884 getContext().startActivity(intent);
888 holder.button_.setVisibility(View.GONE);
889 holder.divider_.setVisibility(View.GONE);
892 updateCommonHeaderView(header, holder);
895 case HEADER_TYPE_NORMAL:
896 updateCommonHeaderView(header, holder);
903 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
904 if (header.extras != null
905 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
906 String accType = header.extras.getString(
907 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
908 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
909 setHeaderIcon(holder, icon);
910 } else if (header.extras != null &&
911 header.extras.containsKey(HomeSettings.CURRENT_HOME)) {
912 ActivityInfo ai = header.extras.getParcelable(HomeSettings.CURRENT_HOME);
913 Drawable icon = ai.loadIcon(getContext().getPackageManager());
914 setHeaderIcon(holder, icon);
916 holder.icon.setImageResource(header.iconRes);
918 holder.title.setText(header.getTitle(getContext().getResources()));
919 CharSequence summary = header.getSummary(getContext().getResources());
920 if (!TextUtils.isEmpty(summary)) {
921 holder.summary.setVisibility(View.VISIBLE);
922 holder.summary.setText(summary);
924 holder.summary.setVisibility(View.GONE);
928 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
929 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
930 lp.width = getContext().getResources().getDimensionPixelSize(
931 R.dimen.header_icon_width);
932 lp.height = lp.width;
933 holder.icon.setLayoutParams(lp);
934 holder.icon.setImageDrawable(icon);
937 public void resume() {
938 mWifiEnabler.resume();
939 mBluetoothEnabler.resume();
942 public void pause() {
943 mWifiEnabler.pause();
944 mBluetoothEnabler.pause();
949 public void onHeaderClick(Header header, int position) {
950 boolean revert = false;
951 if (header.id == R.id.account_add) {
955 super.onHeaderClick(header, position);
957 if (revert && mLastHeader != null) {
958 highlightHeader((int) mLastHeader.id);
960 mLastHeader = header;
965 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
966 // Override the fragment title for Wallpaper settings
967 int titleRes = pref.getTitleRes();
968 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
969 titleRes = R.string.wallpaper_settings_fragment_title;
970 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
971 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
972 if (UserManager.get(this).isLinkedUser()) {
973 titleRes = R.string.profile_info_settings_title;
975 titleRes = R.string.user_info_settings_title;
978 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
984 public boolean shouldUpRecreateTask(Intent targetIntent) {
985 return super.shouldUpRecreateTask(new Intent(this, Settings.class));
989 public void setListAdapter(ListAdapter adapter) {
990 if (adapter == null) {
991 super.setListAdapter(null);
993 DevicePolicyManager dpm =
994 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
995 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
1000 public void onAccountsUpdated(Account[] accounts) {
1001 // TODO: watch for package upgrades to invalidate cache; see 7206643
1002 mAuthenticatorHelper.updateAuthDescriptions(this);
1003 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1004 invalidateHeaders();
1008 * Settings subclasses for launching independently.
1010 public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1011 public static class WirelessSettingsActivity extends Settings { /* empty */ }
1012 public static class TetherSettingsActivity extends Settings { /* empty */ }
1013 public static class VpnSettingsActivity extends Settings { /* empty */ }
1014 public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1015 public static class StorageSettingsActivity extends Settings { /* empty */ }
1016 public static class WifiSettingsActivity extends Settings { /* empty */ }
1017 public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
1018 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
1019 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
1020 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
1021 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
1022 public static class LocalePickerActivity extends Settings { /* empty */ }
1023 public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1024 public static class SoundSettingsActivity extends Settings { /* empty */ }
1025 public static class DisplaySettingsActivity extends Settings { /* empty */ }
1026 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1027 public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1028 public static class ManageApplicationsActivity extends Settings { /* empty */ }
1029 public static class AppOpsSummaryActivity extends Settings { /* empty */ }
1030 public static class StorageUseActivity extends Settings { /* empty */ }
1031 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1032 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
1033 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
1034 public static class SecuritySettingsActivity extends Settings { /* empty */ }
1035 public static class LocationSettingsActivity extends Settings { /* empty */ }
1036 public static class PrivacySettingsActivity extends Settings { /* empty */ }
1037 public static class RunningServicesActivity extends Settings { /* empty */ }
1038 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1039 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1040 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1041 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1042 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1043 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1044 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
1045 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
1046 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
1047 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
1048 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
1049 public static class DreamSettingsActivity extends Settings { /* empty */ }
1050 public static class NotificationStationActivity extends Settings { /* empty */ }
1051 public static class UserSettingsActivity extends Settings { /* empty */ }
1052 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
1053 public static class UsbSettingsActivity extends Settings { /* empty */ }
1054 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
1055 public static class PaymentSettingsActivity extends Settings { /* empty */ }
1056 public static class PrintSettingsActivity extends Settings { /* empty */ }