Home | History | Annotate | Download | only in settings
      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 com.android.settings.accounts.AccountSyncSettings;
     20 import com.android.settings.bluetooth.BluetoothEnabler;
     21 import com.android.settings.fuelgauge.PowerUsageSummary;
     22 import com.android.settings.wifi.WifiEnabler;
     23 
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.pm.ActivityInfo;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.PackageManager.NameNotFoundException;
     30 import android.os.Bundle;
     31 import android.preference.Preference;
     32 import android.preference.PreferenceActivity;
     33 import android.preference.PreferenceFragment;
     34 import android.text.TextUtils;
     35 import android.util.Log;
     36 import android.view.LayoutInflater;
     37 import android.view.View;
     38 import android.view.View.OnClickListener;
     39 import android.view.ViewGroup;
     40 import android.widget.ArrayAdapter;
     41 import android.widget.Button;
     42 import android.widget.ImageView;
     43 import android.widget.ListAdapter;
     44 import android.widget.Switch;
     45 import android.widget.TextView;
     46 
     47 import java.util.ArrayList;
     48 import java.util.HashMap;
     49 import java.util.List;
     50 
     51 /**
     52  * Top-level settings activity to handle single pane and double pane UI layout.
     53  */
     54 public class Settings extends PreferenceActivity implements ButtonBarHandler {
     55 
     56     private static final String LOG_TAG = "Settings";
     57     private static final String META_DATA_KEY_HEADER_ID =
     58         "com.android.settings.TOP_LEVEL_HEADER_ID";
     59     private static final String META_DATA_KEY_FRAGMENT_CLASS =
     60         "com.android.settings.FRAGMENT_CLASS";
     61     private static final String META_DATA_KEY_PARENT_TITLE =
     62         "com.android.settings.PARENT_FRAGMENT_TITLE";
     63     private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
     64         "com.android.settings.PARENT_FRAGMENT_CLASS";
     65 
     66     private static final String EXTRA_CLEAR_UI_OPTIONS = "settings:remove_ui_options";
     67 
     68     private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
     69     private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
     70 
     71     private String mFragmentClass;
     72     private int mTopLevelHeaderId;
     73     private Header mFirstHeader;
     74     private Header mCurrentHeader;
     75     private Header mParentHeader;
     76     private boolean mInLocalHeaderSwitch;
     77 
     78     // TODO: Update Call Settings based on airplane mode state.
     79 
     80     protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
     81     private List<Header> mHeaders;
     82 
     83     @Override
     84     protected void onCreate(Bundle savedInstanceState) {
     85         if (getIntent().getBooleanExtra(EXTRA_CLEAR_UI_OPTIONS, false)) {
     86             getWindow().setUiOptions(0);
     87         }
     88 
     89         getMetaData();
     90         mInLocalHeaderSwitch = true;
     91         super.onCreate(savedInstanceState);
     92         mInLocalHeaderSwitch = false;
     93 
     94         if (!onIsHidingHeaders() && onIsMultiPane()) {
     95             highlightHeader();
     96             // Force the title so that it doesn't get overridden by a direct launch of
     97             // a specific settings screen.
     98             setTitle(R.string.settings_label);
     99         }
    100 
    101         // Retrieve any saved state
    102         if (savedInstanceState != null) {
    103             mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
    104             mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
    105         }
    106 
    107         // If the current header was saved, switch to it
    108         if (savedInstanceState != null && mCurrentHeader != null) {
    109             //switchToHeaderLocal(mCurrentHeader);
    110             showBreadCrumbs(mCurrentHeader.title, null);
    111         }
    112 
    113         if (mParentHeader != null) {
    114             setParentTitle(mParentHeader.title, null, new OnClickListener() {
    115                 public void onClick(View v) {
    116                     switchToParent(mParentHeader.fragment);
    117                 }
    118             });
    119         }
    120 
    121         // TODO Add support for android.R.id.home in all Setting's onOptionsItemSelected
    122         // getActionBar().setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP,
    123         // ActionBar.DISPLAY_HOME_AS_UP);
    124     }
    125 
    126     @Override
    127     protected void onSaveInstanceState(Bundle outState) {
    128         super.onSaveInstanceState(outState);
    129 
    130         // Save the current fragment, if it is the same as originally launched
    131         if (mCurrentHeader != null) {
    132             outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
    133         }
    134         if (mParentHeader != null) {
    135             outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
    136         }
    137     }
    138 
    139     @Override
    140     public void onResume() {
    141         super.onResume();
    142 
    143         ListAdapter listAdapter = getListAdapter();
    144         if (listAdapter instanceof HeaderAdapter) {
    145             ((HeaderAdapter) listAdapter).resume();
    146         }
    147     }
    148 
    149     @Override
    150     public void onPause() {
    151         super.onPause();
    152 
    153         ListAdapter listAdapter = getListAdapter();
    154         if (listAdapter instanceof HeaderAdapter) {
    155             ((HeaderAdapter) listAdapter).pause();
    156         }
    157     }
    158 
    159     private void switchToHeaderLocal(Header header) {
    160         mInLocalHeaderSwitch = true;
    161         switchToHeader(header);
    162         mInLocalHeaderSwitch = false;
    163     }
    164 
    165     @Override
    166     public void switchToHeader(Header header) {
    167         if (!mInLocalHeaderSwitch) {
    168             mCurrentHeader = null;
    169             mParentHeader = null;
    170         }
    171         super.switchToHeader(header);
    172     }
    173 
    174     /**
    175      * Switch to parent fragment and store the grand parent's info
    176      * @param className name of the activity wrapper for the parent fragment.
    177      */
    178     private void switchToParent(String className) {
    179         final ComponentName cn = new ComponentName(this, className);
    180         try {
    181             final PackageManager pm = getPackageManager();
    182             final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
    183 
    184             if (parentInfo != null && parentInfo.metaData != null) {
    185                 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
    186                 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
    187                 Header parentHeader = new Header();
    188                 parentHeader.fragment = fragmentClass;
    189                 parentHeader.title = fragmentTitle;
    190                 mCurrentHeader = parentHeader;
    191 
    192                 switchToHeaderLocal(parentHeader);
    193                 highlightHeader();
    194 
    195                 mParentHeader = new Header();
    196                 mParentHeader.fragment
    197                         = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
    198                 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
    199             }
    200         } catch (NameNotFoundException nnfe) {
    201             Log.w(LOG_TAG, "Could not find parent activity : " + className);
    202         }
    203     }
    204 
    205     @Override
    206     public void onNewIntent(Intent intent) {
    207         super.onNewIntent(intent);
    208 
    209         // If it is not launched from history, then reset to top-level
    210         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0
    211                 && mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
    212             switchToHeaderLocal(mFirstHeader);
    213         }
    214     }
    215 
    216     private void highlightHeader() {
    217         if (mTopLevelHeaderId != 0) {
    218             Integer index = mHeaderIndexMap.get(mTopLevelHeaderId);
    219             if (index != null) {
    220                 getListView().setItemChecked(index, true);
    221                 getListView().smoothScrollToPosition(index);
    222             }
    223         }
    224     }
    225 
    226     @Override
    227     public Intent getIntent() {
    228         Intent superIntent = super.getIntent();
    229         String startingFragment = getStartingFragmentClass(superIntent);
    230         // This is called from super.onCreate, isMultiPane() is not yet reliable
    231         // Do not use onIsHidingHeaders either, which relies itself on this method
    232         if (startingFragment != null && !onIsMultiPane()) {
    233             Intent modIntent = new Intent(superIntent);
    234             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
    235             Bundle args = superIntent.getExtras();
    236             if (args != null) {
    237                 args = new Bundle(args);
    238             } else {
    239                 args = new Bundle();
    240             }
    241             args.putParcelable("intent", superIntent);
    242             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
    243             return modIntent;
    244         }
    245         return superIntent;
    246     }
    247 
    248     /**
    249      * Checks if the component name in the intent is different from the Settings class and
    250      * returns the class name to load as a fragment.
    251      */
    252     protected String getStartingFragmentClass(Intent intent) {
    253         if (mFragmentClass != null) return mFragmentClass;
    254 
    255         String intentClass = intent.getComponent().getClassName();
    256         if (intentClass.equals(getClass().getName())) return null;
    257 
    258         if ("com.android.settings.ManageApplications".equals(intentClass)
    259                 || "com.android.settings.RunningServices".equals(intentClass)
    260                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
    261             // Old names of manage apps.
    262             intentClass = com.android.settings.applications.ManageApplications.class.getName();
    263         }
    264 
    265         return intentClass;
    266     }
    267 
    268     /**
    269      * Override initial header when an activity-alias is causing Settings to be launched
    270      * for a specific fragment encoded in the android:name parameter.
    271      */
    272     @Override
    273     public Header onGetInitialHeader() {
    274         String fragmentClass = getStartingFragmentClass(super.getIntent());
    275         if (fragmentClass != null) {
    276             Header header = new Header();
    277             header.fragment = fragmentClass;
    278             header.title = getTitle();
    279             header.fragmentArguments = getIntent().getExtras();
    280             mCurrentHeader = header;
    281             return header;
    282         }
    283 
    284         return mFirstHeader;
    285     }
    286 
    287     @Override
    288     public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
    289             int titleRes, int shortTitleRes) {
    290         Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
    291                 titleRes, shortTitleRes);
    292 
    293         // some fragments want to avoid split actionbar
    294         if (DataUsageSummary.class.getName().equals(fragmentName) ||
    295                 PowerUsageSummary.class.getName().equals(fragmentName) ||
    296                 AccountSyncSettings.class.getName().equals(fragmentName) ||
    297                 UserDictionarySettings.class.getName().equals(fragmentName)) {
    298             intent.putExtra(EXTRA_CLEAR_UI_OPTIONS, true);
    299         }
    300 
    301         intent.setClass(this, SubSettings.class);
    302         return intent;
    303     }
    304 
    305     /**
    306      * Populate the activity with the top-level headers.
    307      */
    308     @Override
    309     public void onBuildHeaders(List<Header> headers) {
    310         loadHeadersFromResource(R.xml.settings_headers, headers);
    311 
    312         updateHeaderList(headers);
    313 
    314         mHeaders = headers;
    315     }
    316 
    317     private void updateHeaderList(List<Header> target) {
    318         int i = 0;
    319         while (i < target.size()) {
    320             Header header = target.get(i);
    321             // Ids are integers, so downcasting
    322             int id = (int) header.id;
    323             if (id == R.id.dock_settings) {
    324                 if (!needsDockSettings())
    325                     target.remove(header);
    326             } else if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
    327                 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
    328             } else if (id == R.id.wifi_settings) {
    329                 // Remove WiFi Settings if WiFi service is not available.
    330                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
    331                     target.remove(header);
    332                 }
    333             } else if (id == R.id.bluetooth_settings) {
    334                 // Remove Bluetooth Settings if Bluetooth service is not available.
    335                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
    336                     target.remove(header);
    337                 }
    338             }
    339 
    340             // Increment if the current one wasn't removed by the Utils code.
    341             if (target.get(i) == header) {
    342                 // Hold on to the first header, when we need to reset to the top-level
    343                 if (mFirstHeader == null &&
    344                         HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
    345                     mFirstHeader = header;
    346                 }
    347                 mHeaderIndexMap.put(id, i);
    348                 i++;
    349             }
    350         }
    351     }
    352 
    353     private boolean needsDockSettings() {
    354         return getResources().getBoolean(R.bool.has_dock_settings);
    355     }
    356 
    357     private void getMetaData() {
    358         try {
    359             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
    360                     PackageManager.GET_META_DATA);
    361             if (ai == null || ai.metaData == null) return;
    362             mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
    363             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
    364 
    365             // Check if it has a parent specified and create a Header object
    366             final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
    367             String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
    368             if (parentFragmentClass != null) {
    369                 mParentHeader = new Header();
    370                 mParentHeader.fragment = parentFragmentClass;
    371                 if (parentHeaderTitleRes != 0) {
    372                     mParentHeader.title = getResources().getString(parentHeaderTitleRes);
    373                 }
    374             }
    375         } catch (NameNotFoundException nnfe) {
    376             // No recovery
    377         }
    378     }
    379 
    380     @Override
    381     public boolean hasNextButton() {
    382         return super.hasNextButton();
    383     }
    384 
    385     @Override
    386     public Button getNextButton() {
    387         return super.getNextButton();
    388     }
    389 
    390     private static class HeaderAdapter extends ArrayAdapter<Header> {
    391         static final int HEADER_TYPE_CATEGORY = 0;
    392         static final int HEADER_TYPE_NORMAL = 1;
    393         static final int HEADER_TYPE_SWITCH = 2;
    394         private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1;
    395 
    396         private final WifiEnabler mWifiEnabler;
    397         private final BluetoothEnabler mBluetoothEnabler;
    398 
    399         private static class HeaderViewHolder {
    400             ImageView icon;
    401             TextView title;
    402             TextView summary;
    403             Switch switch_;
    404         }
    405 
    406         private LayoutInflater mInflater;
    407 
    408         static int getHeaderType(Header header) {
    409             if (header.fragment == null && header.intent == null) {
    410                 return HEADER_TYPE_CATEGORY;
    411             } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
    412                 return HEADER_TYPE_SWITCH;
    413             } else {
    414                 return HEADER_TYPE_NORMAL;
    415             }
    416         }
    417 
    418         @Override
    419         public int getItemViewType(int position) {
    420             Header header = getItem(position);
    421             return getHeaderType(header);
    422         }
    423 
    424         @Override
    425         public boolean areAllItemsEnabled() {
    426             return false; // because of categories
    427         }
    428 
    429         @Override
    430         public boolean isEnabled(int position) {
    431             return getItemViewType(position) != HEADER_TYPE_CATEGORY;
    432         }
    433 
    434         @Override
    435         public int getViewTypeCount() {
    436             return HEADER_TYPE_COUNT;
    437         }
    438 
    439         @Override
    440         public boolean hasStableIds() {
    441             return true;
    442         }
    443 
    444         public HeaderAdapter(Context context, List<Header> objects) {
    445             super(context, 0, objects);
    446             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    447 
    448             // Temp Switches provided as placeholder until the adapter replaces these with actual
    449             // Switches inflated from their layouts. Must be done before adapter is set in super
    450             mWifiEnabler = new WifiEnabler(context, new Switch(context));
    451             mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
    452         }
    453 
    454         @Override
    455         public View getView(int position, View convertView, ViewGroup parent) {
    456             HeaderViewHolder holder;
    457             Header header = getItem(position);
    458             int headerType = getHeaderType(header);
    459             View view = null;
    460 
    461             if (convertView == null) {
    462                 holder = new HeaderViewHolder();
    463                 switch (headerType) {
    464                     case HEADER_TYPE_CATEGORY:
    465                         view = new TextView(getContext(), null,
    466                                 android.R.attr.listSeparatorTextViewStyle);
    467                         holder.title = (TextView) view;
    468                         break;
    469 
    470                     case HEADER_TYPE_SWITCH:
    471                         view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
    472                                 false);
    473                         holder.icon = (ImageView) view.findViewById(R.id.icon);
    474                         holder.title = (TextView)
    475                                 view.findViewById(com.android.internal.R.id.title);
    476                         holder.summary = (TextView)
    477                                 view.findViewById(com.android.internal.R.id.summary);
    478                         holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
    479                         break;
    480 
    481                     case HEADER_TYPE_NORMAL:
    482                         view = mInflater.inflate(
    483                                 com.android.internal.R.layout.preference_header_item, parent,
    484                                 false);
    485                         holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
    486                         holder.title = (TextView)
    487                                 view.findViewById(com.android.internal.R.id.title);
    488                         holder.summary = (TextView)
    489                                 view.findViewById(com.android.internal.R.id.summary);
    490                         break;
    491                 }
    492                 view.setTag(holder);
    493             } else {
    494                 view = convertView;
    495                 holder = (HeaderViewHolder) view.getTag();
    496             }
    497 
    498             // All view fields must be updated every time, because the view may be recycled
    499             switch (headerType) {
    500                 case HEADER_TYPE_CATEGORY:
    501                     holder.title.setText(header.getTitle(getContext().getResources()));
    502                     break;
    503 
    504                 case HEADER_TYPE_SWITCH:
    505                     // Would need a different treatment if the main menu had more switches
    506                     if (header.id == R.id.wifi_settings) {
    507                         mWifiEnabler.setSwitch(holder.switch_);
    508                     } else {
    509                         mBluetoothEnabler.setSwitch(holder.switch_);
    510                     }
    511                     // No break, fall through on purpose to update common fields
    512 
    513                     //$FALL-THROUGH$
    514                 case HEADER_TYPE_NORMAL:
    515                     holder.icon.setImageResource(header.iconRes);
    516                     holder.title.setText(header.getTitle(getContext().getResources()));
    517                     CharSequence summary = header.getSummary(getContext().getResources());
    518                     if (!TextUtils.isEmpty(summary)) {
    519                         holder.summary.setVisibility(View.VISIBLE);
    520                         holder.summary.setText(summary);
    521                     } else {
    522                         holder.summary.setVisibility(View.GONE);
    523                     }
    524                     break;
    525             }
    526 
    527             return view;
    528         }
    529 
    530         public void resume() {
    531             mWifiEnabler.resume();
    532             mBluetoothEnabler.resume();
    533         }
    534 
    535         public void pause() {
    536             mWifiEnabler.pause();
    537             mBluetoothEnabler.pause();
    538         }
    539     }
    540 
    541     @Override
    542     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
    543         // Override the fragment title for Wallpaper settings
    544         int titleRes = pref.getTitleRes();
    545         if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
    546             titleRes = R.string.wallpaper_settings_fragment_title;
    547         }
    548         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, null, null, 0);
    549         return true;
    550     }
    551 
    552     @Override
    553     public void setListAdapter(ListAdapter adapter) {
    554         if (mHeaders == null) {
    555             mHeaders = new ArrayList<Header>();
    556             // When the saved state provides the list of headers, onBuildHeaders is not called
    557             // Copy the list of Headers from the adapter, preserving their order
    558             for (int i = 0; i < adapter.getCount(); i++) {
    559                 mHeaders.add((Header) adapter.getItem(i));
    560             }
    561         }
    562 
    563         // Ignore the adapter provided by PreferenceActivity and substitute ours instead
    564         super.setListAdapter(new HeaderAdapter(this, mHeaders));
    565     }
    566 
    567     /*
    568      * Settings subclasses for launching independently.
    569      */
    570     public static class BluetoothSettingsActivity extends Settings { /* empty */ }
    571     public static class WirelessSettingsActivity extends Settings { /* empty */ }
    572     public static class TetherSettingsActivity extends Settings { /* empty */ }
    573     public static class VpnSettingsActivity extends Settings { /* empty */ }
    574     public static class DateTimeSettingsActivity extends Settings { /* empty */ }
    575     public static class StorageSettingsActivity extends Settings { /* empty */ }
    576     public static class WifiSettingsActivity extends Settings { /* empty */ }
    577     public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
    578     public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
    579     public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
    580     public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
    581     public static class LocalePickerActivity extends Settings { /* empty */ }
    582     public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
    583     public static class SoundSettingsActivity extends Settings { /* empty */ }
    584     public static class DisplaySettingsActivity extends Settings { /* empty */ }
    585     public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
    586     public static class ApplicationSettingsActivity extends Settings { /* empty */ }
    587     public static class ManageApplicationsActivity extends Settings { /* empty */ }
    588     public static class StorageUseActivity extends Settings { /* empty */ }
    589     public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
    590     public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
    591     public static class SecuritySettingsActivity extends Settings { /* empty */ }
    592     public static class LocationSettingsActivity extends Settings { /* empty */ }
    593     public static class PrivacySettingsActivity extends Settings { /* empty */ }
    594     public static class DockSettingsActivity extends Settings { /* empty */ }
    595     public static class RunningServicesActivity extends Settings { /* empty */ }
    596     public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
    597     public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
    598     public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
    599     public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
    600     public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
    601     public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
    602     public static class DataUsageSummaryActivity extends Settings { /* empty */ }
    603     public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
    604     public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
    605     public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
    606 }
    607