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