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 updateHomeSettingHeaders(header);
568 } else if (id == R.id.user_settings) {
569 if (!UserHandle.MU_ENABLED
570 || !UserManager.supportsMultipleUsers()
571 || Utils.isMonkeyRunning()) {
574 } else if (id == R.id.nfc_payment_settings) {
575 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
578 } else if (id == R.id.development_settings) {
582 } else if (id == R.id.account_add) {
583 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
588 if (i < target.size() && target.get(i) == header
589 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
590 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
594 // Increment if the current one wasn't removed by the Utils code.
595 if (i < target.size() && target.get(i) == header) {
596 // Hold on to the first header, when we need to reset to the top-level
597 if (mFirstHeader == null &&
598 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
599 mFirstHeader = header;
601 mHeaderIndexMap.put(id, i);
607 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
608 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
609 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
610 for (String accountType : accountTypes) {
611 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
616 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
617 boolean skipToAccount = accounts.length == 1
618 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
619 Header accHeader = new Header();
620 accHeader.title = label;
621 if (accHeader.extras == null) {
622 accHeader.extras = new Bundle();
625 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
626 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
627 accHeader.fragment = AccountSyncSettings.class.getName();
628 accHeader.fragmentArguments = new Bundle();
629 // Need this for the icon
630 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
631 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
632 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
635 accHeader.breadCrumbTitle = label;
636 accHeader.breadCrumbShortTitle = label;
637 accHeader.fragment = ManageAccountsSettings.class.getName();
638 accHeader.fragmentArguments = new Bundle();
639 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
640 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
642 if (!isMultiPane()) {
643 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
647 accountHeaders.add(accHeader);
651 Collections.sort(accountHeaders, new Comparator<Header>() {
653 public int compare(Header h1, Header h2) {
654 return h1.title.toString().compareTo(h2.title.toString());
658 for (Header header : accountHeaders) {
659 target.add(headerIndex++, header);
661 if (!mListeningToAccountUpdates) {
662 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
663 mListeningToAccountUpdates = true;
668 private boolean isSystemApp(ResolveInfo ri) {
669 return ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
672 private void updateHomeSettingHeaders(Header header) {
673 final PackageManager pm = getPackageManager();
674 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
676 ComponentName currentHome = pm.getHomeActivities(homeApps);
677 ResolveInfo iconSource = null;
678 if (currentHome == null) {
679 // no current default, so find the system home app and use that
680 for (int which = 0; which < homeApps.size(); which++) {
681 ResolveInfo ri = homeApps.get(which);
682 if (isSystemApp(ri)) {
688 // find the current-home entry in the returned set
689 for (int which = 0; which < homeApps.size(); which++) {
690 ResolveInfo ri = homeApps.get(which);
691 ComponentName riName = new ComponentName(ri.activityInfo.packageName,
692 ri.activityInfo.name);
693 if (riName.equals(currentHome)) {
699 if (iconSource != null) {
700 if (header.extras == null) {
701 header.extras = new Bundle();
703 header.extras.putParcelable(HomeSettings.CURRENT_HOME, iconSource.activityInfo);
705 Log.v(LOG_TAG, "No home app icon found");
707 } catch (Exception e) {
708 // Can't look up the home activity; bail on configuring the icon
709 Log.w(LOG_TAG, "Problem looking up home activity!", e);
713 private void getMetaData() {
715 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
716 PackageManager.GET_META_DATA);
717 if (ai == null || ai.metaData == null) return;
718 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
719 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
721 // Check if it has a parent specified and create a Header object
722 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
723 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
724 if (parentFragmentClass != null) {
725 mParentHeader = new Header();
726 mParentHeader.fragment = parentFragmentClass;
727 if (parentHeaderTitleRes != 0) {
728 mParentHeader.title = getResources().getString(parentHeaderTitleRes);
731 } catch (NameNotFoundException nnfe) {
737 public boolean hasNextButton() {
738 return super.hasNextButton();
742 public Button getNextButton() {
743 return super.getNextButton();
746 private static class HeaderAdapter extends ArrayAdapter<Header> {
747 static final int HEADER_TYPE_CATEGORY = 0;
748 static final int HEADER_TYPE_NORMAL = 1;
749 static final int HEADER_TYPE_SWITCH = 2;
750 static final int HEADER_TYPE_BUTTON = 3;
751 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
753 private final WifiEnabler mWifiEnabler;
754 private final BluetoothEnabler mBluetoothEnabler;
755 private AuthenticatorHelper mAuthHelper;
756 private DevicePolicyManager mDevicePolicyManager;
758 private static class HeaderViewHolder {
767 private LayoutInflater mInflater;
769 static int getHeaderType(Header header) {
770 if (header.fragment == null && header.intent == null) {
771 return HEADER_TYPE_CATEGORY;
772 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
773 return HEADER_TYPE_SWITCH;
774 } else if (header.id == R.id.security_settings) {
775 return HEADER_TYPE_BUTTON;
777 return HEADER_TYPE_NORMAL;
782 public int getItemViewType(int position) {
783 Header header = getItem(position);
784 return getHeaderType(header);
788 public boolean areAllItemsEnabled() {
789 return false; // because of categories
793 public boolean isEnabled(int position) {
794 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
798 public int getViewTypeCount() {
799 return HEADER_TYPE_COUNT;
803 public boolean hasStableIds() {
807 public HeaderAdapter(Context context, List<Header> objects,
808 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
809 super(context, 0, objects);
811 mAuthHelper = authenticatorHelper;
812 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
814 // Temp Switches provided as placeholder until the adapter replaces these with actual
815 // Switches inflated from their layouts. Must be done before adapter is set in super
816 mWifiEnabler = new WifiEnabler(context, new Switch(context));
817 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
818 mDevicePolicyManager = dpm;
822 public View getView(int position, View convertView, ViewGroup parent) {
823 HeaderViewHolder holder;
824 Header header = getItem(position);
825 int headerType = getHeaderType(header);
828 if (convertView == null) {
829 holder = new HeaderViewHolder();
830 switch (headerType) {
831 case HEADER_TYPE_CATEGORY:
832 view = new TextView(getContext(), null,
833 android.R.attr.listSeparatorTextViewStyle);
834 holder.title = (TextView) view;
837 case HEADER_TYPE_SWITCH:
838 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
840 holder.icon = (ImageView) view.findViewById(R.id.icon);
841 holder.title = (TextView)
842 view.findViewById(com.android.internal.R.id.title);
843 holder.summary = (TextView)
844 view.findViewById(com.android.internal.R.id.summary);
845 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
848 case HEADER_TYPE_BUTTON:
849 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
851 holder.icon = (ImageView) view.findViewById(R.id.icon);
852 holder.title = (TextView)
853 view.findViewById(com.android.internal.R.id.title);
854 holder.summary = (TextView)
855 view.findViewById(com.android.internal.R.id.summary);
856 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
857 holder.divider_ = view.findViewById(R.id.divider);
860 case HEADER_TYPE_NORMAL:
861 view = mInflater.inflate(
862 R.layout.preference_header_item, parent,
864 holder.icon = (ImageView) view.findViewById(R.id.icon);
865 holder.title = (TextView)
866 view.findViewById(com.android.internal.R.id.title);
867 holder.summary = (TextView)
868 view.findViewById(com.android.internal.R.id.summary);
874 holder = (HeaderViewHolder) view.getTag();
877 // All view fields must be updated every time, because the view may be recycled
878 switch (headerType) {
879 case HEADER_TYPE_CATEGORY:
880 holder.title.setText(header.getTitle(getContext().getResources()));
883 case HEADER_TYPE_SWITCH:
884 // Would need a different treatment if the main menu had more switches
885 if (header.id == R.id.wifi_settings) {
886 mWifiEnabler.setSwitch(holder.switch_);
888 mBluetoothEnabler.setSwitch(holder.switch_);
890 updateCommonHeaderView(header, holder);
893 case HEADER_TYPE_BUTTON:
894 if (header.id == R.id.security_settings) {
895 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
897 holder.button_.setVisibility(View.VISIBLE);
898 holder.divider_.setVisibility(View.VISIBLE);
899 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
901 holder.button_.setImageResource(R.drawable.ic_qs_certificate_info);
903 holder.button_.setImageResource(
904 android.R.drawable.stat_notify_error);
906 holder.button_.setOnClickListener(new OnClickListener() {
908 public void onClick(View v) {
909 Intent intent = new Intent(
910 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
911 getContext().startActivity(intent);
915 holder.button_.setVisibility(View.GONE);
916 holder.divider_.setVisibility(View.GONE);
919 updateCommonHeaderView(header, holder);
922 case HEADER_TYPE_NORMAL:
923 updateCommonHeaderView(header, holder);
930 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
931 if (header.extras != null
932 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
933 String accType = header.extras.getString(
934 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
935 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
936 setHeaderIcon(holder, icon);
937 } else if (header.extras != null &&
938 header.extras.containsKey(HomeSettings.CURRENT_HOME)) {
939 ActivityInfo ai = header.extras.getParcelable(HomeSettings.CURRENT_HOME);
940 Drawable icon = ai.loadIcon(getContext().getPackageManager());
941 setHeaderIcon(holder, icon);
943 holder.icon.setImageResource(header.iconRes);
945 holder.title.setText(header.getTitle(getContext().getResources()));
946 CharSequence summary = header.getSummary(getContext().getResources());
947 if (!TextUtils.isEmpty(summary)) {
948 holder.summary.setVisibility(View.VISIBLE);
949 holder.summary.setText(summary);
951 holder.summary.setVisibility(View.GONE);
955 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
956 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
957 lp.width = getContext().getResources().getDimensionPixelSize(
958 R.dimen.header_icon_width);
959 lp.height = lp.width;
960 holder.icon.setLayoutParams(lp);
961 holder.icon.setImageDrawable(icon);
964 public void resume() {
965 mWifiEnabler.resume();
966 mBluetoothEnabler.resume();
969 public void pause() {
970 mWifiEnabler.pause();
971 mBluetoothEnabler.pause();
976 public void onHeaderClick(Header header, int position) {
977 boolean revert = false;
978 if (header.id == R.id.account_add) {
982 super.onHeaderClick(header, position);
984 if (revert && mLastHeader != null) {
985 highlightHeader((int) mLastHeader.id);
987 mLastHeader = header;
992 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
993 // Override the fragment title for Wallpaper settings
994 int titleRes = pref.getTitleRes();
995 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
996 titleRes = R.string.wallpaper_settings_fragment_title;
997 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
998 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
999 if (UserManager.get(this).isLinkedUser()) {
1000 titleRes = R.string.profile_info_settings_title;
1002 titleRes = R.string.user_info_settings_title;
1005 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
1011 public boolean shouldUpRecreateTask(Intent targetIntent) {
1012 return super.shouldUpRecreateTask(new Intent(this, Settings.class));
1016 public void setListAdapter(ListAdapter adapter) {
1017 if (adapter == null) {
1018 super.setListAdapter(null);
1020 DevicePolicyManager dpm =
1021 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1022 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
1027 public void onAccountsUpdated(Account[] accounts) {
1028 // TODO: watch for package upgrades to invalidate cache; see 7206643
1029 mAuthenticatorHelper.updateAuthDescriptions(this);
1030 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1031 invalidateHeaders();
1035 * Settings subclasses for launching independently.
1037 public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1038 public static class WirelessSettingsActivity extends Settings { /* empty */ }
1039 public static class TetherSettingsActivity extends Settings { /* empty */ }
1040 public static class VpnSettingsActivity extends Settings { /* empty */ }
1041 public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1042 public static class StorageSettingsActivity extends Settings { /* empty */ }
1043 public static class WifiSettingsActivity extends Settings { /* empty */ }
1044 public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
1045 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
1046 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
1047 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
1048 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
1049 public static class LocalePickerActivity extends Settings { /* empty */ }
1050 public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1051 public static class SoundSettingsActivity extends Settings { /* empty */ }
1052 public static class DisplaySettingsActivity extends Settings { /* empty */ }
1053 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1054 public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1055 public static class ManageApplicationsActivity extends Settings { /* empty */ }
1056 public static class AppOpsSummaryActivity extends Settings { /* empty */ }
1057 public static class StorageUseActivity extends Settings { /* empty */ }
1058 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1059 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
1060 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
1061 public static class SecuritySettingsActivity extends Settings { /* empty */ }
1062 public static class LocationSettingsActivity extends Settings { /* empty */ }
1063 public static class PrivacySettingsActivity extends Settings { /* empty */ }
1064 public static class RunningServicesActivity extends Settings { /* empty */ }
1065 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1066 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1067 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1068 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1069 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1070 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1071 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
1072 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
1073 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
1074 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
1075 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
1076 public static class DreamSettingsActivity extends Settings { /* empty */ }
1077 public static class NotificationStationActivity extends Settings { /* empty */ }
1078 public static class UserSettingsActivity extends Settings { /* empty */ }
1079 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
1080 public static class UsbSettingsActivity extends Settings { /* empty */ }
1081 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
1082 public static class PaymentSettingsActivity extends Settings { /* empty */ }
1083 public static class PrintSettingsActivity extends Settings { /* empty */ }