MoreKeysKeyboard.java revision be34d973349909196dc3427a5653f4e119092ea7
104c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project
304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka *
404c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not
504c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * use this file except in compliance with the License. You may obtain a copy of
604c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * the License at
704c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka *
804c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0
904c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka *
1004c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
1104c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1204c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * License for the specific language governing permissions and limitations under
1404c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * the License.
1504c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka */
1604c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka
1704c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
1804c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka
1932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaokaimport android.graphics.Paint;
2032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
2132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardBuilder;
2232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardParams;
2332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PopupCharactersParser;
2432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
2504c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka
2604c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaokapublic class MiniKeyboard extends Keyboard {
278da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    private final int mDefaultKeyCoordX;
2804c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka
2932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka    private MiniKeyboard(Builder.MiniKeyboardParams params) {
308da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        super(params);
318da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
3204c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka    }
3304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka
3404c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka    public int getDefaultCoordX() {
3504c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka        return mDefaultKeyCoordX;
3604c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka    }
3732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
3832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka    public static class Builder extends KeyboardBuilder<Builder.MiniKeyboardParams> {
3932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        private final CharSequence[] mPopupCharacters;
4032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
4132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        public static class MiniKeyboardParams extends KeyboardParams {
4232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            /* package */int mTopRowAdjustment;
4332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int mNumRows;
4432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int mNumColumns;
4532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int mLeftKeys;
4632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int mRightKeys; // includes default key.
4732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
4832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public MiniKeyboardParams() {
4932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                super();
5032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
5132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
5232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            /* package for test */MiniKeyboardParams(int numKeys, int maxColumns, int keyWidth,
5332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    int rowHeight, int coordXInParent, int parentKeyboardWidth) {
5432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                super();
5532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                setParameters(numKeys, maxColumns, keyWidth, rowHeight, coordXInParent,
5632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        parentKeyboardWidth);
5732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
5832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
5932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            /**
6032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             * Set keyboard parameters of mini keyboard.
6132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             *
6232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             * @param numKeys number of keys in this mini keyboard.
6332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             * @param maxColumns number of maximum columns of this mini keyboard.
6432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             * @param keyWidth mini keyboard key width in pixel, including horizontal gap.
6532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             * @param rowHeight mini keyboard row height in pixel, including vertical gap.
6632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             * @param coordXInParent coordinate x of the popup key in parent keyboard.
6732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             * @param parentKeyboardWidth parent keyboard width in pixel.
6832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka             */
6932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
7032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    int coordXInParent, int parentKeyboardWidth) {
7132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (parentKeyboardWidth / keyWidth < maxColumns) {
7232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    throw new IllegalArgumentException(
7332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                            "Keyboard is too small to hold mini keyboard: " + parentKeyboardWidth
7432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                                    + " " + keyWidth + " " + maxColumns);
7532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
7632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mDefaultKeyWidth = keyWidth;
7732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mDefaultRowHeight = rowHeight;
7832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
7932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int numRows = (numKeys + maxColumns - 1) / maxColumns;
8032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mNumRows = numRows;
8132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int numColumns = getOptimizedColumns(numKeys, maxColumns);
8232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mNumColumns = numColumns;
8332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
8432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int numLeftKeys = (numColumns - 1) / 2;
8532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int numRightKeys = numColumns - numLeftKeys; // including default key.
8632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int maxLeftKeys = coordXInParent / keyWidth;
8732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent)
8832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        / keyWidth);
8932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int leftKeys, rightKeys;
9032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (numLeftKeys > maxLeftKeys) {
9132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    leftKeys = maxLeftKeys;
9232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    rightKeys = numColumns - maxLeftKeys;
9332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                } else if (numRightKeys > maxRightKeys) {
9432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    leftKeys = numColumns - maxRightKeys;
9532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    rightKeys = maxRightKeys;
9632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                } else {
9732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    leftKeys = numLeftKeys;
9832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    rightKeys = numRightKeys;
9932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
10032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                // Shift right if the left edge of mini keyboard is on the edge of parent keyboard
10132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                // unless the parent key is on the left edge.
10232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (leftKeys * keyWidth >= coordXInParent && leftKeys > 0) {
10332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    leftKeys--;
10432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    rightKeys++;
10532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
10632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                // Shift left if the right edge of mini keyboard is on the edge of parent keyboard
10732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                // unless the parent key is on the right edge.
10832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (rightKeys * keyWidth + coordXInParent >= parentKeyboardWidth && rightKeys > 1) {
10932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    leftKeys++;
11032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    rightKeys--;
11132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
11232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mLeftKeys = leftKeys;
11332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mRightKeys = rightKeys;
11432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
11532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                // Centering of the top row.
11632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final boolean onEdge = (leftKeys == 0 || rightKeys == 1);
11732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (numRows < 2 || onEdge || getTopRowEmptySlots(numKeys, numColumns) % 2 == 0) {
11832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    mTopRowAdjustment = 0;
11932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                } else if (mLeftKeys < mRightKeys - 1) {
12032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    mTopRowAdjustment = 1;
12132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                } else {
12232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    mTopRowAdjustment = -1;
12332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
12432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
12532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
12632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
12732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
12832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
12932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // Return key position according to column count (0 is default).
13032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            /* package */int getColumnPos(int n) {
13132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int col = n % mNumColumns;
13232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (col == 0) {
13332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    // default position.
13432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    return 0;
13532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
13632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int pos = 0;
13732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int right = 1; // include default position key.
13832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int left = 0;
13932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int i = 0;
14032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                while (true) {
14132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    // Assign right key if available.
14232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    if (right < mRightKeys) {
14332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        pos = right;
14432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        right++;
14532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        i++;
14632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    }
14732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    if (i >= col)
14832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        break;
14932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    // Assign left key if available.
15032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    if (left < mLeftKeys) {
15132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        left++;
15232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        pos = -left;
15332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        i++;
15432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    }
15532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    if (i >= col)
15632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        break;
15732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
15832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                return pos;
15932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
16032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
16132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            private static int getTopRowEmptySlots(int numKeys, int numColumns) {
16232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int remainingKeys = numKeys % numColumns;
16332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (remainingKeys == 0) {
16432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    return 0;
16532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                } else {
16632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    return numColumns - remainingKeys;
16732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
16832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
16932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
17032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            private int getOptimizedColumns(int numKeys, int maxColumns) {
17132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int numColumns = Math.min(numKeys, maxColumns);
17232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
17332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    numColumns--;
17432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
17532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                return numColumns;
17632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
17732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
17832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int getDefaultKeyCoordX() {
17932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                return mLeftKeys * mDefaultKeyWidth;
18032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
18132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
18232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int getX(int n, int row) {
18332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX();
18432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (isTopRow(row)) {
18532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    return x + mTopRowAdjustment * (mDefaultKeyWidth / 2);
18632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
18732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                return x;
18832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
18932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
19032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int getY(int row) {
19132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
19232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
19332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
19432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            public int getRowFlags(int row) {
19532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int rowFlags = 0;
19632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (row == 0)
19732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    rowFlags |= Keyboard.EDGE_TOP;
19832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (isTopRow(row))
19932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    rowFlags |= Keyboard.EDGE_BOTTOM;
20032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                return rowFlags;
20132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
20232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
20332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            private boolean isTopRow(int rowCount) {
20432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                return rowCount == mNumRows - 1;
20532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
20632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        }
20732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
20832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        public Builder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) {
20932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            super(view.getContext(), new MiniKeyboardParams());
21032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
21132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
21232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // HACK: Current mini keyboard design totally relies on the 9-patch
21332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // padding about horizontal
21432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // and vertical key spacing. To keep the visual of mini keyboard as
21532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // is, these hacks are
21632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // needed to keep having the same horizontal and vertical key
21732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // spacing.
21832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            mParams.mHorizontalGap = 0;
21932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
22032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // TODO: When we have correctly padded key background 9-patch
22132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // drawables for mini keyboard,
22232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // revert the above hacks and uncomment the following lines.
22332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // mParams.mHorizontalGap = parentKeyboard.mHorizontalGap;
22432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            // mParams.mVerticalGap = parentKeyboard.mVerticalGap;
22532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
22632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard;
22732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            mPopupCharacters = parentKey.mPopupCharacters;
22832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
22932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth);
23032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            mParams.setParameters(mPopupCharacters.length, parentKey.mMaxPopupColumn, keyWidth,
23132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    parentKeyboard.mDefaultRowHeight, parentKey.mX
23232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                            + (mParams.mDefaultKeyWidth - keyWidth) / 2, view.getMeasuredWidth());
23332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        }
23432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
23532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
23632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                int minKeyWidth) {
237be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka            final int padding = (int) view.getContext().getResources()
238be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka                    .getDimension(R.dimen.mini_keyboard_key_horizontal_padding);
23932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            Paint paint = null;
2402315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka            int maxWidth = minKeyWidth;
24132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            for (CharSequence popupSpec : popupCharacters) {
24232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final CharSequence label = PopupCharactersParser.getLabel(popupSpec.toString());
24332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                // If the label is single letter, minKeyWidth is enough to hold
24432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                // the label.
24532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                if (label != null && label.length() > 1) {
24632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    if (paint == null) {
24732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        paint = new Paint();
24832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        paint.setAntiAlias(true);
24932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                    }
250be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka                    final int width = (int)view.getDefaultLabelWidth(label, paint) + padding;
2512315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka                    if (maxWidth < width) {
2522315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka                        maxWidth = width;
2532315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka                    }
25432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                }
25532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
256be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka            return maxWidth;
25732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        }
25832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka
25932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        @Override
26032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        public MiniKeyboard build() {
26132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            final MiniKeyboardParams params = mParams;
26232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            for (int n = 0; n < mPopupCharacters.length; n++) {
26332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final CharSequence label = mPopupCharacters[n];
26432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                final int row = n / params.mNumColumns;
265be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka                final Key key = new Key(mResources, params, label, null, params.getX(n, row),
26632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        params.getY(row), params.mDefaultKeyWidth, params.mDefaultRowHeight,
26732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                        params.getRowFlags(row));
26832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka                params.onAddKey(key);
26932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            }
27032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka            return new MiniKeyboard(params);
27132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka        }
27232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka    }
27304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka}
274