diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 19002140c..318286ea8 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -129,4 +129,8 @@ requested keyboard mode, the row will be skipped. --> + + + + diff --git a/java/src/com/android/inputmethod/latin/BaseKeyboard.java b/java/src/com/android/inputmethod/latin/BaseKeyboard.java index 0bf4f4eaa..0327006c9 100644 --- a/java/src/com/android/inputmethod/latin/BaseKeyboard.java +++ b/java/src/com/android/inputmethod/latin/BaseKeyboard.java @@ -24,17 +24,17 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.util.Xml; -import android.util.DisplayMetrics; +import android.view.InflateException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; - /** * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard * consists of rows of keys. @@ -61,6 +61,8 @@ public class BaseKeyboard { private static final String TAG_KEYBOARD = "Keyboard"; private static final String TAG_ROW = "Row"; private static final String TAG_KEY = "Key"; + private static final String TAG_INCLUDE = "include"; + private static final String TAG_MERGE = "merge"; public static final int EDGE_LEFT = 0x01; public static final int EDGE_RIGHT = 0x02; @@ -104,9 +106,6 @@ public class BaseKeyboard { /** List of keys in this keyboard */ private final List mKeys = new ArrayList(); - /** List of modifier keys such as Shift & Alt, if any */ - private final List mModifierKeys = new ArrayList(); - /** Width of the screen available to fit the keyboard */ private final int mDisplayWidth; @@ -177,8 +176,7 @@ public class BaseKeyboard { a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.BaseKeyboard_Row); rowEdgeFlags = a.getInt(R.styleable.BaseKeyboard_Row_rowEdgeFlags, 0); - mode = a.getResourceId(R.styleable.BaseKeyboard_Row_keyboardMode, - 0); + mode = a.getResourceId(R.styleable.BaseKeyboard_Row_keyboardMode, 0); } } @@ -558,10 +556,6 @@ public class BaseKeyboard { return mKeys; } - public List getModifierKeys() { - return mModifierKeys; - } - protected int getHorizontalGap() { return mDefaultHorizontalGap; } @@ -682,79 +676,170 @@ public class BaseKeyboard { return new Key(res, parent, x, y, parser); } - private void loadKeyboard(Context context, XmlResourceParser parser) { - boolean inKey = false; - boolean inRow = false; - int row = 0; - int x = 0; - int y = 0; - Key key = null; - Row currentRow = null; - Resources res = context.getResources(); - boolean skipRow = false; + private static class KeyboardParseState { + private final int mKeyboardMode; + private int mCurrentX = 0; + private int mCurrentY = 0; + private int mMaxRowWidth = 0; + private int mTotalHeight = 0; + private Row mCurrentRow = null; - try { - int event; - while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.START_TAG) { - String tag = parser.getName(); - if (TAG_ROW.equals(tag)) { - inRow = true; - x = 0; - // TODO createRowFromXml should not be called from BaseKeyboard constructor. - currentRow = createRowFromXml(res, parser); - skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode; - if (skipRow) { - skipToEndOfRow(parser); - inRow = false; - } - } else if (TAG_KEY.equals(tag)) { - inKey = true; - // TODO createKeyFromXml should not be called from BaseKeyboard constructor. - key = createKeyFromXml(res, currentRow, x, y, parser); - mKeys.add(key); - if (key.codes[0] == KEYCODE_SHIFT) { - mShiftKeys.add(key); - mModifierKeys.add(key); - } else if (key.codes[0] == KEYCODE_ALT) { - mModifierKeys.add(key); - } - } else if (TAG_KEYBOARD.equals(tag)) { - parseKeyboardAttributes(res, parser); - } - } else if (event == XmlResourceParser.END_TAG) { - if (inKey) { - inKey = false; - x += key.gap + key.width; - if (x > mTotalWidth) { - mTotalWidth = x; - } - } else if (inRow) { - inRow = false; - y += currentRow.verticalGap; - y += currentRow.defaultHeight; - row++; - } else { - // TODO: error or extend? - } - } - } - } catch (Exception e) { - Log.e(TAG, "Parse error:" + e); - e.printStackTrace(); + public KeyboardParseState(int keyboardMode) { + mKeyboardMode = keyboardMode; + } + + public int getX() { + return mCurrentX; + } + + public int getY() { + return mCurrentY; + } + + public Row getRow() { + return mCurrentRow; + } + + // return true if the row is valid for this keyboard mode + public boolean startRow(Row row) { + mCurrentX = 0; + mCurrentRow = row; + return row.mode == 0 || row.mode == mKeyboardMode; + } + + public void skipRow() { + mCurrentRow = null; + } + + public void endRow() { + if (mCurrentRow == null) + throw new InflateException("orphant end row tag"); + mCurrentY += mCurrentRow.verticalGap + mCurrentRow.defaultHeight; + mCurrentRow = null; + } + + public void endKey(Key key) { + mCurrentX += key.gap + key.width; + if (mCurrentX > mMaxRowWidth) + mMaxRowWidth = mCurrentX; + } + + public void endKeyboard(int defaultVerticalGap) { + mTotalHeight = mCurrentY - defaultVerticalGap; + } + + public int getMaxRowWidth() { + return mMaxRowWidth; + } + + public int getTotalHeight() { + return mTotalHeight; } - mTotalHeight = y - mDefaultVerticalGap; } - private void skipToEndOfRow(XmlResourceParser parser) + private void loadKeyboard(Context context, XmlResourceParser parser) { + try { + KeyboardParseState state = new KeyboardParseState(mKeyboardMode); + parseKeyboard(context.getResources(), parser, state); + // mTotalWidth is the width of this keyboard which is maximum width of row. + mTotalWidth = state.getMaxRowWidth(); + mTotalHeight = state.getTotalHeight(); + } catch (XmlPullParserException e) { + throw new IllegalArgumentException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void parseKeyboard(Resources res, XmlResourceParser parser, KeyboardParseState state) + throws XmlPullParserException, IOException { + Key key = null; + + int event; + while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { + if (event == XmlResourceParser.START_TAG) { + String tag = parser.getName(); + if (TAG_ROW.equals(tag)) { + // TODO createRowFromXml should not be called from BaseKeyboard constructor. + Row row = createRowFromXml(res, parser); + if (!state.startRow(row)) + skipToEndOfRow(parser, state); + } else if (TAG_KEY.equals(tag)) { + // TODO createKeyFromXml should not be called from BaseKeyboard constructor. + key = createKeyFromXml(res, state.getRow(), state.getX(), state.getY(), + parser); + mKeys.add(key); + if (key.codes[0] == KEYCODE_SHIFT) + mShiftKeys.add(key); + } else if (TAG_KEYBOARD.equals(tag)) { + parseKeyboardAttributes(res, parser); + } else if (TAG_INCLUDE.equals(tag)) { + if (parser.getDepth() == 0) + throw new InflateException(" cannot be the root element"); + parseInclude(res, parser, state); + } else if (TAG_MERGE.equals(tag)) { + throw new InflateException(" must not be appeared in keyboard XML file"); + } else { + throw new InflateException("unknown start tag: " + tag); + } + } else if (event == XmlResourceParser.END_TAG) { + String tag = parser.getName(); + if (TAG_KEY.equals(tag)) { + state.endKey(key); + } else if (TAG_ROW.equals(tag)) { + state.endRow(); + } else if (TAG_KEYBOARD.equals(tag)) { + state.endKeyboard(mDefaultVerticalGap); + } else if (TAG_INCLUDE.equals(tag)) { + ; + } else if (TAG_MERGE.equals(tag)) { + return; + } else { + throw new InflateException("unknown end tag: " + tag); + } + } + } + } + + private void parseInclude(Resources res, XmlResourceParser parent, KeyboardParseState state) + throws XmlPullParserException, IOException { + final TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parent), + R.styleable.BaseKeyboard_Include); + final int keyboardLayout = a.getResourceId( + R.styleable.BaseKeyboard_Include_keyboardLayout, 0); + a.recycle(); + if (keyboardLayout == 0) + throw new InflateException(" must have keyboardLayout attribute"); + final XmlResourceParser parser = res.getLayout(keyboardLayout); + + int event; + while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { + if (event == XmlResourceParser.START_TAG) { + String name = parser.getName(); + if (TAG_MERGE.equals(name)) { + parseKeyboard(res, parser, state); + return; + } else { + throw new InflateException( + "include keyboard layout must have root element"); + } + } + } + } + + private void skipToEndOfRow(XmlResourceParser parser, KeyboardParseState state) throws XmlPullParserException, IOException { int event; while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.END_TAG - && parser.getName().equals(TAG_ROW)) { - break; + if (event == XmlResourceParser.END_TAG) { + String tag = parser.getName(); + if (TAG_ROW.equals(tag)) { + state.skipRow(); + return; + } } } + throw new InflateException("can not find "); } private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {