Home | History | Annotate | Download | only in keyboard
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.inputmethod.keyboard;
     18 
     19 import android.util.Log;
     20 import android.util.SparseArray;
     21 
     22 import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
     23 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
     24 import com.android.inputmethod.keyboard.internal.KeyboardParams;
     25 import com.android.inputmethod.latin.CollectionUtils;
     26 
     27 
     28 
     29 /**
     30  * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
     31  * consists of rows of keys.
     32  * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
     33  * <pre>
     34  * &lt;Keyboard
     35  *         latin:keyWidth="%10p"
     36  *         latin:keyHeight="50px"
     37  *         latin:horizontalGap="2px"
     38  *         latin:verticalGap="2px" &gt;
     39  *     &lt;Row latin:keyWidth="32px" &gt;
     40  *         &lt;Key latin:keyLabel="A" /&gt;
     41  *         ...
     42  *     &lt;/Row&gt;
     43  *     ...
     44  * &lt;/Keyboard&gt;
     45  * </pre>
     46  */
     47 public class Keyboard {
     48     private static final String TAG = Keyboard.class.getSimpleName();
     49 
     50     /** Some common keys code. Must be positive.
     51      * These should be aligned with values/keycodes.xml
     52      */
     53     public static final int CODE_ENTER = '\n';
     54     public static final int CODE_TAB = '\t';
     55     public static final int CODE_SPACE = ' ';
     56     public static final int CODE_PERIOD = '.';
     57     public static final int CODE_DASH = '-';
     58     public static final int CODE_SINGLE_QUOTE = '\'';
     59     public static final int CODE_DOUBLE_QUOTE = '"';
     60     public static final int CODE_QUESTION_MARK = '?';
     61     public static final int CODE_EXCLAMATION_MARK = '!';
     62     // TODO: Check how this should work for right-to-left languages. It seems to stand
     63     // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
     64     // managed by the font? Or is it a different char?
     65     public static final int CODE_CLOSING_PARENTHESIS = ')';
     66     public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
     67     public static final int CODE_CLOSING_CURLY_BRACKET = '}';
     68     public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
     69 
     70     /** Special keys code. Must be negative.
     71      * These should be aligned with KeyboardCodesSet.ID_TO_NAME[],
     72      * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[]
     73      */
     74     public static final int CODE_SHIFT = -1;
     75     public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
     76     public static final int CODE_OUTPUT_TEXT = -3;
     77     public static final int CODE_DELETE = -4;
     78     public static final int CODE_SETTINGS = -5;
     79     public static final int CODE_SHORTCUT = -6;
     80     public static final int CODE_ACTION_ENTER = -7;
     81     public static final int CODE_ACTION_NEXT = -8;
     82     public static final int CODE_ACTION_PREVIOUS = -9;
     83     public static final int CODE_LANGUAGE_SWITCH = -10;
     84     public static final int CODE_RESEARCH = -11;
     85     // Code value representing the code is not specified.
     86     public static final int CODE_UNSPECIFIED = -12;
     87 
     88     public final KeyboardId mId;
     89     public final int mThemeId;
     90 
     91     /** Total height of the keyboard, including the padding and keys */
     92     public final int mOccupiedHeight;
     93     /** Total width of the keyboard, including the padding and keys */
     94     public final int mOccupiedWidth;
     95 
     96     /** The padding above the keyboard */
     97     public final int mTopPadding;
     98     /** Default gap between rows */
     99     public final int mVerticalGap;
    100 
    101     /** Per keyboard key visual parameters */
    102     public final KeyVisualAttributes mKeyVisualAttributes;
    103 
    104     public final int mMostCommonKeyHeight;
    105     public final int mMostCommonKeyWidth;
    106 
    107     /** More keys keyboard template */
    108     public final int mMoreKeysTemplate;
    109 
    110     /** Maximum column for more keys keyboard */
    111     public final int mMaxMoreKeysKeyboardColumn;
    112 
    113     /** Array of keys and icons in this keyboard */
    114     public final Key[] mKeys;
    115     public final Key[] mShiftKeys;
    116     public final Key[] mAltCodeKeysWhileTyping;
    117     public final KeyboardIconsSet mIconsSet;
    118 
    119     private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray();
    120 
    121     private final ProximityInfo mProximityInfo;
    122     private final boolean mProximityCharsCorrectionEnabled;
    123 
    124     public Keyboard(final KeyboardParams params) {
    125         mId = params.mId;
    126         mThemeId = params.mThemeId;
    127         mOccupiedHeight = params.mOccupiedHeight;
    128         mOccupiedWidth = params.mOccupiedWidth;
    129         mMostCommonKeyHeight = params.mMostCommonKeyHeight;
    130         mMostCommonKeyWidth = params.mMostCommonKeyWidth;
    131         mMoreKeysTemplate = params.mMoreKeysTemplate;
    132         mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
    133         mKeyVisualAttributes = params.mKeyVisualAttributes;
    134         mTopPadding = params.mTopPadding;
    135         mVerticalGap = params.mVerticalGap;
    136 
    137         mKeys = params.mKeys.toArray(new Key[params.mKeys.size()]);
    138         mShiftKeys = params.mShiftKeys.toArray(new Key[params.mShiftKeys.size()]);
    139         mAltCodeKeysWhileTyping = params.mAltCodeKeysWhileTyping.toArray(
    140                 new Key[params.mAltCodeKeysWhileTyping.size()]);
    141         mIconsSet = params.mIconsSet;
    142 
    143         mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
    144                 params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
    145                 mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
    146         mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
    147     }
    148 
    149     public boolean hasProximityCharsCorrection(final int code) {
    150         if (!mProximityCharsCorrectionEnabled) {
    151             return false;
    152         }
    153         // Note: The native code has the main keyboard layout only at this moment.
    154         // TODO: Figure out how to handle proximity characters information of all layouts.
    155         final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = (
    156                 mId.mElementId == KeyboardId.ELEMENT_ALPHABET
    157                 || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED);
    158         return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code);
    159     }
    160 
    161     public ProximityInfo getProximityInfo() {
    162         return mProximityInfo;
    163     }
    164 
    165     public Key getKey(final int code) {
    166         if (code == CODE_UNSPECIFIED) {
    167             return null;
    168         }
    169         synchronized (mKeyCache) {
    170             final int index = mKeyCache.indexOfKey(code);
    171             if (index >= 0) {
    172                 return mKeyCache.valueAt(index);
    173             }
    174 
    175             for (final Key key : mKeys) {
    176                 if (key.mCode == code) {
    177                     mKeyCache.put(code, key);
    178                     return key;
    179                 }
    180             }
    181             mKeyCache.put(code, null);
    182             return null;
    183         }
    184     }
    185 
    186     public boolean hasKey(final Key aKey) {
    187         if (mKeyCache.indexOfValue(aKey) >= 0) {
    188             return true;
    189         }
    190 
    191         for (final Key key : mKeys) {
    192             if (key == aKey) {
    193                 mKeyCache.put(key.mCode, key);
    194                 return true;
    195             }
    196         }
    197         return false;
    198     }
    199 
    200     public static boolean isLetterCode(final int code) {
    201         return code >= CODE_SPACE;
    202     }
    203 
    204     @Override
    205     public String toString() {
    206         return mId.toString();
    207     }
    208 
    209     /**
    210      * Returns the array of the keys that are closest to the given point.
    211      * @param x the x-coordinate of the point
    212      * @param y the y-coordinate of the point
    213      * @return the array of the nearest keys to the given point. If the given
    214      * point is out of range, then an array of size zero is returned.
    215      */
    216     public Key[] getNearestKeys(final int x, final int y) {
    217         // Avoid dead pixels at edges of the keyboard
    218         final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
    219         final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
    220         return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
    221     }
    222 
    223     public static String printableCode(final int code) {
    224         switch (code) {
    225         case CODE_SHIFT: return "shift";
    226         case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
    227         case CODE_OUTPUT_TEXT: return "text";
    228         case CODE_DELETE: return "delete";
    229         case CODE_SETTINGS: return "settings";
    230         case CODE_SHORTCUT: return "shortcut";
    231         case CODE_ACTION_ENTER: return "actionEnter";
    232         case CODE_ACTION_NEXT: return "actionNext";
    233         case CODE_ACTION_PREVIOUS: return "actionPrevious";
    234         case CODE_LANGUAGE_SWITCH: return "languageSwitch";
    235         case CODE_UNSPECIFIED: return "unspec";
    236         case CODE_TAB: return "tab";
    237         case CODE_ENTER: return "enter";
    238         default:
    239             if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code);
    240             if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
    241             if (code < 0x100) return String.format("'%c'", code);
    242             return String.format("'\\u%04x'", code);
    243         }
    244     }
    245 }
    246