OSDN Git Service

Activity for messaging installed CA certs, and icon in Settings.
[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.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;
57
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;
90
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;
96
97 /**
98  * Top-level settings activity to handle single pane and double pane UI layout.
99  */
100 public class Settings extends PreferenceActivity
101         implements ButtonBarHandler, OnAccountsUpdateListener {
102
103     private static final String LOG_TAG = "Settings";
104
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";
113
114     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
115
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";
118
119     private String mFragmentClass;
120     private int mTopLevelHeaderId;
121     private Header mFirstHeader;
122     private Header mCurrentHeader;
123     private Header mParentHeader;
124     private boolean mInLocalHeaderSwitch;
125
126     // Show only these settings for restricted users
127     private int[] SETTINGS_FOR_RESTRICTED = {
128             R.id.wireless_section,
129             R.id.wifi_settings,
130             R.id.bluetooth_settings,
131             R.id.data_usage_settings,
132             R.id.wireless_settings,
133             R.id.device_section,
134             R.id.sound_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,
143             R.id.user_settings,
144             R.id.account_settings,
145             R.id.account_add,
146             R.id.system_section,
147             R.id.date_time_settings,
148             R.id.about_settings,
149             R.id.accessibility_settings,
150             R.id.print_settings,
151             R.id.nfc_payment_settings,
152             R.id.home_settings
153     };
154
155     private SharedPreferences mDevelopmentPreferences;
156     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
157
158     // TODO: Update Call Settings based on airplane mode state.
159
160     protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
161
162     private AuthenticatorHelper mAuthenticatorHelper;
163     private Header mLastHeader;
164     private boolean mListeningToAccountUpdates;
165
166     private boolean mBatteryPresent = true;
167     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
168
169         @Override
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);
174
175                 if (mBatteryPresent != batteryPresent) {
176                     mBatteryPresent = batteryPresent;
177                     invalidateHeaders();
178                 }
179             }
180         }
181     };
182
183     @Override
184     protected void onCreate(Bundle savedInstanceState) {
185         if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
186             getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
187         }
188
189         mAuthenticatorHelper = new AuthenticatorHelper();
190         mAuthenticatorHelper.updateAuthDescriptions(this);
191         mAuthenticatorHelper.onAccountsUpdated(this, null);
192
193         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
194                 Context.MODE_PRIVATE);
195
196         getMetaData();
197         mInLocalHeaderSwitch = true;
198         super.onCreate(savedInstanceState);
199         mInLocalHeaderSwitch = false;
200
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);
206         }
207
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);
212         }
213
214         // If the current header was saved, switch to it
215         if (savedInstanceState != null && mCurrentHeader != null) {
216             //switchToHeaderLocal(mCurrentHeader);
217             showBreadCrumbs(mCurrentHeader.title, null);
218         }
219
220         if (mParentHeader != null) {
221             setParentTitle(mParentHeader.title, null, new OnClickListener() {
222                 @Override
223                 public void onClick(View v) {
224                     switchToParent(mParentHeader.fragment);
225                 }
226             });
227         }
228
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);
233         }
234     }
235
236     @Override
237     protected void onSaveInstanceState(Bundle outState) {
238         super.onSaveInstanceState(outState);
239
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);
243         }
244         if (mParentHeader != null) {
245             outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
246         }
247     }
248
249     @Override
250     public void onResume() {
251         super.onResume();
252
253         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
254             @Override
255             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
256                 invalidateHeaders();
257             }
258         };
259         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
260                 mDevelopmentPreferencesListener);
261
262         ListAdapter listAdapter = getListAdapter();
263         if (listAdapter instanceof HeaderAdapter) {
264             ((HeaderAdapter) listAdapter).resume();
265         }
266         invalidateHeaders();
267
268         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
269     }
270
271     @Override
272     public void onPause() {
273         super.onPause();
274
275         unregisterReceiver(mBatteryInfoReceiver);
276
277         ListAdapter listAdapter = getListAdapter();
278         if (listAdapter instanceof HeaderAdapter) {
279             ((HeaderAdapter) listAdapter).pause();
280         }
281
282         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
283                 mDevelopmentPreferencesListener);
284         mDevelopmentPreferencesListener = null;
285     }
286
287     @Override
288     public void onDestroy() {
289         super.onDestroy();
290         if (mListeningToAccountUpdates) {
291             AccountManager.get(this).removeOnAccountsUpdatedListener(this);
292         }
293     }
294
295     @Override
296     public boolean onIsMultiPane() {
297         return false;
298     }
299
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()
345     };
346
347     @Override
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;
353         }
354         return false;
355     }
356
357     private void switchToHeaderLocal(Header header) {
358         mInLocalHeaderSwitch = true;
359         switchToHeader(header);
360         mInLocalHeaderSwitch = false;
361     }
362
363     @Override
364     public void switchToHeader(Header header) {
365         if (!mInLocalHeaderSwitch) {
366             mCurrentHeader = null;
367             mParentHeader = null;
368         }
369         super.switchToHeader(header);
370     }
371
372     /**
373      * Switch to parent fragment and store the grand parent's info
374      * @param className name of the activity wrapper for the parent fragment.
375      */
376     private void switchToParent(String className) {
377         final ComponentName cn = new ComponentName(this, className);
378         try {
379             final PackageManager pm = getPackageManager();
380             final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
381
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;
389
390                 switchToHeaderLocal(parentHeader);
391                 highlightHeader(mTopLevelHeaderId);
392
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);
397             }
398         } catch (NameNotFoundException nnfe) {
399             Log.w(LOG_TAG, "Could not find parent activity : " + className);
400         }
401     }
402
403     @Override
404     public void onNewIntent(Intent intent) {
405         super.onNewIntent(intent);
406
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);
411             }
412             getListView().setSelectionFromTop(0, 0);
413         }
414     }
415
416     private void highlightHeader(int id) {
417         if (id != 0) {
418             Integer index = mHeaderIndexMap.get(id);
419             if (index != null) {
420                 getListView().setItemChecked(index, true);
421                 if (isMultiPane()) {
422                     getListView().smoothScrollToPosition(index);
423                 }
424             }
425         }
426     }
427
428     @Override
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();
438             if (args != null) {
439                 args = new Bundle(args);
440             } else {
441                 args = new Bundle();
442             }
443             args.putParcelable("intent", superIntent);
444             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
445             return modIntent;
446         }
447         return superIntent;
448     }
449
450     /**
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.
453      */
454     protected String getStartingFragmentClass(Intent intent) {
455         if (mFragmentClass != null) return mFragmentClass;
456
457         String intentClass = intent.getComponent().getClassName();
458         if (intentClass.equals(getClass().getName())) return null;
459
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();
465         }
466
467         return intentClass;
468     }
469
470     /**
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.
473      */
474     @Override
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;
483             return header;
484         }
485
486         return mFirstHeader;
487     }
488
489     @Override
490     public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
491             int titleRes, int shortTitleRes) {
492         Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
493                 titleRes, shortTitleRes);
494
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);
507         }
508
509         intent.setClass(this, SubSettings.class);
510         return intent;
511     }
512
513     /**
514      * Populate the activity with the top-level headers.
515      */
516     @Override
517     public void onBuildHeaders(List<Header> headers) {
518         loadHeadersFromResource(R.xml.settings_headers, headers);
519         updateHeaderList(headers);
520     }
521
522     private void updateHeaderList(List<Header> target) {
523         final boolean showDev = mDevelopmentPreferences.getBoolean(
524                 DevelopmentSettings.PREF_SHOW,
525                 android.os.Build.TYPE.equals("eng"));
526         int i = 0;
527
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)) {
539                     target.remove(i);
540                 }
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)) {
544                     target.remove(i);
545                 }
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));
550                 try {
551                     if (!netManager.isBandwidthControlEnabled()) {
552                         target.remove(i);
553                     }
554                 } catch (RemoteException e) {
555                     // ignored
556                 }
557             } else if (id == R.id.battery_settings) {
558                 // Remove battery settings when battery is not available. (e.g. TV)
559
560                 if (!mBatteryPresent) {
561                     target.remove(i);
562                 }
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()) {
572                     target.remove(i);
573                 }
574             } else if (id == R.id.nfc_payment_settings) {
575                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
576                     target.remove(i);
577                 }
578             } else if (id == R.id.development_settings) {
579                 if (!showDev) {
580                     target.remove(i);
581                 }
582             } else if (id == R.id.account_add) {
583                 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
584                     target.remove(i);
585                 }
586             }
587
588             if (i < target.size() && target.get(i) == header
589                     && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
590                     && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
591                 target.remove(i);
592             }
593
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;
600                 }
601                 mHeaderIndexMap.put(id, i);
602                 i++;
603             }
604         }
605     }
606
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);
612             if (label == null) {
613                 continue;
614             }
615
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();
623             }
624             if (skipToAccount) {
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,
633                         accounts[0]);
634             } else {
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,
641                         accountType);
642                 if (!isMultiPane()) {
643                     accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
644                             label.toString());
645                 }
646             }
647             accountHeaders.add(accHeader);
648         }
649
650         // Sort by label
651         Collections.sort(accountHeaders, new Comparator<Header>() {
652             @Override
653             public int compare(Header h1, Header h2) {
654                 return h1.title.toString().compareTo(h2.title.toString());
655             }
656         });
657
658         for (Header header : accountHeaders) {
659             target.add(headerIndex++, header);
660         }
661         if (!mListeningToAccountUpdates) {
662             AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
663             mListeningToAccountUpdates = true;
664         }
665         return headerIndex;
666     }
667
668     private boolean isSystemApp(ResolveInfo ri) {
669         return ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
670     }
671
672     private void updateHomeSettingHeaders(Header header) {
673         final PackageManager pm = getPackageManager();
674         final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
675         try {
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)) {
683                         iconSource = ri;
684                         break;
685                     }
686                 }
687             } else {
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)) {
694                         iconSource = ri;
695                         break;
696                     }
697                 }
698             }
699             if (iconSource != null) {
700                 if (header.extras == null) {
701                     header.extras = new Bundle();
702                 }
703                 header.extras.putParcelable(HomeSettings.CURRENT_HOME, iconSource.activityInfo);
704             } else {
705                 Log.v(LOG_TAG, "No home app icon found");
706             }
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);
710         }
711     }
712
713     private void getMetaData() {
714         try {
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);
720
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);
729                 }
730             }
731         } catch (NameNotFoundException nnfe) {
732             // No recovery
733         }
734     }
735
736     @Override
737     public boolean hasNextButton() {
738         return super.hasNextButton();
739     }
740
741     @Override
742     public Button getNextButton() {
743         return super.getNextButton();
744     }
745
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;
752
753         private final WifiEnabler mWifiEnabler;
754         private final BluetoothEnabler mBluetoothEnabler;
755         private AuthenticatorHelper mAuthHelper;
756         private DevicePolicyManager mDevicePolicyManager;
757
758         private static class HeaderViewHolder {
759             ImageView icon;
760             TextView title;
761             TextView summary;
762             Switch switch_;
763             ImageButton button_;
764             View divider_;
765         }
766
767         private LayoutInflater mInflater;
768
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;
776             } else {
777                 return HEADER_TYPE_NORMAL;
778             }
779         }
780
781         @Override
782         public int getItemViewType(int position) {
783             Header header = getItem(position);
784             return getHeaderType(header);
785         }
786
787         @Override
788         public boolean areAllItemsEnabled() {
789             return false; // because of categories
790         }
791
792         @Override
793         public boolean isEnabled(int position) {
794             return getItemViewType(position) != HEADER_TYPE_CATEGORY;
795         }
796
797         @Override
798         public int getViewTypeCount() {
799             return HEADER_TYPE_COUNT;
800         }
801
802         @Override
803         public boolean hasStableIds() {
804             return true;
805         }
806
807         public HeaderAdapter(Context context, List<Header> objects,
808                 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
809             super(context, 0, objects);
810
811             mAuthHelper = authenticatorHelper;
812             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
813
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;
819         }
820
821         @Override
822         public View getView(int position, View convertView, ViewGroup parent) {
823             HeaderViewHolder holder;
824             Header header = getItem(position);
825             int headerType = getHeaderType(header);
826             View view = null;
827
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;
835                         break;
836
837                     case HEADER_TYPE_SWITCH:
838                         view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
839                                 false);
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);
846                         break;
847
848                     case HEADER_TYPE_BUTTON:
849                         view = mInflater.inflate(R.layout.preference_header_button_item, parent,
850                                 false);
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);
858                         break;
859
860                     case HEADER_TYPE_NORMAL:
861                         view = mInflater.inflate(
862                                 R.layout.preference_header_item, parent,
863                                 false);
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);
869                         break;
870                 }
871                 view.setTag(holder);
872             } else {
873                 view = convertView;
874                 holder = (HeaderViewHolder) view.getTag();
875             }
876
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()));
881                     break;
882
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_);
887                     } else {
888                         mBluetoothEnabler.setSwitch(holder.switch_);
889                     }
890                     updateCommonHeaderView(header, holder);
891                     break;
892
893                 case HEADER_TYPE_BUTTON:
894                     if (header.id == R.id.security_settings) {
895                         boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
896                         if (hasCert) {
897                             holder.button_.setVisibility(View.VISIBLE);
898                             holder.divider_.setVisibility(View.VISIBLE);
899                             boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
900                             if (isManaged) {
901                                 holder.button_.setImageResource(R.drawable.ic_qs_certificate_info);
902                             } else {
903                                 holder.button_.setImageResource(
904                                         android.R.drawable.stat_notify_error);
905                             }
906                             holder.button_.setOnClickListener(new OnClickListener() {
907                                 @Override
908                                 public void onClick(View v) {
909                                     Intent intent = new Intent(
910                                             android.provider.Settings.ACTION_MONITORING_CERT_INFO);
911                                     getContext().startActivity(intent);
912                                 }
913                             });
914                         } else {
915                             holder.button_.setVisibility(View.GONE);
916                             holder.divider_.setVisibility(View.GONE);
917                         }
918                     }
919                     updateCommonHeaderView(header, holder);
920                     break;
921
922                 case HEADER_TYPE_NORMAL:
923                     updateCommonHeaderView(header, holder);
924                     break;
925             }
926
927             return view;
928         }
929
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);
942                 } else {
943                     holder.icon.setImageResource(header.iconRes);
944                 }
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);
950                 } else {
951                     holder.summary.setVisibility(View.GONE);
952                 }
953             }
954
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);
962         }
963
964         public void resume() {
965             mWifiEnabler.resume();
966             mBluetoothEnabler.resume();
967         }
968
969         public void pause() {
970             mWifiEnabler.pause();
971             mBluetoothEnabler.pause();
972         }
973     }
974
975     @Override
976     public void onHeaderClick(Header header, int position) {
977         boolean revert = false;
978         if (header.id == R.id.account_add) {
979             revert = true;
980         }
981
982         super.onHeaderClick(header, position);
983
984         if (revert && mLastHeader != null) {
985             highlightHeader((int) mLastHeader.id);
986         } else {
987             mLastHeader = header;
988         }
989     }
990
991     @Override
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;
1001             } else {
1002                 titleRes = R.string.user_info_settings_title;
1003             }
1004         }
1005         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
1006                 null, 0);
1007         return true;
1008     }
1009
1010     @Override
1011     public boolean shouldUpRecreateTask(Intent targetIntent) {
1012         return super.shouldUpRecreateTask(new Intent(this, Settings.class));
1013     }
1014
1015     @Override
1016     public void setListAdapter(ListAdapter adapter) {
1017         if (adapter == null) {
1018             super.setListAdapter(null);
1019         } else {
1020             DevicePolicyManager dpm =
1021                     (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1022             super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
1023         }
1024     }
1025
1026     @Override
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();
1032     }
1033
1034     /*
1035      * Settings subclasses for launching independently.
1036      */
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 */ }
1084 }