OSDN Git Service

merge in klp-release history after reset to klp-dev
[android-x86/packages-apps-Settings.git] / src / com / android / settings / Settings.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.settings;
18
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.accounts.OnAccountsUpdateListener;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.SharedPreferences;
29 import android.content.pm.ActivityInfo;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.PackageManager.NameNotFoundException;
33 import android.content.pm.ResolveInfo;
34 import android.graphics.drawable.Drawable;
35 import android.nfc.NfcAdapter;
36 import android.os.Bundle;
37 import android.os.INetworkManagementService;
38 import android.os.RemoteException;
39 import android.os.ServiceManager;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.preference.Preference;
43 import android.preference.PreferenceActivity;
44 import android.preference.PreferenceFragment;
45 import android.text.TextUtils;
46 import android.util.Log;
47 import android.view.LayoutInflater;
48 import android.view.View;
49 import android.view.View.OnClickListener;
50 import android.view.ViewGroup;
51 import android.widget.ArrayAdapter;
52 import android.widget.Button;
53 import android.widget.ImageButton;
54 import android.widget.ImageView;
55 import android.widget.ListAdapter;
56 import android.widget.Switch;
57 import android.widget.TextView;
58
59 import com.android.internal.util.ArrayUtils;
60 import com.android.settings.accessibility.AccessibilitySettings;
61 import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
62 import com.android.settings.accessibility.ToggleCaptioningPreferenceFragment;
63 import com.android.settings.accounts.AccountSyncSettings;
64 import com.android.settings.accounts.AuthenticatorHelper;
65 import com.android.settings.accounts.ManageAccountsSettings;
66 import com.android.settings.applications.AppOpsSummary;
67 import com.android.settings.applications.ManageApplications;
68 import com.android.settings.applications.ProcessStatsUi;
69 import com.android.settings.bluetooth.BluetoothEnabler;
70 import com.android.settings.bluetooth.BluetoothSettings;
71 import com.android.settings.deviceinfo.Memory;
72 import com.android.settings.deviceinfo.UsbSettings;
73 import com.android.settings.fuelgauge.PowerUsageSummary;
74 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
75 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
76 import com.android.settings.inputmethod.SpellCheckersSettings;
77 import com.android.settings.inputmethod.UserDictionaryList;
78 import com.android.settings.location.LocationSettings;
79 import com.android.settings.nfc.AndroidBeam;
80 import com.android.settings.nfc.PaymentSettings;
81 import com.android.settings.print.PrintServiceSettingsFragment;
82 import com.android.settings.print.PrintSettingsFragment;
83 import com.android.settings.tts.TextToSpeechSettings;
84 import com.android.settings.users.UserSettings;
85 import com.android.settings.vpn2.VpnSettings;
86 import com.android.settings.wfd.WifiDisplaySettings;
87 import com.android.settings.wifi.AdvancedWifiSettings;
88 import com.android.settings.wifi.WifiEnabler;
89 import com.android.settings.wifi.WifiSettings;
90 import com.android.settings.wifi.p2p.WifiP2pSettings;
91
92 import java.util.ArrayList;
93 import java.util.Collections;
94 import java.util.Comparator;
95 import java.util.HashMap;
96 import java.util.List;
97
98 /**
99  * Top-level settings activity to handle single pane and double pane UI layout.
100  */
101 public class Settings extends PreferenceActivity
102         implements ButtonBarHandler, OnAccountsUpdateListener {
103
104     private static final String LOG_TAG = "Settings";
105
106     private static final String META_DATA_KEY_HEADER_ID =
107         "com.android.settings.TOP_LEVEL_HEADER_ID";
108     private static final String META_DATA_KEY_FRAGMENT_CLASS =
109         "com.android.settings.FRAGMENT_CLASS";
110     private static final String META_DATA_KEY_PARENT_TITLE =
111         "com.android.settings.PARENT_FRAGMENT_TITLE";
112     private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
113         "com.android.settings.PARENT_FRAGMENT_CLASS";
114
115     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
116
117     private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
118     private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
119
120     private String mFragmentClass;
121     private int mTopLevelHeaderId;
122     private Header mFirstHeader;
123     private Header mCurrentHeader;
124     private Header mParentHeader;
125     private boolean mInLocalHeaderSwitch;
126
127     // Show only these settings for restricted users
128     private int[] SETTINGS_FOR_RESTRICTED = {
129             R.id.wireless_section,
130             R.id.wifi_settings,
131             R.id.bluetooth_settings,
132             R.id.data_usage_settings,
133             R.id.wireless_settings,
134             R.id.device_section,
135             R.id.sound_settings,
136             R.id.display_settings,
137             R.id.storage_settings,
138             R.id.application_settings,
139             R.id.battery_settings,
140             R.id.personal_section,
141             R.id.location_settings,
142             R.id.security_settings,
143             R.id.language_settings,
144             R.id.user_settings,
145             R.id.account_settings,
146             R.id.account_add,
147             R.id.system_section,
148             R.id.date_time_settings,
149             R.id.about_settings,
150             R.id.accessibility_settings,
151             R.id.print_settings,
152             R.id.nfc_payment_settings,
153             R.id.home_settings
154     };
155
156     private SharedPreferences mDevelopmentPreferences;
157     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
158
159     // TODO: Update Call Settings based on airplane mode state.
160
161     protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
162
163     private AuthenticatorHelper mAuthenticatorHelper;
164     private Header mLastHeader;
165     private boolean mListeningToAccountUpdates;
166
167     private boolean mBatteryPresent = true;
168     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
169
170         @Override
171         public void onReceive(Context context, Intent intent) {
172             String action = intent.getAction();
173             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
174                 boolean batteryPresent = Utils.isBatteryPresent(intent);
175
176                 if (mBatteryPresent != batteryPresent) {
177                     mBatteryPresent = batteryPresent;
178                     invalidateHeaders();
179                 }
180             }
181         }
182     };
183
184     @Override
185     protected void onCreate(Bundle savedInstanceState) {
186         if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
187             getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
188         }
189
190         mAuthenticatorHelper = new AuthenticatorHelper();
191         mAuthenticatorHelper.updateAuthDescriptions(this);
192         mAuthenticatorHelper.onAccountsUpdated(this, null);
193
194         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
195                 Context.MODE_PRIVATE);
196
197         getMetaData();
198         mInLocalHeaderSwitch = true;
199         super.onCreate(savedInstanceState);
200         mInLocalHeaderSwitch = false;
201
202         if (!onIsHidingHeaders() && onIsMultiPane()) {
203             highlightHeader(mTopLevelHeaderId);
204             // Force the title so that it doesn't get overridden by a direct launch of
205             // a specific settings screen.
206             setTitle(R.string.settings_label);
207         }
208
209         // Retrieve any saved state
210         if (savedInstanceState != null) {
211             mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
212             mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
213         }
214
215         // If the current header was saved, switch to it
216         if (savedInstanceState != null && mCurrentHeader != null) {
217             //switchToHeaderLocal(mCurrentHeader);
218             showBreadCrumbs(mCurrentHeader.title, null);
219         }
220
221         if (mParentHeader != null) {
222             setParentTitle(mParentHeader.title, null, new OnClickListener() {
223                 @Override
224                 public void onClick(View v) {
225                     switchToParent(mParentHeader.fragment);
226                 }
227             });
228         }
229
230         // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
231         if (onIsMultiPane()) {
232             getActionBar().setDisplayHomeAsUpEnabled(false);
233             getActionBar().setHomeButtonEnabled(false);
234         }
235     }
236
237     @Override
238     protected void onSaveInstanceState(Bundle outState) {
239         super.onSaveInstanceState(outState);
240
241         // Save the current fragment, if it is the same as originally launched
242         if (mCurrentHeader != null) {
243             outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
244         }
245         if (mParentHeader != null) {
246             outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
247         }
248     }
249
250     @Override
251     public void onResume() {
252         super.onResume();
253
254         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
255             @Override
256             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
257                 invalidateHeaders();
258             }
259         };
260         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
261                 mDevelopmentPreferencesListener);
262
263         ListAdapter listAdapter = getListAdapter();
264         if (listAdapter instanceof HeaderAdapter) {
265             ((HeaderAdapter) listAdapter).resume();
266         }
267         invalidateHeaders();
268
269         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
270     }
271
272     @Override
273     public void onPause() {
274         super.onPause();
275
276         unregisterReceiver(mBatteryInfoReceiver);
277
278         ListAdapter listAdapter = getListAdapter();
279         if (listAdapter instanceof HeaderAdapter) {
280             ((HeaderAdapter) listAdapter).pause();
281         }
282
283         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
284                 mDevelopmentPreferencesListener);
285         mDevelopmentPreferencesListener = null;
286     }
287
288     @Override
289     public void onDestroy() {
290         super.onDestroy();
291         if (mListeningToAccountUpdates) {
292             AccountManager.get(this).removeOnAccountsUpdatedListener(this);
293         }
294     }
295
296     @Override
297     public boolean onIsMultiPane() {
298         return false;
299     }
300
301     private static final String[] ENTRY_FRAGMENTS = {
302         WirelessSettings.class.getName(),
303         WifiSettings.class.getName(),
304         AdvancedWifiSettings.class.getName(),
305         BluetoothSettings.class.getName(),
306         TetherSettings.class.getName(),
307         WifiP2pSettings.class.getName(),
308         VpnSettings.class.getName(),
309         DateTimeSettings.class.getName(),
310         LocalePicker.class.getName(),
311         InputMethodAndLanguageSettings.class.getName(),
312         SpellCheckersSettings.class.getName(),
313         UserDictionaryList.class.getName(),
314         UserDictionarySettings.class.getName(),
315         SoundSettings.class.getName(),
316         DisplaySettings.class.getName(),
317         DeviceInfoSettings.class.getName(),
318         ManageApplications.class.getName(),
319         ProcessStatsUi.class.getName(),
320         NotificationStation.class.getName(),
321         AppOpsSummary.class.getName(),
322         LocationSettings.class.getName(),
323         SecuritySettings.class.getName(),
324         PrivacySettings.class.getName(),
325         DeviceAdminSettings.class.getName(),
326         AccessibilitySettings.class.getName(),
327         ToggleCaptioningPreferenceFragment.class.getName(),
328         TextToSpeechSettings.class.getName(),
329         Memory.class.getName(),
330         DevelopmentSettings.class.getName(),
331         UsbSettings.class.getName(),
332         AndroidBeam.class.getName(),
333         WifiDisplaySettings.class.getName(),
334         PowerUsageSummary.class.getName(),
335         AccountSyncSettings.class.getName(),
336         CryptKeeperSettings.class.getName(),
337         DataUsageSummary.class.getName(),
338         DreamSettings.class.getName(),
339         UserSettings.class.getName(),
340         NotificationAccessSettings.class.getName(),
341         ManageAccountsSettings.class.getName(),
342         PrintSettingsFragment.class.getName(),
343         TrustedCredentialsSettings.class.getName(),
344         PaymentSettings.class.getName(),
345         KeyboardLayoutPickerFragment.class.getName()
346     };
347
348     @Override
349     protected boolean isValidFragment(String fragmentName) {
350         // Almost all fragments are wrapped in this,
351         // except for a few that have their own activities.
352         for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
353             if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
354         }
355         return false;
356     }
357
358     private void switchToHeaderLocal(Header header) {
359         mInLocalHeaderSwitch = true;
360         switchToHeader(header);
361         mInLocalHeaderSwitch = false;
362     }
363
364     @Override
365     public void switchToHeader(Header header) {
366         if (!mInLocalHeaderSwitch) {
367             mCurrentHeader = null;
368             mParentHeader = null;
369         }
370         super.switchToHeader(header);
371     }
372
373     /**
374      * Switch to parent fragment and store the grand parent's info
375      * @param className name of the activity wrapper for the parent fragment.
376      */
377     private void switchToParent(String className) {
378         final ComponentName cn = new ComponentName(this, className);
379         try {
380             final PackageManager pm = getPackageManager();
381             final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
382
383             if (parentInfo != null && parentInfo.metaData != null) {
384                 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
385                 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
386                 Header parentHeader = new Header();
387                 parentHeader.fragment = fragmentClass;
388                 parentHeader.title = fragmentTitle;
389                 mCurrentHeader = parentHeader;
390
391                 switchToHeaderLocal(parentHeader);
392                 highlightHeader(mTopLevelHeaderId);
393
394                 mParentHeader = new Header();
395                 mParentHeader.fragment
396                         = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
397                 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
398             }
399         } catch (NameNotFoundException nnfe) {
400             Log.w(LOG_TAG, "Could not find parent activity : " + className);
401         }
402     }
403
404     @Override
405     public void onNewIntent(Intent intent) {
406         super.onNewIntent(intent);
407
408         // If it is not launched from history, then reset to top-level
409         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
410             if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
411                 switchToHeaderLocal(mFirstHeader);
412             }
413             getListView().setSelectionFromTop(0, 0);
414         }
415     }
416
417     private void highlightHeader(int id) {
418         if (id != 0) {
419             Integer index = mHeaderIndexMap.get(id);
420             if (index != null) {
421                 getListView().setItemChecked(index, true);
422                 if (isMultiPane()) {
423                     getListView().smoothScrollToPosition(index);
424                 }
425             }
426         }
427     }
428
429     @Override
430     public Intent getIntent() {
431         Intent superIntent = super.getIntent();
432         String startingFragment = getStartingFragmentClass(superIntent);
433         // This is called from super.onCreate, isMultiPane() is not yet reliable
434         // Do not use onIsHidingHeaders either, which relies itself on this method
435         if (startingFragment != null && !onIsMultiPane()) {
436             Intent modIntent = new Intent(superIntent);
437             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
438             Bundle args = superIntent.getExtras();
439             if (args != null) {
440                 args = new Bundle(args);
441             } else {
442                 args = new Bundle();
443             }
444             args.putParcelable("intent", superIntent);
445             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
446             return modIntent;
447         }
448         return superIntent;
449     }
450
451     /**
452      * Checks if the component name in the intent is different from the Settings class and
453      * returns the class name to load as a fragment.
454      */
455     protected String getStartingFragmentClass(Intent intent) {
456         if (mFragmentClass != null) return mFragmentClass;
457
458         String intentClass = intent.getComponent().getClassName();
459         if (intentClass.equals(getClass().getName())) return null;
460
461         if ("com.android.settings.ManageApplications".equals(intentClass)
462                 || "com.android.settings.RunningServices".equals(intentClass)
463                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
464             // Old names of manage apps.
465             intentClass = com.android.settings.applications.ManageApplications.class.getName();
466         }
467
468         return intentClass;
469     }
470
471     /**
472      * Override initial header when an activity-alias is causing Settings to be launched
473      * for a specific fragment encoded in the android:name parameter.
474      */
475     @Override
476     public Header onGetInitialHeader() {
477         String fragmentClass = getStartingFragmentClass(super.getIntent());
478         if (fragmentClass != null) {
479             Header header = new Header();
480             header.fragment = fragmentClass;
481             header.title = getTitle();
482             header.fragmentArguments = getIntent().getExtras();
483             mCurrentHeader = header;
484             return header;
485         }
486
487         return mFirstHeader;
488     }
489
490     @Override
491     public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
492             int titleRes, int shortTitleRes) {
493         Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
494                 titleRes, shortTitleRes);
495
496         // Some fragments want split ActionBar; these should stay in sync with
497         // uiOptions for fragments also defined as activities in manifest.
498         if (WifiSettings.class.getName().equals(fragmentName) ||
499                 WifiP2pSettings.class.getName().equals(fragmentName) ||
500                 WifiDisplaySettings.class.getName().equals(fragmentName) ||
501                 BluetoothSettings.class.getName().equals(fragmentName) ||
502                 DreamSettings.class.getName().equals(fragmentName) ||
503                 LocationSettings.class.getName().equals(fragmentName) ||
504                 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
505                 PrintSettingsFragment.class.getName().equals(fragmentName) ||
506                 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
507             intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
508         }
509
510         intent.setClass(this, SubSettings.class);
511         return intent;
512     }
513
514     /**
515      * Populate the activity with the top-level headers.
516      */
517     @Override
518     public void onBuildHeaders(List<Header> headers) {
519         loadHeadersFromResource(R.xml.settings_headers, headers);
520         updateHeaderList(headers);
521     }
522
523     private void updateHeaderList(List<Header> target) {
524         final boolean showDev = mDevelopmentPreferences.getBoolean(
525                 DevelopmentSettings.PREF_SHOW,
526                 android.os.Build.TYPE.equals("eng"));
527         int i = 0;
528
529         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
530         mHeaderIndexMap.clear();
531         while (i < target.size()) {
532             Header header = target.get(i);
533             // Ids are integers, so downcasting
534             int id = (int) header.id;
535             if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
536                 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
537             } else if (id == R.id.wifi_settings) {
538                 // Remove WiFi Settings if WiFi service is not available.
539                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
540                     target.remove(i);
541                 }
542             } else if (id == R.id.bluetooth_settings) {
543                 // Remove Bluetooth Settings if Bluetooth service is not available.
544                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
545                     target.remove(i);
546                 }
547             } else if (id == R.id.data_usage_settings) {
548                 // Remove data usage when kernel module not enabled
549                 final INetworkManagementService netManager = INetworkManagementService.Stub
550                         .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
551                 try {
552                     if (!netManager.isBandwidthControlEnabled()) {
553                         target.remove(i);
554                     }
555                 } catch (RemoteException e) {
556                     // ignored
557                 }
558             } else if (id == R.id.battery_settings) {
559                 // Remove battery settings when battery is not available. (e.g. TV)
560
561                 if (!mBatteryPresent) {
562                     target.remove(i);
563                 }
564             } else if (id == R.id.account_settings) {
565                 int headerIndex = i + 1;
566                 i = insertAccountsHeaders(target, headerIndex);
567             } else if (id == R.id.home_settings) {
568                 if (!updateHomeSettingHeaders(header)) {
569                     target.remove(i);
570                 }
571             } else if (id == R.id.user_settings) {
572                 if (!UserHandle.MU_ENABLED
573                         || !UserManager.supportsMultipleUsers()
574                         || Utils.isMonkeyRunning()) {
575                     target.remove(i);
576                 }
577             } else if (id == R.id.nfc_payment_settings) {
578                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
579                     target.remove(i);
580                 } else {
581                     // Only show if NFC is on and we have the HCE feature
582                     NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
583                     if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
584                             PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
585                         target.remove(i);
586                     }
587                 }
588             } else if (id == R.id.development_settings) {
589                 if (!showDev) {
590                     target.remove(i);
591                 }
592             } else if (id == R.id.account_add) {
593                 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
594                     target.remove(i);
595                 }
596             }
597
598             if (i < target.size() && target.get(i) == header
599                     && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
600                     && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
601                 target.remove(i);
602             }
603
604             // Increment if the current one wasn't removed by the Utils code.
605             if (i < target.size() && target.get(i) == header) {
606                 // Hold on to the first header, when we need to reset to the top-level
607                 if (mFirstHeader == null &&
608                         HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
609                     mFirstHeader = header;
610                 }
611                 mHeaderIndexMap.put(id, i);
612                 i++;
613             }
614         }
615     }
616
617     private int insertAccountsHeaders(List<Header> target, int headerIndex) {
618         String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
619         List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
620         for (String accountType : accountTypes) {
621             CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
622             if (label == null) {
623                 continue;
624             }
625
626             Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
627             boolean skipToAccount = accounts.length == 1
628                     && !mAuthenticatorHelper.hasAccountPreferences(accountType);
629             Header accHeader = new Header();
630             accHeader.title = label;
631             if (accHeader.extras == null) {
632                 accHeader.extras = new Bundle();
633             }
634             if (skipToAccount) {
635                 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
636                 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
637                 accHeader.fragment = AccountSyncSettings.class.getName();
638                 accHeader.fragmentArguments = new Bundle();
639                 // Need this for the icon
640                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
641                 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
642                 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
643                         accounts[0]);
644             } else {
645                 accHeader.breadCrumbTitle = label;
646                 accHeader.breadCrumbShortTitle = label;
647                 accHeader.fragment = ManageAccountsSettings.class.getName();
648                 accHeader.fragmentArguments = new Bundle();
649                 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
650                 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
651                         accountType);
652                 if (!isMultiPane()) {
653                     accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
654                             label.toString());
655                 }
656             }
657             accountHeaders.add(accHeader);
658         }
659
660         // Sort by label
661         Collections.sort(accountHeaders, new Comparator<Header>() {
662             @Override
663             public int compare(Header h1, Header h2) {
664                 return h1.title.toString().compareTo(h2.title.toString());
665             }
666         });
667
668         for (Header header : accountHeaders) {
669             target.add(headerIndex++, header);
670         }
671         if (!mListeningToAccountUpdates) {
672             AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
673             mListeningToAccountUpdates = true;
674         }
675         return headerIndex;
676     }
677
678     private boolean updateHomeSettingHeaders(Header header) {
679         try {
680             final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
681             getPackageManager().getHomeActivities(homeApps);
682             if (homeApps.size() < 2) {
683                 // When there's only one available home app, omit this settings
684                 // category entirely at the top level UI.
685                 return false;
686             }
687         } catch (Exception e) {
688             // Can't look up the home activity; bail on configuring the icon
689             Log.w(LOG_TAG, "Problem looking up home activity!", e);
690         }
691         return true;
692     }
693
694     private void getMetaData() {
695         try {
696             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
697                     PackageManager.GET_META_DATA);
698             if (ai == null || ai.metaData == null) return;
699             mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
700             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
701
702             // Check if it has a parent specified and create a Header object
703             final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
704             String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
705             if (parentFragmentClass != null) {
706                 mParentHeader = new Header();
707                 mParentHeader.fragment = parentFragmentClass;
708                 if (parentHeaderTitleRes != 0) {
709                     mParentHeader.title = getResources().getString(parentHeaderTitleRes);
710                 }
711             }
712         } catch (NameNotFoundException nnfe) {
713             // No recovery
714         }
715     }
716
717     @Override
718     public boolean hasNextButton() {
719         return super.hasNextButton();
720     }
721
722     @Override
723     public Button getNextButton() {
724         return super.getNextButton();
725     }
726
727     private static class HeaderAdapter extends ArrayAdapter<Header> {
728         static final int HEADER_TYPE_CATEGORY = 0;
729         static final int HEADER_TYPE_NORMAL = 1;
730         static final int HEADER_TYPE_SWITCH = 2;
731         static final int HEADER_TYPE_BUTTON = 3;
732         private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
733
734         private final WifiEnabler mWifiEnabler;
735         private final BluetoothEnabler mBluetoothEnabler;
736         private AuthenticatorHelper mAuthHelper;
737         private DevicePolicyManager mDevicePolicyManager;
738
739         private static class HeaderViewHolder {
740             ImageView icon;
741             TextView title;
742             TextView summary;
743             Switch switch_;
744             ImageButton button_;
745             View divider_;
746         }
747
748         private LayoutInflater mInflater;
749
750         static int getHeaderType(Header header) {
751             if (header.fragment == null && header.intent == null) {
752                 return HEADER_TYPE_CATEGORY;
753             } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
754                 return HEADER_TYPE_SWITCH;
755             } else if (header.id == R.id.security_settings) {
756                 return HEADER_TYPE_BUTTON;
757             } else {
758                 return HEADER_TYPE_NORMAL;
759             }
760         }
761
762         @Override
763         public int getItemViewType(int position) {
764             Header header = getItem(position);
765             return getHeaderType(header);
766         }
767
768         @Override
769         public boolean areAllItemsEnabled() {
770             return false; // because of categories
771         }
772
773         @Override
774         public boolean isEnabled(int position) {
775             return getItemViewType(position) != HEADER_TYPE_CATEGORY;
776         }
777
778         @Override
779         public int getViewTypeCount() {
780             return HEADER_TYPE_COUNT;
781         }
782
783         @Override
784         public boolean hasStableIds() {
785             return true;
786         }
787
788         public HeaderAdapter(Context context, List<Header> objects,
789                 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
790             super(context, 0, objects);
791
792             mAuthHelper = authenticatorHelper;
793             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
794
795             // Temp Switches provided as placeholder until the adapter replaces these with actual
796             // Switches inflated from their layouts. Must be done before adapter is set in super
797             mWifiEnabler = new WifiEnabler(context, new Switch(context));
798             mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
799             mDevicePolicyManager = dpm;
800         }
801
802         @Override
803         public View getView(int position, View convertView, ViewGroup parent) {
804             HeaderViewHolder holder;
805             Header header = getItem(position);
806             int headerType = getHeaderType(header);
807             View view = null;
808
809             if (convertView == null) {
810                 holder = new HeaderViewHolder();
811                 switch (headerType) {
812                     case HEADER_TYPE_CATEGORY:
813                         view = new TextView(getContext(), null,
814                                 android.R.attr.listSeparatorTextViewStyle);
815                         holder.title = (TextView) view;
816                         break;
817
818                     case HEADER_TYPE_SWITCH:
819                         view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
820                                 false);
821                         holder.icon = (ImageView) view.findViewById(R.id.icon);
822                         holder.title = (TextView)
823                                 view.findViewById(com.android.internal.R.id.title);
824                         holder.summary = (TextView)
825                                 view.findViewById(com.android.internal.R.id.summary);
826                         holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
827                         break;
828
829                     case HEADER_TYPE_BUTTON:
830                         view = mInflater.inflate(R.layout.preference_header_button_item, parent,
831                                 false);
832                         holder.icon = (ImageView) view.findViewById(R.id.icon);
833                         holder.title = (TextView)
834                                 view.findViewById(com.android.internal.R.id.title);
835                         holder.summary = (TextView)
836                                 view.findViewById(com.android.internal.R.id.summary);
837                         holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
838                         holder.divider_ = view.findViewById(R.id.divider);
839                         break;
840
841                     case HEADER_TYPE_NORMAL:
842                         view = mInflater.inflate(
843                                 R.layout.preference_header_item, parent,
844                                 false);
845                         holder.icon = (ImageView) view.findViewById(R.id.icon);
846                         holder.title = (TextView)
847                                 view.findViewById(com.android.internal.R.id.title);
848                         holder.summary = (TextView)
849                                 view.findViewById(com.android.internal.R.id.summary);
850                         break;
851                 }
852                 view.setTag(holder);
853             } else {
854                 view = convertView;
855                 holder = (HeaderViewHolder) view.getTag();
856             }
857
858             // All view fields must be updated every time, because the view may be recycled
859             switch (headerType) {
860                 case HEADER_TYPE_CATEGORY:
861                     holder.title.setText(header.getTitle(getContext().getResources()));
862                     break;
863
864                 case HEADER_TYPE_SWITCH:
865                     // Would need a different treatment if the main menu had more switches
866                     if (header.id == R.id.wifi_settings) {
867                         mWifiEnabler.setSwitch(holder.switch_);
868                     } else {
869                         mBluetoothEnabler.setSwitch(holder.switch_);
870                     }
871                     updateCommonHeaderView(header, holder);
872                     break;
873
874                 case HEADER_TYPE_BUTTON:
875                     if (header.id == R.id.security_settings) {
876                         boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
877                         if (hasCert) {
878                             holder.button_.setVisibility(View.VISIBLE);
879                             holder.divider_.setVisibility(View.VISIBLE);
880                             boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
881                             if (isManaged) {
882                                 holder.button_.setImageResource(R.drawable.ic_settings_about);
883                             } else {
884                                 holder.button_.setImageResource(
885                                         android.R.drawable.stat_notify_error);
886                             }
887                             holder.button_.setOnClickListener(new OnClickListener() {
888                                 @Override
889                                 public void onClick(View v) {
890                                     Intent intent = new Intent(
891                                             android.provider.Settings.ACTION_MONITORING_CERT_INFO);
892                                     getContext().startActivity(intent);
893                                 }
894                             });
895                         } else {
896                             holder.button_.setVisibility(View.GONE);
897                             holder.divider_.setVisibility(View.GONE);
898                         }
899                     }
900                     updateCommonHeaderView(header, holder);
901                     break;
902
903                 case HEADER_TYPE_NORMAL:
904                     updateCommonHeaderView(header, holder);
905                     break;
906             }
907
908             return view;
909         }
910
911         private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
912                 if (header.extras != null
913                         && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
914                     String accType = header.extras.getString(
915                             ManageAccountsSettings.KEY_ACCOUNT_TYPE);
916                     Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
917                     setHeaderIcon(holder, icon);
918                 } else if (header.extras != null &&
919                         header.extras.containsKey(HomeSettings.CURRENT_HOME)) {
920                     ActivityInfo ai = header.extras.getParcelable(HomeSettings.CURRENT_HOME);
921                     Drawable icon = ai.loadIcon(getContext().getPackageManager());
922                     setHeaderIcon(holder, icon);
923                 } else {
924                     holder.icon.setImageResource(header.iconRes);
925                 }
926                 holder.title.setText(header.getTitle(getContext().getResources()));
927                 CharSequence summary = header.getSummary(getContext().getResources());
928                 if (!TextUtils.isEmpty(summary)) {
929                     holder.summary.setVisibility(View.VISIBLE);
930                     holder.summary.setText(summary);
931                 } else {
932                     holder.summary.setVisibility(View.GONE);
933                 }
934             }
935
936         private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
937             ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
938             lp.width = getContext().getResources().getDimensionPixelSize(
939                     R.dimen.header_icon_width);
940             lp.height = lp.width;
941             holder.icon.setLayoutParams(lp);
942             holder.icon.setImageDrawable(icon);
943         }
944
945         public void resume() {
946             mWifiEnabler.resume();
947             mBluetoothEnabler.resume();
948         }
949
950         public void pause() {
951             mWifiEnabler.pause();
952             mBluetoothEnabler.pause();
953         }
954     }
955
956     @Override
957     public void onHeaderClick(Header header, int position) {
958         boolean revert = false;
959         if (header.id == R.id.account_add) {
960             revert = true;
961         }
962
963         super.onHeaderClick(header, position);
964
965         if (revert && mLastHeader != null) {
966             highlightHeader((int) mLastHeader.id);
967         } else {
968             mLastHeader = header;
969         }
970     }
971
972     @Override
973     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
974         // Override the fragment title for Wallpaper settings
975         int titleRes = pref.getTitleRes();
976         if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
977             titleRes = R.string.wallpaper_settings_fragment_title;
978         } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
979                 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
980             if (UserManager.get(this).isLinkedUser()) {
981                 titleRes = R.string.profile_info_settings_title;
982             } else {
983                 titleRes = R.string.user_info_settings_title;
984             }
985         }
986         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
987                 null, 0);
988         return true;
989     }
990
991     @Override
992     public boolean shouldUpRecreateTask(Intent targetIntent) {
993         return super.shouldUpRecreateTask(new Intent(this, Settings.class));
994     }
995
996     @Override
997     public void setListAdapter(ListAdapter adapter) {
998         if (adapter == null) {
999             super.setListAdapter(null);
1000         } else {
1001             DevicePolicyManager dpm =
1002                     (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1003             super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
1004         }
1005     }
1006
1007     @Override
1008     public void onAccountsUpdated(Account[] accounts) {
1009         // TODO: watch for package upgrades to invalidate cache; see 7206643
1010         mAuthenticatorHelper.updateAuthDescriptions(this);
1011         mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1012         invalidateHeaders();
1013     }
1014
1015     /*
1016      * Settings subclasses for launching independently.
1017      */
1018     public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1019     public static class WirelessSettingsActivity extends Settings { /* empty */ }
1020     public static class TetherSettingsActivity extends Settings { /* empty */ }
1021     public static class VpnSettingsActivity extends Settings { /* empty */ }
1022     public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1023     public static class StorageSettingsActivity extends Settings { /* empty */ }
1024     public static class WifiSettingsActivity extends Settings { /* empty */ }
1025     public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
1026     public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
1027     public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
1028     public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
1029     public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
1030     public static class LocalePickerActivity extends Settings { /* empty */ }
1031     public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1032     public static class SoundSettingsActivity extends Settings { /* empty */ }
1033     public static class DisplaySettingsActivity extends Settings { /* empty */ }
1034     public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1035     public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1036     public static class ManageApplicationsActivity extends Settings { /* empty */ }
1037     public static class AppOpsSummaryActivity extends Settings { /* empty */ }
1038     public static class StorageUseActivity extends Settings { /* empty */ }
1039     public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1040     public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
1041     public static class CaptioningSettingsActivity extends Settings { /* empty */ }
1042     public static class SecuritySettingsActivity extends Settings { /* empty */ }
1043     public static class LocationSettingsActivity extends Settings { /* empty */ }
1044     public static class PrivacySettingsActivity extends Settings { /* empty */ }
1045     public static class RunningServicesActivity extends Settings { /* empty */ }
1046     public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1047     public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1048     public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1049     public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1050     public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1051     public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1052     public static class DataUsageSummaryActivity extends Settings { /* empty */ }
1053     public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
1054     public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
1055     public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
1056     public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
1057     public static class DreamSettingsActivity extends Settings { /* empty */ }
1058     public static class NotificationStationActivity extends Settings { /* empty */ }
1059     public static class UserSettingsActivity extends Settings { /* empty */ }
1060     public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
1061     public static class UsbSettingsActivity extends Settings { /* empty */ }
1062     public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
1063     public static class PaymentSettingsActivity extends Settings { /* empty */ }
1064     public static class PrintSettingsActivity extends Settings { /* empty */ }
1065 }