2011-05-18 00:03:25 +00:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2011 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package com.android.inputmethod.accessibility;
|
|
|
|
|
|
|
|
|
|
import android.content.Context;
|
2014-05-20 07:25:33 +00:00
|
|
|
|
import android.content.res.Resources;
|
2011-05-18 00:03:25 +00:00
|
|
|
|
import android.text.TextUtils;
|
2012-02-28 18:01:40 +00:00
|
|
|
|
import android.util.Log;
|
2012-06-29 07:32:45 +00:00
|
|
|
|
import android.util.SparseIntArray;
|
2012-05-31 23:15:21 +00:00
|
|
|
|
import android.view.inputmethod.EditorInfo;
|
2011-05-18 00:03:25 +00:00
|
|
|
|
|
2011-06-23 12:23:44 +00:00
|
|
|
|
import com.android.inputmethod.keyboard.Key;
|
2011-05-18 00:03:25 +00:00
|
|
|
|
import com.android.inputmethod.keyboard.Keyboard;
|
|
|
|
|
import com.android.inputmethod.keyboard.KeyboardId;
|
|
|
|
|
import com.android.inputmethod.latin.R;
|
2014-10-23 09:37:32 +00:00
|
|
|
|
import com.android.inputmethod.latin.common.Constants;
|
2014-10-28 12:31:09 +00:00
|
|
|
|
import com.android.inputmethod.latin.common.StringUtils;
|
2011-05-18 00:03:25 +00:00
|
|
|
|
|
2014-05-20 07:25:33 +00:00
|
|
|
|
import java.util.Locale;
|
2011-05-18 00:03:25 +00:00
|
|
|
|
|
2014-06-03 09:04:08 +00:00
|
|
|
|
final class KeyCodeDescriptionMapper {
|
2012-02-28 18:01:40 +00:00
|
|
|
|
private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName();
|
2014-05-22 04:09:24 +00:00
|
|
|
|
private static final String SPOKEN_LETTER_RESOURCE_NAME_FORMAT = "spoken_accented_letter_%04X";
|
2014-06-02 05:37:42 +00:00
|
|
|
|
private static final String SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT = "spoken_symbol_%04X";
|
2014-05-20 07:25:33 +00:00
|
|
|
|
private static final String SPOKEN_EMOJI_RESOURCE_NAME_FORMAT = "spoken_emoji_%04X";
|
2014-09-29 05:41:26 +00:00
|
|
|
|
private static final String SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX = "spoken_emoticon";
|
|
|
|
|
private static final String SPOKEN_EMOTICON_CODE_POINT_FORMAT = "_%02X";
|
2012-02-28 18:01:40 +00:00
|
|
|
|
|
2011-08-08 18:05:04 +00:00
|
|
|
|
// The resource ID of the string spoken for obscured keys
|
|
|
|
|
private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot;
|
|
|
|
|
|
2014-06-03 09:04:08 +00:00
|
|
|
|
private static final KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
|
2011-05-18 00:03:25 +00:00
|
|
|
|
|
|
|
|
|
public static KeyCodeDescriptionMapper getInstance() {
|
|
|
|
|
return sInstance;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-03 09:04:08 +00:00
|
|
|
|
// Sparse array of spoken description resource IDs indexed by key codes
|
|
|
|
|
private final SparseIntArray mKeyCodeMap = new SparseIntArray();
|
2011-05-18 00:03:25 +00:00
|
|
|
|
|
2014-06-03 09:04:08 +00:00
|
|
|
|
private KeyCodeDescriptionMapper() {
|
2011-05-18 00:03:25 +00:00
|
|
|
|
// Special non-character codes defined in Keyboard
|
2012-10-29 05:46:34 +00:00
|
|
|
|
mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_ENTER, R.string.spoken_description_return);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_SETTINGS, R.string.spoken_description_settings);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH,
|
|
|
|
|
R.string.spoken_description_language_switch);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_ACTION_NEXT, R.string.spoken_description_action_next);
|
|
|
|
|
mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS,
|
|
|
|
|
R.string.spoken_description_action_previous);
|
2014-03-25 02:57:41 +00:00
|
|
|
|
mKeyCodeMap.put(Constants.CODE_EMOJI, R.string.spoken_description_emoji);
|
2014-05-22 04:09:24 +00:00
|
|
|
|
// Because the upper-case and lower-case mappings of the following letters is depending on
|
|
|
|
|
// the locale, the upper case descriptions should be defined here. The lower case
|
|
|
|
|
// descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}.
|
|
|
|
|
// U+0049: "I" LATIN CAPITAL LETTER I
|
|
|
|
|
// U+0069: "i" LATIN SMALL LETTER I
|
|
|
|
|
// U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE
|
|
|
|
|
// U+0131: "ı" LATIN SMALL LETTER DOTLESS I
|
|
|
|
|
mKeyCodeMap.put(0x0049, R.string.spoken_letter_0049);
|
|
|
|
|
mKeyCodeMap.put(0x0130, R.string.spoken_letter_0130);
|
2011-05-18 00:03:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the localized description of the action performed by a specified
|
|
|
|
|
* key based on the current keyboard state.
|
|
|
|
|
*
|
|
|
|
|
* @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.
|
2011-08-08 18:05:04 +00:00
|
|
|
|
* @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
|
2013-01-06 02:10:27 +00:00
|
|
|
|
* @return a character sequence describing the action performed by pressing the key
|
2011-05-18 00:03:25 +00:00
|
|
|
|
*/
|
2013-01-06 02:10:27 +00:00
|
|
|
|
public String getDescriptionForKey(final Context context, final Keyboard keyboard,
|
|
|
|
|
final Key key, final boolean shouldObscure) {
|
2013-08-12 09:05:11 +00:00
|
|
|
|
final int code = key.getCode();
|
2012-01-31 21:03:39 +00:00
|
|
|
|
|
2012-10-29 05:46:34 +00:00
|
|
|
|
if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
|
2012-02-28 18:01:40 +00:00
|
|
|
|
final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
|
2013-01-06 02:10:27 +00:00
|
|
|
|
if (description != null) {
|
2011-05-18 00:03:25 +00:00
|
|
|
|
return description;
|
2013-01-06 02:10:27 +00:00
|
|
|
|
}
|
2011-05-18 00:03:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-10-29 05:46:34 +00:00
|
|
|
|
if (code == Constants.CODE_SHIFT) {
|
2012-01-31 21:03:39 +00:00
|
|
|
|
return getDescriptionForShiftKey(context, keyboard);
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-19 19:45:44 +00:00
|
|
|
|
if (code == Constants.CODE_ENTER) {
|
|
|
|
|
// The following function returns the correct description in all action and
|
|
|
|
|
// regular enter cases, taking care of all modes.
|
2012-05-31 23:15:21 +00:00
|
|
|
|
return getDescriptionForActionKey(context, keyboard, key);
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-20 07:25:33 +00:00
|
|
|
|
if (code == Constants.CODE_OUTPUT_TEXT) {
|
2014-09-29 05:41:26 +00:00
|
|
|
|
final String outputText = key.getOutputText();
|
|
|
|
|
final String description = getSpokenEmoticonDescription(context, outputText);
|
|
|
|
|
return TextUtils.isEmpty(description) ? outputText : description;
|
2011-11-22 01:56:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Just attempt to speak the description.
|
2014-05-20 07:25:33 +00:00
|
|
|
|
if (code != Constants.CODE_UNSPECIFIED) {
|
2014-06-09 06:48:20 +00:00
|
|
|
|
// 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);
|
2011-05-18 00:03:25 +00:00
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL
|
|
|
|
|
* key or {@code null} if there is not a description provided for the
|
|
|
|
|
* current keyboard context.
|
|
|
|
|
*
|
|
|
|
|
* @param context The package's context.
|
|
|
|
|
* @param keyboard The keyboard on which the key resides.
|
2013-01-06 02:10:27 +00:00
|
|
|
|
* @return a character sequence describing the action performed by pressing the key
|
2011-05-18 00:03:25 +00:00
|
|
|
|
*/
|
2014-05-20 07:25:33 +00:00
|
|
|
|
private static String getDescriptionForSwitchAlphaSymbol(final Context context,
|
2013-01-06 02:10:27 +00:00
|
|
|
|
final Keyboard keyboard) {
|
2012-02-07 21:14:18 +00:00
|
|
|
|
final KeyboardId keyboardId = keyboard.mId;
|
|
|
|
|
final int elementId = keyboardId.mElementId;
|
|
|
|
|
final int resId;
|
|
|
|
|
|
|
|
|
|
switch (elementId) {
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET:
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
|
|
|
|
|
resId = R.string.spoken_description_to_symbol;
|
|
|
|
|
break;
|
|
|
|
|
case KeyboardId.ELEMENT_SYMBOLS:
|
2013-09-04 03:57:04 +00:00
|
|
|
|
case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
|
2012-02-07 21:14:18 +00:00
|
|
|
|
resId = R.string.spoken_description_to_alpha;
|
|
|
|
|
break;
|
|
|
|
|
case KeyboardId.ELEMENT_PHONE:
|
|
|
|
|
resId = R.string.spoken_description_to_symbol;
|
|
|
|
|
break;
|
|
|
|
|
case KeyboardId.ELEMENT_PHONE_SYMBOLS:
|
|
|
|
|
resId = R.string.spoken_description_to_numeric;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2012-02-28 18:01:40 +00:00
|
|
|
|
Log.e(TAG, "Missing description for keyboard element ID:" + elementId);
|
2011-05-18 00:03:25 +00:00
|
|
|
|
return null;
|
|
|
|
|
}
|
2012-02-07 21:14:18 +00:00
|
|
|
|
return context.getString(resId);
|
2011-05-18 00:03:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-31 21:03:39 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns a context-sensitive description of the "Shift" key.
|
|
|
|
|
*
|
|
|
|
|
* @param context The package's context.
|
|
|
|
|
* @param keyboard The keyboard on which the key resides.
|
|
|
|
|
* @return A context-sensitive description of the "Shift" key.
|
|
|
|
|
*/
|
2014-05-20 07:25:33 +00:00
|
|
|
|
private static String getDescriptionForShiftKey(final Context context,
|
|
|
|
|
final Keyboard keyboard) {
|
2012-02-07 21:14:18 +00:00
|
|
|
|
final KeyboardId keyboardId = keyboard.mId;
|
|
|
|
|
final int elementId = keyboardId.mElementId;
|
2012-01-31 21:03:39 +00:00
|
|
|
|
final int resId;
|
|
|
|
|
|
2012-02-07 21:14:18 +00:00
|
|
|
|
switch (elementId) {
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
|
2012-01-31 21:03:39 +00:00
|
|
|
|
resId = R.string.spoken_description_caps_lock;
|
2012-02-07 21:14:18 +00:00
|
|
|
|
break;
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
|
|
|
|
|
case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
|
2012-01-31 21:03:39 +00:00
|
|
|
|
resId = R.string.spoken_description_shift_shifted;
|
2012-02-07 21:14:18 +00:00
|
|
|
|
break;
|
2014-05-13 06:36:11 +00:00
|
|
|
|
case KeyboardId.ELEMENT_SYMBOLS:
|
|
|
|
|
resId = R.string.spoken_description_symbols_shift;
|
|
|
|
|
break;
|
|
|
|
|
case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
|
|
|
|
|
resId = R.string.spoken_description_symbols_shift_shifted;
|
|
|
|
|
break;
|
2012-02-07 21:14:18 +00:00
|
|
|
|
default:
|
2012-01-31 21:03:39 +00:00
|
|
|
|
resId = R.string.spoken_description_shift;
|
|
|
|
|
}
|
|
|
|
|
return context.getString(resId);
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-31 23:15:21 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns a context-sensitive description of the "Enter" action key.
|
|
|
|
|
*
|
|
|
|
|
* @param context The package's context.
|
|
|
|
|
* @param keyboard The keyboard on which the key resides.
|
|
|
|
|
* @param key The key to describe.
|
2013-01-06 02:10:27 +00:00
|
|
|
|
* @return Returns a context-sensitive description of the "Enter" action key.
|
2012-05-31 23:15:21 +00:00
|
|
|
|
*/
|
2014-05-20 07:25:33 +00:00
|
|
|
|
private static String getDescriptionForActionKey(final Context context, final Keyboard keyboard,
|
2013-01-06 02:10:27 +00:00
|
|
|
|
final Key key) {
|
2012-05-31 23:15:21 +00:00
|
|
|
|
final KeyboardId keyboardId = keyboard.mId;
|
2013-01-15 08:56:37 +00:00
|
|
|
|
final int actionId = keyboardId.imeAction();
|
2012-05-31 23:15:21 +00:00
|
|
|
|
final int resId;
|
|
|
|
|
|
|
|
|
|
// Always use the label, if available.
|
2013-08-12 09:05:11 +00:00
|
|
|
|
if (!TextUtils.isEmpty(key.getLabel())) {
|
|
|
|
|
return key.getLabel().trim();
|
2012-05-31 23:15:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, use the action ID.
|
|
|
|
|
switch (actionId) {
|
|
|
|
|
case EditorInfo.IME_ACTION_SEARCH:
|
|
|
|
|
resId = R.string.spoken_description_search;
|
|
|
|
|
break;
|
|
|
|
|
case EditorInfo.IME_ACTION_GO:
|
|
|
|
|
resId = R.string.label_go_key;
|
|
|
|
|
break;
|
|
|
|
|
case EditorInfo.IME_ACTION_SEND:
|
|
|
|
|
resId = R.string.label_send_key;
|
|
|
|
|
break;
|
|
|
|
|
case EditorInfo.IME_ACTION_NEXT:
|
|
|
|
|
resId = R.string.label_next_key;
|
|
|
|
|
break;
|
|
|
|
|
case EditorInfo.IME_ACTION_DONE:
|
|
|
|
|
resId = R.string.label_done_key;
|
|
|
|
|
break;
|
|
|
|
|
case EditorInfo.IME_ACTION_PREVIOUS:
|
|
|
|
|
resId = R.string.label_previous_key;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
resId = R.string.spoken_description_return;
|
|
|
|
|
}
|
|
|
|
|
return context.getString(resId);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-18 00:03:25 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns a localized character sequence describing what will happen when
|
2014-06-09 06:48:20 +00:00
|
|
|
|
* the specified key is pressed based on its key code point.
|
2011-05-18 00:03:25 +00:00
|
|
|
|
*
|
|
|
|
|
* @param context The package's context.
|
2014-06-09 06:48:20 +00:00
|
|
|
|
* @param codePoint The code point from which to obtain a description.
|
|
|
|
|
* @return a character sequence describing the code point.
|
2011-05-18 00:03:25 +00:00
|
|
|
|
*/
|
2014-06-09 06:48:20 +00:00
|
|
|
|
public String getDescriptionForCodePoint(final Context context, final int codePoint) {
|
2011-08-08 18:05:04 +00:00
|
|
|
|
// If the key description should be obscured, now is the time to do it.
|
2014-06-09 06:48:20 +00:00
|
|
|
|
final int index = mKeyCodeMap.indexOfKey(codePoint);
|
2014-05-22 04:09:24 +00:00
|
|
|
|
if (index >= 0) {
|
|
|
|
|
return context.getString(mKeyCodeMap.valueAt(index));
|
2013-01-06 02:10:27 +00:00
|
|
|
|
}
|
2014-06-09 06:48:20 +00:00
|
|
|
|
final String accentedLetter = getSpokenAccentedLetterDescription(context, codePoint);
|
2014-05-22 04:09:24 +00:00
|
|
|
|
if (accentedLetter != null) {
|
|
|
|
|
return accentedLetter;
|
|
|
|
|
}
|
2014-05-27 01:40:36 +00:00
|
|
|
|
// Here, <code>code</code> may be a base (non-accented) letter.
|
2014-06-09 06:48:20 +00:00
|
|
|
|
final String unsupportedSymbol = getSpokenSymbolDescription(context, codePoint);
|
2014-06-02 05:37:42 +00:00
|
|
|
|
if (unsupportedSymbol != null) {
|
|
|
|
|
return unsupportedSymbol;
|
|
|
|
|
}
|
2014-06-09 06:48:20 +00:00
|
|
|
|
final String emojiDescription = getSpokenEmojiDescription(context, codePoint);
|
2014-05-27 01:40:36 +00:00
|
|
|
|
if (emojiDescription != null) {
|
|
|
|
|
return emojiDescription;
|
2014-05-20 07:25:33 +00:00
|
|
|
|
}
|
2014-06-09 06:48:20 +00:00
|
|
|
|
if (Character.isDefined(codePoint) && !Character.isISOControl(codePoint)) {
|
|
|
|
|
return StringUtils.newSingleCodePointString(codePoint);
|
2011-05-18 00:03:25 +00:00
|
|
|
|
}
|
2014-06-09 06:48:20 +00:00
|
|
|
|
return null;
|
2011-05-18 00:03:25 +00:00
|
|
|
|
}
|
2014-05-20 07:25:33 +00:00
|
|
|
|
|
2014-06-02 05:37:42 +00:00
|
|
|
|
// TODO: Remove this method once TTS supports those accented letters' verbalization.
|
2014-05-27 01:40:36 +00:00
|
|
|
|
private String getSpokenAccentedLetterDescription(final Context context, final int code) {
|
2014-05-22 04:09:24 +00:00
|
|
|
|
final boolean isUpperCase = Character.isUpperCase(code);
|
|
|
|
|
final int baseCode = isUpperCase ? Character.toLowerCase(code) : code;
|
|
|
|
|
final int baseIndex = mKeyCodeMap.indexOfKey(baseCode);
|
|
|
|
|
final int resId = (baseIndex >= 0) ? mKeyCodeMap.valueAt(baseIndex)
|
|
|
|
|
: getSpokenDescriptionId(context, baseCode, SPOKEN_LETTER_RESOURCE_NAME_FORMAT);
|
|
|
|
|
if (resId == 0) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
final String spokenText = context.getString(resId);
|
2014-05-27 01:40:36 +00:00
|
|
|
|
return isUpperCase ? context.getString(R.string.spoken_description_upper_case, spokenText)
|
|
|
|
|
: spokenText;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-02 05:37:42 +00:00
|
|
|
|
// 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.
|
2014-05-27 01:40:36 +00:00
|
|
|
|
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);
|
2014-06-02 05:37:42 +00:00
|
|
|
|
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);
|
2014-05-22 04:09:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int getSpokenDescriptionId(final Context context, final int code,
|
2014-05-20 07:25:33 +00:00
|
|
|
|
final String resourceNameFormat) {
|
|
|
|
|
final String resourceName = String.format(Locale.ROOT, resourceNameFormat, code);
|
|
|
|
|
final Resources resources = context.getResources();
|
2014-05-22 04:09:24 +00:00
|
|
|
|
// Note that the resource package name may differ from the context package name.
|
|
|
|
|
final String resourcePackageName = resources.getResourcePackageName(
|
2014-05-20 07:25:33 +00:00
|
|
|
|
R.string.spoken_description_unknown);
|
2014-05-22 04:09:24 +00:00
|
|
|
|
final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName);
|
|
|
|
|
if (resId != 0) {
|
|
|
|
|
mKeyCodeMap.append(code, resId);
|
|
|
|
|
}
|
|
|
|
|
return resId;
|
2014-05-20 07:25:33 +00:00
|
|
|
|
}
|
2014-09-29 05:41:26 +00:00
|
|
|
|
|
|
|
|
|
// TODO: Remove this method once TTS supports emoticon verbalization.
|
2014-10-20 05:48:56 +00:00
|
|
|
|
private static String getSpokenEmoticonDescription(final Context context,
|
|
|
|
|
final String outputText) {
|
2014-09-29 05:41:26 +00:00
|
|
|
|
final StringBuilder sb = new StringBuilder(SPOKEN_EMOTICON_RESOURCE_NAME_PREFIX);
|
|
|
|
|
final int textLength = outputText.length();
|
|
|
|
|
for (int index = 0; index < textLength; index = outputText.offsetByCodePoints(index, 1)) {
|
|
|
|
|
final int codePoint = outputText.codePointAt(index);
|
|
|
|
|
sb.append(String.format(Locale.ROOT, SPOKEN_EMOTICON_CODE_POINT_FORMAT, codePoint));
|
|
|
|
|
}
|
|
|
|
|
final String resourceName = sb.toString();
|
|
|
|
|
final Resources resources = context.getResources();
|
|
|
|
|
// Note that the resource package name may differ from the context package name.
|
|
|
|
|
final String resourcePackageName = resources.getResourcePackageName(
|
|
|
|
|
R.string.spoken_description_unknown);
|
|
|
|
|
final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName);
|
|
|
|
|
return (resId == 0) ? null : resources.getString(resId);
|
|
|
|
|
}
|
2011-05-18 00:03:25 +00:00
|
|
|
|
}
|