package com.app.input.breeze.board.sourcecode;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.util.Xml;
import androidx.annotation.XmlRes;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import com.app.input.breeze.board.R;

public class Keyboard {

    static final String TAG = "------------Keyboard-----------";

    // 键盘 XML 标签
    private static final String TAG_KEYBOARD = "Keyboard";
    private static final String TAG_ROW = "Row";
    private static final String TAG_KEY = "Key";

    public static final int EDGE_LEFT = 0x01;
    public static final int EDGE_RIGHT = 0x02;
    public static final int EDGE_TOP = 0x04;
    public static final int EDGE_BOTTOM = 0x08;
    public static final int KEYCODE_SHIFT = -1;
    public static final int KEYCODE_MODE_CHANGE = -2;
    public static final int KEYCODE_CANCEL = -3;
    public static final int KEYCODE_DONE = -4;
    public static final int KEYCODE_DELETE = -5;
    public static final int KEYCODE_ALT = -6;

    /** 键盘标签 **/
    private CharSequence mLabel;

    /** 所有行的默认水平间距 */
    private int mDefaultHorizontalGap;

    /** 默认按键宽度 */
    private int mDefaultWidth;

    /** 默认按键高度 */
    private int mDefaultHeight;

    /** 行之间的默认间距 */
    private int mDefaultVerticalGap;

    /** 键盘是否处于大写状态 */
    private boolean mShifted;

    /** Shift 键的实例（如果存在） */
    private Keyboard.Key[] mShiftKeys = { null, null };

    /** Shift 键的索引（如果存在） */
    private int[] mShiftKeyIndices = {-1, -1};

    /** 加载键盘时的当前按键宽度 */
    private int mKeyWidth;

    /** 加载键盘时的当前按键高度 */
    private int mKeyHeight;

    /** 键盘的总高度，包括内边距和按键 */
    private int mTotalHeight;

    /**
     * 键盘的总宽度，包括左侧间距和按键，但不包括右侧的任何间距。
     */
    private int mTotalWidth;

    /** 此键盘中的按键列表 */
    private List<Keyboard.Key> mKeys;

    /** 修饰键列表，如 Shift 和 Alt（如果有） */
    private List<Keyboard.Key> mModifierKeys;

    /** 可用于放置键盘的屏幕宽度 */
    private int mDisplayWidth;

    /** 屏幕高度 */
    private int mDisplayHeight;

    /** 键盘模式，如果没有则为零 */
    private int mKeyboardMode;

    // 用于预计算最近按键的变量
    private static final int GRID_WIDTH = 10;
    private static final int GRID_HEIGHT = 5;
    private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
    private int mCellWidth;
    private int mCellHeight;
    private int[][] mGridNeighbors;
    private int mProximityThreshold;

    /** 从当前触摸点到搜索最近按键的按键宽度数量 */
    private static float SEARCH_DISTANCE = 1.8f;

    private ArrayList<Keyboard.Row> rows = new ArrayList<>();

    /**
     * 键盘中按键的容器。一行中的所有按键具有相同的 Y 坐标。
     * 某些按键大小的默认值可以在每行中覆盖 {@link Keyboard} 定义的值。
     */
    public static class Row {

        /** 此行中按键的默认宽度 */
        public int defaultWidth;

        /** 此行中按键的默认高度 */
        public int defaultHeight;

        /** 此行中按键之间的默认水平间距 */
        public int defaultHorizontalGap;

        /** 此行之后的垂直间距 */
        public int verticalGap;

        ArrayList<Keyboard.Key> mKeys = new ArrayList<>();

        /**
         * 此行按键的边缘标志。可以赋值的可能值有
         * {@link Keyboard#EDGE_TOP EDGE_TOP} 和 {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
         */
        public int rowEdgeFlags;

        /** 此行的键盘模式 */
        public int mode;

        private Keyboard parent;

        public Row(Keyboard parent) {
            this.parent = parent;
        }

