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