• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
KeyCommonAttributes(XmlResourceParser xrp)303         KeyCommonAttributes(XmlResourceParser xrp) {
304             mXrp = xrp;
305             balloon = true;
306         }
307 
308         // Make sure the default object is not null.
getAttributes(KeyCommonAttributes defAttr)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 
XmlKeyboardLoader(Context context)322     public XmlKeyboardLoader(Context context) {
323         mContext = context;
324         mResources = mContext.getResources();
325     }
326 
loadSkbTemplate(int resourceId)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 
loadKeyboard(int resourceId, int skbWidth, int skbHeight)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.
getSoftKey(XmlResourceParser xrp, KeyCommonAttributes attrKey)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 
getToggleStates( KeyCommonAttributes attrKey, SoftKeyToggle softKey, int defKeyCode)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 
getInteger(XmlResourceParser xrp, String name, int defValue)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 
getColor(XmlResourceParser xrp, String name, int defValue)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 
getString(XmlResourceParser xrp, String name, String defValue)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 
getFloat(XmlResourceParser xrp, String name, float defValue)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 
getBoolean(XmlResourceParser xrp, String name, boolean defValue)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 
getDrawable(XmlResourceParser xrp, String name, Drawable defValue)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