Keyboard XML file supports include and merge tag

Keyboard XML file can include other keyboard XML file using directive
<include keyboardLayout="@xml/...">.  The keyboard XML file which is
included must have <merge> tag as root element.

Change-Id: I06c35fe7b3db5232acdb33f73a79f38d31261b32
main
Tadashi G. Takaoka 2010-10-07 17:13:30 +09:00
parent 4d9add5482
commit d663555c9f
2 changed files with 164 additions and 75 deletions

View File

@ -129,4 +129,8 @@
requested keyboard mode, the row will be skipped. --> requested keyboard mode, the row will be skipped. -->
<attr name="keyboardMode" format="reference" /> <attr name="keyboardMode" format="reference" />
</declare-styleable> </declare-styleable>
<declare-styleable name="BaseKeyboard_Include">
<attr name="keyboardLayout" format="reference" />
</declare-styleable>
</resources> </resources>

View File

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