Home | History | Annotate | Download | only in inputmethod
      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.inputmethod;
     18 
     19 import com.android.settings.R;
     20 import com.android.settings.Settings.KeyboardLayoutPickerActivity;
     21 import com.android.settings.Settings.SpellCheckersSettingsActivity;
     22 import com.android.settings.SettingsPreferenceFragment;
     23 import com.android.settings.Utils;
     24 import com.android.settings.VoiceInputOutputSettings;
     25 
     26 import android.app.Activity;
     27 import android.content.ContentResolver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.pm.PackageManager;
     31 import android.content.res.Configuration;
     32 import android.content.res.Resources;
     33 import android.database.ContentObserver;
     34 import android.hardware.input.InputManager;
     35 import android.hardware.input.KeyboardLayout;
     36 import android.os.Bundle;
     37 import android.os.Handler;
     38 import android.preference.CheckBoxPreference;
     39 import android.preference.ListPreference;
     40 import android.preference.Preference;
     41 import android.preference.PreferenceCategory;
     42 import android.preference.PreferenceScreen;
     43 import android.provider.Settings;
     44 import android.provider.Settings.System;
     45 import android.text.TextUtils;
     46 import android.view.InputDevice;
     47 import android.view.inputmethod.InputMethodInfo;
     48 import android.view.inputmethod.InputMethodManager;
     49 
     50 import java.util.ArrayList;
     51 import java.util.Collections;
     52 import java.util.List;
     53 import java.util.TreeSet;
     54 
     55 public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
     56         implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
     57         KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener {
     58 
     59     private static final String KEY_PHONE_LANGUAGE = "phone_language";
     60     private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
     61     private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
     62     private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
     63     // false: on ICS or later
     64     private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
     65 
     66     private static final String[] sSystemSettingNames = {
     67         System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE,
     68     };
     69 
     70     private static final String[] sHardKeyboardKeys = {
     71         "auto_replace", "auto_caps", "auto_punctuate",
     72     };
     73 
     74     private int mDefaultInputMethodSelectorVisibility = 0;
     75     private ListPreference mShowInputMethodSelectorPref;
     76     private PreferenceCategory mKeyboardSettingsCategory;
     77     private PreferenceCategory mHardKeyboardCategory;
     78     private PreferenceCategory mGameControllerCategory;
     79     private Preference mLanguagePref;
     80     private final ArrayList<InputMethodPreference> mInputMethodPreferenceList =
     81             new ArrayList<InputMethodPreference>();
     82     private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList =
     83             new ArrayList<PreferenceScreen>();
     84     private InputManager mIm;
     85     private InputMethodManager mImm;
     86     private List<InputMethodInfo> mImis;
     87     private boolean mIsOnlyImeSettings;
     88     private Handler mHandler;
     89     @SuppressWarnings("unused")
     90     private SettingsObserver mSettingsObserver;
     91     private Intent mIntentWaitingForResult;
     92 
     93     @Override
     94     public void onCreate(Bundle icicle) {
     95         super.onCreate(icicle);
     96 
     97         addPreferencesFromResource(R.xml.language_settings);
     98 
     99         try {
    100             mDefaultInputMethodSelectorVisibility = Integer.valueOf(
    101                     getString(R.string.input_method_selector_visibility_default_value));
    102         } catch (NumberFormatException e) {
    103         }
    104 
    105         if (getActivity().getAssets().getLocales().length == 1) {
    106             // No "Select language" pref if there's only one system locale available.
    107             getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
    108         } else {
    109             mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
    110         }
    111         if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    112             mShowInputMethodSelectorPref = (ListPreference)findPreference(
    113                     KEY_INPUT_METHOD_SELECTOR);
    114             mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
    115             // TODO: Update current input method name on summary
    116             updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
    117         }
    118 
    119         new VoiceInputOutputSettings(this).onCreate();
    120 
    121         // Get references to dynamically constructed categories.
    122         mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
    123         mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
    124                 "keyboard_settings_category");
    125         mGameControllerCategory = (PreferenceCategory)findPreference(
    126                 "game_controller_settings_category");
    127 
    128         // Filter out irrelevant features if invoked from IME settings button.
    129         mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
    130                 getActivity().getIntent().getAction());
    131         getActivity().getIntent().setAction(null);
    132         if (mIsOnlyImeSettings) {
    133             getPreferenceScreen().removeAll();
    134             getPreferenceScreen().addPreference(mHardKeyboardCategory);
    135             if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    136                 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
    137             }
    138             getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
    139         }
    140 
    141         // Build IME preference category.
    142         mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    143         mImis = mImm.getInputMethodList();
    144 
    145         mKeyboardSettingsCategory.removeAll();
    146         if (!mIsOnlyImeSettings) {
    147             final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null);
    148             currentIme.setKey(KEY_CURRENT_INPUT_METHOD);
    149             currentIme.setTitle(getResources().getString(R.string.current_input_method));
    150             mKeyboardSettingsCategory.addPreference(currentIme);
    151         }
    152 
    153         mInputMethodPreferenceList.clear();
    154         final int N = (mImis == null ? 0 : mImis.size());
    155         for (int i = 0; i < N; ++i) {
    156             final InputMethodInfo imi = mImis.get(i);
    157             final InputMethodPreference pref = getInputMethodPreference(imi, N);
    158             mInputMethodPreferenceList.add(pref);
    159         }
    160 
    161         if (!mInputMethodPreferenceList.isEmpty()) {
    162             Collections.sort(mInputMethodPreferenceList);
    163             for (int i = 0; i < N; ++i) {
    164                 mKeyboardSettingsCategory.addPreference(mInputMethodPreferenceList.get(i));
    165             }
    166         }
    167 
    168         // Build hard keyboard and game controller preference categories.
    169         mIm = (InputManager)getActivity().getSystemService(Context.INPUT_SERVICE);
    170         updateInputDevices();
    171 
    172         // Spell Checker
    173         final Intent intent = new Intent(Intent.ACTION_MAIN);
    174         intent.setClass(getActivity(), SpellCheckersSettingsActivity.class);
    175         final SpellCheckersPreference scp = ((SpellCheckersPreference)findPreference(
    176                 "spellcheckers_settings"));
    177         if (scp != null) {
    178             scp.setFragmentIntent(this, intent);
    179         }
    180 
    181         mHandler = new Handler();
    182         mSettingsObserver = new SettingsObserver(mHandler, getActivity());
    183     }
    184 
    185     private void updateInputMethodSelectorSummary(int value) {
    186         String[] inputMethodSelectorTitles = getResources().getStringArray(
    187                 R.array.input_method_selector_titles);
    188         if (inputMethodSelectorTitles.length > value) {
    189             mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
    190             mShowInputMethodSelectorPref.setValue(String.valueOf(value));
    191         }
    192     }
    193 
    194     private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
    195         final Activity activity = getActivity();
    196         final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity);
    197         if (null == localeList) {
    198             // The locale list is null if and only if the user dictionary service is
    199             // not present or disabled. In this case we need to remove the preference.
    200             getPreferenceScreen().removePreference(userDictionaryPreference);
    201         } else if (localeList.size() <= 1) {
    202             final Intent intent =
    203                     new Intent(UserDictionaryList.USER_DICTIONARY_SETTINGS_INTENT_ACTION);
    204             userDictionaryPreference.setTitle(R.string.user_dict_single_settings_title);
    205             userDictionaryPreference.setIntent(intent);
    206             userDictionaryPreference.setFragment(
    207                     com.android.settings.UserDictionarySettings.class.getName());
    208             // If the size of localeList is 0, we don't set the locale parameter in the
    209             // extras. This will be interpreted by the UserDictionarySettings class as
    210             // meaning "the current locale".
    211             // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet()
    212             // the locale list always has at least one element, since it always includes the current
    213             // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet().
    214             if (localeList.size() == 1) {
    215                 final String locale = (String)localeList.toArray()[0];
    216                 userDictionaryPreference.getExtras().putString("locale", locale);
    217             }
    218         } else {
    219             userDictionaryPreference.setTitle(R.string.user_dict_multiple_settings_title);
    220             userDictionaryPreference.setFragment(UserDictionaryList.class.getName());
    221         }
    222     }
    223 
    224     @Override
    225     public void onResume() {
    226         super.onResume();
    227 
    228         mIm.registerInputDeviceListener(this, null);
    229 
    230         if (!mIsOnlyImeSettings) {
    231             if (mLanguagePref != null) {
    232                 Configuration conf = getResources().getConfiguration();
    233                 String language = conf.locale.getLanguage();
    234                 String localeString;
    235                 // TODO: This is not an accurate way to display the locale, as it is
    236                 // just working around the fact that we support limited dialects
    237                 // and want to pretend that the language is valid for all locales.
    238                 // We need a way to support languages that aren't tied to a particular
    239                 // locale instead of hiding the locale qualifier.
    240                 if (hasOnlyOneLanguageInstance(language,
    241                         Resources.getSystem().getAssets().getLocales())) {
    242                     localeString = conf.locale.getDisplayLanguage(conf.locale);
    243                 } else {
    244                     localeString = conf.locale.getDisplayName(conf.locale);
    245                 }
    246                 if (localeString.length() > 1) {
    247                     localeString = Character.toUpperCase(localeString.charAt(0))
    248                             + localeString.substring(1);
    249                     mLanguagePref.setSummary(localeString);
    250                 }
    251             }
    252 
    253             updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
    254             if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    255                 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
    256             }
    257         }
    258 
    259         // Hard keyboard
    260         if (!mHardKeyboardPreferenceList.isEmpty()) {
    261             for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
    262                 CheckBoxPreference chkPref = (CheckBoxPreference)
    263                         mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]);
    264                 chkPref.setChecked(
    265                         System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0);
    266             }
    267         }
    268 
    269         updateInputDevices();
    270 
    271         // IME
    272         InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
    273                 this, getContentResolver(), mImis, null);
    274         updateActiveInputMethodsSummary();
    275     }
    276 
    277     @Override
    278     public void onPause() {
    279         super.onPause();
    280 
    281         mIm.unregisterInputDeviceListener(this);
    282 
    283         if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    284             mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
    285         }
    286         InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
    287                 this, getContentResolver(), mImis, !mHardKeyboardPreferenceList.isEmpty());
    288     }
    289 
    290     @Override
    291     public void onInputDeviceAdded(int deviceId) {
    292         updateInputDevices();
    293     }
    294 
    295     @Override
    296     public void onInputDeviceChanged(int deviceId) {
    297         updateInputDevices();
    298     }
    299 
    300     @Override
    301     public void onInputDeviceRemoved(int deviceId) {
    302         updateInputDevices();
    303     }
    304 
    305     @Override
    306     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    307         // Input Method stuff
    308         if (Utils.isMonkeyRunning()) {
    309             return false;
    310         }
    311         if (preference instanceof PreferenceScreen) {
    312             if (preference.getFragment() != null) {
    313                 // Fragment will be handled correctly by the super class.
    314             } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
    315                 final InputMethodManager imm = (InputMethodManager)
    316                         getSystemService(Context.INPUT_METHOD_SERVICE);
    317                 imm.showInputMethodPicker();
    318             }
    319         } else if (preference instanceof CheckBoxPreference) {
    320             final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
    321             if (!mHardKeyboardPreferenceList.isEmpty()) {
    322                 for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
    323                     if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) {
    324                         System.putInt(getContentResolver(), sSystemSettingNames[i],
    325                                 chkPref.isChecked() ? 1 : 0);
    326                         return true;
    327                     }
    328                 }
    329             }
    330             if (chkPref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
    331                 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
    332                         chkPref.isChecked() ? 1 : 0);
    333                 return true;
    334             }
    335         }
    336         return super.onPreferenceTreeClick(preferenceScreen, preference);
    337     }
    338 
    339     private boolean hasOnlyOneLanguageInstance(String languageCode, String[] locales) {
    340         int count = 0;
    341         for (String localeCode : locales) {
    342             if (localeCode.length() > 2
    343                     && localeCode.startsWith(languageCode)) {
    344                 count++;
    345                 if (count > 1) {
    346                     return false;
    347                 }
    348             }
    349         }
    350         return count == 1;
    351     }
    352 
    353     private void saveInputMethodSelectorVisibility(String value) {
    354         try {
    355             int intValue = Integer.valueOf(value);
    356             Settings.Secure.putInt(getContentResolver(),
    357                     Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
    358             updateInputMethodSelectorSummary(intValue);
    359         } catch(NumberFormatException e) {
    360         }
    361     }
    362 
    363     private int loadInputMethodSelectorVisibility() {
    364         return Settings.Secure.getInt(getContentResolver(),
    365                 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
    366                 mDefaultInputMethodSelectorVisibility);
    367     }
    368 
    369     @Override
    370     public boolean onPreferenceChange(Preference preference, Object value) {
    371         if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    372             if (preference == mShowInputMethodSelectorPref) {
    373                 if (value instanceof String) {
    374                     saveInputMethodSelectorVisibility((String)value);
    375                 }
    376             }
    377         }
    378         return false;
    379     }
    380 
    381     private void updateActiveInputMethodsSummary() {
    382         for (Preference pref : mInputMethodPreferenceList) {
    383             if (pref instanceof InputMethodPreference) {
    384                 ((InputMethodPreference)pref).updateSummary();
    385             }
    386         }
    387         updateCurrentImeName();
    388     }
    389 
    390     private void updateCurrentImeName() {
    391         final Context context = getActivity();
    392         if (context == null || mImm == null) return;
    393         final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
    394         if (curPref != null) {
    395             final CharSequence curIme = InputMethodAndSubtypeUtil.getCurrentInputMethodName(
    396                     context, getContentResolver(), mImm, mImis, getPackageManager());
    397             if (!TextUtils.isEmpty(curIme)) {
    398                 synchronized(this) {
    399                     curPref.setSummary(curIme);
    400                 }
    401             }
    402         }
    403     }
    404 
    405     private InputMethodPreference getInputMethodPreference(InputMethodInfo imi, int imiSize) {
    406         final PackageManager pm = getPackageManager();
    407         final CharSequence label = imi.loadLabel(pm);
    408         // IME settings
    409         final Intent intent;
    410         final String settingsActivity = imi.getSettingsActivity();
    411         if (!TextUtils.isEmpty(settingsActivity)) {
    412             intent = new Intent(Intent.ACTION_MAIN);
    413             intent.setClassName(imi.getPackageName(), settingsActivity);
    414         } else {
    415             intent = null;
    416         }
    417 
    418         // Add a check box for enabling/disabling IME
    419         InputMethodPreference pref = new InputMethodPreference(this, intent, mImm, imi, imiSize);
    420         pref.setKey(imi.getId());
    421         pref.setTitle(label);
    422         return pref;
    423     }
    424 
    425     private void updateInputDevices() {
    426         updateHardKeyboards();
    427         updateGameControllers();
    428     }
    429 
    430     private void updateHardKeyboards() {
    431         mHardKeyboardPreferenceList.clear();
    432         if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) {
    433             final int[] devices = InputDevice.getDeviceIds();
    434             for (int i = 0; i < devices.length; i++) {
    435                 InputDevice device = InputDevice.getDevice(devices[i]);
    436                 if (device != null
    437                         && !device.isVirtual()
    438                         && device.isFullKeyboard()) {
    439                     final String inputDeviceDescriptor = device.getDescriptor();
    440                     final String keyboardLayoutDescriptor =
    441                             mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor);
    442                     final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
    443                             mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
    444 
    445                     final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
    446                     pref.setTitle(device.getName());
    447                     if (keyboardLayout != null) {
    448                         pref.setSummary(keyboardLayout.toString());
    449                     } else {
    450                         pref.setSummary(R.string.keyboard_layout_default_label);
    451                     }
    452                     pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
    453                         @Override
    454                         public boolean onPreferenceClick(Preference preference) {
    455                             showKeyboardLayoutDialog(inputDeviceDescriptor);
    456                             return true;
    457                         }
    458                     });
    459                     mHardKeyboardPreferenceList.add(pref);
    460                 }
    461             }
    462         }
    463 
    464         if (!mHardKeyboardPreferenceList.isEmpty()) {
    465             for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
    466                 final Preference pref = mHardKeyboardCategory.getPreference(i);
    467                 if (pref.getOrder() < 1000) {
    468                     mHardKeyboardCategory.removePreference(pref);
    469                 }
    470             }
    471 
    472             Collections.sort(mHardKeyboardPreferenceList);
    473             final int count = mHardKeyboardPreferenceList.size();
    474             for (int i = 0; i < count; i++) {
    475                 final Preference pref = mHardKeyboardPreferenceList.get(i);
    476                 pref.setOrder(i);
    477                 mHardKeyboardCategory.addPreference(pref);
    478             }
    479 
    480             getPreferenceScreen().addPreference(mHardKeyboardCategory);
    481         } else {
    482             getPreferenceScreen().removePreference(mHardKeyboardCategory);
    483         }
    484     }
    485 
    486     private void showKeyboardLayoutDialog(String inputDeviceDescriptor) {
    487         KeyboardLayoutDialogFragment fragment =
    488                 new KeyboardLayoutDialogFragment(inputDeviceDescriptor);
    489         fragment.setTargetFragment(this, 0);
    490         fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
    491     }
    492 
    493     @Override
    494     public void onSetupKeyboardLayouts(String inputDeviceDescriptor) {
    495         final Intent intent = new Intent(Intent.ACTION_MAIN);
    496         intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
    497         intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR,
    498                 inputDeviceDescriptor);
    499         mIntentWaitingForResult = intent;
    500         startActivityForResult(intent, 0);
    501     }
    502 
    503     @Override
    504     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    505         super.onActivityResult(requestCode, resultCode, data);
    506 
    507         if (mIntentWaitingForResult != null) {
    508             String inputDeviceDescriptor = mIntentWaitingForResult.getStringExtra(
    509                     KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR);
    510             mIntentWaitingForResult = null;
    511             showKeyboardLayoutDialog(inputDeviceDescriptor);
    512         }
    513     }
    514 
    515     private void updateGameControllers() {
    516         if (haveInputDeviceWithVibrator()) {
    517             getPreferenceScreen().addPreference(mGameControllerCategory);
    518 
    519             CheckBoxPreference chkPref = (CheckBoxPreference)
    520                     mGameControllerCategory.findPreference("vibrate_input_devices");
    521             chkPref.setChecked(System.getInt(getContentResolver(),
    522                     Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
    523         } else {
    524             getPreferenceScreen().removePreference(mGameControllerCategory);
    525         }
    526     }
    527 
    528     private boolean haveInputDeviceWithVibrator() {
    529         final int[] devices = InputDevice.getDeviceIds();
    530         for (int i = 0; i < devices.length; i++) {
    531             InputDevice device = InputDevice.getDevice(devices[i]);
    532             if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
    533                 return true;
    534             }
    535         }
    536         return false;
    537     }
    538 
    539     private class SettingsObserver extends ContentObserver {
    540         public SettingsObserver(Handler handler, Context context) {
    541             super(handler);
    542             final ContentResolver cr = context.getContentResolver();
    543             cr.registerContentObserver(
    544                     Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
    545             cr.registerContentObserver(Settings.Secure.getUriFor(
    546                     Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
    547         }
    548 
    549         @Override public void onChange(boolean selfChange) {
    550             updateCurrentImeName();
    551         }
    552     }
    553 }
    554