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.Activity;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.app.DialogFragment;
26 import android.app.admin.DevicePolicyManager;
27 import android.content.BroadcastReceiver;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.SharedPreferences;
33 import android.content.pm.ActivityInfo;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageManager.NameNotFoundException;
36 import android.content.pm.ResolveInfo;
37 import android.graphics.drawable.Drawable;
38 import android.nfc.NfcAdapter;
39 import android.os.Bundle;
40 import android.os.INetworkManagementService;
41 import android.os.RemoteException;
42 import android.os.ServiceManager;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.preference.Preference;
46 import android.preference.PreferenceActivity;
47 import android.preference.PreferenceFragment;
48 import android.text.TextUtils;
49 import android.util.Log;
50 import android.view.LayoutInflater;
51 import android.view.View;
52 import android.view.View.OnClickListener;
53 import android.view.ViewGroup;
54 import android.widget.ArrayAdapter;
55 import android.widget.Button;
56 import android.widget.ImageButton;
57 import android.widget.ImageView;
58 import android.widget.ListAdapter;
59 import android.widget.Switch;
60 import android.widget.TextView;
62 import com.android.internal.util.ArrayUtils;
63 import com.android.settings.accessibility.AccessibilitySettings;
64 import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
65 import com.android.settings.accessibility.ToggleCaptioningPreferenceFragment;
66 import com.android.settings.accounts.AccountSyncSettings;
67 import com.android.settings.accounts.AuthenticatorHelper;
68 import com.android.settings.accounts.ManageAccountsSettings;
69 import com.android.settings.applications.AppOpsSummary;
70 import com.android.settings.applications.ManageApplications;
71 import com.android.settings.applications.ProcessStatsUi;
72 import com.android.settings.bluetooth.BluetoothEnabler;
73 import com.android.settings.bluetooth.BluetoothSettings;
74 import com.android.settings.deviceinfo.Memory;
75 import com.android.settings.deviceinfo.UsbSettings;
76 import com.android.settings.fuelgauge.PowerUsageSummary;
77 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
78 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
79 import com.android.settings.inputmethod.SpellCheckersSettings;
80 import com.android.settings.inputmethod.UserDictionaryList;
81 import com.android.settings.location.LocationSettings;
82 import com.android.settings.nfc.AndroidBeam;
83 import com.android.settings.nfc.PaymentSettings;
84 import com.android.settings.print.PrintServiceSettingsFragment;
85 import com.android.settings.print.PrintSettingsFragment;
86 import com.android.settings.tts.TextToSpeechSettings;
87 import com.android.settings.users.UserSettings;
88 import com.android.settings.vpn2.VpnSettings;
89 import com.android.settings.wfd.WifiDisplaySettings;
90 import com.android.settings.wifi.AdvancedWifiSettings;
91 import com.android.settings.wifi.WifiEnabler;
92 import com.android.settings.wifi.WifiSettings;
93 import com.android.settings.wifi.p2p.WifiP2pSettings;
95 import java.util.ArrayList;
96 import java.util.Collections;
97 import java.util.Comparator;
98 import java.util.HashMap;
99 import java.util.List;
102 * Top-level settings activity to handle single pane and double pane UI layout.
104 public class Settings extends PreferenceActivity
105 implements ButtonBarHandler, OnAccountsUpdateListener {
107 private static final String LOG_TAG = "Settings";
109 private static final String META_DATA_KEY_HEADER_ID =
110 "com.android.settings.TOP_LEVEL_HEADER_ID";
111 private static final String META_DATA_KEY_FRAGMENT_CLASS =
112 "com.android.settings.FRAGMENT_CLASS";
113 private static final String META_DATA_KEY_PARENT_TITLE =
114 "com.android.settings.PARENT_FRAGMENT_TITLE";
115 private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
116 "com.android.settings.PARENT_FRAGMENT_CLASS";
118 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
120 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
121 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
123 static final int DIALOG_ONLY_ONE_HOME = 1;
125 private static boolean sShowNoHomeNotice = false;
127 private String mFragmentClass;
128 private int mTopLevelHeaderId;
129 private Header mFirstHeader;
130 private Header mCurrentHeader;
131 private Header mParentHeader;
132 private boolean mInLocalHeaderSwitch;
134 // Show only these settings for restricted users
135 private int[] SETTINGS_FOR_RESTRICTED = {
136 R.id.wireless_section,
138 R.id.bluetooth_settings,
139 R.id.data_usage_settings,
140 R.id.wireless_settings,
143 R.id.display_settings,
144 R.id.storage_settings,
145 R.id.application_settings,
146 R.id.battery_settings,
147 R.id.personal_section,
148 R.id.location_settings,
149 R.id.security_settings,
150 R.id.language_settings,
152 R.id.account_settings,
155 R.id.date_time_settings,
157 R.id.accessibility_settings,
159 R.id.nfc_payment_settings,
163 private SharedPreferences mDevelopmentPreferences;
164 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
166 // TODO: Update Call Settings based on airplane mode state.
168 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
170 private AuthenticatorHelper mAuthenticatorHelper;
171 private Header mLastHeader;
172 private boolean mListeningToAccountUpdates;
174 private boolean mBatteryPresent = true;
175 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
178 public void onReceive(Context context, Intent intent) {
179 String action = intent.getAction();
180 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
181 boolean batteryPresent = Utils.isBatteryPresent(intent);
183 if (mBatteryPresent != batteryPresent) {
184 mBatteryPresent = batteryPresent;
192 protected void onCreate(Bundle savedInstanceState) {
193 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
194 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
197 mAuthenticatorHelper = new AuthenticatorHelper();
198 mAuthenticatorHelper.updateAuthDescriptions(this);
199 mAuthenticatorHelper.onAccountsUpdated(this, null);
201 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
202 Context.MODE_PRIVATE);
205 mInLocalHeaderSwitch = true;
206 super.onCreate(savedInstanceState);
207 mInLocalHeaderSwitch = false;
209 if (!onIsHidingHeaders() && onIsMultiPane()) {
210 highlightHeader(mTopLevelHeaderId);
211 // Force the title so that it doesn't get overridden by a direct launch of
212 // a specific settings screen.
213 setTitle(R.string.settings_label);
216 // Retrieve any saved state
217 if (savedInstanceState != null) {
218 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
219 mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
222 // If the current header was saved, switch to it
223 if (savedInstanceState != null && mCurrentHeader != null) {
224 //switchToHeaderLocal(mCurrentHeader);
225 showBreadCrumbs(mCurrentHeader.title, null);
228 if (mParentHeader != null) {
229 setParentTitle(mParentHeader.title, null, new OnClickListener() {
231 public void onClick(View v) {
232 switchToParent(mParentHeader.fragment);
237 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
238 if (onIsMultiPane()) {
239 getActionBar().setDisplayHomeAsUpEnabled(false);
240 getActionBar().setHomeButtonEnabled(false);
245 protected void onSaveInstanceState(Bundle outState) {
246 super.onSaveInstanceState(outState);
248 // Save the current fragment, if it is the same as originally launched
249 if (mCurrentHeader != null) {
250 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
252 if (mParentHeader != null) {
253 outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
258 public void onResume() {
261 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
263 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
267 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
268 mDevelopmentPreferencesListener);
270 ListAdapter listAdapter = getListAdapter();
271 if (listAdapter instanceof HeaderAdapter) {
272 ((HeaderAdapter) listAdapter).resume();
276 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
280 public void onPause() {
283 unregisterReceiver(mBatteryInfoReceiver);
285 ListAdapter listAdapter = getListAdapter();
286 if (listAdapter instanceof HeaderAdapter) {
287 ((HeaderAdapter) listAdapter).pause();
290 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
291 mDevelopmentPreferencesListener);
292 mDevelopmentPreferencesListener = null;
296 public void onDestroy() {
298 if (mListeningToAccountUpdates) {
299 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
304 public boolean onIsMultiPane() {
308 private static final String[] ENTRY_FRAGMENTS = {
309 WirelessSettings.class.getName(),
310 WifiSettings.class.getName(),
311 AdvancedWifiSettings.class.getName(),
312 BluetoothSettings.class.getName(),
313 TetherSettings.class.getName(),
314 WifiP2pSettings.class.getName(),
315 VpnSettings.class.getName(),
316 DateTimeSettings.class.getName(),
317 LocalePicker.class.getName(),
318 InputMethodAndLanguageSettings.class.getName(),
319 SpellCheckersSettings.class.getName(),
320 UserDictionaryList.class.getName(),
321 UserDictionarySettings.class.getName(),
322 SoundSettings.class.getName(),
323 DisplaySettings.class.getName(),
324 DeviceInfoSettings.class.getName(),
325 ManageApplications.class.getName(),
326 ProcessStatsUi.class.getName(),
327 NotificationStation.class.getName(),
328 AppOpsSummary.class.getName(),
329 LocationSettings.class.getName(),
330 SecuritySettings.class.getName(),
331 PrivacySettings.class.getName(),
332 DeviceAdminSettings.class.getName(),
333 AccessibilitySettings.class.getName(),
334 ToggleCaptioningPreferenceFragment.class.getName(),
335 TextToSpeechSettings.class.getName(),
336 Memory.class.getName(),
337 DevelopmentSettings.class.getName(),
338 UsbSettings.class.getName(),
339 AndroidBeam.class.getName(),
340 WifiDisplaySettings.class.getName(),
341 PowerUsageSummary.class.getName(),
342 AccountSyncSettings.class.getName(),
343 CryptKeeperSettings.class.getName(),
344 DataUsageSummary.class.getName(),
345 DreamSettings.class.getName(),
346 UserSettings.class.getName(),
347 NotificationAccessSettings.class.getName(),
348 ManageAccountsSettings.class.getName(),
349 PrintSettingsFragment.class.getName(),
350 TrustedCredentialsSettings.class.getName(),
351 PaymentSettings.class.getName(),
352 KeyboardLayoutPickerFragment.class.getName()
356 protected boolean isValidFragment(String fragmentName) {
357 // Almost all fragments are wrapped in this,
358 // except for a few that have their own activities.
359 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
360 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
365 private void switchToHeaderLocal(Header header) {
366 mInLocalHeaderSwitch = true;
367 switchToHeader(header);
368 mInLocalHeaderSwitch = false;
372 public void switchToHeader(Header header) {
373 if (!mInLocalHeaderSwitch) {
374 mCurrentHeader = null;
375 mParentHeader = null;
377 super.switchToHeader(header);
381 * Switch to parent fragment and store the grand parent's info
382 * @param className name of the activity wrapper for the parent fragment.
384 private void switchToParent(String className) {
385 final ComponentName cn = new ComponentName(this, className);
387 final PackageManager pm = getPackageManager();
388 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
390 if (parentInfo != null && parentInfo.metaData != null) {
391 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
392 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
393 Header parentHeader = new Header();
394 parentHeader.fragment = fragmentClass;
395 parentHeader.title = fragmentTitle;
396 mCurrentHeader = parentHeader;
398 switchToHeaderLocal(parentHeader);
399 highlightHeader(mTopLevelHeaderId);
401 mParentHeader = new Header();
402 mParentHeader.fragment
403 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
404 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
406 } catch (NameNotFoundException nnfe) {
407 Log.w(LOG_TAG, "Could not find parent activity : " + className);
412 public void onNewIntent(Intent intent) {
413 super.onNewIntent(intent);
415 // If it is not launched from history, then reset to top-level
416 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
417 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
418 switchToHeaderLocal(mFirstHeader);
420 getListView().setSelectionFromTop(0, 0);
424 private void highlightHeader(int id) {
426 Integer index = mHeaderIndexMap.get(id);
428 getListView().setItemChecked(index, true);
430 getListView().smoothScrollToPosition(index);
437 public Intent getIntent() {
438 Intent superIntent = super.getIntent();
439 String startingFragment = getStartingFragmentClass(superIntent);
440 // This is called from super.onCreate, isMultiPane() is not yet reliable
441 // Do not use onIsHidingHeaders either, which relies itself on this method
442 if (startingFragment != null && !onIsMultiPane()) {
443 Intent modIntent = new Intent(superIntent);
444 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
445 Bundle args = superIntent.getExtras();
447 args = new Bundle(args);
451 args.putParcelable("intent", superIntent);
452 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
459 * Checks if the component name in the intent is different from the Settings class and
460 * returns the class name to load as a fragment.
462 protected String getStartingFragmentClass(Intent intent) {
463 if (mFragmentClass != null) return mFragmentClass;
465 String intentClass = intent.getComponent().getClassName();
466 if (intentClass.equals(getClass().getName())) return null;
468 if ("com.android.settings.ManageApplications".equals(intentClass)
469 || "com.android.settings.RunningServices".equals(intentClass)
470 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
471 // Old names of manage apps.
472 intentClass = com.android.settings.applications.ManageApplications.class.getName();
479 * Override initial header when an activity-alias is causing Settings to be launched
480 * for a specific fragment encoded in the android:name parameter.
483 public Header onGetInitialHeader() {
484 String fragmentClass = getStartingFragmentClass(super.getIntent());
485 if (fragmentClass != null) {
486 Header header = new Header();
487 header.fragment = fragmentClass;
488 header.title = getTitle();
489 header.fragmentArguments = getIntent().getExtras();
490 mCurrentHeader = header;
498 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
499 int titleRes, int shortTitleRes) {
500 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
501 titleRes, shortTitleRes);
503 // Some fragments want split ActionBar; these should stay in sync with
504 // uiOptions for fragments also defined as activities in manifest.
505 if (WifiSettings.class.getName().equals(fragmentName) ||
506 WifiP2pSettings.class.getName().equals(fragmentName) ||
507 WifiDisplaySettings.class.getName().equals(fragmentName) ||
508 BluetoothSettings.class.getName().equals(fragmentName) ||
509 DreamSettings.class.getName().equals(fragmentName) ||
510 LocationSettings.class.getName().equals(fragmentName) ||
511 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
512 PrintSettingsFragment.class.getName().equals(fragmentName) ||
513 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
514 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
517 intent.setClass(this, SubSettings.class);
522 * Populate the activity with the top-level headers.
525 public void onBuildHeaders(List<Header> headers) {
526 if (!onIsHidingHeaders()) {
527 loadHeadersFromResource(R.xml.settings_headers, headers);
528 updateHeaderList(headers);
532 private void updateHeaderList(List<Header> target) {
533 final boolean showDev = mDevelopmentPreferences.getBoolean(
534 DevelopmentSettings.PREF_SHOW,
535 android.os.Build.TYPE.equals("eng"));
538 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
539 mHeaderIndexMap.clear();
540 while (i < target.size()) {
541 Header header = target.get(i);
542 // Ids are integers, so downcasting
543 int id = (int) header.id;
544 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
545 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
546 } else if (id == R.id.wifi_settings) {
547 // Remove WiFi Settings if WiFi service is not available.
548 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
551 } else if (id == R.id.bluetooth_settings) {
552 // Remove Bluetooth Settings if Bluetooth service is not available.
553 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
556 } else if (id == R.id.data_usage_settings) {
557 // Remove data usage when kernel module not enabled
558 final INetworkManagementService netManager = INetworkManagementService.Stub
559 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
561 if (!netManager.isBandwidthControlEnabled()) {
564 } catch (RemoteException e) {
567 } else if (id == R.id.battery_settings) {
568 // Remove battery settings when battery is not available. (e.g. TV)
570 if (!mBatteryPresent) {
573 } else if (id == R.id.account_settings) {
574 int headerIndex = i + 1;
575 i = insertAccountsHeaders(target, headerIndex);
576 } else if (id == R.id.home_settings) {
577 if (!updateHomeSettingHeaders(header)) {
580 } else if (id == R.id.user_settings) {
581 if (!UserHandle.MU_ENABLED
582 || !UserManager.supportsMultipleUsers()
583 || Utils.isMonkeyRunning()) {
586 } else if (id == R.id.nfc_payment_settings) {
587 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
590 // Only show if NFC is on and we have the HCE feature
591 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
592 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
593 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
597 } else if (id == R.id.development_settings) {
601 } else if (id == R.id.account_add) {
602 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
607 if (i < target.size() && target.get(i) == header
608 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
609 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
613 // Increment if the current one wasn't removed by the Utils code.
614 if (i < target.size() && target.get(i) == header) {
615 // Hold on to the first header, when we need to reset to the top-level
616 if (mFirstHeader == null &&
617 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
618 mFirstHeader = header;
620 mHeaderIndexMap.put(id, i);
626 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
627 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
628 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
629 for (String accountType : accountTypes) {
630 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
635 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
636 boolean skipToAccount = accounts.length == 1
637 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
638 Header accHeader = new Header();
639 accHeader.title = label;
640 if (accHeader.extras == null) {
641 accHeader.extras = new Bundle();
644 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
645 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
646 accHeader.fragment = AccountSyncSettings.class.getName();
647 accHeader.fragmentArguments = new Bundle();
648 // Need this for the icon
649 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
650 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
651 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
654 accHeader.breadCrumbTitle = label;
655 accHeader.breadCrumbShortTitle = label;
656 accHeader.fragment = ManageAccountsSettings.class.getName();
657 accHeader.fragmentArguments = new Bundle();
658 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
659 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
661 if (!isMultiPane()) {
662 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
666 accountHeaders.add(accHeader);
667 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
671 Collections.sort(accountHeaders, new Comparator<Header>() {
673 public int compare(Header h1, Header h2) {
674 return h1.title.toString().compareTo(h2.title.toString());
678 for (Header header : accountHeaders) {
679 target.add(headerIndex++, header);
681 if (!mListeningToAccountUpdates) {
682 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
683 mListeningToAccountUpdates = true;
688 private boolean updateHomeSettingHeaders(Header header) {
689 // Once we decide to show Home settings, keep showing it forever
690 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
691 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
696 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
697 getPackageManager().getHomeActivities(homeApps);
698 if (homeApps.size() < 2) {
699 // When there's only one available home app, omit this settings
700 // category entirely at the top level UI. If the user just
701 // uninstalled the penultimate home app candidiate, we also
702 // now tell them about why they aren't seeing 'Home' in the list.
703 if (sShowNoHomeNotice) {
704 sShowNoHomeNotice = false;
705 NoHomeDialogFragment.show(this);
709 // Okay, we're allowing the Home settings category. Tell it, when
710 // invoked via this front door, that we'll need to be told about the
711 // case when the user uninstalls all but one home app.
712 if (header.fragmentArguments == null) {
713 header.fragmentArguments = new Bundle();
715 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
717 } catch (Exception e) {
718 // Can't look up the home activity; bail on configuring the icon
719 Log.w(LOG_TAG, "Problem looking up home activity!", e);
722 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
726 private void getMetaData() {
728 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
729 PackageManager.GET_META_DATA);
730 if (ai == null || ai.metaData == null) return;
731 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
732 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
734 // Check if it has a parent specified and create a Header object
735 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
736 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
737 if (parentFragmentClass != null) {
738 mParentHeader = new Header();
739 mParentHeader.fragment = parentFragmentClass;
740 if (parentHeaderTitleRes != 0) {
741 mParentHeader.title = getResources().getString(parentHeaderTitleRes);
744 } catch (NameNotFoundException nnfe) {
750 public boolean hasNextButton() {
751 return super.hasNextButton();
755 public Button getNextButton() {
756 return super.getNextButton();
759 public static class NoHomeDialogFragment extends DialogFragment {
760 public static void show(Activity parent) {
761 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
762 dialog.show(parent.getFragmentManager(), null);
766 public Dialog onCreateDialog(Bundle savedInstanceState) {
767 return new AlertDialog.Builder(getActivity())
768 .setMessage(R.string.only_one_home_message)
769 .setPositiveButton(android.R.string.ok, null)
774 private static class HeaderAdapter extends ArrayAdapter<Header> {
775 static final int HEADER_TYPE_CATEGORY = 0;
776 static final int HEADER_TYPE_NORMAL = 1;
777 static final int HEADER_TYPE_SWITCH = 2;
778 static final int HEADER_TYPE_BUTTON = 3;
779 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
781 private final WifiEnabler mWifiEnabler;
782 private final BluetoothEnabler mBluetoothEnabler;
783 private AuthenticatorHelper mAuthHelper;
784 private DevicePolicyManager mDevicePolicyManager;
786 private static class HeaderViewHolder {
795 private LayoutInflater mInflater;
797 static int getHeaderType(Header header) {
798 if (header.fragment == null && header.intent == null) {
799 return HEADER_TYPE_CATEGORY;
800 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
801 return HEADER_TYPE_SWITCH;
802 } else if (header.id == R.id.security_settings) {
803 return HEADER_TYPE_BUTTON;
805 return HEADER_TYPE_NORMAL;
810 public int getItemViewType(int position) {
811 Header header = getItem(position);
812 return getHeaderType(header);
816 public boolean areAllItemsEnabled() {
817 return false; // because of categories
821 public boolean isEnabled(int position) {
822 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
826 public int getViewTypeCount() {
827 return HEADER_TYPE_COUNT;
831 public boolean hasStableIds() {
835 public HeaderAdapter(Context context, List<Header> objects,
836 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
837 super(context, 0, objects);
839 mAuthHelper = authenticatorHelper;
840 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
842 // Temp Switches provided as placeholder until the adapter replaces these with actual
843 // Switches inflated from their layouts. Must be done before adapter is set in super
844 mWifiEnabler = new WifiEnabler(context, new Switch(context));
845 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
846 mDevicePolicyManager = dpm;
850 public View getView(int position, View convertView, ViewGroup parent) {
851 HeaderViewHolder holder;
852 Header header = getItem(position);
853 int headerType = getHeaderType(header);
856 if (convertView == null) {
857 holder = new HeaderViewHolder();
858 switch (headerType) {
859 case HEADER_TYPE_CATEGORY:
860 view = new TextView(getContext(), null,
861 android.R.attr.listSeparatorTextViewStyle);
862 holder.title = (TextView) view;
865 case HEADER_TYPE_SWITCH:
866 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
868 holder.icon = (ImageView) view.findViewById(R.id.icon);
869 holder.title = (TextView)
870 view.findViewById(com.android.internal.R.id.title);
871 holder.summary = (TextView)
872 view.findViewById(com.android.internal.R.id.summary);
873 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
876 case HEADER_TYPE_BUTTON:
877 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
879 holder.icon = (ImageView) view.findViewById(R.id.icon);
880 holder.title = (TextView)
881 view.findViewById(com.android.internal.R.id.title);
882 holder.summary = (TextView)
883 view.findViewById(com.android.internal.R.id.summary);
884 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
885 holder.divider_ = view.findViewById(R.id.divider);
888 case HEADER_TYPE_NORMAL:
889 view = mInflater.inflate(
890 R.layout.preference_header_item, parent,
892 holder.icon = (ImageView) view.findViewById(R.id.icon);
893 holder.title = (TextView)
894 view.findViewById(com.android.internal.R.id.title);
895 holder.summary = (TextView)
896 view.findViewById(com.android.internal.R.id.summary);
902 holder = (HeaderViewHolder) view.getTag();
905 // All view fields must be updated every time, because the view may be recycled
906 switch (headerType) {
907 case HEADER_TYPE_CATEGORY:
908 holder.title.setText(header.getTitle(getContext().getResources()));
911 case HEADER_TYPE_SWITCH:
912 // Would need a different treatment if the main menu had more switches
913 if (header.id == R.id.wifi_settings) {
914 mWifiEnabler.setSwitch(holder.switch_);
916 mBluetoothEnabler.setSwitch(holder.switch_);
918 updateCommonHeaderView(header, holder);
921 case HEADER_TYPE_BUTTON:
922 if (header.id == R.id.security_settings) {
923 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
925 holder.button_.setVisibility(View.VISIBLE);
926 holder.divider_.setVisibility(View.VISIBLE);
927 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
929 holder.button_.setImageResource(R.drawable.ic_settings_about);
931 holder.button_.setImageResource(
932 android.R.drawable.stat_notify_error);
934 holder.button_.setOnClickListener(new OnClickListener() {
936 public void onClick(View v) {
937 Intent intent = new Intent(
938 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
939 getContext().startActivity(intent);
943 holder.button_.setVisibility(View.GONE);
944 holder.divider_.setVisibility(View.GONE);
947 updateCommonHeaderView(header, holder);
950 case HEADER_TYPE_NORMAL:
951 updateCommonHeaderView(header, holder);
958 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
959 if (header.extras != null
960 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
961 String accType = header.extras.getString(
962 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
963 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
964 setHeaderIcon(holder, icon);
966 holder.icon.setImageResource(header.iconRes);
968 holder.title.setText(header.getTitle(getContext().getResources()));
969 CharSequence summary = header.getSummary(getContext().getResources());
970 if (!TextUtils.isEmpty(summary)) {
971 holder.summary.setVisibility(View.VISIBLE);
972 holder.summary.setText(summary);
974 holder.summary.setVisibility(View.GONE);
978 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
979 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
980 lp.width = getContext().getResources().getDimensionPixelSize(
981 R.dimen.header_icon_width);
982 lp.height = lp.width;
983 holder.icon.setLayoutParams(lp);
984 holder.icon.setImageDrawable(icon);
987 public void resume() {
988 mWifiEnabler.resume();
989 mBluetoothEnabler.resume();
992 public void pause() {
993 mWifiEnabler.pause();
994 mBluetoothEnabler.pause();
999 public void onHeaderClick(Header header, int position) {
1000 boolean revert = false;
1001 if (header.id == R.id.account_add) {
1005 super.onHeaderClick(header, position);
1007 if (revert && mLastHeader != null) {
1008 highlightHeader((int) mLastHeader.id);
1010 mLastHeader = header;
1015 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
1016 // Override the fragment title for Wallpaper settings
1017 int titleRes = pref.getTitleRes();
1018 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
1019 titleRes = R.string.wallpaper_settings_fragment_title;
1020 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
1021 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
1022 if (UserManager.get(this).isLinkedUser()) {
1023 titleRes = R.string.profile_info_settings_title;
1025 titleRes = R.string.user_info_settings_title;
1028 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
1034 public boolean shouldUpRecreateTask(Intent targetIntent) {
1035 return super.shouldUpRecreateTask(new Intent(this, Settings.class));
1039 public void setListAdapter(ListAdapter adapter) {
1040 if (adapter == null) {
1041 super.setListAdapter(null);
1043 DevicePolicyManager dpm =
1044 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1045 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
1050 public void onAccountsUpdated(Account[] accounts) {
1051 // TODO: watch for package upgrades to invalidate cache; see 7206643
1052 mAuthenticatorHelper.updateAuthDescriptions(this);
1053 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1054 invalidateHeaders();
1057 public static void requestHomeNotice() {
1058 sShowNoHomeNotice = true;
1062 * Settings subclasses for launching independently.
1064 public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1065 public static class WirelessSettingsActivity extends Settings { /* empty */ }
1066 public static class TetherSettingsActivity extends Settings { /* empty */ }
1067 public static class VpnSettingsActivity extends Settings { /* empty */ }
1068 public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1069 public static class StorageSettingsActivity extends Settings { /* empty */ }
1070 public static class WifiSettingsActivity extends Settings { /* empty */ }
1071 public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
1072 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
1073 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
1074 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
1075 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
1076 public static class LocalePickerActivity extends Settings { /* empty */ }
1077 public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1078 public static class SoundSettingsActivity extends Settings { /* empty */ }
1079 public static class DisplaySettingsActivity extends Settings { /* empty */ }
1080 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1081 public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1082 public static class ManageApplicationsActivity extends Settings { /* empty */ }
1083 public static class AppOpsSummaryActivity extends Settings { /* empty */ }
1084 public static class StorageUseActivity extends Settings { /* empty */ }
1085 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1086 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
1087 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
1088 public static class SecuritySettingsActivity extends Settings { /* empty */ }
1089 public static class LocationSettingsActivity extends Settings { /* empty */ }
1090 public static class PrivacySettingsActivity extends Settings { /* empty */ }
1091 public static class RunningServicesActivity extends Settings { /* empty */ }
1092 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1093 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1094 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1095 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1096 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1097 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1098 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
1099 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
1100 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
1101 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
1102 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
1103 public static class DreamSettingsActivity extends Settings { /* empty */ }
1104 public static class NotificationStationActivity extends Settings { /* empty */ }
1105 public static class UserSettingsActivity extends Settings { /* empty */ }
1106 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
1107 public static class UsbSettingsActivity extends Settings { /* empty */ }
1108 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
1109 public static class PaymentSettingsActivity extends Settings { /* empty */ }
1110 public static class PrintSettingsActivity extends Settings { /* empty */ }