KeyCodeDescriptionMapper.java revision 02dd837805fec1a0f7b0ddf888b3548f5ac2922b
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 public static void init() { 42 sInstance.initInternal(); 43 } 44 45 public static KeyCodeDescriptionMapper getInstance() { 46 return sInstance; 47 } 48 49 private KeyCodeDescriptionMapper() { 50 mKeyLabelMap = new HashMap<CharSequence, Integer>(); 51 mKeyCodeMap = new HashMap<Integer, Integer>(); 52 } 53 54 private void initInternal() { 55 // Manual label substitutions for key labels with no string resource 56 mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); 57 58 // Symbols that most TTS engines can't speak 59 mKeyCodeMap.put((int) ' ', R.string.spoken_description_space); 60 61 // Special non-character codes defined in Keyboard 62 mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete); 63 mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return); 64 mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings); 65 mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift); 66 mKeyCodeMap.put(Keyboard.CODE_CAPSLOCK, R.string.spoken_description_caps_lock); 67 mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic); 68 mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol); 69 mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab); 70 } 71 72 /** 73 * Returns the localized description of the action performed by a specified 74 * key based on the current keyboard state. 75 * <p> 76 * The order of precedence for key descriptions is: 77 * <ol> 78 * <li>Manually-defined based on the key label</li> 79 * <li>Automatic or manually-defined based on the key code</li> 80 * <li>Automatically based on the key label</li> 81 * <li>{code null} for keys with no label or key code defined</li> 82 * </p> 83 * 84 * @param context The package's context. 85 * @param keyboard The keyboard on which the key resides. 86 * @param key The key from which to obtain a description. 87 * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured. 88 * @return a character sequence describing the action performed by pressing 89 * the key 90 */ 91 public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key, 92 boolean shouldObscure) { 93 final int code = key.mCode; 94 95 if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { 96 final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard); 97 if (description != null) 98 return description; 99 } 100 101 if (code == Keyboard.CODE_SHIFT) { 102 return getDescriptionForShiftKey(context, keyboard); 103 } 104 105 if (!TextUtils.isEmpty(key.mLabel)) { 106 final String label = key.mLabel.toString().trim(); 107 108 // First, attempt to map the label to a pre-defined description. 109 if (mKeyLabelMap.containsKey(label)) { 110 return context.getString(mKeyLabelMap.get(label)); 111 } 112 } 113 114 // Just attempt to speak the description. 115 if (key.mCode != Keyboard.CODE_UNSPECIFIED) { 116 return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); 117 } 118 119 return null; 120 } 121 122 /** 123 * Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL 124 * key or {@code null} if there is not a description provided for the 125 * current keyboard context. 126 * 127 * @param context The package's context. 128 * @param keyboard The keyboard on which the key resides. 129 * @return a character sequence describing the action performed by pressing 130 * the key 131 */ 132 private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) { 133 final KeyboardId id = keyboard.mId; 134 135 if (id.isAlphabetKeyboard()) { 136 return context.getString(R.string.spoken_description_to_symbol); 137 } else if (id.isSymbolsKeyboard()) { 138 return context.getString(R.string.spoken_description_to_alpha); 139 } else if (id.isPhoneShiftKeyboard()) { 140 return context.getString(R.string.spoken_description_to_numeric); 141 } else if (id.isPhoneKeyboard()) { 142 return context.getString(R.string.spoken_description_to_symbol); 143 } else { 144 return null; 145 } 146 } 147 148 /** 149 * Returns a context-sensitive description of the "Shift" key. 150 * 151 * @param context The package's context. 152 * @param keyboard The keyboard on which the key resides. 153 * @return A context-sensitive description of the "Shift" key. 154 */ 155 private CharSequence getDescriptionForShiftKey(Context context, Keyboard keyboard) { 156 final int resId; 157 158 if (keyboard.isShiftLocked()) { 159 resId = R.string.spoken_description_caps_lock; 160 } else if (keyboard.isShiftedOrShiftLocked()) { 161 resId = R.string.spoken_description_shift_shifted; 162 } else { 163 resId = R.string.spoken_description_shift; 164 } 165 166 return context.getString(resId); 167 } 168 169 /** 170 * Returns a localized character sequence describing what will happen when 171 * the specified key is pressed based on its key code. 172 * <p> 173 * The order of precedence for key code descriptions is: 174 * <ol> 175 * <li>Manually-defined shift-locked description</li> 176 * <li>Manually-defined shifted description</li> 177 * <li>Manually-defined normal description</li> 178 * <li>Automatic based on the character represented by the key code</li> 179 * <li>Fall-back for undefined or control characters</li> 180 * </ol> 181 * </p> 182 * 183 * @param context The package's context. 184 * @param keyboard The keyboard on which the key resides. 185 * @param key The key from which to obtain a description. 186 * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured. 187 * @return a character sequence describing the action performed by pressing 188 * the key 189 */ 190 private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key, 191 boolean shouldObscure) { 192 final int code = key.mCode; 193 194 // If the key description should be obscured, now is the time to do it. 195 final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code); 196 if (shouldObscure && isDefinedNonCtrl) { 197 return context.getString(OBSCURED_KEY_RES_ID); 198 } 199 200 if (mKeyCodeMap.containsKey(code)) { 201 return context.getString(mKeyCodeMap.get(code)); 202 } else if (isDefinedNonCtrl) { 203 return Character.toString((char) code); 204 } else { 205 return context.getString(R.string.spoken_description_unknown, code); 206 } 207 } 208} 209