19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008-2009 Google Inc.
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * License for the specific language governing permissions and limitations under
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.inputmethodservice;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParserException;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
217b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbyeimport android.annotation.XmlRes;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.XmlResourceParser;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextUtils;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.TypedValue;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Xml;
3158feea74b42bbaaa0552d76af23873bdd0b5dca2Mitsuru Oshimaimport android.util.DisplayMetrics;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.StringTokenizer;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * consists of rows of keys.
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <pre>
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * &lt;Keyboard
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         android:keyWidth="%10p"
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         android:keyHeight="50px"
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         android:horizontalGap="2px"
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         android:verticalGap="2px" &gt;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *     &lt;Row android:keyWidth="32px" &gt;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         &lt;Key android:keyLabel="A" /&gt;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         ...
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *     &lt;/Row&gt;
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *     ...
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * &lt;/Keyboard&gt;
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * </pre>
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#Keyboard_keyWidth
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#Keyboard_keyHeight
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#Keyboard_horizontalGap
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#Keyboard_verticalGap
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class Keyboard {
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String TAG = "Keyboard";
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Keyboard XML Tags
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG_KEYBOARD = "Keyboard";
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG_ROW = "Row";
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG_KEY = "Key";
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int EDGE_LEFT = 0x01;
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int EDGE_RIGHT = 0x02;
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int EDGE_TOP = 0x04;
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int EDGE_BOTTOM = 0x08;
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int KEYCODE_SHIFT = -1;
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int KEYCODE_MODE_CHANGE = -2;
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int KEYCODE_CANCEL = -3;
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int KEYCODE_DONE = -4;
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int KEYCODE_DELETE = -5;
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int KEYCODE_ALT = -6;
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Keyboard label **/
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private CharSequence mLabel;
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Horizontal gap default for all rows */
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDefaultHorizontalGap;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Default key width */
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDefaultWidth;
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Default key height */
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDefaultHeight;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Default gap between rows */
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDefaultVerticalGap;
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Is the keyboard in the shifted state */
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mShifted;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Key instance for the shift key, if present */
1016465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller    private Key[] mShiftKeys = { null, null };
1026465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Key index for the shift key, if present */
1046465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller    private int[] mShiftKeyIndices = {-1, -1};
1056465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Current key width, while loading the keyboard */
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mKeyWidth;
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Current key height, while loading the keyboard */
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mKeyHeight;
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Total height of the keyboard, including the padding and keys */
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTotalHeight;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * right side.
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTotalWidth;
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** List of keys in this keyboard */
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private List<Key> mKeys;
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** List of modifier keys such as Shift & Alt, if any */
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private List<Key> mModifierKeys;
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Width of the screen available to fit the keyboard */
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDisplayWidth;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Height of the screen */
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDisplayHeight;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Keyboard mode, or zero, if none.  */
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mKeyboardMode;
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Variables for pre-computing nearest keys.
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int GRID_WIDTH = 10;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int GRID_HEIGHT = 5;
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mCellWidth;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mCellHeight;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int[][] mGridNeighbors;
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mProximityThreshold;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Number of key widths from current touch point to search for nearest keys. */
146ae09878698ba6ad56ea43843f5a0895c94c32d90Amith Yamasani    private static float SEARCH_DISTANCE = 1.8f;
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
148a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase    private ArrayList<Row> rows = new ArrayList<Row>();
149a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * defines.
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_keyWidth
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_keyHeight
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_horizontalGap
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_verticalGap
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Row_rowEdgeFlags
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Row_keyboardMode
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class Row {
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Default width of a key in this row. */
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int defaultWidth;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Default height of a key in this row. */
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int defaultHeight;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Default horizontal gap between keys in this row. */
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int defaultHorizontalGap;
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Vertical gap following this row. */
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int verticalGap;
170a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase
171a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        ArrayList<Key> mKeys = new ArrayList<Key>();
172a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Edge flags for this row of keys. Possible values that can be assigned are
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int rowEdgeFlags;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The keyboard mode for this row */
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int mode;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Keyboard parent;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Row(Keyboard parent) {
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.parent = parent;
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.parent = parent;
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard);
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            defaultWidth = getDimensionOrFraction(a,
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_keyWidth,
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    parent.mDisplayWidth, parent.mDefaultWidth);
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            defaultHeight = getDimensionOrFraction(a,
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_keyHeight,
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    parent.mDisplayHeight, parent.mDefaultHeight);
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            defaultHorizontalGap = getDimensionOrFraction(a,
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_horizontalGap,
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    parent.mDisplayWidth, parent.mDefaultHorizontalGap);
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            verticalGap = getDimensionOrFraction(a,
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_verticalGap,
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    parent.mDisplayHeight, parent.mDefaultVerticalGap);
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            a.recycle();
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            a = res.obtainAttributes(Xml.asAttributeSet(parser),
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Row);
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            rowEdgeFlags = a.getInt(com.android.internal.R.styleable.Keyboard_Row_rowEdgeFlags, 0);
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mode = a.getResourceId(com.android.internal.R.styleable.Keyboard_Row_keyboardMode,
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    0);
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Class for describing the position and characteristics of a single key in the keyboard.
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_keyWidth
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_keyHeight
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_horizontalGap
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_codes
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_keyIcon
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_keyLabel
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_iconPreview
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_isSticky
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_isRepeatable
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_isModifier
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_popupKeyboard
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_popupCharacters
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_keyOutputText
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @attr ref android.R.styleable#Keyboard_Key_keyEdgeFlags
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class Key {
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * All the key codes (unicode or custom code) that this key could generate, zero'th
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * being the most important.
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int[] codes;
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Label to display */
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence label;
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Icon to display instead of a label. Icon takes precedence over a label */
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Drawable icon;
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Preview version of the icon, for the preview popup */
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Drawable iconPreview;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Width of the key, not including the gap */
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int width;
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Height of the key, not including the gap */
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int height;
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The horizontal gap before this key */
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int gap;
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Whether this key is sticky, i.e., a toggle key */
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean sticky;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** X coordinate of the key in the keyboard layout */
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int x;
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Y coordinate of the key in the keyboard layout */
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int y;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The current pressed state of this key */
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean pressed;
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** If this is a sticky key, is it on? */
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean on;
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Text to output when pressed. This can be multiple characters, like ".com" */
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence text;
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Popup characters */
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence popupCharacters;
265a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Flags that specify the anchoring to edges of the keyboard for detecting touch events
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * that are just out of the boundary of the key. This is a bit mask of
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, {@link Keyboard#EDGE_TOP} and
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * {@link Keyboard#EDGE_BOTTOM}.
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int edgeFlags;
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Whether this is a modifier key, such as Shift or Alt */
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean modifier;
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The keyboard that this key belongs to */
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Keyboard keyboard;
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * keyboard.
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int popupResId;
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Whether this key repeats itself when held down */
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean repeatable;
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final static int[] KEY_STATE_NORMAL_ON = {
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_checkable,
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_checked
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final static int[] KEY_STATE_PRESSED_ON = {
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_pressed,
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_checkable,
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_checked
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final static int[] KEY_STATE_NORMAL_OFF = {
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_checkable
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final static int[] KEY_STATE_PRESSED_OFF = {
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_pressed,
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_checkable
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final static int[] KEY_STATE_NORMAL = {
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final static int[] KEY_STATE_PRESSED = {
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            android.R.attr.state_pressed
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Create an empty key with no attributes. */
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Key(Row parent) {
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            keyboard = parent.parent;
316b65b7cb5808a3cea59cbfa72ecd46bdda90351faTadashi G. Takaoka            height = parent.defaultHeight;
317b65b7cb5808a3cea59cbfa72ecd46bdda90351faTadashi G. Takaoka            width = parent.defaultWidth;
318b65b7cb5808a3cea59cbfa72ecd46bdda90351faTadashi G. Takaoka            gap = parent.defaultHorizontalGap;
319b65b7cb5808a3cea59cbfa72ecd46bdda90351faTadashi G. Takaoka            edgeFlags = parent.rowEdgeFlags;
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Create a key with the given top-left coordinate and extract its attributes from
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * the XML parser.
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param res resources associated with the caller's context
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param parent the row that this key belongs to. The row must already be attached to
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * a {@link Keyboard}.
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param x the x coordinate of the top-left
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param y the y coordinate of the top-left
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param parser the XML parser containing the attributes for this key
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser) {
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this(parent);
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.x = x;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.y = y;
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard);
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            width = getDimensionOrFraction(a,
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_keyWidth,
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    keyboard.mDisplayWidth, parent.defaultWidth);
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            height = getDimensionOrFraction(a,
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_keyHeight,
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    keyboard.mDisplayHeight, parent.defaultHeight);
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            gap = getDimensionOrFraction(a,
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_horizontalGap,
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    keyboard.mDisplayWidth, parent.defaultHorizontalGap);
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            a.recycle();
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            a = res.obtainAttributes(Xml.asAttributeSet(parser),
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Key);
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.x += gap;
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TypedValue codesValue = new TypedValue();
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            a.getValue(com.android.internal.R.styleable.Keyboard_Key_codes,
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    codesValue);
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (codesValue.type == TypedValue.TYPE_INT_DEC
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    || codesValue.type == TypedValue.TYPE_INT_HEX) {
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                codes = new int[] { codesValue.data };
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (codesValue.type == TypedValue.TYPE_STRING) {
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                codes = parseCSV(codesValue.string.toString());
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            iconPreview = a.getDrawable(com.android.internal.R.styleable.Keyboard_Key_iconPreview);
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (iconPreview != null) {
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        iconPreview.getIntrinsicHeight());
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            popupCharacters = a.getText(
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Key_popupCharacters);
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            popupResId = a.getResourceId(
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Key_popupKeyboard, 0);
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            repeatable = a.getBoolean(
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Key_isRepeatable, false);
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            modifier = a.getBoolean(
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Key_isModifier, false);
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sticky = a.getBoolean(
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Key_isSticky, false);
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            edgeFlags = a.getInt(com.android.internal.R.styleable.Keyboard_Key_keyEdgeFlags, 0);
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            edgeFlags |= parent.rowEdgeFlags;
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            icon = a.getDrawable(
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    com.android.internal.R.styleable.Keyboard_Key_keyIcon);
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (icon != null) {
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            label = a.getText(com.android.internal.R.styleable.Keyboard_Key_keyLabel);
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text = a.getText(com.android.internal.R.styleable.Keyboard_Key_keyOutputText);
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (codes == null && !TextUtils.isEmpty(label)) {
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                codes = new int[] { label.charAt(0) };
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            a.recycle();
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Informs the key that it has been pressed, in case it needs to change its appearance or
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * state.
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #onReleased(boolean)
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onPressed() {
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            pressed = !pressed;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4035c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
4055c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         * Changes the pressed state of the key.
4065c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         *
4075c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         * <p>Toggled state of the key will be flipped when all the following conditions are
4085c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         * fulfilled:</p>
4095c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         *
4105c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         * <ul>
4115c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         *     <li>This is a sticky key, that is, {@link #sticky} is {@code true}.
4125c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         *     <li>The parameter {@code inside} is {@code true}.
4135b2a0987fa0d889db1b4827c79ad3b4ce6409cecYohei Yukawa         *     <li>{@link android.os.Build.VERSION#SDK_INT} is greater than
4145b2a0987fa0d889db1b4827c79ad3b4ce6409cecYohei Yukawa         *         {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}.
4155c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         * </ul>
4165c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         *
4175c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         * @param inside whether the finger was released inside the key. Works only on Android M and
4185c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa         * later. See the method document for details.
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #onPressed()
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onReleased(boolean inside) {
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            pressed = !pressed;
4235c31de33ccc99404f54452de1974bb2960b31343Yohei Yukawa            if (sticky && inside) {
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                on = !on;
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] parseCSV(String value) {
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int count = 0;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int lastIndex = 0;
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (value.length() > 0) {
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                count++;
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    count++;
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int[] values = new int[count];
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            count = 0;
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringTokenizer st = new StringTokenizer(value, ",");
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (st.hasMoreTokens()) {
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values[count++] = Integer.parseInt(st.nextToken());
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (NumberFormatException nfe) {
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.e(TAG, "Error parsing keycodes " + value);
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return values;
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Detects if a point falls inside this key.
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param x the x-coordinate of the point
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param y the y-coordinate of the point
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return whether or not the point falls inside the key. If the key is attached to an edge,
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * it will assume that all points between the key and the edge are considered to be inside
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * the key.
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isInside(int x, int y) {
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((x >= this.x || (leftEdge && x <= this.x + this.width))
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && (x < this.x + this.width || (rightEdge && x >= this.x))
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && (y >= this.y || (topEdge && y <= this.y + this.height))
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && (y < this.y + this.height || (bottomEdge && y >= this.y))) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the square of the distance between the center of the key and the given point.
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param x the x-coordinate of the point
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param y the y-coordinate of the point
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return the square of the distance of the point from the center of the key
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int squaredDistanceFrom(int x, int y) {
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int xDist = this.x + width / 2 - x;
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int yDist = this.y + height / 2 - y;
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return xDist * xDist + yDist * yDist;
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the drawable state for the key, based on the current state and type of the key.
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return the drawable state of the key.
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see android.graphics.drawable.StateListDrawable#setState(int[])
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int[] getCurrentDrawableState() {
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int[] states = KEY_STATE_NORMAL;
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (on) {
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (pressed) {
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    states = KEY_STATE_PRESSED_ON;
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    states = KEY_STATE_NORMAL_ON;
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (sticky) {
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (pressed) {
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        states = KEY_STATE_PRESSED_OFF;
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        states = KEY_STATE_NORMAL_OFF;
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (pressed) {
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        states = KEY_STATE_PRESSED;
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return states;
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a keyboard from the given xml key layout file.
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the application or service context
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Keyboard(Context context, int xmlLayoutResId) {
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, xmlLayoutResId, 0);
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5248171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung
5258171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung    /**
5268171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     * Creates a keyboard from the given xml key layout file. Weeds out rows
5278171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     * that have a keyboard mode defined but don't match the specified mode.
5288171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     * @param context the application or service context
5298171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
5308171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     * @param modeId keyboard mode identifier
5318171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     * @param width sets width of keyboard
5328171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     * @param height sets height of keyboard
5338171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung     */
5347b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye    public Keyboard(Context context, @XmlRes int xmlLayoutResId, int modeId, int width,
5357b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye            int height) {
5368171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mDisplayWidth = width;
5378171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mDisplayHeight = height;
5388171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung
5398171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mDefaultHorizontalGap = 0;
5408171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mDefaultWidth = mDisplayWidth / 10;
5418171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mDefaultVerticalGap = 0;
5428171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mDefaultHeight = mDefaultWidth;
5438171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mKeys = new ArrayList<Key>();
5448171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mModifierKeys = new ArrayList<Key>();
5458171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        mKeyboardMode = modeId;
5468171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
5478171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung    }
5488171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a keyboard from the given xml key layout file. Weeds out rows
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * that have a keyboard mode defined but don't match the specified mode.
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the application or service context
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param modeId keyboard mode identifier
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5567b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye    public Keyboard(Context context, @XmlRes int xmlLayoutResId, int modeId) {
55758feea74b42bbaaa0552d76af23873bdd0b5dca2Mitsuru Oshima        DisplayMetrics dm = context.getResources().getDisplayMetrics();
55858feea74b42bbaaa0552d76af23873bdd0b5dca2Mitsuru Oshima        mDisplayWidth = dm.widthPixels;
55958feea74b42bbaaa0552d76af23873bdd0b5dca2Mitsuru Oshima        mDisplayHeight = dm.heightPixels;
56058feea74b42bbaaa0552d76af23873bdd0b5dca2Mitsuru Oshima        //Log.v(TAG, "keyboard's display metrics:" + dm);
56158feea74b42bbaaa0552d76af23873bdd0b5dca2Mitsuru Oshima
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultHorizontalGap = 0;
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultWidth = mDisplayWidth / 10;
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultVerticalGap = 0;
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultHeight = mDefaultWidth;
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mKeys = new ArrayList<Key>();
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mModifierKeys = new ArrayList<Key>();
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mKeyboardMode = modeId;
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Creates a blank keyboard from the given resource file and populates it with the specified
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * </p>
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * possible in each row.</p>
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the application or service context
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param layoutTemplateResId the layout template file, containing no keys.
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param characters the list of characters to display on the keyboard. One key will be created
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * for each character.
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param columns the number of columns of keys to display. If this number is greater than the
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * number of keys that can fit in a row, it will be ignored. If this number is -1, the
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * keyboard will fit as many keys as possible in each row.
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Keyboard(Context context, int layoutTemplateResId,
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            CharSequence characters, int columns, int horizontalPadding) {
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, layoutTemplateResId);
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int x = 0;
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int y = 0;
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int column = 0;
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTotalWidth = 0;
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Row row = new Row(this);
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        row.defaultHeight = mDefaultHeight;
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        row.defaultWidth = mDefaultWidth;
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        row.defaultHorizontalGap = mDefaultHorizontalGap;
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        row.verticalGap = mDefaultVerticalGap;
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < characters.length(); i++) {
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = characters.charAt(i);
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (column >= maxColumns
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                x = 0;
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                y += mDefaultVerticalGap + mDefaultHeight;
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                column = 0;
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Key key = new Key(row);
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            key.x = x;
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            key.y = y;
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            key.label = String.valueOf(c);
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            key.codes = new int[] { c };
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            column++;
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x += key.width + key.gap;
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mKeys.add(key);
617a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            row.mKeys.add(key);
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (x > mTotalWidth) {
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTotalWidth = x;
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
622a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        mTotalHeight = y + mDefaultHeight;
623a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        rows.add(row);
624a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase    }
625a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase
626a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase    final void resize(int newWidth, int newHeight) {
627a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        int numRows = rows.size();
628a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
629a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            Row row = rows.get(rowIndex);
630a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            int numKeys = row.mKeys.size();
631a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            int totalGap = 0;
632a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            int totalWidth = 0;
633a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
634a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                Key key = row.mKeys.get(keyIndex);
635a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                if (keyIndex > 0) {
636a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                    totalGap += key.gap;
637a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                }
638a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                totalWidth += key.width;
639a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            }
640a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            if (totalGap + totalWidth > newWidth) {
641a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                int x = 0;
642a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                float scaleFactor = (float)(newWidth - totalGap) / totalWidth;
643a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
644a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                    Key key = row.mKeys.get(keyIndex);
645a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                    key.width *= scaleFactor;
646a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                    key.x = x;
647a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                    x += key.width + key.gap;
648a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                }
649a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase            }
650a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        }
651a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        mTotalWidth = newWidth;
652a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        // TODO: This does not adjust the vertical placement according to the new size.
653a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        // The main problem in the previous code was horizontal placement/size, but we should
654a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase        // also recalculate the vertical sizes/positions when we get this resize call.
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public List<Key> getKeys() {
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mKeys;
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public List<Key> getModifierKeys() {
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mModifierKeys;
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected int getHorizontalGap() {
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDefaultHorizontalGap;
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void setHorizontalGap(int gap) {
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultHorizontalGap = gap;
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected int getVerticalGap() {
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDefaultVerticalGap;
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void setVerticalGap(int gap) {
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultVerticalGap = gap;
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected int getKeyHeight() {
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDefaultHeight;
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void setKeyHeight(int height) {
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultHeight = height;
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected int getKeyWidth() {
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDefaultWidth;
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void setKeyWidth(int width) {
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultWidth = width;
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the total height of the keyboard
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the total height of the keyboard
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getHeight() {
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mTotalHeight;
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getMinWidth() {
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mTotalWidth;
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean setShifted(boolean shiftState) {
7106465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller        for (Key shiftKey : mShiftKeys) {
7116465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller            if (shiftKey != null) {
7126465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                shiftKey.on = shiftState;
7136465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller            }
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mShifted != shiftState) {
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mShifted = shiftState;
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isShifted() {
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mShifted;
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7266465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller    /**
7276465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller     * @hide
7286465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller     */
7296465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller    public int[] getShiftKeyIndices() {
7306465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller        return mShiftKeyIndices;
7316465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller    }
7326465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getShiftKeyIndex() {
7346465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller        return mShiftKeyIndices[0];
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void computeNearestNeighbors() {
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Round-up so we don't have any pixels outside the grid
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGridNeighbors = new int[GRID_SIZE][];
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] indices = new int[mKeys.size()];
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int gridWidth = GRID_WIDTH * mCellWidth;
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int gridHeight = GRID_HEIGHT * mCellHeight;
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int x = 0; x < gridWidth; x += mCellWidth) {
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int y = 0; y < gridHeight; y += mCellHeight) {
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int count = 0;
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < mKeys.size(); i++) {
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    final Key key = mKeys.get(i);
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                < mProximityThreshold ||
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        indices[count++] = i;
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int [] cell = new int[count];
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                System.arraycopy(indices, 0, cell, 0, count);
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the indices of the keys that are closest to the given point.
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param x the x-coordinate of the point
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param y the y-coordinate of the point
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the array of integer indices for the nearest keys to the given point. If the given
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * point is out of range, then an array of size zero is returned.
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int[] getNearestKeys(int x, int y) {
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mGridNeighbors == null) computeNearestNeighbors();
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (index < GRID_SIZE) {
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mGridNeighbors[index];
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new int[0];
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new Row(res, this, parser);
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            XmlResourceParser parser) {
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new Key(res, parent, x, y, parser);
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void loadKeyboard(Context context, XmlResourceParser parser) {
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean inKey = false;
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean inRow = false;
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean leftMostKey = false;
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int row = 0;
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int x = 0;
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int y = 0;
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Key key = null;
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Row currentRow = null;
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Resources res = context.getResources();
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean skipRow = false;
803a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int event;
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (event == XmlResourceParser.START_TAG) {
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String tag = parser.getName();
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (TAG_ROW.equals(tag)) {
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        inRow = true;
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        x = 0;
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        currentRow = createRowFromXml(res, parser);
813a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                        rows.add(currentRow);
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (skipRow) {
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            skipToEndOfRow(parser);
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            inRow = false;
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                   } else if (TAG_KEY.equals(tag)) {
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        inKey = true;
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        key = createKeyFromXml(res, currentRow, x, y, parser);
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mKeys.add(key);
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (key.codes[0] == KEYCODE_SHIFT) {
8246465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                            // Find available shift key slot and put this shift key in it
8256465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                            for (int i = 0; i < mShiftKeys.length; i++) {
8266465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                                if (mShiftKeys[i] == null) {
8276465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                                    mShiftKeys[i] = key;
8286465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                                    mShiftKeyIndices[i] = mKeys.size()-1;
8296465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                                    break;
8306465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                                }
8316465f7753783a614948fb3ffbd8c072345b4eea1Jim Miller                            }
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mModifierKeys.add(key);
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else if (key.codes[0] == KEYCODE_ALT) {
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mModifierKeys.add(key);
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
836a95e1087b2258b118a7ccb2bedb44da359d3abd0Chet Haase                        currentRow.mKeys.add(key);
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (TAG_KEYBOARD.equals(tag)) {
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        parseKeyboardAttributes(res, parser);
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (event == XmlResourceParser.END_TAG) {
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (inKey) {
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        inKey = false;
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        x += key.gap + key.width;
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (x > mTotalWidth) {
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mTotalWidth = x;
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (inRow) {
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        inRow = false;
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        y += currentRow.verticalGap;
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        y += currentRow.defaultHeight;
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        row++;
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // TODO: error or extend?
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (Exception e) {
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "Parse error:" + e);
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.printStackTrace();
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTotalHeight = y - mDefaultVerticalGap;
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void skipToEndOfRow(XmlResourceParser parser)
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws XmlPullParserException, IOException {
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int event;
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (event == XmlResourceParser.END_TAG
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && parser.getName().equals(TAG_ROW)) {
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                com.android.internal.R.styleable.Keyboard);
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultWidth = getDimensionOrFraction(a,
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                com.android.internal.R.styleable.Keyboard_keyWidth,
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDisplayWidth, mDisplayWidth / 10);
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultHeight = getDimensionOrFraction(a,
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                com.android.internal.R.styleable.Keyboard_keyHeight,
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDisplayHeight, 50);
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultHorizontalGap = getDimensionOrFraction(a,
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                com.android.internal.R.styleable.Keyboard_horizontalGap,
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDisplayWidth, 0);
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultVerticalGap = getDimensionOrFraction(a,
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                com.android.internal.R.styleable.Keyboard_verticalGap,
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDisplayHeight, 0);
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        a.recycle();
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TypedValue value = a.peekValue(index);
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (value == null) return defValue;
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (value.type == TypedValue.TYPE_DIMENSION) {
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return a.getDimensionPixelOffset(index, defValue);
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (value.type == TypedValue.TYPE_FRACTION) {
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Round it to avoid values like 47.9999 from getting truncated
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return Math.round(a.getFraction(index, base, base, defValue));
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return defValue;
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
908