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
17package com.android.settings;
18
19import com.android.internal.util.ArrayUtils;
20import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment;
21import com.android.settings.accounts.AccountSyncSettings;
22import com.android.settings.accounts.AuthenticatorHelper;
23import com.android.settings.accounts.ManageAccountsSettings;
24import com.android.settings.applications.InstalledAppDetails;
25import com.android.settings.applications.ManageApplications;
26import com.android.settings.bluetooth.BluetoothEnabler;
27import com.android.settings.deviceinfo.Memory;
28import com.android.settings.fuelgauge.PowerUsageSummary;
29import com.android.settings.vpn2.VpnSettings;
30import com.android.settings.wifi.WifiEnabler;
31
32import android.accounts.Account;
33import android.accounts.AccountManager;
34import android.accounts.OnAccountsUpdateListener;
35import android.content.ComponentName;
36import android.content.Context;
37import android.content.Intent;
38import android.content.SharedPreferences;
39import android.content.pm.ActivityInfo;
40import android.content.pm.PackageManager;
41import android.content.pm.PackageManager.NameNotFoundException;
42import android.graphics.drawable.Drawable;
43import android.os.Bundle;
44import android.os.INetworkManagementService;
45import android.os.RemoteException;
46import android.os.ServiceManager;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.preference.Preference;
50import android.preference.PreferenceActivity;
51import android.preference.PreferenceFragment;
52import android.text.TextUtils;
53import android.util.Log;
54import android.view.LayoutInflater;
55import android.view.View;
56import android.view.View.OnClickListener;
57import android.view.ViewGroup;
58import android.widget.ArrayAdapter;
59import android.widget.Button;
60import android.widget.ImageView;
61import android.widget.ListAdapter;
62import android.widget.Switch;
63import android.widget.TextView;
64
65import java.util.ArrayList;
66import java.util.Collections;
67import java.util.Comparator;
68import java.util.HashMap;
69import java.util.List;
70
71/**
72 * Top-level settings activity to handle single pane and double pane UI layout.
73 */
74public class Settings extends PreferenceActivity
75        implements ButtonBarHandler, OnAccountsUpdateListener {
76
77    private static final String LOG_TAG = "Settings";
78
79    private static final String META_DATA_KEY_HEADER_ID =
80        "com.android.settings.TOP_LEVEL_HEADER_ID";
81    private static final String META_DATA_KEY_FRAGMENT_CLASS =
82        "com.android.settings.FRAGMENT_CLASS";
83    private static final String META_DATA_KEY_PARENT_TITLE =
84        "com.android.settings.PARENT_FRAGMENT_TITLE";
85    private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
86        "com.android.settings.PARENT_FRAGMENT_CLASS";
87
88    private static final String EXTRA_CLEAR_UI_OPTIONS = "settings:remove_ui_options";
89
90    private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
91    private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
92
93    private String mFragmentClass;
94    private int mTopLevelHeaderId;
95    private Header mFirstHeader;
96    private Header mCurrentHeader;
97    private Header mParentHeader;
98    private boolean mInLocalHeaderSwitch;
99
100    // Show only these settings for restricted users
101    private int[] SETTINGS_FOR_RESTRICTED = {
102            R.id.wireless_section,
103            R.id.wifi_settings,
104            R.id.bluetooth_settings,
105            R.id.data_usage_settings,
106            R.id.wireless_settings,
107            R.id.device_section,
108            R.id.sound_settings,
109            R.id.display_settings,
110            R.id.storage_settings,
111            R.id.application_settings,
112            R.id.battery_settings,
113            R.id.personal_section,
114            R.id.location_settings,
115            R.id.security_settings,
116            R.id.language_settings,
117            R.id.user_settings,
118            R.id.account_settings,
119            R.id.account_add,
120            R.id.system_section,
121            R.id.date_time_settings,
122            R.id.about_settings,
123            R.id.accessibility_settings
124    };
125
126    private SharedPreferences mDevelopmentPreferences;
127    private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
128
129    // TODO: Update Call Settings based on airplane mode state.
130
131    protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
132
133    private AuthenticatorHelper mAuthenticatorHelper;
134    private Header mLastHeader;
135    private boolean mListeningToAccountUpdates;
136
137    @Override
138    protected void onCreate(Bundle savedInstanceState) {
139        if (getIntent().getBooleanExtra(EXTRA_CLEAR_UI_OPTIONS, false)) {
140            getWindow().setUiOptions(0);
141        }
142
143        mAuthenticatorHelper = new AuthenticatorHelper();
144        mAuthenticatorHelper.updateAuthDescriptions(this);
145        mAuthenticatorHelper.onAccountsUpdated(this, null);
146
147        mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
148                Context.MODE_PRIVATE);
149
150        getMetaData();
151        mInLocalHeaderSwitch = true;
152        super.onCreate(savedInstanceState);
153        mInLocalHeaderSwitch = false;
154
155        if (!onIsHidingHeaders() && onIsMultiPane()) {
156            highlightHeader(mTopLevelHeaderId);
157            // Force the title so that it doesn't get overridden by a direct launch of
158            // a specific settings screen.
159            setTitle(R.string.settings_label);
160        }
161
162        // Retrieve any saved state
163        if (savedInstanceState != null) {
164            mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
165            mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
166        }
167
168        // If the current header was saved, switch to it
169        if (savedInstanceState != null && mCurrentHeader != null) {
170            //switchToHeaderLocal(mCurrentHeader);
171            showBreadCrumbs(mCurrentHeader.title, null);
172        }
173
174        if (mParentHeader != null) {
175            setParentTitle(mParentHeader.title, null, new OnClickListener() {
176                public void onClick(View v) {
177                    switchToParent(mParentHeader.fragment);
178                }
179            });
180        }
181
182        // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
183        if (onIsMultiPane()) {
184            getActionBar().setDisplayHomeAsUpEnabled(false);
185            getActionBar().setHomeButtonEnabled(false);
186        }
187    }
188
189    @Override
190    protected void onSaveInstanceState(Bundle outState) {
191        super.onSaveInstanceState(outState);
192
193        // Save the current fragment, if it is the same as originally launched
194        if (mCurrentHeader != null) {
195            outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
196        }
197        if (mParentHeader != null) {
198            outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
199        }
200    }
201
202    @Override
203    public void onResume() {
204        super.onResume();
205
206        mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
207            @Override
208            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
209                invalidateHeaders();
210            }
211        };
212        mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
213                mDevelopmentPreferencesListener);
214
215        ListAdapter listAdapter = getListAdapter();
216        if (listAdapter instanceof HeaderAdapter) {
217            ((HeaderAdapter) listAdapter).resume();
218        }
219        invalidateHeaders();
220    }
221
222    @Override
223    public void onPause() {
224        super.onPause();
225
226        ListAdapter listAdapter = getListAdapter();
227        if (listAdapter instanceof HeaderAdapter) {
228            ((HeaderAdapter) listAdapter).pause();
229        }
230
231        mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
232                mDevelopmentPreferencesListener);
233        mDevelopmentPreferencesListener = null;
234    }
235
236    @Override
237    public void onDestroy() {
238        super.onDestroy();
239        if (mListeningToAccountUpdates) {
240            AccountManager.get(this).removeOnAccountsUpdatedListener(this);
241        }
242    }
243
244    private void switchToHeaderLocal(Header header) {
245        mInLocalHeaderSwitch = true;
246        switchToHeader(header);
247        mInLocalHeaderSwitch = false;
248    }
249
250    @Override
251    public void switchToHeader(Header header) {
252        if (!mInLocalHeaderSwitch) {
253            mCurrentHeader = null;
254            mParentHeader = null;
255        }
256        super.switchToHeader(header);
257    }
258
259    /**
260     * Switch to parent fragment and store the grand parent's info
261     * @param className name of the activity wrapper for the parent fragment.
262     */
263    private void switchToParent(String className) {
264        final ComponentName cn = new ComponentName(this, className);
265        try {
266            final PackageManager pm = getPackageManager();
267            final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
268
269            if (parentInfo != null && parentInfo.metaData != null) {
270                String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
271                CharSequence fragmentTitle = parentInfo.loadLabel(pm);
272                Header parentHeader = new Header();
273                parentHeader.fragment = fragmentClass;
274                parentHeader.title = fragmentTitle;
275                mCurrentHeader = parentHeader;
276
277                switchToHeaderLocal(parentHeader);
278                highlightHeader(mTopLevelHeaderId);
279
280                mParentHeader = new Header();
281                mParentHeader.fragment
282                        = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
283                mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
284            }
285        } catch (NameNotFoundException nnfe) {
286            Log.w(LOG_TAG, "Could not find parent activity : " + className);
287        }
288    }
289
290    @Override
291    public void onNewIntent(Intent intent) {
292        super.onNewIntent(intent);
293
294        // If it is not launched from history, then reset to top-level
295        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
296            if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
297                switchToHeaderLocal(mFirstHeader);
298            }
299            getListView().setSelectionFromTop(0, 0);
300        }
301    }
302
303    private void highlightHeader(int id) {
304        if (id != 0) {
305            Integer index = mHeaderIndexMap.get(id);
306            if (index != null) {
307                getListView().setItemChecked(index, true);
308                if (isMultiPane()) {
309                    getListView().smoothScrollToPosition(index);
310                }
311            }
312        }
313    }
314
315    @Override
316    public Intent getIntent() {
317        Intent superIntent = super.getIntent();
318        String startingFragment = getStartingFragmentClass(superIntent);
319        // This is called from super.onCreate, isMultiPane() is not yet reliable
320        // Do not use onIsHidingHeaders either, which relies itself on this method
321        if (startingFragment != null && !onIsMultiPane()) {
322            Intent modIntent = new Intent(superIntent);
323            modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
324            Bundle args = superIntent.getExtras();
325            if (args != null) {
326                args = new Bundle(args);
327            } else {
328                args = new Bundle();
329            }
330            args.putParcelable("intent", superIntent);
331            modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
332            return modIntent;
333        }
334        return superIntent;
335    }
336
337    /**
338     * Checks if the component name in the intent is different from the Settings class and
339     * returns the class name to load as a fragment.
340     */
341    protected String getStartingFragmentClass(Intent intent) {
342        if (mFragmentClass != null) return mFragmentClass;
343
344        String intentClass = intent.getComponent().getClassName();
345        if (intentClass.equals(getClass().getName())) return null;
346
347        if ("com.android.settings.ManageApplications".equals(intentClass)
348                || "com.android.settings.RunningServices".equals(intentClass)
349                || "com.android.settings.applications.StorageUse".equals(intentClass)) {
350            // Old names of manage apps.
351            intentClass = com.android.settings.applications.ManageApplications.class.getName();
352        }
353
354        return intentClass;
355    }
356
357    /**
358     * Override initial header when an activity-alias is causing Settings to be launched
359     * for a specific fragment encoded in the android:name parameter.
360     */
361    @Override
362    public Header onGetInitialHeader() {
363        String fragmentClass = getStartingFragmentClass(super.getIntent());
364        if (fragmentClass != null) {
365            Header header = new Header();
366            header.fragment = fragmentClass;
367            header.title = getTitle();
368            header.fragmentArguments = getIntent().getExtras();
369            mCurrentHeader = header;
370            return header;
371        }
372
373        return mFirstHeader;
374    }
375
376    @Override
377    public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
378            int titleRes, int shortTitleRes) {
379        Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
380                titleRes, shortTitleRes);
381
382        // some fragments want to avoid split actionbar
383        if (DataUsageSummary.class.getName().equals(fragmentName) ||
384                PowerUsageSummary.class.getName().equals(fragmentName) ||
385                AccountSyncSettings.class.getName().equals(fragmentName) ||
386                UserDictionarySettings.class.getName().equals(fragmentName) ||
387                Memory.class.getName().equals(fragmentName) ||
388                ManageApplications.class.getName().equals(fragmentName) ||
389                WirelessSettings.class.getName().equals(fragmentName) ||
390                SoundSettings.class.getName().equals(fragmentName) ||
391                PrivacySettings.class.getName().equals(fragmentName) ||
392                ManageAccountsSettings.class.getName().equals(fragmentName) ||
393                VpnSettings.class.getName().equals(fragmentName) ||
394                SecuritySettings.class.getName().equals(fragmentName) ||
395                InstalledAppDetails.class.getName().equals(fragmentName) ||
396                ChooseLockGenericFragment.class.getName().equals(fragmentName) ||
397                TetherSettings.class.getName().equals(fragmentName) ||
398                ApnSettings.class.getName().equals(fragmentName) ||
399                LocationSettings.class.getName().equals(fragmentName) ||
400                ZonePicker.class.getName().equals(fragmentName)) {
401            intent.putExtra(EXTRA_CLEAR_UI_OPTIONS, true);
402        }
403
404        intent.setClass(this, SubSettings.class);
405        return intent;
406    }
407
408    /**
409     * Populate the activity with the top-level headers.
410     */
411    @Override
412    public void onBuildHeaders(List<Header> headers) {
413        loadHeadersFromResource(R.xml.settings_headers, headers);
414
415        updateHeaderList(headers);
416    }
417
418    private void updateHeaderList(List<Header> target) {
419        final boolean showDev = mDevelopmentPreferences.getBoolean(
420                DevelopmentSettings.PREF_SHOW,
421                android.os.Build.TYPE.equals("eng"));
422        int i = 0;
423
424        mHeaderIndexMap.clear();
425        while (i < target.size()) {
426            Header header = target.get(i);
427            // Ids are integers, so downcasting
428            int id = (int) header.id;
429            if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
430                Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
431            } else if (id == R.id.wifi_settings) {
432                // Remove WiFi Settings if WiFi service is not available.
433                if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
434                    target.remove(i);
435                }
436            } else if (id == R.id.bluetooth_settings) {
437                // Remove Bluetooth Settings if Bluetooth service is not available.
438                if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
439                    target.remove(i);
440                }
441            } else if (id == R.id.data_usage_settings) {
442                // Remove data usage when kernel module not enabled
443                final INetworkManagementService netManager = INetworkManagementService.Stub
444                        .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
445                try {
446                    if (!netManager.isBandwidthControlEnabled()) {
447                        target.remove(i);
448                    }
449                } catch (RemoteException e) {
450                    // ignored
451                }
452            } else if (id == R.id.account_settings) {
453                int headerIndex = i + 1;
454                i = insertAccountsHeaders(target, headerIndex);
455            } else if (id == R.id.user_settings) {
456                if (!UserHandle.MU_ENABLED
457                        || !UserManager.supportsMultipleUsers()
458                        || Utils.isMonkeyRunning()) {
459                    target.remove(i);
460                }
461            } else if (id == R.id.development_settings) {
462                if (!showDev) {
463                    target.remove(i);
464                }
465            }
466
467            if (target.get(i) == header
468                    && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
469                    && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
470                target.remove(i);
471            }
472
473            // Increment if the current one wasn't removed by the Utils code.
474            if (target.get(i) == header) {
475                // Hold on to the first header, when we need to reset to the top-level
476                if (mFirstHeader == null &&
477                        HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
478                    mFirstHeader = header;
479                }
480                mHeaderIndexMap.put(id, i);
481                i++;
482            }
483        }
484    }
485
486    private int insertAccountsHeaders(List<Header> target, int headerIndex) {
487        String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
488        List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
489        for (String accountType : accountTypes) {
490            CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
491            if (label == null) {
492                continue;
493            }
494
495            Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
496            boolean skipToAccount = accounts.length == 1
497                    && !mAuthenticatorHelper.hasAccountPreferences(accountType);
498            Header accHeader = new Header();
499            accHeader.title = label;
500            if (accHeader.extras == null) {
501                accHeader.extras = new Bundle();
502            }
503            if (skipToAccount) {
504                accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
505                accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
506                accHeader.fragment = AccountSyncSettings.class.getName();
507                accHeader.fragmentArguments = new Bundle();
508                // Need this for the icon
509                accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
510                accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
511                accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
512                        accounts[0]);
513            } else {
514                accHeader.breadCrumbTitle = label;
515                accHeader.breadCrumbShortTitle = label;
516                accHeader.fragment = ManageAccountsSettings.class.getName();
517                accHeader.fragmentArguments = new Bundle();
518                accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
519                accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
520                        accountType);
521                if (!isMultiPane()) {
522                    accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
523                            label.toString());
524                }
525            }
526            accountHeaders.add(accHeader);
527        }
528
529        // Sort by label
530        Collections.sort(accountHeaders, new Comparator<Header>() {
531            @Override
532            public int compare(Header h1, Header h2) {
533                return h1.title.toString().compareTo(h2.title.toString());
534            }
535        });
536
537        for (Header header : accountHeaders) {
538            target.add(headerIndex++, header);
539        }
540        if (!mListeningToAccountUpdates) {
541            AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
542            mListeningToAccountUpdates = true;
543        }
544        return headerIndex;
545    }
546
547    private void getMetaData() {
548        try {
549            ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
550                    PackageManager.GET_META_DATA);
551            if (ai == null || ai.metaData == null) return;
552            mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
553            mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
554
555            // Check if it has a parent specified and create a Header object
556            final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
557            String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
558            if (parentFragmentClass != null) {
559                mParentHeader = new Header();
560                mParentHeader.fragment = parentFragmentClass;
561                if (parentHeaderTitleRes != 0) {
562                    mParentHeader.title = getResources().getString(parentHeaderTitleRes);
563                }
564            }
565        } catch (NameNotFoundException nnfe) {
566            // No recovery
567        }
568    }
569
570    @Override
571    public boolean hasNextButton() {
572        return super.hasNextButton();
573    }
574
575    @Override
576    public Button getNextButton() {
577        return super.getNextButton();
578    }
579
580    private static class HeaderAdapter extends ArrayAdapter<Header> {
581        static final int HEADER_TYPE_CATEGORY = 0;
582        static final int HEADER_TYPE_NORMAL = 1;
583        static final int HEADER_TYPE_SWITCH = 2;
584        private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1;
585
586        private final WifiEnabler mWifiEnabler;
587        private final BluetoothEnabler mBluetoothEnabler;
588        private AuthenticatorHelper mAuthHelper;
589
590        private static class HeaderViewHolder {
591            ImageView icon;
592            TextView title;
593            TextView summary;
594            Switch switch_;
595        }
596
597        private LayoutInflater mInflater;
598
599        static int getHeaderType(Header header) {
600            if (header.fragment == null && header.intent == null) {
601                return HEADER_TYPE_CATEGORY;
602            } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
603                return HEADER_TYPE_SWITCH;
604            } else {
605                return HEADER_TYPE_NORMAL;
606            }
607        }
608
609        @Override
610        public int getItemViewType(int position) {
611            Header header = getItem(position);
612            return getHeaderType(header);
613        }
614
615        @Override
616        public boolean areAllItemsEnabled() {
617            return false; // because of categories
618        }
619
620        @Override
621        public boolean isEnabled(int position) {
622            return getItemViewType(position) != HEADER_TYPE_CATEGORY;
623        }
624
625        @Override
626        public int getViewTypeCount() {
627            return HEADER_TYPE_COUNT;
628        }
629
630        @Override
631        public boolean hasStableIds() {
632            return true;
633        }
634
635        public HeaderAdapter(Context context, List<Header> objects,
636                AuthenticatorHelper authenticatorHelper) {
637            super(context, 0, objects);
638
639            mAuthHelper = authenticatorHelper;
640            mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
641
642            // Temp Switches provided as placeholder until the adapter replaces these with actual
643            // Switches inflated from their layouts. Must be done before adapter is set in super
644            mWifiEnabler = new WifiEnabler(context, new Switch(context));
645            mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
646        }
647
648        @Override
649        public View getView(int position, View convertView, ViewGroup parent) {
650            HeaderViewHolder holder;
651            Header header = getItem(position);
652            int headerType = getHeaderType(header);
653            View view = null;
654
655            if (convertView == null) {
656                holder = new HeaderViewHolder();
657                switch (headerType) {
658                    case HEADER_TYPE_CATEGORY:
659                        view = new TextView(getContext(), null,
660                                android.R.attr.listSeparatorTextViewStyle);
661                        holder.title = (TextView) view;
662                        break;
663
664                    case HEADER_TYPE_SWITCH:
665                        view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
666                                false);
667                        holder.icon = (ImageView) view.findViewById(R.id.icon);
668                        holder.title = (TextView)
669                                view.findViewById(com.android.internal.R.id.title);
670                        holder.summary = (TextView)
671                                view.findViewById(com.android.internal.R.id.summary);
672                        holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
673                        break;
674
675                    case HEADER_TYPE_NORMAL:
676                        view = mInflater.inflate(
677                                R.layout.preference_header_item, parent,
678                                false);
679                        holder.icon = (ImageView) view.findViewById(R.id.icon);
680                        holder.title = (TextView)
681                                view.findViewById(com.android.internal.R.id.title);
682                        holder.summary = (TextView)
683                                view.findViewById(com.android.internal.R.id.summary);
684                        break;
685                }
686                view.setTag(holder);
687            } else {
688                view = convertView;
689                holder = (HeaderViewHolder) view.getTag();
690            }
691
692            // All view fields must be updated every time, because the view may be recycled
693            switch (headerType) {
694                case HEADER_TYPE_CATEGORY:
695                    holder.title.setText(header.getTitle(getContext().getResources()));
696                    break;
697
698                case HEADER_TYPE_SWITCH:
699                    // Would need a different treatment if the main menu had more switches
700                    if (header.id == R.id.wifi_settings) {
701                        mWifiEnabler.setSwitch(holder.switch_);
702                    } else {
703                        mBluetoothEnabler.setSwitch(holder.switch_);
704                    }
705                    // No break, fall through on purpose to update common fields
706
707                    //$FALL-THROUGH$
708                case HEADER_TYPE_NORMAL:
709                    if (header.extras != null
710                            && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
711                        String accType = header.extras.getString(
712                                ManageAccountsSettings.KEY_ACCOUNT_TYPE);
713                        ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
714                        lp.width = getContext().getResources().getDimensionPixelSize(
715                                R.dimen.header_icon_width);
716                        lp.height = lp.width;
717                        holder.icon.setLayoutParams(lp);
718                        Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
719                        holder.icon.setImageDrawable(icon);
720                    } else {
721                        holder.icon.setImageResource(header.iconRes);
722                    }
723                    holder.title.setText(header.getTitle(getContext().getResources()));
724                    CharSequence summary = header.getSummary(getContext().getResources());
725                    if (!TextUtils.isEmpty(summary)) {
726                        holder.summary.setVisibility(View.VISIBLE);
727                        holder.summary.setText(summary);
728                    } else {
729                        holder.summary.setVisibility(View.GONE);
730                    }
731                    break;
732            }
733
734            return view;
735        }
736
737        public void resume() {
738            mWifiEnabler.resume();
739            mBluetoothEnabler.resume();
740        }
741
742        public void pause() {
743            mWifiEnabler.pause();
744            mBluetoothEnabler.pause();
745        }
746    }
747
748    @Override
749    public void onHeaderClick(Header header, int position) {
750        boolean revert = false;
751        if (header.id == R.id.account_add) {
752            revert = true;
753        }
754
755        super.onHeaderClick(header, position);
756
757        if (revert && mLastHeader != null) {
758            highlightHeader((int) mLastHeader.id);
759        } else {
760            mLastHeader = header;
761        }
762    }
763
764    @Override
765    public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
766        // Override the fragment title for Wallpaper settings
767        int titleRes = pref.getTitleRes();
768        if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
769            titleRes = R.string.wallpaper_settings_fragment_title;
770        } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
771                && UserHandle.myUserId() != UserHandle.USER_OWNER) {
772            titleRes = R.string.user_info_settings_title;
773        }
774        startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
775                null, 0);
776        return true;
777    }
778
779    public boolean shouldUpRecreateTask(Intent targetIntent) {
780        return super.shouldUpRecreateTask(new Intent(this, Settings.class));
781    }
782
783    @Override
784    public void setListAdapter(ListAdapter adapter) {
785        if (adapter == null) {
786            super.setListAdapter(null);
787        } else {
788            super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper));
789        }
790    }
791
792    @Override
793    public void onAccountsUpdated(Account[] accounts) {
794        // TODO: watch for package upgrades to invalidate cache; see 7206643
795        mAuthenticatorHelper.updateAuthDescriptions(this);
796        mAuthenticatorHelper.onAccountsUpdated(this, accounts);
797        invalidateHeaders();
798    }
799
800    /*
801     * Settings subclasses for launching independently.
802     */
803    public static class BluetoothSettingsActivity extends Settings { /* empty */ }
804    public static class WirelessSettingsActivity extends Settings { /* empty */ }
805    public static class TetherSettingsActivity extends Settings { /* empty */ }
806    public static class VpnSettingsActivity extends Settings { /* empty */ }
807    public static class DateTimeSettingsActivity extends Settings { /* empty */ }
808    public static class StorageSettingsActivity extends Settings { /* empty */ }
809    public static class WifiSettingsActivity extends Settings { /* empty */ }
810    public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
811    public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
812    public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
813    public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
814    public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
815    public static class LocalePickerActivity extends Settings { /* empty */ }
816    public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
817    public static class SoundSettingsActivity extends Settings { /* empty */ }
818    public static class DisplaySettingsActivity extends Settings { /* empty */ }
819    public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
820    public static class ApplicationSettingsActivity extends Settings { /* empty */ }
821    public static class ManageApplicationsActivity extends Settings { /* empty */ }
822    public static class StorageUseActivity extends Settings { /* empty */ }
823    public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
824    public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
825    public static class SecuritySettingsActivity extends Settings { /* empty */ }
826    public static class LocationSettingsActivity extends Settings { /* empty */ }
827    public static class PrivacySettingsActivity extends Settings { /* empty */ }
828    public static class RunningServicesActivity extends Settings { /* empty */ }
829    public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
830    public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
831    public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
832    public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
833    public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
834    public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
835    public static class DataUsageSummaryActivity extends Settings { /* empty */ }
836    public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
837    public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
838    public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
839    public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
840    public static class DreamSettingsActivity extends Settings { /* empty */ }
841}
842