OSDN Git Service

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