diff options
Diffstat (limited to 'java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java')
-rw-r--r-- | java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java | 127 |
1 files changed, 68 insertions, 59 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 2c87fc1e9..7a3510ee1 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -28,35 +28,29 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.StringUtils; import java.util.Locale; -public final class KeyCodeDescriptionMapper { +final class KeyCodeDescriptionMapper { private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName(); private static final String SPOKEN_LETTER_RESOURCE_NAME_FORMAT = "spoken_accented_letter_%04X"; + private static final String SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT = "spoken_symbol_%04X"; private static final String SPOKEN_EMOJI_RESOURCE_NAME_FORMAT = "spoken_emoji_%04X"; // The resource ID of the string spoken for obscured keys private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot; - private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); - - // Sparse array of spoken description resource IDs indexed by key codes - private final SparseIntArray mKeyCodeMap; - - public static void init() { - sInstance.initInternal(); - } + private static final KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); public static KeyCodeDescriptionMapper getInstance() { return sInstance; } - private KeyCodeDescriptionMapper() { - mKeyCodeMap = new SparseIntArray(); - } + // Sparse array of spoken description resource IDs indexed by key codes + private final SparseIntArray mKeyCodeMap = new SparseIntArray(); - private void initInternal() { + private KeyCodeDescriptionMapper() { // Special non-character codes defined in Keyboard mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space); mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete); @@ -86,14 +80,6 @@ public final class KeyCodeDescriptionMapper { /** * Returns the localized description of the action performed by a specified * key based on the current keyboard state. - * <p> - * The order of precedence for key descriptions is: - * <ol> - * <li>Manually-defined based on the key label</li> - * <li>Automatic or manually-defined based on the key code</li> - * <li>Automatically based on the key label</li> - * <li>{code null} for keys with no label or key code defined</li> - * </p> * * @param context The package's context. * @param keyboard The keyboard on which the key resides. @@ -128,7 +114,20 @@ public final class KeyCodeDescriptionMapper { // Just attempt to speak the description. if (code != Constants.CODE_UNSPECIFIED) { - return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); + // If the key description should be obscured, now is the time to do it. + final boolean isDefinedNonCtrl = Character.isDefined(code) + && !Character.isISOControl(code); + if (shouldObscure && isDefinedNonCtrl) { + return context.getString(OBSCURED_KEY_RES_ID); + } + final String description = getDescriptionForCodePoint(context, code); + if (description != null) { + return description; + } + if (!TextUtils.isEmpty(key.getLabel())) { + return key.getLabel(); + } + return context.getString(R.string.spoken_description_unknown); } return null; } @@ -254,57 +253,39 @@ public final class KeyCodeDescriptionMapper { /** * Returns a localized character sequence describing what will happen when - * the specified key is pressed based on its key code. - * <p> - * The order of precedence for key code descriptions is: - * <ol> - * <li>Manually-defined shift-locked description</li> - * <li>Manually-defined shifted description</li> - * <li>Manually-defined normal description</li> - * <li>Automatic based on the character represented by the key code</li> - * <li>Fall-back for undefined or control characters</li> - * </ol> - * </p> + * the specified key is pressed based on its key code point. * * @param context The package's context. - * @param keyboard The keyboard on which the key resides. - * @param key The key from which to obtain a description. - * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured. - * @return a character sequence describing the action performed by pressing the key + * @param codePoint The code point from which to obtain a description. + * @return a character sequence describing the code point. */ - private String getDescriptionForKeyCode(final Context context, final Keyboard keyboard, - final Key key, final boolean shouldObscure) { - final int code = key.getCode(); - + public String getDescriptionForCodePoint(final Context context, final int codePoint) { // If the key description should be obscured, now is the time to do it. - final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code); - if (shouldObscure && isDefinedNonCtrl) { - return context.getString(OBSCURED_KEY_RES_ID); - } - final int index = mKeyCodeMap.indexOfKey(code); + final int index = mKeyCodeMap.indexOfKey(codePoint); if (index >= 0) { return context.getString(mKeyCodeMap.valueAt(index)); } - final String accentedLetter = getSpokenAccentedLetterDescriptionId(context, code); + final String accentedLetter = getSpokenAccentedLetterDescription(context, codePoint); if (accentedLetter != null) { return accentedLetter; } - // Here, <code>code</code> may be a base letter. - final int spokenEmojiId = getSpokenDescriptionId( - context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT); - if (spokenEmojiId != 0) { - return context.getString(spokenEmojiId); + // Here, <code>code</code> may be a base (non-accented) letter. + final String unsupportedSymbol = getSpokenSymbolDescription(context, codePoint); + if (unsupportedSymbol != null) { + return unsupportedSymbol; } - if (isDefinedNonCtrl) { - return Character.toString((char) code); + final String emojiDescription = getSpokenEmojiDescription(context, codePoint); + if (emojiDescription != null) { + return emojiDescription; } - if (!TextUtils.isEmpty(key.getLabel())) { - return key.getLabel(); + if (Character.isDefined(codePoint) && !Character.isISOControl(codePoint)) { + return StringUtils.newSingleCodePointString(codePoint); } - return context.getString(R.string.spoken_description_unknown, code); + return null; } - private String getSpokenAccentedLetterDescriptionId(final Context context, final int code) { + // TODO: Remove this method once TTS supports those accented letters' verbalization. + private String getSpokenAccentedLetterDescription(final Context context, final int code) { final boolean isUpperCase = Character.isUpperCase(code); final int baseCode = isUpperCase ? Character.toLowerCase(code) : code; final int baseIndex = mKeyCodeMap.indexOfKey(baseCode); @@ -314,10 +295,38 @@ public final class KeyCodeDescriptionMapper { return null; } final String spokenText = context.getString(resId); - return isUpperCase ? context.getString(R.string.spoke_description_upper_case, spokenText) + return isUpperCase ? context.getString(R.string.spoken_description_upper_case, spokenText) : spokenText; } + // TODO: Remove this method once TTS supports those symbols' verbalization. + private String getSpokenSymbolDescription(final Context context, final int code) { + final int resId = getSpokenDescriptionId(context, code, SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT); + if (resId == 0) { + return null; + } + final String spokenText = context.getString(resId); + if (!TextUtils.isEmpty(spokenText)) { + return spokenText; + } + // If a translated description is empty, fall back to unknown symbol description. + return context.getString(R.string.spoken_symbol_unknown); + } + + // TODO: Remove this method once TTS supports emoji verbalization. + private String getSpokenEmojiDescription(final Context context, final int code) { + final int resId = getSpokenDescriptionId(context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT); + if (resId == 0) { + return null; + } + final String spokenText = context.getString(resId); + if (!TextUtils.isEmpty(spokenText)) { + return spokenText; + } + // If a translated description is empty, fall back to unknown emoji description. + return context.getString(R.string.spoken_emoji_unknown); + } + private int getSpokenDescriptionId(final Context context, final int code, final String resourceNameFormat) { final String resourceName = String.format(Locale.ROOT, resourceNameFormat, code); |