Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2011 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.inputmethod.latin.settings;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.content.pm.PackageInfo;
     22 import android.content.res.Configuration;
     23 import android.content.res.Resources;
     24 import android.util.Log;
     25 import android.view.inputmethod.EditorInfo;
     26 
     27 import com.android.inputmethod.compat.AppWorkaroundsUtils;
     28 import com.android.inputmethod.latin.InputAttributes;
     29 import com.android.inputmethod.latin.R;
     30 import com.android.inputmethod.latin.RichInputMethodManager;
     31 import com.android.inputmethod.latin.SubtypeSwitcher;
     32 import com.android.inputmethod.latin.utils.AsyncResultHolder;
     33 import com.android.inputmethod.latin.utils.ResourceUtils;
     34 import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
     35 
     36 import java.util.Arrays;
     37 import java.util.Locale;
     38 
     39 /**
     40  * When you call the constructor of this class, you may want to change the current system locale by
     41  * using {@link com.android.inputmethod.latin.utils.RunInLocale}.
     42  */
     43 public final class SettingsValues {
     44     private static final String TAG = SettingsValues.class.getSimpleName();
     45     // "floatMaxValue" and "floatNegativeInfinity" are special marker strings for
     46     // Float.NEGATIVE_INFINITE and Float.MAX_VALUE. Currently used for auto-correction settings.
     47     private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue";
     48     private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity";
     49     private static final int TIMEOUT_TO_GET_TARGET_PACKAGE = 5; // seconds
     50 
     51     // From resources:
     52     public final SpacingAndPunctuations mSpacingAndPunctuations;
     53     public final int mDelayInMillisecondsToUpdateOldSuggestions;
     54     public final long mDoubleSpacePeriodTimeout;
     55     // From configuration:
     56     public final Locale mLocale;
     57     public final boolean mHasHardwareKeyboard;
     58     public final int mDisplayOrientation;
     59     // From preferences, in the same order as xml/prefs.xml:
     60     public final boolean mAutoCap;
     61     public final boolean mVibrateOn;
     62     public final boolean mSoundOn;
     63     public final boolean mKeyPreviewPopupOn;
     64     public final boolean mShowsVoiceInputKey;
     65     public final boolean mIncludesOtherImesInLanguageSwitchList;
     66     public final boolean mShowsLanguageSwitchKey;
     67     public final boolean mUseContactsDict;
     68     public final boolean mUsePersonalizedDicts;
     69     public final boolean mUseDoubleSpacePeriod;
     70     public final boolean mBlockPotentiallyOffensive;
     71     // Use bigrams to predict the next word when there is no input for it yet
     72     public final boolean mBigramPredictionEnabled;
     73     public final boolean mGestureInputEnabled;
     74     public final boolean mGestureTrailEnabled;
     75     public final boolean mGestureFloatingPreviewTextEnabled;
     76     public final boolean mSlidingKeyInputPreviewEnabled;
     77     public final boolean mPhraseGestureEnabled;
     78     public final int mKeyLongpressTimeout;
     79     public final boolean mEnableMetricsLogging;
     80     public final boolean mShouldShowUiToAcceptTypedWord;
     81 
     82     // From the input box
     83     public final InputAttributes mInputAttributes;
     84 
     85     // Deduced settings
     86     public final int mKeypressVibrationDuration;
     87     public final float mKeypressSoundVolume;
     88     public final int mKeyPreviewPopupDismissDelay;
     89     private final boolean mAutoCorrectEnabled;
     90     public final float mAutoCorrectionThreshold;
     91     public final boolean mAutoCorrectionEnabledPerUserSettings;
     92     private final boolean mSuggestionsEnabledPerUserSettings;
     93     private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds;
     94 
     95     // Setting values for additional features
     96     public final int[] mAdditionalFeaturesSettingValues =
     97             new int[AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
     98 
     99     // TextDecorator
    100     public final int mTextHighlightColorForAddToDictionaryIndicator;
    101 
    102     // Debug settings
    103     public final boolean mIsInternal;
    104     public final boolean mHasCustomKeyPreviewAnimationParams;
    105     public final int mKeyPreviewShowUpDuration;
    106     public final int mKeyPreviewDismissDuration;
    107     public final float mKeyPreviewShowUpStartXScale;
    108     public final float mKeyPreviewShowUpStartYScale;
    109     public final float mKeyPreviewDismissEndXScale;
    110     public final float mKeyPreviewDismissEndYScale;
    111 
    112     public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res,
    113             final InputAttributes inputAttributes) {
    114         mLocale = res.getConfiguration().locale;
    115         // Get the resources
    116         mDelayInMillisecondsToUpdateOldSuggestions =
    117                 res.getInteger(R.integer.config_delay_in_milliseconds_to_update_old_suggestions);
    118         mSpacingAndPunctuations = new SpacingAndPunctuations(res);
    119 
    120         // Store the input attributes
    121         if (null == inputAttributes) {
    122             mInputAttributes = new InputAttributes(
    123                     null, false /* isFullscreenMode */, context.getPackageName());
    124         } else {
    125             mInputAttributes = inputAttributes;
    126         }
    127 
    128         // Get the settings preferences
    129         mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
    130         mVibrateOn = Settings.readVibrationEnabled(prefs, res);
    131         mSoundOn = Settings.readKeypressSoundEnabled(prefs, res);
    132         mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res);
    133         mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
    134                 DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true);
    135         mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res)
    136                 && mInputAttributes.mShouldShowVoiceInputKey
    137                 && SubtypeSwitcher.getInstance().isShortcutImeEnabled();
    138         final String autoCorrectionThresholdRawValue = prefs.getString(
    139                 Settings.PREF_AUTO_CORRECTION_THRESHOLD,
    140                 res.getString(R.string.auto_correction_threshold_mode_index_modest));
    141         mIncludesOtherImesInLanguageSwitchList = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS
    142                 ? prefs.getBoolean(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false)
    143                 : true /* forcibly */;
    144         mShowsLanguageSwitchKey = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS
    145                 ? Settings.readShowsLanguageSwitchKey(prefs) : true /* forcibly */;
    146         mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
    147         mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true);
    148         mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true)
    149                 && inputAttributes.mIsGeneralTextInput;
    150         mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res);
    151         mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res);
    152         mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
    153         mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
    154         mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
    155         mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
    156         mShouldShowUiToAcceptTypedWord = Settings.HAS_UI_TO_ACCEPT_TYPED_WORD
    157                 && prefs.getBoolean(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD, true);
    158         // Compute other readable settings
    159         mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
    160         mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
    161         mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res);
    162         mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res);
    163         mAutoCorrectionThreshold = readAutoCorrectionThreshold(res,
    164                 autoCorrectionThresholdRawValue);
    165         mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res);
    166         mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true);
    167         mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
    168                 Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
    169         mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res);
    170         mAutoCorrectionEnabledPerUserSettings = mAutoCorrectEnabled
    171                 && !mInputAttributes.mInputTypeNoAutoCorrect;
    172         mSuggestionsEnabledPerUserSettings = readSuggestionsEnabled(prefs);
    173         AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray(
    174                 prefs, mAdditionalFeaturesSettingValues);
    175         mTextHighlightColorForAddToDictionaryIndicator = res.getColor(
    176                 R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color);
    177         mIsInternal = Settings.isInternal(prefs);
    178         mHasCustomKeyPreviewAnimationParams = prefs.getBoolean(
    179                 DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false);
    180         mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration(
    181                 prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
    182                 res.getInteger(R.integer.config_key_preview_show_up_duration));
    183         mKeyPreviewDismissDuration = Settings.readKeyPreviewAnimationDuration(
    184                 prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION,
    185                 res.getInteger(R.integer.config_key_preview_dismiss_duration));
    186         final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction(
    187                 res, R.fraction.config_key_preview_show_up_start_scale);
    188         final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction(
    189                 res, R.fraction.config_key_preview_dismiss_end_scale);
    190         mKeyPreviewShowUpStartXScale = Settings.readKeyPreviewAnimationScale(
    191                 prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE,
    192                 defaultKeyPreviewShowUpStartScale);
    193         mKeyPreviewShowUpStartYScale = Settings.readKeyPreviewAnimationScale(
    194                 prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE,
    195                 defaultKeyPreviewShowUpStartScale);
    196         mKeyPreviewDismissEndXScale = Settings.readKeyPreviewAnimationScale(
    197                 prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE,
    198                 defaultKeyPreviewDismissEndScale);
    199         mKeyPreviewDismissEndYScale = Settings.readKeyPreviewAnimationScale(
    200                 prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE,
    201                 defaultKeyPreviewDismissEndScale);
    202         mDisplayOrientation = res.getConfiguration().orientation;
    203         mAppWorkarounds = new AsyncResultHolder<>();
    204         final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(
    205                 mInputAttributes.mTargetApplicationPackageName);
    206         if (null != packageInfo) {
    207             mAppWorkarounds.set(new AppWorkaroundsUtils(packageInfo));
    208         } else {
    209             new TargetPackageInfoGetterTask(context, mAppWorkarounds)
    210                     .execute(mInputAttributes.mTargetApplicationPackageName);
    211         }
    212     }
    213 
    214     public boolean isApplicationSpecifiedCompletionsOn() {
    215         return mInputAttributes.mApplicationSpecifiedCompletionOn;
    216     }
    217 
    218     public boolean needsToLookupSuggestions() {
    219         return mInputAttributes.mShouldShowSuggestions
    220                 && (mAutoCorrectionEnabledPerUserSettings || isSuggestionsEnabledPerUserSettings());
    221     }
    222 
    223     public boolean isSuggestionsEnabledPerUserSettings() {
    224         return mSuggestionsEnabledPerUserSettings;
    225     }
    226 
    227     public boolean isWordSeparator(final int code) {
    228         return mSpacingAndPunctuations.isWordSeparator(code);
    229     }
    230 
    231     public boolean isWordConnector(final int code) {
    232         return mSpacingAndPunctuations.isWordConnector(code);
    233     }
    234 
    235     public boolean isWordCodePoint(final int code) {
    236         return Character.isLetter(code) || isWordConnector(code)
    237                 || Character.COMBINING_SPACING_MARK == Character.getType(code);
    238     }
    239 
    240     public boolean isUsuallyPrecededBySpace(final int code) {
    241         return mSpacingAndPunctuations.isUsuallyPrecededBySpace(code);
    242     }
    243 
    244     public boolean isUsuallyFollowedBySpace(final int code) {
    245         return mSpacingAndPunctuations.isUsuallyFollowedBySpace(code);
    246     }
    247 
    248     public boolean shouldInsertSpacesAutomatically() {
    249         return mInputAttributes.mShouldInsertSpacesAutomatically;
    250     }
    251 
    252     public boolean isLanguageSwitchKeyEnabled() {
    253         if (!mShowsLanguageSwitchKey) {
    254             return false;
    255         }
    256         final RichInputMethodManager imm = RichInputMethodManager.getInstance();
    257         if (mIncludesOtherImesInLanguageSwitchList) {
    258             return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */);
    259         } else {
    260             return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */);
    261         }
    262     }
    263 
    264     public boolean isSameInputType(final EditorInfo editorInfo) {
    265         return mInputAttributes.isSameInputType(editorInfo);
    266     }
    267 
    268     public boolean hasSameOrientation(final Configuration configuration) {
    269         return mDisplayOrientation == configuration.orientation;
    270     }
    271 
    272     public boolean isBeforeJellyBean() {
    273         final AppWorkaroundsUtils appWorkaroundUtils
    274                 = mAppWorkarounds.get(null, TIMEOUT_TO_GET_TARGET_PACKAGE);
    275         return null == appWorkaroundUtils ? false : appWorkaroundUtils.isBeforeJellyBean();
    276     }
    277 
    278     public boolean isBrokenByRecorrection() {
    279         final AppWorkaroundsUtils appWorkaroundUtils
    280                 = mAppWorkarounds.get(null, TIMEOUT_TO_GET_TARGET_PACKAGE);
    281         return null == appWorkaroundUtils ? false : appWorkaroundUtils.isBrokenByRecorrection();
    282     }
    283 
    284     private static final String SUGGESTIONS_VISIBILITY_HIDE_VALUE_OBSOLETE = "2";
    285 
    286     private static boolean readSuggestionsEnabled(final SharedPreferences prefs) {
    287         if (prefs.contains(Settings.PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE)) {
    288             final boolean alwaysHide = SUGGESTIONS_VISIBILITY_HIDE_VALUE_OBSOLETE.equals(
    289                     prefs.getString(Settings.PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE, null));
    290             prefs.edit()
    291                     .remove(Settings.PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE)
    292                     .putBoolean(Settings.PREF_SHOW_SUGGESTIONS, !alwaysHide)
    293                     .apply();
    294         }
    295         return prefs.getBoolean(Settings.PREF_SHOW_SUGGESTIONS, true);
    296     }
    297 
    298     private static boolean readBigramPredictionEnabled(final SharedPreferences prefs,
    299             final Resources res) {
    300         return prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, res.getBoolean(
    301                 R.bool.config_default_next_word_prediction));
    302     }
    303 
    304     private static float readAutoCorrectionThreshold(final Resources res,
    305             final String currentAutoCorrectionSetting) {
    306         final String[] autoCorrectionThresholdValues = res.getStringArray(
    307                 R.array.auto_correction_threshold_values);
    308         // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
    309         final float autoCorrectionThreshold;
    310         try {
    311             final int arrayIndex = Integer.parseInt(currentAutoCorrectionSetting);
    312             if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
    313                 final String val = autoCorrectionThresholdValues[arrayIndex];
    314                 if (FLOAT_MAX_VALUE_MARKER_STRING.equals(val)) {
    315                     autoCorrectionThreshold = Float.MAX_VALUE;
    316                 } else if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) {
    317                     autoCorrectionThreshold = Float.NEGATIVE_INFINITY;
    318                 } else {
    319                     autoCorrectionThreshold = Float.parseFloat(val);
    320                 }
    321             } else {
    322                 autoCorrectionThreshold = Float.MAX_VALUE;
    323             }
    324         } catch (final NumberFormatException e) {
    325             // Whenever the threshold settings are correct, never come here.
    326             Log.w(TAG, "Cannot load auto correction threshold setting."
    327                     + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
    328                     + ", autoCorrectionThresholdValues: "
    329                     + Arrays.toString(autoCorrectionThresholdValues), e);
    330             return Float.MAX_VALUE;
    331         }
    332         return autoCorrectionThreshold;
    333     }
    334 
    335     private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs,
    336             final Resources res) {
    337         // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
    338         // {@link Settings#PREF_VOICE_INPUT_KEY}.
    339         if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {
    340             final String voiceModeMain = res.getString(R.string.voice_mode_main);
    341             final String voiceMode = prefs.getString(
    342                     Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
    343             final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode);
    344             prefs.edit()
    345                     .putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey)
    346                     // Remove the obsolete preference if exists.
    347                     .remove(Settings.PREF_VOICE_MODE_OBSOLETE)
    348                     .apply();
    349         }
    350         return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true);
    351     }
    352 
    353     public String dump() {
    354         final StringBuilder sb = new StringBuilder("Current settings :");
    355         sb.append("\n   mSpacingAndPunctuations = ");
    356         sb.append("" + mSpacingAndPunctuations.dump());
    357         sb.append("\n   mDelayInMillisecondsToUpdateOldSuggestions = ");
    358         sb.append("" + mDelayInMillisecondsToUpdateOldSuggestions);
    359         sb.append("\n   mAutoCap = ");
    360         sb.append("" + mAutoCap);
    361         sb.append("\n   mVibrateOn = ");
    362         sb.append("" + mVibrateOn);
    363         sb.append("\n   mSoundOn = ");
    364         sb.append("" + mSoundOn);
    365         sb.append("\n   mKeyPreviewPopupOn = ");
    366         sb.append("" + mKeyPreviewPopupOn);
    367         sb.append("\n   mShowsVoiceInputKey = ");
    368         sb.append("" + mShowsVoiceInputKey);
    369         sb.append("\n   mIncludesOtherImesInLanguageSwitchList = ");
    370         sb.append("" + mIncludesOtherImesInLanguageSwitchList);
    371         sb.append("\n   mShowsLanguageSwitchKey = ");
    372         sb.append("" + mShowsLanguageSwitchKey);
    373         sb.append("\n   mUseContactsDict = ");
    374         sb.append("" + mUseContactsDict);
    375         sb.append("\n   mUsePersonalizedDicts = ");
    376         sb.append("" + mUsePersonalizedDicts);
    377         sb.append("\n   mUseDoubleSpacePeriod = ");
    378         sb.append("" + mUseDoubleSpacePeriod);
    379         sb.append("\n   mBlockPotentiallyOffensive = ");
    380         sb.append("" + mBlockPotentiallyOffensive);
    381         sb.append("\n   mBigramPredictionEnabled = ");
    382         sb.append("" + mBigramPredictionEnabled);
    383         sb.append("\n   mGestureInputEnabled = ");
    384         sb.append("" + mGestureInputEnabled);
    385         sb.append("\n   mGestureTrailEnabled = ");
    386         sb.append("" + mGestureTrailEnabled);
    387         sb.append("\n   mGestureFloatingPreviewTextEnabled = ");
    388         sb.append("" + mGestureFloatingPreviewTextEnabled);
    389         sb.append("\n   mSlidingKeyInputPreviewEnabled = ");
    390         sb.append("" + mSlidingKeyInputPreviewEnabled);
    391         sb.append("\n   mPhraseGestureEnabled = ");
    392         sb.append("" + mPhraseGestureEnabled);
    393         sb.append("\n   mKeyLongpressTimeout = ");
    394         sb.append("" + mKeyLongpressTimeout);
    395         sb.append("\n   mLocale = ");
    396         sb.append("" + mLocale);
    397         sb.append("\n   mInputAttributes = ");
    398         sb.append("" + mInputAttributes);
    399         sb.append("\n   mKeypressVibrationDuration = ");
    400         sb.append("" + mKeypressVibrationDuration);
    401         sb.append("\n   mKeypressSoundVolume = ");
    402         sb.append("" + mKeypressSoundVolume);
    403         sb.append("\n   mKeyPreviewPopupDismissDelay = ");
    404         sb.append("" + mKeyPreviewPopupDismissDelay);
    405         sb.append("\n   mAutoCorrectEnabled = ");
    406         sb.append("" + mAutoCorrectEnabled);
    407         sb.append("\n   mAutoCorrectionThreshold = ");
    408         sb.append("" + mAutoCorrectionThreshold);
    409         sb.append("\n   mAutoCorrectionEnabledPerUserSettings = ");
    410         sb.append("" + mAutoCorrectionEnabledPerUserSettings);
    411         sb.append("\n   mSuggestionsEnabledPerUserSettings = ");
    412         sb.append("" + mSuggestionsEnabledPerUserSettings);
    413         sb.append("\n   mDisplayOrientation = ");
    414         sb.append("" + mDisplayOrientation);
    415         sb.append("\n   mAppWorkarounds = ");
    416         final AppWorkaroundsUtils awu = mAppWorkarounds.get(null, 0);
    417         sb.append("" + (null == awu ? "null" : awu.toString()));
    418         sb.append("\n   mAdditionalFeaturesSettingValues = ");
    419         sb.append("" + Arrays.toString(mAdditionalFeaturesSettingValues));
    420         sb.append("\n   mTextHighlightColorForAddToDictionaryIndicator = ");
    421         sb.append("" + mTextHighlightColorForAddToDictionaryIndicator);
    422         sb.append("\n   mIsInternal = ");
    423         sb.append("" + mIsInternal);
    424         sb.append("\n   mKeyPreviewShowUpDuration = ");
    425         sb.append("" + mKeyPreviewShowUpDuration);
    426         sb.append("\n   mKeyPreviewDismissDuration = ");
    427         sb.append("" + mKeyPreviewDismissDuration);
    428         sb.append("\n   mKeyPreviewShowUpStartScaleX = ");
    429         sb.append("" + mKeyPreviewShowUpStartXScale);
    430         sb.append("\n   mKeyPreviewShowUpStartScaleY = ");
    431         sb.append("" + mKeyPreviewShowUpStartYScale);
    432         sb.append("\n   mKeyPreviewDismissEndScaleX = ");
    433         sb.append("" + mKeyPreviewDismissEndXScale);
    434         sb.append("\n   mKeyPreviewDismissEndScaleY = ");
    435         sb.append("" + mKeyPreviewDismissEndYScale);
    436         return sb.toString();
    437     }
    438 }
    439