Home | History | Annotate | Download | only in pinyin
      1 /*
      2  * Copyright (C) 2009 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 
     17 package com.android.inputmethod.pinyin;
     18 
     19 import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow;
     20 
     21 import android.content.Context;
     22 import android.content.res.Resources;
     23 import android.content.res.XmlResourceParser;
     24 import android.graphics.drawable.Drawable;
     25 
     26 import java.io.IOException;
     27 import java.util.regex.Pattern;
     28 
     29 import org.xmlpull.v1.XmlPullParserException;
     30 
     31 /**
     32  * Class used to load a soft keyboard or a soft keyboard template from xml
     33  * files.
     34  */
     35 public class XmlKeyboardLoader {
     36     /**
     37      * The tag used to define an xml-based soft keyboard template.
     38      */
     39     private static final String XMLTAG_SKB_TEMPLATE = "skb_template";
     40 
     41     /**
     42      * The tag used to indicate the soft key type which is defined inside the
     43      * {@link #XMLTAG_SKB_TEMPLATE} element in the xml file. file.
     44      */
     45     private static final String XMLTAG_KEYTYPE = "key_type";
     46 
     47     /**
     48      * The tag used to define a default key icon for enter/delete/space keys. It
     49      * is defined inside the {@link #XMLTAG_SKB_TEMPLATE} element in the xml
     50      * file.
     51      */
     52     private static final String XMLTAG_KEYICON = "key_icon";
     53 
     54     /**
     55      * Attribute tag of the left and right margin for a key. A key's width
     56      * should be larger than double of this value. Defined inside
     57      * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}.
     58      */
     59     private static final String XMLATTR_KEY_XMARGIN = "key_xmargin";
     60 
     61     /**
     62      * Attribute tag of the top and bottom margin for a key. A key's height
     63      * should be larger than double of this value. Defined inside
     64      * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}.
     65      */
     66     private static final String XMLATTR_KEY_YMARGIN = "key_ymargin";
     67 
     68     /**
     69      * Attribute tag of the keyboard background image. Defined inside
     70      * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}.
     71      */
     72     private static final String XMLATTR_SKB_BG = "skb_bg";
     73 
     74     /**
     75      * Attribute tag of the balloon background image for key press. Defined
     76      * inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYBOARD}.
     77      */
     78     private static final String XMLATTR_BALLOON_BG = "balloon_bg";
     79 
     80     /**
     81      * Attribute tag of the popup balloon background image for key press or
     82      * popup mini keyboard. Defined inside {@link #XMLTAG_SKB_TEMPLATE} and
     83      * {@link #XMLTAG_KEYBOARD}.
     84      */
     85     private static final String XMLATTR_POPUP_BG = "popup_bg";
     86 
     87     /**
     88      * Attribute tag of the color to draw key label. Defined inside
     89      * {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}.
     90      */
     91     private static final String XMLATTR_COLOR = "color";
     92 
     93     /**
     94      * Attribute tag of the color to draw key's highlighted label. Defined
     95      * inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}.
     96      */
     97     private static final String XMLATTR_COLOR_HIGHLIGHT = "color_highlight";
     98 
     99     /**
    100      * Attribute tag of the color to draw key's label in the popup balloon.
    101      * Defined inside {@link #XMLTAG_SKB_TEMPLATE} and {@link #XMLTAG_KEYTYPE}.
    102      */
    103     private static final String XMLATTR_COLOR_BALLOON = "color_balloon";
    104 
    105     /**
    106      * Attribute tag of the id of {@link #XMLTAG_KEYTYPE} and
    107      * {@link #XMLTAG_KEY}. Key types and keys defined in a soft keyboard
    108      * template should have id, because a soft keyboard needs the id to refer to
    109      * these default definitions. If a key defined in {@link #XMLTAG_KEYBOARD}
    110      * does not id, that means the key is newly defined; if it has id (and only
    111      * has id), the id is used to find the default definition from the soft
    112      * keyboard template.
    113      */
    114     private static final String XMLATTR_ID = "id";
    115 
    116     /**
    117      * Attribute tag of the key background for a specified key type. Defined
    118      * inside {@link #XMLTAG_KEYTYPE}.
    119      */
    120     private static final String XMLATTR_KEYTYPE_BG = "bg";
    121 
    122     /**
    123      * Attribute tag of the key high-light background for a specified key type.
    124      * Defined inside {@link #XMLTAG_KEYTYPE}.
    125      */
    126     private static final String XMLATTR_KEYTYPE_HLBG = "hlbg";
    127 
    128     /**
    129      * Attribute tag of the starting x-position of an element. It can be defined
    130      * in {@link #XMLTAG_ROW} and {@link #XMLTAG_KEY} in {XMLTAG_SKB_TEMPLATE}.
    131      * If not defined, 0 will be used. For a key defined in
    132      * {@link #XMLTAG_KEYBOARD}, it always use its previous keys information to
    133      * calculate its own position.
    134      */
    135     private static final String XMLATTR_START_POS_X = "start_pos_x";
    136 
    137     /**
    138      * Attribute tag of the starting y-position of an element. It can be defined
    139      * in {@link #XMLTAG_ROW} and {@link #XMLTAG_KEY} in {XMLTAG_SKB_TEMPLATE}.
    140      * If not defined, 0 will be used. For a key defined in
    141      * {@link #XMLTAG_KEYBOARD}, it always use its previous keys information to
    142      * calculate its own position.
    143      */
    144     private static final String XMLATTR_START_POS_Y = "start_pos_y";
    145 
    146     /**
    147      * Attribute tag of a row's id. Defined {@link #XMLTAG_ROW}. If not defined,
    148      * -1 will be used. Rows with id -1 will be enabled always, rows with same
    149      * row id will be enabled when the id is the same to the activated id of the
    150      * soft keyboard.
    151      */
    152     private static final String XMLATTR_ROW_ID = "row_id";
    153 
    154     /** The tag used to indicate the keyboard element in the xml file. */
    155     private static final String XMLTAG_KEYBOARD = "keyboard";
    156 
    157     /** The tag used to indicate the row element in the xml file. */
    158     private static final String XMLTAG_ROW = "row";
    159 
    160     /** The tag used to indicate key-array element in the xml file. */
    161     private static final String XMLTAG_KEYS = "keys";
    162 
    163     /**
    164      * The tag used to indicate a key element in the xml file. If the element is
    165      * defined in a soft keyboard template, it should have an id. If it is
    166      * defined in a soft keyboard, id is not required.
    167      */
    168     private static final String XMLTAG_KEY = "key";
    169 
    170     /** The tag used to indicate a key's toggle element in the xml file. */
    171     private static final String XMLTAG_TOGGLE_STATE = "toggle_state";
    172 
    173     /**
    174      * Attribute tag of the toggle state id for toggle key. Defined inside
    175      * {@link #XMLTAG_TOGGLE_STATE}
    176      */
    177     private static final String XMLATTR_TOGGLE_STATE_ID = "state_id";
    178 
    179     /** Attribute tag of key template for the soft keyboard. */
    180     private static final String XMLATTR_SKB_TEMPLATE = "skb_template";
    181 
    182     /**
    183      * Attribute tag used to indicate whether this soft keyboard needs to be
    184      * cached in memory for future use. {@link #DEFAULT_SKB_CACHE_FLAG}
    185      * specifies the default value.
    186      */
    187     private static final String XMLATTR_SKB_CACHE_FLAG = "skb_cache_flag";
    188 
    189     /**
    190      * Attribute tag used to indicate whether this soft keyboard is sticky. A
    191      * sticky soft keyboard will keep the current layout unless user makes a
    192      * switch explicitly. A none sticky soft keyboard will automatically goes
    193      * back to the previous keyboard after click a none-function key.
    194      * {@link #DEFAULT_SKB_STICKY_FLAG} specifies the default value.
    195      */
    196     private static final String XMLATTR_SKB_STICKY_FLAG = "skb_sticky_flag";
    197 
    198     /** Attribute tag to indicate whether it is a QWERTY soft keyboard. */
    199     private static final String XMLATTR_QWERTY = "qwerty";
    200 
    201     /**
    202      * When the soft keyboard is a QWERTY one, this attribute tag to get the
    203      * information that whether it is defined in upper case.
    204      */
    205     private static final String XMLATTR_QWERTY_UPPERCASE = "qwerty_uppercase";
    206 
    207     /** Attribute tag of key type. */
    208     private static final String XMLATTR_KEY_TYPE = "key_type";
    209 
    210     /** Attribute tag of key width. */
    211     private static final String XMLATTR_KEY_WIDTH = "width";
    212 
    213     /** Attribute tag of key height. */
    214     private static final String XMLATTR_KEY_HEIGHT = "height";
    215 
    216     /** Attribute tag of the key's repeating ability. */
    217     private static final String XMLATTR_KEY_REPEAT = "repeat";
    218 
    219     /** Attribute tag of the key's behavior for balloon. */
    220     private static final String XMLATTR_KEY_BALLOON = "balloon";
    221 
    222     /** Attribute tag of the key splitter in a key array. */
    223     private static final String XMLATTR_KEY_SPLITTER = "splitter";
    224 
    225     /** Attribute tag of the key labels in a key array. */
    226     private static final String XMLATTR_KEY_LABELS = "labels";
    227 
    228     /** Attribute tag of the key codes in a key array. */
    229     private static final String XMLATTR_KEY_CODES = "codes";
    230 
    231     /** Attribute tag of the key label in a key. */
    232     private static final String XMLATTR_KEY_LABEL = "label";
    233 
    234     /** Attribute tag of the key code in a key. */
    235     private static final String XMLATTR_KEY_CODE = "code";
    236 
    237     /** Attribute tag of the key icon in a key. */
    238     private static final String XMLATTR_KEY_ICON = "icon";
    239 
    240     /** Attribute tag of the key's popup icon in a key. */
    241     private static final String XMLATTR_KEY_ICON_POPUP = "icon_popup";
    242 
    243     /** The id for a mini popup soft keyboard. */
    244     private static final String XMLATTR_KEY_POPUP_SKBID = "popup_skb";
    245 
    246     private static boolean DEFAULT_SKB_CACHE_FLAG = true;
    247 
    248     private static boolean DEFAULT_SKB_STICKY_FLAG = true;
    249 
    250     /**
    251      * The key type id for invalid key type. It is also used to generate next
    252      * valid key type id by adding 1.
    253      */
    254     private static final int KEYTYPE_ID_LAST = -1;
    255 
    256     private Context mContext;
    257 
    258     private Resources mResources;
    259 
    260     /** The event type in parsing the xml file. */
    261     private int mXmlEventType;
    262 
    263     /**
    264      * The current soft keyboard template used by the current soft keyboard
    265      * under loading.
    266      **/
    267     private SkbTemplate mSkbTemplate;
    268 
    269     /** The x position for the next key. */
    270     float mKeyXPos;
    271 
    272     /** The y position for the next key. */
    273     float mKeyYPos;
    274 
    275     /** The width of the keyboard to load. */
    276     int mSkbWidth;
    277 
    278     /** The height of the keyboard to load. */
    279     int mSkbHeight;
    280 
    281     /** Key margin in x-way. */
    282     float mKeyXMargin = 0;
    283 
    284     /** Key margin in y-way. */
    285     float mKeyYMargin = 0;
    286 
    287     /**
    288      * Used to indicate whether next event has been fetched during processing
    289      * the the current event.
    290      */
    291     boolean mNextEventFetched = false;
    292 
    293     String mAttrTmp;
    294 
    295     class KeyCommonAttributes {
    296         XmlResourceParser mXrp;
    297         int keyType;
    298         float keyWidth;
    299         float keyHeight;
    300         boolean repeat;
    301         boolean balloon;
    302 
    303         KeyCommonAttributes(XmlResourceParser xrp) {
    304             mXrp = xrp;
    305             balloon = true;
    306         }
    307 
    308         // Make sure the default object is not null.
    309         boolean getAttributes(KeyCommonAttributes defAttr) {
    310             keyType = getInteger(mXrp, XMLATTR_KEY_TYPE, defAttr.keyType);
    311             keyWidth = getFloat(mXrp, XMLATTR_KEY_WIDTH, defAttr.keyWidth);
    312             keyHeight = getFloat(mXrp, XMLATTR_KEY_HEIGHT, defAttr.keyHeight);
    313             repeat = getBoolean(mXrp, XMLATTR_KEY_REPEAT, defAttr.repeat);
    314             balloon = getBoolean(mXrp, XMLATTR_KEY_BALLOON, defAttr.balloon);
    315             if (keyType < 0 || keyWidth <= 0 || keyHeight <= 0) {
    316                 return false;
    317             }
    318             return true;
    319         }
    320     }
    321 
    322     public XmlKeyboardLoader(Context context) {
    323         mContext = context;
    324         mResources = mContext.getResources();
    325     }
    326 
    327     public SkbTemplate loadSkbTemplate(int resourceId) {
    328         if (null == mContext || 0 == resourceId) {
    329             return null;
    330         }
    331         Resources r = mResources;
    332         XmlResourceParser xrp = r.getXml(resourceId);
    333 
    334         KeyCommonAttributes attrDef = new KeyCommonAttributes(xrp);
    335         KeyCommonAttributes attrKey = new KeyCommonAttributes(xrp);
    336 
    337         mSkbTemplate = new SkbTemplate(resourceId);
    338         int lastKeyTypeId = KEYTYPE_ID_LAST;
    339         int globalColor = 0;
    340         int globalColorHl = 0;
    341         int globalColorBalloon = 0;
    342         try {
    343             mXmlEventType = xrp.next();
    344             while (mXmlEventType != XmlResourceParser.END_DOCUMENT) {
    345                 mNextEventFetched = false;
    346                 if (mXmlEventType == XmlResourceParser.START_TAG) {
    347                     String attribute = xrp.getName();
    348                     if (XMLTAG_SKB_TEMPLATE.compareTo(attribute) == 0) {
    349                         Drawable skbBg = getDrawable(xrp, XMLATTR_SKB_BG, null);
    350                         Drawable balloonBg = getDrawable(xrp,
    351                                 XMLATTR_BALLOON_BG, null);
    352                         Drawable popupBg = getDrawable(xrp, XMLATTR_POPUP_BG,
    353                                 null);
    354                         if (null == skbBg || null == balloonBg
    355                                 || null == popupBg) {
    356                             return null;
    357                         }
    358                         mSkbTemplate.setBackgrounds(skbBg, balloonBg, popupBg);
    359 
    360                         float xMargin = getFloat(xrp, XMLATTR_KEY_XMARGIN, 0);
    361                         float yMargin = getFloat(xrp, XMLATTR_KEY_YMARGIN, 0);
    362                         mSkbTemplate.setMargins(xMargin, yMargin);
    363 
    364                         // Get default global colors.
    365                         globalColor = getColor(xrp, XMLATTR_COLOR, 0);
    366                         globalColorHl = getColor(xrp, XMLATTR_COLOR_HIGHLIGHT,
    367                                 0xffffffff);
    368                         globalColorBalloon = getColor(xrp,
    369                                 XMLATTR_COLOR_BALLOON, 0xffffffff);
    370                     } else if (XMLTAG_KEYTYPE.compareTo(attribute) == 0) {
    371                         int id = getInteger(xrp, XMLATTR_ID, KEYTYPE_ID_LAST);
    372                         Drawable bg = getDrawable(xrp, XMLATTR_KEYTYPE_BG, null);
    373                         Drawable hlBg = getDrawable(xrp, XMLATTR_KEYTYPE_HLBG,
    374                                 null);
    375                         int color = getColor(xrp, XMLATTR_COLOR, globalColor);
    376                         int colorHl = getColor(xrp, XMLATTR_COLOR_HIGHLIGHT,
    377                                 globalColorHl);
    378                         int colorBalloon = getColor(xrp, XMLATTR_COLOR_BALLOON,
    379                                 globalColorBalloon);
    380                         if (id != lastKeyTypeId + 1) {
    381                             return null;
    382                         }
    383                         SoftKeyType keyType = mSkbTemplate.createKeyType(id,
    384                                 bg, hlBg);
    385                         keyType.setColors(color, colorHl, colorBalloon);
    386                         if (!mSkbTemplate.addKeyType(keyType)) {
    387                             return null;
    388                         }
    389                         lastKeyTypeId = id;
    390                     } else if (XMLTAG_KEYICON.compareTo(attribute) == 0) {
    391                         int keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0);
    392                         Drawable icon = getDrawable(xrp, XMLATTR_KEY_ICON, null);
    393                         Drawable iconPopup = getDrawable(xrp,
    394                                 XMLATTR_KEY_ICON_POPUP, null);
    395                         if (null != icon && null != iconPopup) {
    396                             mSkbTemplate.addDefaultKeyIcons(keyCode, icon,
    397                                     iconPopup);
    398                         }
    399                     } else if (XMLTAG_KEY.compareTo(attribute) == 0) {
    400                         int keyId = this.getInteger(xrp, XMLATTR_ID, -1);
    401                         if (-1 == keyId) return null;
    402 
    403                         if (!attrKey.getAttributes(attrDef)) {
    404                             return null;
    405                         }
    406 
    407                         // Update the key position for the key.
    408                         mKeyXPos = getFloat(xrp, XMLATTR_START_POS_X, 0);
    409                         mKeyYPos = getFloat(xrp, XMLATTR_START_POS_Y, 0);
    410 
    411                         SoftKey softKey = getSoftKey(xrp, attrKey);
    412                         if (null == softKey) return null;
    413                         mSkbTemplate.addDefaultKey(keyId, softKey);
    414                     }
    415                 }
    416                 // Get the next tag.
    417                 if (!mNextEventFetched) mXmlEventType = xrp.next();
    418             }
    419             xrp.close();
    420             return mSkbTemplate;
    421         } catch (XmlPullParserException e) {
    422             // Log.e(TAG, "Ill-formatted keyboard template resource file");
    423         } catch (IOException e) {
    424             // Log.e(TAG, "Unable to keyboard template resource file");
    425         }
    426         return null;
    427     }
    428 
    429     public SoftKeyboard loadKeyboard(int resourceId, int skbWidth, int skbHeight) {
    430         if (null == mContext) return null;
    431         Resources r = mResources;
    432         SkbPool skbPool = SkbPool.getInstance();
    433         XmlResourceParser xrp = mContext.getResources().getXml(resourceId);
    434         mSkbTemplate = null;
    435         SoftKeyboard softKeyboard = null;
    436         Drawable skbBg;
    437         Drawable popupBg;
    438         Drawable balloonBg;
    439         SoftKey softKey = null;
    440 
    441         KeyCommonAttributes attrDef = new KeyCommonAttributes(xrp);
    442         KeyCommonAttributes attrSkb = new KeyCommonAttributes(xrp);
    443         KeyCommonAttributes attrRow = new KeyCommonAttributes(xrp);
    444         KeyCommonAttributes attrKeys = new KeyCommonAttributes(xrp);
    445         KeyCommonAttributes attrKey = new KeyCommonAttributes(xrp);
    446 
    447         mKeyXPos = 0;
    448         mKeyYPos = 0;
    449         mSkbWidth = skbWidth;
    450         mSkbHeight = skbHeight;
    451 
    452         try {
    453             mKeyXMargin = 0;
    454             mKeyYMargin = 0;
    455             mXmlEventType = xrp.next();
    456             while (mXmlEventType != XmlResourceParser.END_DOCUMENT) {
    457                 mNextEventFetched = false;
    458                 if (mXmlEventType == XmlResourceParser.START_TAG) {
    459                     String attr = xrp.getName();
    460                     // 1. Is it the root element, "keyboard"?
    461                     if (XMLTAG_KEYBOARD.compareTo(attr) == 0) {
    462                         // 1.1 Get the keyboard template id.
    463                         int skbTemplateId = xrp.getAttributeResourceValue(null,
    464                                 XMLATTR_SKB_TEMPLATE, 0);
    465 
    466                         // 1.2 Try to get the template from pool. If it is not
    467                         // in, the pool will try to load it.
    468                         mSkbTemplate = skbPool.getSkbTemplate(skbTemplateId,
    469                                 mContext);
    470 
    471                         if (null == mSkbTemplate
    472                                 || !attrSkb.getAttributes(attrDef)) {
    473                             return null;
    474                         }
    475 
    476                         boolean cacheFlag = getBoolean(xrp,
    477                                 XMLATTR_SKB_CACHE_FLAG, DEFAULT_SKB_CACHE_FLAG);
    478                         boolean stickyFlag = getBoolean(xrp,
    479                                 XMLATTR_SKB_STICKY_FLAG,
    480                                 DEFAULT_SKB_STICKY_FLAG);
    481                         boolean isQwerty = getBoolean(xrp, XMLATTR_QWERTY,
    482                                 false);
    483                         boolean isQwertyUpperCase = getBoolean(xrp,
    484                                 XMLATTR_QWERTY_UPPERCASE, false);
    485 
    486                         softKeyboard = new SoftKeyboard(resourceId,
    487                                 mSkbTemplate, mSkbWidth, mSkbHeight);
    488                         softKeyboard.setFlags(cacheFlag, stickyFlag, isQwerty,
    489                                 isQwertyUpperCase);
    490 
    491                         mKeyXMargin = getFloat(xrp, XMLATTR_KEY_XMARGIN,
    492                                 mSkbTemplate.getXMargin());
    493                         mKeyYMargin = getFloat(xrp, XMLATTR_KEY_YMARGIN,
    494                                 mSkbTemplate.getYMargin());
    495                         skbBg = getDrawable(xrp, XMLATTR_SKB_BG, null);
    496                         popupBg = getDrawable(xrp, XMLATTR_POPUP_BG, null);
    497                         balloonBg = getDrawable(xrp, XMLATTR_BALLOON_BG, null);
    498                         if (null != skbBg) {
    499                             softKeyboard.setSkbBackground(skbBg);
    500                         }
    501                         if (null != popupBg) {
    502                             softKeyboard.setPopupBackground(popupBg);
    503                         }
    504                         if (null != balloonBg) {
    505                             softKeyboard.setKeyBalloonBackground(balloonBg);
    506                         }
    507                         softKeyboard.setKeyMargins(mKeyXMargin, mKeyYMargin);
    508                     } else if (XMLTAG_ROW.compareTo(attr) == 0) {
    509                         if (!attrRow.getAttributes(attrSkb)) {
    510                             return null;
    511                         }
    512                         // Get the starting positions for the row.
    513                         mKeyXPos = getFloat(xrp, XMLATTR_START_POS_X, 0);
    514                         mKeyYPos = getFloat(xrp, XMLATTR_START_POS_Y, mKeyYPos);
    515                         int rowId = getInteger(xrp, XMLATTR_ROW_ID,
    516                                 KeyRow.ALWAYS_SHOW_ROW_ID);
    517                         softKeyboard.beginNewRow(rowId, mKeyYPos);
    518                     } else if (XMLTAG_KEYS.compareTo(attr) == 0) {
    519                         if (null == softKeyboard) return null;
    520                         if (!attrKeys.getAttributes(attrRow)) {
    521                             return null;
    522                         }
    523 
    524                         String splitter = xrp.getAttributeValue(null,
    525                                 XMLATTR_KEY_SPLITTER);
    526                         splitter = Pattern.quote(splitter);
    527                         String labels = xrp.getAttributeValue(null,
    528                                 XMLATTR_KEY_LABELS);
    529                         String codes = xrp.getAttributeValue(null,
    530                                 XMLATTR_KEY_CODES);
    531                         if (null == splitter || null == labels) {
    532                             return null;
    533                         }
    534                         String labelArr[] = labels.split(splitter);
    535                         String codeArr[] = null;
    536                         if (null != codes) {
    537                             codeArr = codes.split(splitter);
    538                             if (labelArr.length != codeArr.length) {
    539                                 return null;
    540                             }
    541                         }
    542 
    543                         for (int i = 0; i < labelArr.length; i++) {
    544                             softKey = new SoftKey();
    545                             int keyCode = 0;
    546                             if (null != codeArr) {
    547                                 keyCode = Integer.valueOf(codeArr[i]);
    548                             }
    549                             softKey.setKeyAttribute(keyCode, labelArr[i],
    550                                     attrKeys.repeat, attrKeys.balloon);
    551 
    552                             softKey.setKeyType(mSkbTemplate
    553                                     .getKeyType(attrKeys.keyType), null, null);
    554 
    555                             float left, right, top, bottom;
    556                             left = mKeyXPos;
    557 
    558                             right = left + attrKeys.keyWidth;
    559                             top = mKeyYPos;
    560                             bottom = top + attrKeys.keyHeight;
    561 
    562                             if (right - left < 2 * mKeyXMargin) return null;
    563                             if (bottom - top < 2 * mKeyYMargin) return null;
    564 
    565                             softKey.setKeyDimensions(left, top, right, bottom);
    566                             softKeyboard.addSoftKey(softKey);
    567                             mKeyXPos = right;
    568                             if ((int) mKeyXPos * mSkbWidth > mSkbWidth) {
    569                                 return null;
    570                             }
    571                         }
    572                     } else if (XMLTAG_KEY.compareTo(attr) == 0) {
    573                         if (null == softKeyboard) {
    574                             return null;
    575                         }
    576                         if (!attrKey.getAttributes(attrRow)) {
    577                             return null;
    578                         }
    579 
    580                         int keyId = this.getInteger(xrp, XMLATTR_ID, -1);
    581                         if (keyId >= 0) {
    582                             softKey = mSkbTemplate.getDefaultKey(keyId);
    583                         } else {
    584                             softKey = getSoftKey(xrp, attrKey);
    585                         }
    586                         if (null == softKey) return null;
    587 
    588                         // Update the position for next key.
    589                         mKeyXPos = softKey.mRightF;
    590                         if ((int) mKeyXPos * mSkbWidth > mSkbWidth) {
    591                             return null;
    592                         }
    593                         // If the current xml event type becomes a starting tag,
    594                         // it indicates that we have parsed too much to get
    595                         // toggling states, and we started a new row. In this
    596                         // case, the row starting position information should
    597                         // be updated.
    598                         if (mXmlEventType == XmlResourceParser.START_TAG) {
    599                             attr = xrp.getName();
    600                             if (XMLTAG_ROW.compareTo(attr) == 0) {
    601                                 mKeyYPos += attrRow.keyHeight;
    602                                 if ((int) mKeyYPos * mSkbHeight > mSkbHeight) {
    603                                     return null;
    604                                 }
    605                             }
    606                         }
    607                         softKeyboard.addSoftKey(softKey);
    608                     }
    609                 } else if (mXmlEventType == XmlResourceParser.END_TAG) {
    610                     String attr = xrp.getName();
    611                     if (XMLTAG_ROW.compareTo(attr) == 0) {
    612                         mKeyYPos += attrRow.keyHeight;
    613                         if ((int) mKeyYPos * mSkbHeight > mSkbHeight) {
    614                             return null;
    615                         }
    616                     }
    617                 }
    618 
    619                 // Get the next tag.
    620                 if (!mNextEventFetched) mXmlEventType = xrp.next();
    621             }
    622             xrp.close();
    623             softKeyboard.setSkbCoreSize(mSkbWidth, mSkbHeight);
    624             return softKeyboard;
    625         } catch (XmlPullParserException e) {
    626             // Log.e(TAG, "Ill-formatted keybaord resource file");
    627         } catch (IOException e) {
    628             // Log.e(TAG, "Unable to read keyboard resource file");
    629         }
    630         return null;
    631     }
    632 
    633     // Caller makes sure xrp and r are valid.
    634     private SoftKey getSoftKey(XmlResourceParser xrp,
    635             KeyCommonAttributes attrKey) throws XmlPullParserException,
    636             IOException {
    637         int keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0);
    638         String keyLabel = getString(xrp, XMLATTR_KEY_LABEL, null);
    639         Drawable keyIcon = getDrawable(xrp, XMLATTR_KEY_ICON, null);
    640         Drawable keyIconPopup = getDrawable(xrp, XMLATTR_KEY_ICON_POPUP, null);
    641         int popupSkbId = xrp.getAttributeResourceValue(null,
    642                 XMLATTR_KEY_POPUP_SKBID, 0);
    643 
    644         if (null == keyLabel && null == keyIcon) {
    645             keyIcon = mSkbTemplate.getDefaultKeyIcon(keyCode);
    646             keyIconPopup = mSkbTemplate.getDefaultKeyIconPopup(keyCode);
    647             if (null == keyIcon || null == keyIconPopup) return null;
    648         }
    649 
    650         // Dimension information must been initialized before
    651         // getting toggle state, because mKeyYPos may be changed
    652         // to next row when trying to get toggle state.
    653         float left, right, top, bottom;
    654         left = mKeyXPos;
    655         right = left + attrKey.keyWidth;
    656         top = mKeyYPos;
    657         bottom = top + attrKey.keyHeight;
    658 
    659         if (right - left < 2 * mKeyXMargin) return null;
    660         if (bottom - top < 2 * mKeyYMargin) return null;
    661 
    662         // Try to find if the next tag is
    663         // {@link #XMLTAG_TOGGLE_STATE_OF_KEY}, if yes, try to
    664         // create a toggle key.
    665         boolean toggleKey = false;
    666         mXmlEventType = xrp.next();
    667         mNextEventFetched = true;
    668 
    669         SoftKey softKey;
    670         if (mXmlEventType == XmlResourceParser.START_TAG) {
    671             mAttrTmp = xrp.getName();
    672             if (mAttrTmp.compareTo(XMLTAG_TOGGLE_STATE) == 0) {
    673                 toggleKey = true;
    674             }
    675         }
    676         if (toggleKey) {
    677             softKey = new SoftKeyToggle();
    678             if (!((SoftKeyToggle) softKey).setToggleStates(getToggleStates(
    679                     attrKey, (SoftKeyToggle) softKey, keyCode))) {
    680                 return null;
    681             }
    682         } else {
    683             softKey = new SoftKey();
    684         }
    685 
    686         // Set the normal state
    687         softKey.setKeyAttribute(keyCode, keyLabel, attrKey.repeat,
    688                 attrKey.balloon);
    689         softKey.setPopupSkbId(popupSkbId);
    690         softKey.setKeyType(mSkbTemplate.getKeyType(attrKey.keyType), keyIcon,
    691                 keyIconPopup);
    692 
    693         softKey.setKeyDimensions(left, top, right, bottom);
    694         return softKey;
    695     }
    696 
    697     private SoftKeyToggle.ToggleState getToggleStates(
    698             KeyCommonAttributes attrKey, SoftKeyToggle softKey, int defKeyCode)
    699             throws XmlPullParserException, IOException {
    700         XmlResourceParser xrp = attrKey.mXrp;
    701         int stateId = getInteger(xrp, XMLATTR_TOGGLE_STATE_ID, 0);
    702         if (0 == stateId) return null;
    703 
    704         String keyLabel = getString(xrp, XMLATTR_KEY_LABEL, null);
    705         int keyTypeId = getInteger(xrp, XMLATTR_KEY_TYPE, KEYTYPE_ID_LAST);
    706         int keyCode;
    707         if (null == keyLabel) {
    708             keyCode = getInteger(xrp, XMLATTR_KEY_CODE, defKeyCode);
    709         } else {
    710             keyCode = getInteger(xrp, XMLATTR_KEY_CODE, 0);
    711         }
    712         Drawable icon = getDrawable(xrp, XMLATTR_KEY_ICON, null);
    713         Drawable iconPopup = getDrawable(xrp, XMLATTR_KEY_ICON_POPUP, null);
    714         if (null == icon && null == keyLabel) {
    715             return null;
    716         }
    717         SoftKeyToggle.ToggleState rootState = softKey.createToggleState();
    718         rootState.setStateId(stateId);
    719         rootState.mKeyType = null;
    720         if (KEYTYPE_ID_LAST != keyTypeId) {
    721             rootState.mKeyType = mSkbTemplate.getKeyType(keyTypeId);
    722         }
    723         rootState.mKeyCode = keyCode;
    724         rootState.mKeyIcon = icon;
    725         rootState.mKeyIconPopup = iconPopup;
    726         rootState.mKeyLabel = keyLabel;
    727 
    728         boolean repeat = getBoolean(xrp, XMLATTR_KEY_REPEAT, attrKey.repeat);
    729         boolean balloon = getBoolean(xrp, XMLATTR_KEY_BALLOON, attrKey.balloon);
    730         rootState.setStateFlags(repeat, balloon);
    731 
    732         rootState.mNextState = null;
    733 
    734         // If there is another toggle state.
    735         mXmlEventType = xrp.next();
    736         while (mXmlEventType != XmlResourceParser.START_TAG
    737                 && mXmlEventType != XmlResourceParser.END_DOCUMENT) {
    738             mXmlEventType = xrp.next();
    739         }
    740         if (mXmlEventType == XmlResourceParser.START_TAG) {
    741             String attr = xrp.getName();
    742             if (attr.compareTo(XMLTAG_TOGGLE_STATE) == 0) {
    743                 SoftKeyToggle.ToggleState nextState = getToggleStates(attrKey,
    744                         softKey, defKeyCode);
    745                 if (null == nextState) return null;
    746                 rootState.mNextState = nextState;
    747             }
    748         }
    749 
    750         return rootState;
    751     }
    752 
    753     private int getInteger(XmlResourceParser xrp, String name, int defValue) {
    754         int resId = xrp.getAttributeResourceValue(null, name, 0);
    755         String s;
    756         if (resId == 0) {
    757             s = xrp.getAttributeValue(null, name);
    758             if (null == s) return defValue;
    759             try {
    760                 int ret = Integer.valueOf(s);
    761                 return ret;
    762             } catch (NumberFormatException e) {
    763                 return defValue;
    764             }
    765         } else {
    766             return Integer.parseInt(mContext.getResources().getString(resId));
    767         }
    768     }
    769 
    770     private int getColor(XmlResourceParser xrp, String name, int defValue) {
    771         int resId = xrp.getAttributeResourceValue(null, name, 0);
    772         String s;
    773         if (resId == 0) {
    774             s = xrp.getAttributeValue(null, name);
    775             if (null == s) return defValue;
    776             try {
    777                 int ret = Integer.valueOf(s);
    778                 return ret;
    779             } catch (NumberFormatException e) {
    780                 return defValue;
    781             }
    782         } else {
    783             return mContext.getResources().getColor(resId);
    784         }
    785     }
    786 
    787     private String getString(XmlResourceParser xrp, String name, String defValue) {
    788         int resId = xrp.getAttributeResourceValue(null, name, 0);
    789         if (resId == 0) {
    790             return xrp.getAttributeValue(null, name);
    791         } else {
    792             return mContext.getResources().getString(resId);
    793         }
    794     }
    795 
    796     private float getFloat(XmlResourceParser xrp, String name, float defValue) {
    797         int resId = xrp.getAttributeResourceValue(null, name, 0);
    798         if (resId == 0) {
    799             String s = xrp.getAttributeValue(null, name);
    800             if (null == s) return defValue;
    801             try {
    802                 float ret;
    803                 if (s.endsWith("%p")) {
    804                     ret = Float.parseFloat(s.substring(0, s.length() - 2)) / 100;
    805                 } else {
    806                     ret = Float.parseFloat(s);
    807                 }
    808                 return ret;
    809             } catch (NumberFormatException e) {
    810                 return defValue;
    811             }
    812         } else {
    813             return mContext.getResources().getDimension(resId);
    814         }
    815     }
    816 
    817     private boolean getBoolean(XmlResourceParser xrp, String name,
    818             boolean defValue) {
    819         String s = xrp.getAttributeValue(null, name);
    820         if (null == s) return defValue;
    821         try {
    822             boolean ret = Boolean.parseBoolean(s);
    823             return ret;
    824         } catch (NumberFormatException e) {
    825             return defValue;
    826         }
    827     }
    828 
    829     private Drawable getDrawable(XmlResourceParser xrp, String name,
    830             Drawable defValue) {
    831         int resId = xrp.getAttributeResourceValue(null, name, 0);
    832         if (0 == resId) return defValue;
    833         return mResources.getDrawable(resId);
    834     }
    835 }
    836