KeyCodeDescriptionMapper.java revision ca2f051cc173acc3bce384ebfe08068564bc8e07
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 17package com.android.inputmethod.accessibility; 18 19import android.content.Context; 20import android.text.TextUtils; 21 22import com.android.inputmethod.keyboard.Key; 23import com.android.inputmethod.keyboard.Keyboard; 24import com.android.inputmethod.keyboard.KeyboardId; 25import com.android.inputmethod.latin.R; 26 27import java.util.HashMap; 28 29public class KeyCodeDescriptionMapper { 30 // The resource ID of the string spoken for obscured keys 31 private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot; 32 33 private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); 34 35 // Map of key labels to spoken description resource IDs 36 private final HashMap<CharSequence, Integer> mKeyLabelMap; 37 38 // Map of key codes to spoken description resource IDs 39 private final HashMap<Integer, Integer> mKeyCodeMap; 40 41 // Map of shifted key codes to spoken description resource IDs 42 private final HashMap<Integer, Integer> mShiftedKeyCodeMap; 43 44 // Map of shift-locked key codes to spoken description resource IDs 45 private final HashMap<Integer, Integer> mShiftLockedKeyCodeMap; 46 47 public static void init() { 48 sInstance.initInternal(); 49 } 50 51 public static KeyCodeDescriptionMapper getInstance() { 52 return sInstance; 53 } 54 55 private KeyCodeDescriptionMapper() { 56 mKeyLabelMap = new HashMap<CharSequence, Integer>(); 57 mKeyCodeMap = new HashMap<Integer, Integer>(); 58 mShiftedKeyCodeMap = new HashMap<Integer, Integer>(); 59 mShiftLockedKeyCodeMap = new HashMap<Integer, Integer>(); 60 } 61 62 private void initInternal() { 63 // Manual label substitutions for key labels with no string resource 64 mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); 65 66 // Symbols that most TTS engines can't speak 67 mKeyCodeMap.put((int) '.', R.string.spoken_description_period); 68 mKeyCodeMap.put((int) ',', R.string.spoken_description_comma); 69 mKeyCodeMap.put((int) '(', R.string.spoken_description_left_parenthesis); 70 mKeyCodeMap.put((int) ')', R.string.spoken_description_right_parenthesis); 71 mKeyCodeMap.put((int) ':', R.string.spoken_description_colon); 72 mKeyCodeMap.put((int) ';', R.string.spoken_description_semicolon); 73 mKeyCodeMap.put((int) '!', R.string.spoken_description_exclamation_mark); 74 mKeyCodeMap.put((int) '?', R.string.spoken_description_question_mark); 75 mKeyCodeMap.put((int) '\"', R.string.spoken_description_double_quote); 76 mKeyCodeMap.put((int) '\'', R.string.spoken_description_single_quote); 77 mKeyCodeMap.put((int) '*', R.string.spoken_description_star); 78 mKeyCodeMap.put((int) '#', R.string.spoken_description_pound); 79 mKeyCodeMap.put((int) ' ', R.string.spoken_description_space); 80 81 // Non-ASCII symbols (must use escape codes!) 82 mKeyCodeMap.put((int) '\u2022', R.string.spoken_description_dot); 83 mKeyCodeMap.put((int) '\u221A', R.string.spoken_description_square_root); 84 mKeyCodeMap.put((int) '\u03C0', R.string.spoken_description_pi); 85 mKeyCodeMap.put((int) '\u0394', R.string.spoken_description_delta); 86 mKeyCodeMap.put((int) '\u2122', R.string.spoken_description_trademark); 87 mKeyCodeMap.put((int) '\u2105', R.string.spoken_description_care_of); 88 mKeyCodeMap.put((int) '\u2026', R.string.spoken_description_ellipsis); 89 mKeyCodeMap.put((int) '\u201E', R.string.spoken_description_low_double_quote); 90 mKeyCodeMap.put((int) '\uFF0A', R.string.spoken_description_star); 91 92 // Special non-character codes defined in Keyboard 93 mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete); 94 mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return); 95 mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings); 96 mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift); 97 mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic); 98 mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol); 99 mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab); 100 101 // Shifted versions of non-character codes defined in Keyboard 102 mShiftedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift_shifted); 103 104 // Shift-locked versions of non-character codes defined in Keyboard 105 mShiftLockedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_caps_lock); 106 } 107 108 /** 109 * Returns the localized description of the action performed by a specified 110 * key based on the current keyboard state. 111 * <p> 112 * The order of precedence for key descriptions is: 113 * <ol> 114 * <li>Manually-defined based on the key label</li> 115 * <li>Automatic or manually-defined based on the key code</li> 116 * <li>Automatically based on the key label</li> 117 * <li>{code null} for keys with no label or key code defined</li> 118 * </p> 119 * 120 * @param context The package's context. 121 * @param keyboard The keyboard on which the key resides. 122 * @param key The key from which to obtain a description. 123 * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured. 124 * @return a character sequence describing the action performed by pressing 125 * the key 126 */ 127 public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key, 128 boolean shouldObscure) { 129 if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { 130 final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard); 131 if (description != null) 132 return description; 133 } 134 135 if (!TextUtils.isEmpty(key.mLabel)) { 136 final String label = key.mLabel.toString().trim(); 137 138 // First, attempt to map the label to a pre-defined description. 139 if (mKeyLabelMap.containsKey(label)) { 140 return context.getString(mKeyLabelMap.get(label)); 141 } 142 } 143 144 // Just attempt to speak the description. 145 if (key.mCode != Keyboard.CODE_UNSPECIFIED) { 146 return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); 147 } 148 149 return null; 150 } 151 152 /** 153 * Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL 154 * key or {@code null} if there is not a description provided for the 155 * current keyboard context. 156 * 157 * @param context The package's context. 158 * @param keyboard The keyboard on which the key resides. 159 * @return a character sequence describing the action performed by pressing 160 * the key 161 */ 162 private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) { 163 final KeyboardId id = keyboard.mId; 164 165 if (id.isAlphabetKeyboard()) { 166 return context.getString(R.string.spoken_description_to_symbol); 167 } else if (id.isSymbolsKeyboard()) { 168 return context.getString(R.string.spoken_description_to_alpha); 169 } else if (id.isPhoneShiftKeyboard()) { 170 return context.getString(R.string.spoken_description_to_numeric); 171 } else if (id.isPhoneKeyboard()) { 172 return context.getString(R.string.spoken_description_to_symbol); 173 } else { 174 return null; 175 } 176 } 177 178 /** 179 * Returns a localized character sequence describing what will happen when 180 * the specified key is pressed based on its key code. 181 * <p> 182 * The order of precedence for key code descriptions is: 183 * <ol> 184 * <li>Manually-defined shift-locked description</li> 185 * <li>Manually-defined shifted description</li> 186 * <li>Manually-defined normal description</li> 187 * <li>Automatic based on the character represented by the key code</li> 188 * <li>Fall-back for undefined or control characters</li> 189 * </ol> 190 * </p> 191 * 192 * @param context The package's context. 193 * @param keyboard The keyboard on which the key resides. 194 * @param key The key from which to obtain a description. 195 * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured. 196 * @return a character sequence describing the action performed by pressing 197 * the key 198 */ 199 private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key, 200 boolean shouldObscure) { 201 final int code = key.mCode; 202 203 if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) { 204 return context.getString(mShiftLockedKeyCodeMap.get(code)); 205 } else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) { 206 return context.getString(mShiftedKeyCodeMap.get(code)); 207 } 208 209 // If the key description should be obscured, now is the time to do it. 210 final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code); 211 if (shouldObscure && isDefinedNonCtrl) { 212 return context.getString(OBSCURED_KEY_RES_ID); 213 } 214 215 if (mKeyCodeMap.containsKey(code)) { 216 return context.getString(mKeyCodeMap.get(code)); 217 } else if (isDefinedNonCtrl) { 218 return Character.toString((char) code); 219 } else { 220 return context.getString(R.string.spoken_description_unknown, code); 221 } 222 } 223} 224