159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma/*
259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * Licensed under the Apache License, Version 2.0 (the "License");
559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * you may not use this file except in compliance with the License.
659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * You may obtain a copy of the License at
759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *      http://www.apache.org/licenses/LICENSE-2.0
959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
1059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * Unless required by applicable law or agreed to in writing, software
1159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * distributed under the License is distributed on an "AS IS" BASIS,
1259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * See the License for the specific language governing permissions and
1459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * limitations under the License.
1559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma */
1659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma/*
1759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * This file is porting from Android framework.
1859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *   frameworks/base/core/java/android/inputmethodservice/Keyboard.java
1959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
2059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * package android.inputmethodservice;
2159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma */
2259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmapackage jp.co.omronsoft.openwnn;
2359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
2459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.content.Context;
2559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.content.res.Resources;
2659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.content.res.TypedArray;
2759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.content.res.XmlResourceParser;
2859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.drawable.Drawable;
2959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.text.TextUtils;
3059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.util.Log;
3159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.util.TypedValue;
3259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.util.Xml;
3359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.util.DisplayMetrics;
3459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
3559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.io.IOException;
3659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.util.ArrayList;
3759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.util.List;
3859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.util.StringTokenizer;
3959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
4059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport org.xmlpull.v1.XmlPullParserException;
4159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
4259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma/**
4359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
4459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * consists of rows of keys.
4559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
4659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * <pre>
4759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * &lt;Keyboard
4859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *         android:keyWidth="%10p"
4959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *         android:keyHeight="50px"
5059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *         android:horizontalGap="2px"
5159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *         android:verticalGap="2px" &gt;
5259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *     &lt;Row android:keyWidth="32px" &gt;
5359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *         &lt;Key android:keyLabel="A" /&gt;
5459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *         ...
5559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *     &lt;/Row&gt;
5659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *     ...
5759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * &lt;/Keyboard&gt;
5859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * </pre>
5959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma */
6059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmapublic class Keyboard {
6159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
6259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    static final String TAG = "Keyboard";
6359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
6459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final String TAG_KEYBOARD = "Keyboard";
6559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final String TAG_ROW = "Row";
6659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final String TAG_KEY = "Key";
6759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
6859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Edge of left */
6959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int EDGE_LEFT = 0x01;
7059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
7159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Edge of right */
7259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int EDGE_RIGHT = 0x02;
7359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
7459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Edge of top */
7559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int EDGE_TOP = 0x04;
7659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
7759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Edge of bottom */
7859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int EDGE_BOTTOM = 0x08;
7959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
8059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keycode of SHIFT */
8159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int KEYCODE_SHIFT = -1;
8259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
8359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keycode of MODE_CHANGE */
8459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int KEYCODE_MODE_CHANGE = -2;
8559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
8659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keycode of CANCEL */
8759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int KEYCODE_CANCEL = -3;
8859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
8959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keycode of DONE */
9059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int KEYCODE_DONE = -4;
9159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
9259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keycode of DELETE */
9359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int KEYCODE_DELETE = -5;
9459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
9559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keycode of ALT */
9659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static final int KEYCODE_ALT = -6;
9759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
9859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keyboard label **/
9959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private CharSequence mLabel;
10059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
10159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Horizontal gap default for all rows */
10259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mDefaultHorizontalGap;
10359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
10459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Default key width */
10559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mDefaultWidth;
10659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
10759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Default key height */
10859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mDefaultHeight;
10959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
11059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Default gap between rows */
11159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mDefaultVerticalGap;
11259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
11359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Is the keyboard in the shifted state */
11459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mShifted;
11559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
11659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Key instance for the shift key, if present */
11759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Key mShiftKey;
11859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
11959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Key index for the shift key, if present */
12059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mShiftKeyIndex = -1;
12159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
12259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Current key width, while loading the keyboard */
12359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mKeyWidth;
12459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
12559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Current key height, while loading the keyboard */
12659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mKeyHeight;
12759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
12859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Total height of the keyboard, including the padding and keys */
12959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mTotalHeight;
13059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
13159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
13259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
13359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * right side.
13459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
13559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mTotalWidth;
13659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
13759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** List of keys in this keyboard */
13859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private List<Key> mKeys;
13959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
14059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** List of modifier keys such as Shift & Alt, if any */
14159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private List<Key> mModifierKeys;
14259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
14359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Width of the screen available to fit the keyboard */
14459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mDisplayWidth;
14559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
14659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Height of the screen */
14759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mDisplayHeight;
14859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
14959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Keyboard mode, or zero, if none.  */
15059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mKeyboardMode;
15159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
15259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
15359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int GRID_WIDTH = 10;
15459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int GRID_HEIGHT = 5;
15559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
15659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mCellWidth;
15759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mCellHeight;
15859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int[][] mGridNeighbors;
15959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mProximityThreshold;
16059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Number of key widths from current touch point to search for nearest keys. */
16159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static float SEARCH_DISTANCE = 1.8f;
16259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
16359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
16459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
16559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
16659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * defines.
16759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
16859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static class Row {
16959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Default width of a key in this row. */
17059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int defaultWidth;
17159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Default height of a key in this row. */
17259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int defaultHeight;
17359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Default horizontal gap between keys in this row. */
17459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int defaultHorizontalGap;
17559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Vertical gap following this row. */
17659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int verticalGap;
17759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
17859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Edge flags for this row of keys. Possible values that can be assigned are
17959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
18059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
18159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int rowEdgeFlags;
18259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
18359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** The keyboard mode for this row */
18459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int mode;
18559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
18659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private Keyboard parent;
18759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
18859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Constructor */
18959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public Row(Keyboard parent) {
19059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            this.parent = parent;
19159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
19259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
19359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Constructor */
19459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
19559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            this.parent = parent;
19659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
19759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard);
19859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            defaultWidth = getDimensionOrFraction(a,
19959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_keyWidth,
20059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    parent.mDisplayWidth, parent.mDefaultWidth);
20159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            defaultHeight = getDimensionOrFraction(a,
20259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_keyHeight,
20359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    parent.mDisplayHeight, parent.mDefaultHeight);
20459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            defaultHorizontalGap = getDimensionOrFraction(a,
20559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_horizontalGap,
20659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    parent.mDisplayWidth, parent.mDefaultHorizontalGap);
20759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            verticalGap = getDimensionOrFraction(a,
20859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_verticalGap,
20959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    parent.mDisplayHeight, parent.mDefaultVerticalGap);
21059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a.recycle();
21159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a = res.obtainAttributes(Xml.asAttributeSet(parser),
21259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Row);
21359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            rowEdgeFlags = a.getInt(android.R.styleable.Keyboard_Row_rowEdgeFlags, 0);
21459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mode = a.getResourceId(android.R.styleable.Keyboard_Row_keyboardMode,
21559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    0);
21659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
21759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
21859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
21959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
22059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Class for describing the position and characteristics of a single key in the keyboard.
22159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
22259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public static class Key {
22359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
22459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * All the key codes (unicode or custom code) that this key could generate, zero'th
22559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * being the most important.
22659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
22759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int[] codes;
22859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
22959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Label to display */
23059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public CharSequence label;
23159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
23259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Icon to display instead of a label. Icon takes precedence over a label */
23359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public Drawable icon;
23459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Preview version of the icon, for the preview popup */
23559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public Drawable iconPreview;
23659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Width of the key, not including the gap */
23759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int width;
23859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Height of the key, not including the gap */
23959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int height;
24059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** The horizontal gap before this key */
24159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int gap;
24259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Whether this key is sticky, i.e., a toggle key */
24359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean sticky;
24459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** X coordinate of the key in the keyboard layout */
24559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int x;
24659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Y coordinate of the key in the keyboard layout */
24759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int y;
24859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** The current pressed state of this key */
24959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean pressed;
25059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** If this is a sticky key, is it on? */
25159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean on;
25259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Text to output when pressed. This can be multiple characters, like ".com" */
25359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public CharSequence text;
25459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Popup characters */
25559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public CharSequence popupCharacters;
25659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
25759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
25859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Flags that specify the anchoring to edges of the keyboard for detecting touch events
25959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * that are just out of the boundary of the key. This is a bit mask of
26059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, {@link Keyboard#EDGE_TOP} and
26159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * {@link Keyboard#EDGE_BOTTOM}.
26259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
26359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int edgeFlags;
26459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Whether this is a modifier key, such as Shift or Alt */
26559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean modifier;
26659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** The keyboard that this key belongs to */
26759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private Keyboard keyboard;
26859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
26959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
27059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * keyboard.
27159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
27259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int popupResId;
27359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Whether this key repeats itself when held down */
27459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean repeatable;
27559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Whether this key is 2nd key */
27659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean isSecondKey;
27759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
27859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private final static int[] KEY_STATE_NORMAL_ON = {
27959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_checkable,
28059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_checked
28159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        };
28259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
28359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private final static int[] KEY_STATE_PRESSED_ON = {
28459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_pressed,
28559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_checkable,
28659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_checked
28759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        };
28859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
28959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private final static int[] KEY_STATE_NORMAL_OFF = {
29059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_checkable
29159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        };
29259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
29359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private final static int[] KEY_STATE_PRESSED_OFF = {
29459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_pressed,
29559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_checkable
29659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        };
29759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
29859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private final static int[] KEY_STATE_NORMAL = {
29959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        };
30059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
30159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private final static int[] KEY_STATE_PRESSED = {
30259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            android.R.attr.state_pressed
30359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        };
30459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
30559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Create an empty key with no attributes. */
30659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public Key(Row parent) {
30759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            keyboard = parent.parent;
30859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            height = parent.defaultHeight;
30959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            width = parent.defaultWidth;
31059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            gap = parent.defaultHorizontalGap;
31159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            edgeFlags = parent.rowEdgeFlags;
31259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
31359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
31459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /** Create a key with the given top-left coordinate and extract its attributes from
31559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * the XML parser.
31659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param res resources associated with the caller's context
31759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param parent the row that this key belongs to. The row must already be attached to
31859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * a {@link Keyboard}.
31959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param x the x coordinate of the top-left
32059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param y the y coordinate of the top-left
32159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param parser the XML parser containing the attributes for this key
32259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
32359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser) {
32459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            this(parent);
32559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
32659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            this.x = x;
32759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            this.y = y;
32859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
32959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
33059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard);
33159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
33259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            width = getDimensionOrFraction(a,
33359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_keyWidth,
33459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    keyboard.mDisplayWidth, parent.defaultWidth);
33559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            height = getDimensionOrFraction(a,
33659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_keyHeight,
33759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    keyboard.mDisplayHeight, parent.defaultHeight);
33859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            gap = getDimensionOrFraction(a,
33959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_horizontalGap,
34059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    keyboard.mDisplayWidth, parent.defaultHorizontalGap);
34159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a.recycle();
34259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a = res.obtainAttributes(Xml.asAttributeSet(parser),
34359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Key);
34459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            this.x += gap;
34559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            TypedValue codesValue = new TypedValue();
34659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a.getValue(android.R.styleable.Keyboard_Key_codes,
34759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    codesValue);
34859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (codesValue.type == TypedValue.TYPE_INT_DEC
34959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    || codesValue.type == TypedValue.TYPE_INT_HEX) {
35059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                codes = new int[] { codesValue.data };
35159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else if (codesValue.type == TypedValue.TYPE_STRING) {
35259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                codes = parseCSV(codesValue.string.toString());
35359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
35459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
35559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            iconPreview = a.getDrawable(android.R.styleable.Keyboard_Key_iconPreview);
35659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (iconPreview != null) {
35759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
35859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        iconPreview.getIntrinsicHeight());
35959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
36059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            popupCharacters = a.getText(
36159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Key_popupCharacters);
36259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            popupResId = a.getResourceId(
36359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Key_popupKeyboard, 0);
36459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            repeatable = a.getBoolean(
36559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Key_isRepeatable, false);
36659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            modifier = a.getBoolean(
36759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Key_isModifier, false);
36859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            sticky = a.getBoolean(
36959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Key_isSticky, false);
37059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            edgeFlags = a.getInt(android.R.styleable.Keyboard_Key_keyEdgeFlags, 0);
37159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            edgeFlags |= parent.rowEdgeFlags;
37259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
37359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            icon = a.getDrawable(
37459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    android.R.styleable.Keyboard_Key_keyIcon);
37559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (icon != null) {
37659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
37759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
37859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            label = a.getText(android.R.styleable.Keyboard_Key_keyLabel);
37959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            text = a.getText(android.R.styleable.Keyboard_Key_keyOutputText);
38059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
38159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (codes == null && !TextUtils.isEmpty(label)) {
38259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                codes = new int[] { label.charAt(0) };
38359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
38459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a.recycle();
38559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.WnnKeyboard_Key);
38659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            isSecondKey = a.getBoolean(R.styleable.WnnKeyboard_Key_isSecondKey, false);
38759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            a.recycle();
38859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
38959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
39059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
39159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Informs the key that it has been pressed, in case it needs to change its appearance or
39259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * state.
39359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @see #onReleased(boolean)
39459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
39559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public void onPressed() {
39659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            pressed = !pressed;
39759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
39859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
39959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
40059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Changes the pressed state of the key. If it is a sticky key, it will also change the
40159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * toggled state of the key if the finger was release inside.
40259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param inside whether the finger was released inside the key
40359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @see #onPressed()
40459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
40559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public void onReleased(boolean inside) {
40659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            pressed = !pressed;
40759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (sticky) {
40859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                on = !on;
40959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
41059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
41159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
41259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int[] parseCSV(String value) {
41359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int count = 0;
41459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int lastIndex = 0;
41559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (value.length() > 0) {
41659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                count++;
41759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
41859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    count++;
41959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
42059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
42159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int[] values = new int[count];
42259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            count = 0;
42359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            StringTokenizer st = new StringTokenizer(value, ",");
42459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            while (st.hasMoreTokens()) {
42559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                try {
42659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    values[count++] = Integer.parseInt(st.nextToken());
42759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } catch (NumberFormatException nfe) {
42859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    Log.e(TAG, "Error parsing keycodes " + value);
42959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
43059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
43159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return values;
43259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
43359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
43459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
43559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Detects if a point falls inside this key.
43659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param x the x-coordinate of the point
43759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param y the y-coordinate of the point
43859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @return whether or not the point falls inside the key. If the key is attached to an edge,
43959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * it will assume that all points between the key and the edge are considered to be inside
44059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * the key.
44159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
44259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean isInside(int x, int y) {
44359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
44459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
44559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
44659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
44759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if ((x >= this.x || (leftEdge && x <= this.x + this.width))
44859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && (x < this.x + this.width || (rightEdge && x >= this.x))
44959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && (y >= this.y || (topEdge && y <= this.y + this.height))
45059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && (y < this.y + this.height || (bottomEdge && y >= this.y))) {
45159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return true;
45259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
45359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return false;
45459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
45559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
45659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
45759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
45859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Detects if a area falls inside this key.
45959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param x the x-coordinate of the area
46059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param y the y-coordinate of the area
46159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param w the width of the area
46259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param h the height of the area
46359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @return whether or not the area falls inside the key.
46459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
46559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public boolean isInside(int x, int y, int w, int h) {
46659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if ((this.x <= (x + w)) && (x <= (this.x + this.width))
46759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && (this.y <= (y + h)) && (y <= (this.y + this.height))) {
46859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return true;
46959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
47059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return false;
47159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
47259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
47359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
47459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
47559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Returns the square of the distance between the center of the key and the given point.
47659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param x the x-coordinate of the point
47759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param y the y-coordinate of the point
47859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @return the square of the distance of the point from the center of the key
47959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
48059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int squaredDistanceFrom(int x, int y) {
48159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int xDist = this.x + width / 2 - x;
48259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int yDist = this.y + height / 2 - y;
48359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return xDist * xDist + yDist * yDist;
48459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
48559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
48659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
48759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Returns the drawable state for the key, based on the current state and type of the key.
48859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @return the drawable state of the key.
48959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @see android.graphics.drawable.StateListDrawable#setState(int[])
49059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
49159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public int[] getCurrentDrawableState() {
49259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int[] states = KEY_STATE_NORMAL;
49359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
49459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (on) {
49559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (pressed) {
49659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    states = KEY_STATE_PRESSED_ON;
49759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
49859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    states = KEY_STATE_NORMAL_ON;
49959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
50059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
50159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (sticky) {
50259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (pressed) {
50359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        states = KEY_STATE_PRESSED_OFF;
50459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
50559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        states = KEY_STATE_NORMAL_OFF;
50659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
50759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
50859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (pressed) {
50959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        states = KEY_STATE_PRESSED;
51059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
51159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
51259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
51359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return states;
51459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
51559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
51659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
51759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
51859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Creates a keyboard from the given xml key layout file.
51959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param context the application or service context
52059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
52159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
52259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public Keyboard(Context context, int xmlLayoutResId) {
52359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        this(context, xmlLayoutResId, 0);
52459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
52559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
52659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
52759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Creates a keyboard from the given xml key layout file. Weeds out rows
52859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * that have a keyboard mode defined but don't match the specified mode.
52959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param context the application or service context
53059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
53159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param modeId keyboard mode identifier
53259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
53359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public Keyboard(Context context, int xmlLayoutResId, int modeId) {
53459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        DisplayMetrics dm = context.getResources().getDisplayMetrics();
53559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDisplayWidth = dm.widthPixels;
53659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDisplayHeight = dm.heightPixels;
53759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
53859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultHorizontalGap = 0;
53959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultWidth = mDisplayWidth / 10;
54059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultVerticalGap = 0;
54159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultHeight = mDefaultWidth;
54259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeys = new ArrayList<Key>();
54359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mModifierKeys = new ArrayList<Key>();
54459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboardMode = modeId;
54559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
54659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
54759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
54859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
54959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * <p>Creates a blank keyboard from the given resource file and populates it with the specified
55059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
55159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * </p>
55259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
55359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * possible in each row.</p>
55459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param context the application or service context
55559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param layoutTemplateResId the layout template file, containing no keys.
55659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param characters the list of characters to display on the keyboard. One key will be created
55759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * for each character.
55859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param columns the number of columns of keys to display. If this number is greater than the
55959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * number of keys that can fit in a row, it will be ignored. If this number is -1, the
56059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * keyboard will fit as many keys as possible in each row.
56159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
56259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public Keyboard(Context context, int layoutTemplateResId,
56359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            CharSequence characters, int columns, int horizontalPadding) {
56459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        this(context, layoutTemplateResId);
56559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int x = 0;
56659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int y = 0;
56759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int column = 0;
56859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mTotalWidth = 0;
56959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
57059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Row row = new Row(this);
57159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        row.defaultHeight = mDefaultHeight;
57259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        row.defaultWidth = mDefaultWidth;
57359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        row.defaultHorizontalGap = mDefaultHorizontalGap;
57459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        row.verticalGap = mDefaultVerticalGap;
57559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
57659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
57759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        for (int i = 0; i < characters.length(); i++) {
57859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            char c = characters.charAt(i);
57959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (column >= maxColumns
58059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
58159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                x = 0;
58259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                y += mDefaultVerticalGap + mDefaultHeight;
58359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                column = 0;
58459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
58559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final Key key = new Key(row);
58659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            key.x = x;
58759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            key.y = y;
58859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            key.label = String.valueOf(c);
58959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            key.codes = new int[] { c };
59059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            column++;
59159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            x += key.width + key.gap;
59259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mKeys.add(key);
59359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (x > mTotalWidth) {
59459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mTotalWidth = x;
59559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
59659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
59759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mTotalHeight = y + mDefaultHeight;
59859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
59959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
60059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
60159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Get the list of keys in this keyboard.
60259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     *
60359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return The list of keys.
60459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
60559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public List<Key> getKeys() {
60659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mKeys;
60759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
60859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
60959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
61059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Get the list of modifier keys such as Shift & Alt, if any.
61159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     *
61259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return The list of modifier keys.
61359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
61459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public List<Key> getModifierKeys() {
61559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mModifierKeys;
61659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
61759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
61859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected int getHorizontalGap() {
61959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mDefaultHorizontalGap;
62059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
62159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
62259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void setHorizontalGap(int gap) {
62359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultHorizontalGap = gap;
62459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
62559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
62659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected int getVerticalGap() {
62759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mDefaultVerticalGap;
62859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
62959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
63059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void setVerticalGap(int gap) {
63159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultVerticalGap = gap;
63259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
63359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
63459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected int getKeyHeight() {
63559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mDefaultHeight;
63659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
63759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
63859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void setKeyHeight(int height) {
63959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultHeight = height;
64059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
64159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
64259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected int getKeyWidth() {
64359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mDefaultWidth;
64459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
64559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
64659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void setKeyWidth(int width) {
64759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultWidth = width;
64859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
64959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
65059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
65159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the total height of the keyboard
65259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return the total height of the keyboard
65359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
65459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public int getHeight() {
65559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mTotalHeight;
65659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
65759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
65859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
65959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the total minimum width of the keyboard
66059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return the total minimum width of the keyboard
66159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
66259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public int getMinWidth() {
66359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mTotalWidth;
66459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
66559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
66659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
66759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Sets the keyboard to be shifted.
66859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     *
66959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param shiftState  the keyboard shift state.
67059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return {@code true} if shift state changed.
67159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
67259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean setShifted(boolean shiftState) {
67359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mShiftKey != null) {
67459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mShiftKey.on = shiftState;
67559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
67659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mShifted != shiftState) {
67759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mShifted = shiftState;
67859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return true;
67959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
68059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return false;
68159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
68259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
68359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
68459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns whether keyboard is shift state or not.
68559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     *
68659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return  {@code true} if keyboard is shift state; otherwise, {@code false}.
68759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
68859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean isShifted() {
68959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mShifted;
69059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
69159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
69259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
69359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the shift key index.
69459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     *
69559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return  the shift key index.
69659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
69759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public int getShiftKeyIndex() {
69859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mShiftKeyIndex;
69959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
70059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
70159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void computeNearestNeighbors() {
70259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
70359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
70459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mGridNeighbors = new int[GRID_SIZE][];
70559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int[] indices = new int[mKeys.size()];
70659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int gridWidth = GRID_WIDTH * mCellWidth;
70759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int gridHeight = GRID_HEIGHT * mCellHeight;
70859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        for (int x = 0; x < gridWidth; x += mCellWidth) {
70959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            for (int y = 0; y < gridHeight; y += mCellHeight) {
71059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int count = 0;
71159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                for (int i = 0; i < mKeys.size(); i++) {
71259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    final Key key = mKeys.get(i);
71359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
71459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
71559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
71659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                < mProximityThreshold ||
71759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold ||
71859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            key.isInside(x, y, mCellWidth, mCellHeight)) {
71959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        indices[count++] = i;
72059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
72159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
72259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int [] cell = new int[count];
72359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                System.arraycopy(indices, 0, cell, 0, count);
72459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
72559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
72659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
72759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
72859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
72959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
73059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the indices of the keys that are closest to the given point.
73159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param x the x-coordinate of the point
73259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param y the y-coordinate of the point
73359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return the array of integer indices for the nearest keys to the given point. If the given
73459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * point is out of range, then an array of size zero is returned.
73559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
73659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public int[] getNearestKeys(int x, int y) {
73759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mGridNeighbors == null) computeNearestNeighbors();
73859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
73959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
74059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (index < GRID_SIZE) {
74159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return mGridNeighbors[index];
74259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
74359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
74459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return new int[0];
74559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
74659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
74759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
74859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return new Row(res, this, parser);
74959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
75059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
75159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
75259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            XmlResourceParser parser) {
75359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return new Key(res, parent, x, y, parser);
75459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
75559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
75659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void loadKeyboard(Context context, XmlResourceParser parser) {
75759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean inKey = false;
75859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean inRow = false;
75959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean leftMostKey = false;
76059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int row = 0;
76159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int x = 0;
76259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int y = 0;
76359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Key key = null;
76459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Row currentRow = null;
76559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Resources res = context.getResources();
76659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean skipRow = false;
76759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
76859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        try {
76959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int event;
77059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
77159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (event == XmlResourceParser.START_TAG) {
77259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    String tag = parser.getName();
77359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (TAG_ROW.equals(tag)) {
77459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        inRow = true;
77559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        x = 0;
77659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        currentRow = createRowFromXml(res, parser);
77759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
77859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        if (skipRow) {
77959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            skipToEndOfRow(parser);
78059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            inRow = false;
78159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        }
78259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                   } else if (TAG_KEY.equals(tag)) {
78359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        inKey = true;
78459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        key = createKeyFromXml(res, currentRow, x, y, parser);
78559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mKeys.add(key);
78659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        if (key.codes[0] == KEYCODE_SHIFT) {
78759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mShiftKey = key;
78859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mShiftKeyIndex = mKeys.size()-1;
78959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mModifierKeys.add(key);
79059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        } else if (key.codes[0] == KEYCODE_ALT) {
79159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mModifierKeys.add(key);
79259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        }
79359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else if (TAG_KEYBOARD.equals(tag)) {
79459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        parseKeyboardAttributes(res, parser);
79559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
79659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else if (event == XmlResourceParser.END_TAG) {
79759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (inKey) {
79859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        inKey = false;
79959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        x += key.gap + key.width;
80059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        if (x > mTotalWidth) {
80159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mTotalWidth = x;
80259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        }
80359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else if (inRow) {
80459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        inRow = false;
80559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        y += currentRow.verticalGap;
80659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        y += currentRow.defaultHeight;
80759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        row++;
80859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
80959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
81059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
81159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
81259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } catch (Exception e) {
81359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            Log.e(TAG, "Parse error:" + e);
81459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            e.printStackTrace();
81559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
81659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mTotalHeight = y - mDefaultVerticalGap;
81759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
81859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
81959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void skipToEndOfRow(XmlResourceParser parser)
82059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            throws XmlPullParserException, IOException {
82159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int event;
82259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
82359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (event == XmlResourceParser.END_TAG
82459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && parser.getName().equals(TAG_ROW)) {
82559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
82659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
82759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
82859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
82959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
83059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
83159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
83259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                android.R.styleable.Keyboard);
83359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
83459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultWidth = getDimensionOrFraction(a,
83559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                android.R.styleable.Keyboard_keyWidth,
83659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mDisplayWidth, mDisplayWidth / 10);
83759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultHeight = getDimensionOrFraction(a,
83859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                android.R.styleable.Keyboard_keyHeight,
83959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mDisplayHeight, 75);
84059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultHorizontalGap = getDimensionOrFraction(a,
84159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                android.R.styleable.Keyboard_horizontalGap,
84259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mDisplayWidth, 0);
84359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDefaultVerticalGap = getDimensionOrFraction(a,
84459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                android.R.styleable.Keyboard_verticalGap,
84559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mDisplayHeight, 0);
84659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
84759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mProximityThreshold = mProximityThreshold * mProximityThreshold;
84859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        a.recycle();
84959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
85059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
85159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
85259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        TypedValue value = a.peekValue(index);
85359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (value == null) return defValue;
85459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (value.type == TypedValue.TYPE_DIMENSION) {
85559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return a.getDimensionPixelOffset(index, defValue);
85659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else if (value.type == TypedValue.TYPE_FRACTION) {
85759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return Math.round(a.getFraction(index, base, base, defValue));
85859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
85959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return defValue;
86059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
86159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma}
862