        public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
            this.parent = parent;
            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.My_Keyboard_view);
            defaultWidth = getDimensionOrFraction(a,
                    R.styleable.My_Keyboard_view_android_keyWidth,
                    parent.mDisplayWidth, parent.mDefaultWidth);
            defaultHeight = getDimensionOrFraction(a,
                    R.styleable.My_Keyboard_view_android_keyHeight,
                    parent.mDisplayHeight, parent.mDefaultHeight);
            defaultHorizontalGap = getDimensionOrFraction(a,
                    R.styleable.My_Keyboard_view_android_horizontalGap,
                    parent.mDisplayWidth, parent.mDefaultHorizontalGap);
            verticalGap = getDimensionOrFraction(a,
                    R.styleable.My_Keyboard_view_android_verticalGap,
                    parent.mDisplayHeight, parent.mDefaultVerticalGap);
            a.recycle();
            a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.Kil_Keyboard_Row);
            rowEdgeFlags = a.getInt(R.styleable.Kil_Keyboard_Row_android_rowEdgeFlags, 0);
            mode = a.getResourceId(R.styleable.Kil_Keyboard_Row_android_keyboardMode, 0);
        }
    }

    /**
     * 用于描述键盘中单个按键的位置和特征的类。
     */
    public static class Key {

        /**
         * 此按键可以生成的所有键码（Unicode 或自定义代码），
         * 第零个是最重要的。
         */
        public int[] codes;

        /** 要显示的标签 */
        public CharSequence label;

        /** 要显示的图标（替代标签）。图标优先于标签 */
        public Drawable icon;

        /** 图标的预览版本，用于预览弹出窗口 */
        public Drawable iconPreview;

        /** 按键的宽度，不包括间距 */
        public int width;

        /** 按键的高度，不包括间距 */
        public int height;

        /** 此按键之前的水平间距 */
        public int gap;

        /** 此按键是否为粘性键，即切换键 */
        public boolean sticky;

        /** 按键在键盘布局中的 X 坐标 */
        public int x;

        /** 按键在键盘布局中的 Y 坐标 */
        public int y;

        /** 此按键的当前按下状态 */
        public boolean pressed;

        /** 如果这是粘性键，它是否处于开启状态？ */
        public boolean on;

        /** 按下时输出的文本。可以是多个字符，如 ".com" */
        public CharSequence text;

        /** 弹出字符 */
        public CharSequence popupCharacters;

        /**
         * 指定键盘边缘锚定的标志，用于检测刚好超出按键边界的触摸事件。
         * 这是 {@link Keyboard#EDGE_LEFT}、{@link Keyboard#EDGE_RIGHT}、{@link Keyboard#EDGE_TOP} 和
         * {@link Keyboard#EDGE_BOTTOM} 的位掩码。
         */
        public int edgeFlags;

        /** 这是否为修饰键，如 Shift 或 Alt */
        public boolean modifier;

        /** 此按键所属的键盘 */
        private Keyboard keyboard;

        /**
         * 如果此按键弹出迷你键盘，这是该键盘的 XML 布局的资源 ID。
         */
        public int popupResId;

        /** 此按键在按住时是否重复 */
        public boolean repeatable;

        private final static int[] KEY_STATE_NORMAL_ON = {
                android.R.attr.state_checkable,
                android.R.attr.state_checked
        };

        private final static int[] KEY_STATE_PRESSED_ON = {
                android.R.attr.state_pressed,
                android.R.attr.state_checkable,
                android.R.attr.state_checked
        };

        private final static int[] KEY_STATE_NORMAL_OFF = {
                android.R.attr.state_checkable
        };

        private final static int[] KEY_STATE_PRESSED_OFF = {
                android.R.attr.state_pressed,
                android.R.attr.state_checkable
        };

        private final static int[] KEY_STATE_NORMAL = {
        };

        private final static int[] KEY_STATE_PRESSED = {
                android.R.attr.state_pressed
        };

        /** 创建一个没有属性的空按键 */
        public Key(Keyboard.Row parent) {
            keyboard = parent.parent;
            height = parent.defaultHeight;
            width = parent.defaultWidth;
            gap = parent.defaultHorizontalGap;
            edgeFlags = parent.rowEdgeFlags;
        }

        /** 使用给定的左上角坐标创建按键，并从 XML 解析器中提取其属性 */
        public Key(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) {
            this(parent);
            this.x = x;
            this.y = y;
            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.My_Keyboard_view);
            width = getDimensionOrFraction(a,
                    R.styleable.My_Keyboard_view_android_keyWidth,
                    keyboard.mDisplayWidth, parent.defaultWidth);
            height = getDimensionOrFraction(a,
                    R.styleable.My_Keyboard_view_android_keyHeight,
                    keyboard.mDisplayHeight, parent.defaultHeight);
            gap = getDimensionOrFraction(a,
                    R.styleable.My_Keyboard_view_android_horizontalGap,
                    keyboard.mDisplayWidth, parent.defaultHorizontalGap);
            a.recycle();
            a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.K_Keyboard_Key);
            this.x += gap;
            TypedValue codesValue = new TypedValue();
            a.getValue(R.styleable.K_Keyboard_Key_android_codes, codesValue);
            if (codesValue.type == TypedValue.TYPE_INT_DEC
                    || codesValue.type == TypedValue.TYPE_INT_HEX) {
                codes = new int[] { codesValue.data };
            } else if (codesValue.type == TypedValue.TYPE_STRING) {
                codes = parseCSV(codesValue.string.toString());
            }
            iconPreview = a.getDrawable(R.styleable.K_Keyboard_Key_android_iconPreview);
            if (iconPreview != null) {
                iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
                        iconPreview.getIntrinsicHeight());
            }
            popupCharacters = a.getText(
                    R.styleable.K_Keyboard_Key_android_popupCharacters);
            popupResId = a.getResourceId(
                    R.styleable.K_Keyboard_Key_android_popupKeyboard, 0);
            repeatable = a.getBoolean(
                    R.styleable.K_Keyboard_Key_android_isRepeatable, false);
            modifier = a.getBoolean(
                    R.styleable.K_Keyboard_Key_android_isModifier, false);
            sticky = a.getBoolean(
                    R.styleable.K_Keyboard_Key_android_isSticky, false);
            edgeFlags = a.getInt(R.styleable.K_Keyboard_Key_android_keyEdgeFlags, 0);
            edgeFlags |= parent.rowEdgeFlags;
            icon = a.getDrawable(
                    R.styleable.K_Keyboard_Key_android_keyIcon);
            if (icon != null) {
                icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
            }
            label = a.getText(R.styleable.K_Keyboard_Key_android_keyLabel);
            text = a.getText(R.styleable.K_Keyboard_Key_android_keyOutputText);
            if (codes == null && !TextUtils.isEmpty(label)) {
                codes = new int[] { label.charAt(0) };
            }
            a.recycle();
        }

        /**
         * 通知按键已被按下，以防它需要更改外观或状态。
         */
        public void onPressed() {
            pressed = !pressed;
        }

        /**
         * 更改按键的按下状态。
         */
        public void onReleased(boolean inside) {
            pressed = !pressed;
            if (sticky && inside) {
                on = !on;
            }
        }

        int[] parseCSV(String value) {
            int count = 0;
            int lastIndex = 0;
            if (value.length() > 0) {
                count++;
                while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
                    count++;
                }
            }
            int[] values = new int[count];
            count = 0;
            StringTokenizer st = new StringTokenizer(value, ",");
            while (st.hasMoreTokens()) {
                try {
                    values[count++] = Integer.parseInt(st.nextToken());
                } catch (NumberFormatException nfe) {
                }
            }
            return values;
        }

        /**
         * 检测点是否落在此按键内。
         */
        public boolean isInside(int x, int y) {
            boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
            boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
            boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
            boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
            if ((x >= this.x || (leftEdge && x <= this.x + this.width))
                    && (x < this.x + this.width || (rightEdge && x >= this.x))
                    && (y >= this.y || (topEdge && y <= this.y + this.height))
                    && (y < this.y + this.height || (bottomEdge && y >= this.y))) {
                return true;
            } else {
                return false;
            }
        }

        /**
         * 返回按键中心与给定点之间距离的平方。
         */
        public int squaredDistanceFrom(int x, int y) {
            int xDist = this.x + width / 2 - x;
            int yDist = this.y + height / 2 - y;
            return xDist * xDist + yDist * yDist;
        }

        /**
         * 根据按键的当前状态和类型返回按键的可绘制状态。
         */
        public int[] getCurrentDrawableState() {
            int[] states = KEY_STATE_NORMAL;
            if (on) {
                if (pressed) {
                    states = KEY_STATE_PRESSED_ON;
                } else {
                    states = KEY_STATE_NORMAL_ON;
                }
            } else {
                if (sticky) {
                    if (pressed) {
                        states = KEY_STATE_PRESSED_OFF;
                    } else {
                        states = KEY_STATE_NORMAL_OFF;
                    }
                } else {
                    if (pressed) {
                        states = KEY_STATE_PRESSED;
                    }
                }
            }
            return states;
        }
    }

    /**
     * 从给定的 XML 按键布局文件创建键盘。
     */
    public Keyboard(Context context, int xmlLayoutResId) {
        this(context, xmlLayoutResId, 0);
    }

    /**
     * 从给定的 XML 按键布局文件创建键盘。过滤掉定义了键盘模式但与指定模式不匹配的行。
     */
    public Keyboard(Context context, @XmlRes int xmlLayoutResId, int modeId, int width,
                    int height) {
        mDisplayWidth = width;
        mDisplayHeight = height;
        mDefaultHorizontalGap = 0;
        mDefaultWidth = mDisplayWidth / 10;
        mDefaultVerticalGap = 0;
        mDefaultHeight = mDefaultWidth;
        mKeys = new ArrayList<>();
        mModifierKeys = new ArrayList<>();
        mKeyboardMode = modeId;
        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
    }

    /**
     * 从给定的 XML 按键布局文件创建键盘。过滤掉定义了键盘模式但与指定模式不匹配的行。
     */
    public Keyboard(Context context, @XmlRes int xmlLayoutResId, int modeId) {
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        mDisplayWidth = dm.widthPixels;
        mDisplayHeight = dm.heightPixels;
        mDefaultHorizontalGap = 0;
        mDefaultWidth = mDisplayWidth / 10;
        mDefaultVerticalGap = 0;
        mDefaultHeight = mDefaultWidth;
        mKeys = new ArrayList<>();
        mModifierKeys = new ArrayList<>();
        mKeyboardMode = modeId;
        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
    }

    public Keyboard(Context context, int layoutTemplateResId,
                    CharSequence characters, int columns, int horizontalPadding) {
        this(context, layoutTemplateResId);
        int x = 0;
        int y = 0;
        int column = 0;
        mTotalWidth = 0;
        Keyboard.Row row = new Keyboard.Row(this);
        row.defaultHeight = mDefaultHeight;
        row.defaultWidth = mDefaultWidth;
        row.defaultHorizontalGap = mDefaultHorizontalGap;
        row.verticalGap = mDefaultVerticalGap;
        row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
        for (int i = 0; i < characters.length(); i++) {
            char c = characters.charAt(i);
            if (column >= maxColumns
                    || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
                x = 0;
                y += mDefaultVerticalGap + mDefaultHeight;
                column = 0;
            }
            final Keyboard.Key key = new Keyboard.Key(row);
            key.x = x;
            key.y = y;
            key.label = String.valueOf(c);
            key.codes = new int[] { c };
            column++;
            x += key.width + key.gap;
            mKeys.add(key);
            row.mKeys.add(key);
            if (x > mTotalWidth) {
                mTotalWidth = x;
            }
        }
        mTotalHeight = y + mDefaultHeight;
        rows.add(row);
    }

    final void resize(int newWidth, int newHeight) {
        int numRows = rows.size();
        for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
            Keyboard.Row row = rows.get(rowIndex);
            int numKeys = row.mKeys.size();
            int totalGap = 0;
            int totalWidth = 0;
            for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
                Keyboard.Key key = row.mKeys.get(keyIndex);
                if (keyIndex > 0) {
                    totalGap += key.gap;
                }
                totalWidth += key.width;
            }
            if (totalGap + totalWidth > newWidth) {
                int x = 0;
                float scaleFactor = (float)(newWidth - totalGap) / totalWidth;
                for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
                    Keyboard.Key key = row.mKeys.get(keyIndex);
                    key.width *= scaleFactor;
                    key.x = x;
                    x += key.width + key.gap;
                }
            }
        }
        mTotalWidth = newWidth;
    }

    public List<Keyboard.Key> getKeys() {
        return mKeys;
    }

    public List<Keyboard.Key> getModifierKeys() {
        return mModifierKeys;
    }

    protected int getHorizontalGap() {
        return mDefaultHorizontalGap;
    }

    protected void setHorizontalGap(int gap) {
        mDefaultHorizontalGap = gap;
    }

    protected int getVerticalGap() {
        return mDefaultVerticalGap;
    }

    protected void setVerticalGap(int gap) {
        mDefaultVerticalGap = gap;
    }

    protected int getKeyHeight() {
        return mDefaultHeight;
    }

    protected void setKeyHeight(int height) {
        mDefaultHeight = height;
    }

    protected int getKeyWidth() {
        return mDefaultWidth;
    }

    protected void setKeyWidth(int width) {
        mDefaultWidth = width;
    }

    /**
     * 返回键盘的总高度
     */
    public int getHeight() {
        return mTotalHeight;
    }

    public int getMinWidth() {
        return mTotalWidth;
    }

    public boolean setShifted(boolean shiftState) {
        for (Keyboard.Key shiftKey : mShiftKeys) {
            if (shiftKey != null) {
                shiftKey.on = shiftState;
            }
        }
        if (mShifted != shiftState) {
            mShifted = shiftState;
            return true;
        }
        return false;
    }

    public boolean isShifted() {
        return mShifted;
    }

    public int[] getShiftKeyIndices() {
        return mShiftKeyIndices;
    }

    public int getShiftKeyIndex() {
        return mShiftKeyIndices[0];
    }

    private void computeNearestNeighbors() {
        mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
        mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
        mGridNeighbors = new int[GRID_SIZE][];
        int[] indices = new int[mKeys.size()];
        final int gridWidth = GRID_WIDTH * mCellWidth;
        final int gridHeight = GRID_HEIGHT * mCellHeight;
        for (int x = 0; x < gridWidth; x += mCellWidth) {
            for (int y = 0; y < gridHeight; y += mCellHeight) {
                int count = 0;
                for (int i = 0; i < mKeys.size(); i++) {
                    final Keyboard.Key key = mKeys.get(i);
                    if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
                            key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
                            key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
                                    < mProximityThreshold ||
                            key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {
                        indices[count++] = i;
                    }
                }
                int [] cell = new int[count];
                System.arraycopy(indices, 0, cell, 0, count);
                mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
            }
        }
    }

    /**
     * 返回最接近给定点的按键索引。
     */
    public int[] getNearestKeys(int x, int y) {
        if (mGridNeighbors == null) computeNearestNeighbors();
        if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
            int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
            if (index < GRID_SIZE) {
                return mGridNeighbors[index];
            }
        }
        return new int[0];
    }

    protected Keyboard.Row createRowFromXml(Resources res, XmlResourceParser parser) {
        return new Keyboard.Row(res, this, parser);
    }

    protected Keyboard.Key createKeyFromXml(Resources res, Keyboard.Row parent, int x, int y,
                                            XmlResourceParser parser) {
        return new Keyboard.Key(res, parent, x, y, parser);
    }

    private void loadKeyboard(Context context, XmlResourceParser parser) {
        boolean inKey = false;
        boolean inRow = false;
        boolean leftMostKey = false;
        int row = 0;
        int x = 0;
        int y = 0;
        Keyboard.Key key = null;
        Keyboard.Row currentRow = null;
        Resources res = context.getResources();
        boolean skipRow = false;
        try {
            int event;
            while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
                if (event == XmlResourceParser.START_TAG) {
                    String tag = parser.getName();
                    if (TAG_ROW.equals(tag)) {
                        inRow = true;
                        x = 0;
                        currentRow = createRowFromXml(res, parser);
                        rows.add(currentRow);
                        skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
                        if (skipRow) {
                            skipToEndOfRow(parser);
                            inRow = false;
                        }
                    } else if (TAG_KEY.equals(tag)) {
                        inKey = true;
                        key = createKeyFromXml(res, currentRow, x, y, parser);
                        mKeys.add(key);
                        if (key.codes[0] == KEYCODE_SHIFT) {
                            for (int i = 0; i < mShiftKeys.length; i++) {
                                if (mShiftKeys[i] == null) {
                                    mShiftKeys[i] = key;
                                    mShiftKeyIndices[i] = mKeys.size()-1;
                                    break;
                                }
                            }
                            mModifierKeys.add(key);
                        } else if (key.codes[0] == KEYCODE_ALT) {
                            mModifierKeys.add(key);
                        }
                        currentRow.mKeys.add(key);
                    } else if (TAG_KEYBOARD.equals(tag)) {
                        parseKeyboardAttributes(res, parser);
                    }
                } else if (event == XmlResourceParser.END_TAG) {
                    if (inKey) {
                        inKey = false;
                        x += key.gap + key.width;
                        if (x > mTotalWidth) {
                            mTotalWidth = x;
                        }
                    } else if (inRow) {
                        inRow = false;
                        y += currentRow.verticalGap;
                        y += currentRow.defaultHeight;
                        row++;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        mTotalHeight = y - mDefaultVerticalGap;
    }

    private void skipToEndOfRow(XmlResourceParser parser)
            throws XmlPullParserException, IOException {
        int event;
        while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
            if (event == XmlResourceParser.END_TAG
                    && parser.getName().equals(TAG_ROW)) {
                break;
            }
        }
    }

    private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                R.styleable.My_Keyboard_view);
        mDefaultWidth = getDimensionOrFraction(a,
                R.styleable.My_Keyboard_view_android_keyWidth,
                mDisplayWidth, mDisplayWidth / 10);
        mDefaultHeight = getDimensionOrFraction(a,
                R.styleable.My_Keyboard_view_android_keyHeight,
                mDisplayHeight, 50);
        mDefaultHorizontalGap = getDimensionOrFraction(a,
                R.styleable.My_Keyboard_view_android_horizontalGap,
                mDisplayWidth, 0);
        mDefaultVerticalGap = getDimensionOrFraction(a,
                R.styleable.My_Keyboard_view_android_verticalGap,
                mDisplayHeight, 0);
        mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
        mProximityThreshold = mProximityThreshold * mProximityThreshold;
        a.recycle();
    }

    static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
        TypedValue value = a.peekValue(index);
        if (value == null) return defValue;
        if (value.type == TypedValue.TYPE_DIMENSION) {
            return a.getDimensionPixelOffset(index, defValue);
        } else if (value.type == TypedValue.TYPE_FRACTION) {
            return Math.round(a.getFraction(index, base, base, defValue));
        }
        return defValue;
    }
}

