15ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette/*
25ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Copyright (C) 2011 The Android Open Source Project
35ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette *
45ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
55ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * you may not use this file except in compliance with the License.
65ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * You may obtain a copy of the License at
75ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette *
85ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
95ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette *
105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Unless required by applicable law or agreed to in writing, software
115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
125ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * See the License for the specific language governing permissions and
145ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * limitations under the License.
155ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */
165ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
175ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverettepackage com.android.inputmethod.accessibility;
185ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
195ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.content.Context;
205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.content.SharedPreferences;
215ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.text.TextUtils;
225ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
23e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaokaimport com.android.inputmethod.keyboard.Key;
245ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.keyboard.Keyboard;
255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.keyboard.KeyboardId;
265ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.latin.R;
275ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
285ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport java.util.HashMap;
295ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
305ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverettepublic class KeyCodeDescriptionMapper {
3158e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette    // The resource ID of the string spoken for obscured keys
3258e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette    private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot;
3358e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette
345ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
355ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
365ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    // Map of key labels to spoken description resource IDs
375ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private final HashMap<CharSequence, Integer> mKeyLabelMap;
385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    // Map of key codes to spoken description resource IDs
405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private final HashMap<Integer, Integer> mKeyCodeMap;
415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    // Map of shifted key codes to spoken description resource IDs
435ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private final HashMap<Integer, Integer> mShiftedKeyCodeMap;
445ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
455ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    // Map of shift-locked key codes to spoken description resource IDs
465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private final HashMap<Integer, Integer> mShiftLockedKeyCodeMap;
475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    public static void init(Context context, SharedPreferences prefs) {
495ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        sInstance.initInternal(context, prefs);
505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    public static KeyCodeDescriptionMapper getInstance() {
535ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        return sInstance;
545ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private KeyCodeDescriptionMapper() {
575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyLabelMap = new HashMap<CharSequence, Integer>();
585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap = new HashMap<Integer, Integer>();
595ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mShiftedKeyCodeMap = new HashMap<Integer, Integer>();
605ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mShiftLockedKeyCodeMap = new HashMap<Integer, Integer>();
615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private void initInternal(Context context, SharedPreferences prefs) {
645ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // Manual label substitutions for key labels with no string resource
655ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
675ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // Symbols that most TTS engines can't speak
685ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '.', R.string.spoken_description_period);
695ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) ',', R.string.spoken_description_comma);
705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '(', R.string.spoken_description_left_parenthesis);
715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) ')', R.string.spoken_description_right_parenthesis);
725ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) ':', R.string.spoken_description_colon);
735ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) ';', R.string.spoken_description_semicolon);
745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '!', R.string.spoken_description_exclamation_mark);
755ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '?', R.string.spoken_description_question_mark);
765ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\"', R.string.spoken_description_double_quote);
775ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\'', R.string.spoken_description_single_quote);
785ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '*', R.string.spoken_description_star);
795ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '#', R.string.spoken_description_pound);
805ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // Non-ASCII symbols (must use escape codes!)
835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u2022', R.string.spoken_description_dot);
845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u221A', R.string.spoken_description_square_root);
855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u03C0', R.string.spoken_description_pi);
865ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u0394', R.string.spoken_description_delta);
875ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u2122', R.string.spoken_description_trademark);
885ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u2105', R.string.spoken_description_care_of);
895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u2026', R.string.spoken_description_ellipsis);
905ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put((int) '\u201E', R.string.spoken_description_low_double_quote);
910464850e6c27eaad642b9dacad44e654cab120aeTadashi G. Takaoka        mKeyCodeMap.put((int) '\uFF0A', R.string.spoken_description_star);
925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // Special non-character codes defined in Keyboard
945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return);
965ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings);
975ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift);
985ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic);
995ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
1005ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab);
1015ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1025ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // Shifted versions of non-character codes defined in Keyboard
1035ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mShiftedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift_shifted);
1045ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1055ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // Shift-locked versions of non-character codes defined in Keyboard
1065ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        mShiftLockedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_caps_lock);
1075ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
1085ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1095ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    /**
1105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * Returns the localized description of the action performed by a specified
1115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * key based on the current keyboard state.
1125ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <p>
1135ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * The order of precedence for key descriptions is:
1145ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <ol>
1155ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Manually-defined based on the key label</li>
1165ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Automatic or manually-defined based on the key code</li>
1175ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Automatically based on the key label</li>
1185ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>{code null} for keys with no label or key code defined</li>
1195ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * </p>
1205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     *
1215ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param context The package's context.
1225ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param keyboard The keyboard on which the key resides.
1235ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param key The key from which to obtain a description.
12458e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette     * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
1255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @return a character sequence describing the action performed by pressing
1265ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     *         the key
1275ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     */
12858e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette    public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key,
12958e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette            boolean shouldObscure) {
1305ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
1315ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
1325ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            if (description != null)
1335ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette                return description;
1345ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        }
1355ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1365ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        if (!TextUtils.isEmpty(key.mLabel)) {
1375ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            final String label = key.mLabel.toString().trim();
1385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            if (mKeyLabelMap.containsKey(label)) {
1405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette                return context.getString(mKeyLabelMap.get(label));
1415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            } else if (label.length() == 1
1425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette                    || (keyboard.isManualTemporaryUpperCase() && !TextUtils
143520a297ad1d148a57bcf6559a9802d5d49182d70Tadashi G. Takaoka                            .isEmpty(key.mHintLabel))) {
14458e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette                return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
1455ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            } else {
1465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette                return label;
1475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            }
1485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        } else if (key.mCode != Keyboard.CODE_DUMMY) {
14958e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette            return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
1505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        }
1515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        return null;
1535ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
1545ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    /**
1565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL
1575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * key or {@code null} if there is not a description provided for the
1585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * current keyboard context.
1595ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     *
1605ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param context The package's context.
1615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param keyboard The keyboard on which the key resides.
1625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @return a character sequence describing the action performed by pressing
1635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     *         the key
1645ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     */
1655ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
1665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        final KeyboardId id = keyboard.mId;
1675ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1685ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        if (id.isAlphabetKeyboard()) {
1695ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(R.string.spoken_description_to_symbol);
1705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        } else if (id.isSymbolsKeyboard()) {
1715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(R.string.spoken_description_to_alpha);
17260ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        } else if (id.isPhoneShiftKeyboard()) {
1735ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(R.string.spoken_description_to_numeric);
1745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        } else if (id.isPhoneKeyboard()) {
1755ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(R.string.spoken_description_to_symbol);
1765ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        } else {
1775ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return null;
1785ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        }
1795ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
1805ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    /**
1825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * Returns the keycode for the specified key given the current keyboard
1835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * state.
1845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     *
1855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param keyboard The keyboard on which the key resides.
1865ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param key The key from which to obtain a key code.
1875ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @return the key code for the specified key
1885ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     */
1895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    private int getCorrectKeyCode(Keyboard keyboard, Key key) {
190520a297ad1d148a57bcf6559a9802d5d49182d70Tadashi G. Takaoka        if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLabel)) {
191520a297ad1d148a57bcf6559a9802d5d49182d70Tadashi G. Takaoka            return key.mHintLabel.charAt(0);
1925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        } else {
1935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return key.mCode;
1945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        }
1955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
1965ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
1975ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    /**
1985ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * Returns a localized character sequence describing what will happen when
1995ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * the specified key is pressed based on its key code.
2005ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <p>
2015ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * The order of precedence for key code descriptions is:
2025ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <ol>
2035ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Manually-defined shift-locked description</li>
2045ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Manually-defined shifted description</li>
2055ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Manually-defined normal description</li>
2065ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Automatic based on the character represented by the key code</li>
2075ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * <li>Fall-back for undefined or control characters</li>
2085ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * </ol>
2095ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * </p>
2105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     *
2115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param context The package's context.
2125ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param keyboard The keyboard on which the key resides.
2135ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @param key The key from which to obtain a description.
21458e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette     * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
2155ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     * @return a character sequence describing the action performed by pressing
2165ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     *         the key
2175ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette     */
21858e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette    private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key,
21958e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette            boolean shouldObscure) {
2205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        final int code = getCorrectKeyCode(keyboard, key);
2215ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
2225ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) {
2235ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(mShiftLockedKeyCodeMap.get(code));
2245ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        } else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) {
2255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(mShiftedKeyCodeMap.get(code));
22658e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette        }
22758e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette
22858e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette        // If the key description should be obscured, now is the time to do it.
22958e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette        final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code);
23058e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette        if (shouldObscure && isDefinedNonCtrl) {
23158e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette            return context.getString(OBSCURED_KEY_RES_ID);
23258e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette        }
23358e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette
23458e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette        if (mKeyCodeMap.containsKey(code)) {
2355ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(mKeyCodeMap.get(code));
23658e3f1065ef47e7116299b9d5087ba2a2b6065a2Alan Viverette        } else if (isDefinedNonCtrl) {
2375ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return Character.toString((char) code);
2385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        } else {
2395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette            return context.getString(R.string.spoken_description_unknown, code);
2405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        }
2415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette    }
2425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette}
243