1ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka/*
2ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project
3ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka *
4ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
5ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * you may not use this file except in compliance with the License.
6ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * You may obtain a copy of the License at
7ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka *
8ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka *
10ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
11ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
12ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * See the License for the specific language governing permissions and
14ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka * limitations under the License.
15ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka */
16ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
17ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaokapackage com.android.inputmethod.keyboard;
18ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
19ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaokaimport android.content.Context;
20727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaokaimport android.content.res.TypedArray;
21727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaokaimport android.graphics.Canvas;
22727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaokaimport android.graphics.Paint;
23727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaokaimport android.graphics.drawable.Drawable;
24ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaokaimport android.util.AttributeSet;
2535580bad6f3da3b204653825bbb6871563e70728Tom Ouyangimport android.view.MotionEvent;
26ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaokaimport android.view.View;
277f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaokaimport android.view.ViewGroup;
28ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
2962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
3062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaokaimport com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate;
31727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
32e07b51e7c65220ac183546103010dd617d164f60Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
339342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport com.android.inputmethod.latin.common.Constants;
345b91b551e5ffaf2c2e691dfbd434f21c82293986Jean Chalardimport com.android.inputmethod.latin.common.CoordinateUtils;
35e07b51e7c65220ac183546103010dd617d164f60Tadashi G. Takaoka
36ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka/**
372affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and
382affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * detecting key presses and touch movements.
39ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka */
4035580bad6f3da3b204653825bbb6871563e70728Tom Ouyangpublic class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
41359c35e0f6e534d096efc1d9f2ff585139e3b3acTadashi G. Takaoka    private final int[] mCoordinates = CoordinateUtils.newInstance();
42ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
43727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka    private final Drawable mDivider;
441dbeb251b2570dd3025dcd7134090de0fc860d04Tadashi G. Takaoka    protected final KeyDetector mKeyDetector;
4509e4530ca75d4664eb72708face83b466839c522Tadashi G. Takaoka    private Controller mController = EMPTY_CONTROLLER;
4635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    protected KeyboardActionListener mListener;
47ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka    private int mOriginX;
48ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka    private int mOriginY;
4935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private Key mCurrentKey;
5063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
5135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private int mActivePointerId;
52ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
53aea429200e997762cd92744a2b1f3f586fc00b35Tadashi G. Takaoka    protected MoreKeysKeyboardAccessibilityDelegate mAccessibilityDelegate;
5462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka
55bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) {
562affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
57ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka    }
58ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
59bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public MoreKeysKeyboardView(final Context context, final AttributeSet attrs,
60bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final int defStyle) {
61ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka        super(context, attrs, defStyle);
62727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        final TypedArray moreKeysKeyboardViewAttr = context.obtainStyledAttributes(attrs,
63727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka                R.styleable.MoreKeysKeyboardView, defStyle, R.style.MoreKeysKeyboardView);
64727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        mDivider = moreKeysKeyboardViewAttr.getDrawable(R.styleable.MoreKeysKeyboardView_divider);
65727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        if (mDivider != null) {
66727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka            // TODO: Drawable itself should have an alpha value.
67727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka            mDivider.setAlpha(128);
68727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        }
69727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        moreKeysKeyboardViewAttr.recycle();
7062316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        mKeyDetector = new MoreKeysDetector(getResources().getDimension(
7162316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka                R.dimen.config_more_keys_keyboard_slide_allowance));
72ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka    }
73ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
74ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka    @Override
75bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
7638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
7738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        if (keyboard != null) {
788da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
798da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
8038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            setMeasuredDimension(width, height);
8138f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        } else {
8238f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
8338f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        }
8438f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    }
8538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
8638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    @Override
87727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
88727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka            final KeyDrawParams params) {
89727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        if (!key.isSpacer() || !(key instanceof MoreKeysKeyboard.MoreKeyDivider)
90727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka                || mDivider == null) {
91727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
92727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka            return;
93727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        }
94727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        final int keyWidth = key.getDrawWidth();
95727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        final int keyHeight = key.getHeight();
96727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        final int iconWidth = Math.min(mDivider.getIntrinsicWidth(), keyWidth);
97727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        final int iconHeight = mDivider.getIntrinsicHeight();
98727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center
99727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        final int iconY = (keyHeight - iconHeight) / 2; // Align vertically center
100727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka        drawIcon(canvas, mDivider, iconX, iconY, iconWidth, iconHeight);
101727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka    }
102727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka
103727e818e95ef68460ebafb2eb3b11c23a2d8fcd8Tadashi G. Takaoka    @Override
104bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
10563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        super.setKeyboard(keyboard);
1061dbeb251b2570dd3025dcd7134090de0fc860d04Tadashi G. Takaoka        mKeyDetector.setKeyboard(
1071dbeb251b2570dd3025dcd7134090de0fc860d04Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
10804cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
109dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka            if (mAccessibilityDelegate == null) {
110dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka                mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate(
111dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka                        this, mKeyDetector);
112dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka                mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_keys_keyboard);
113dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka                mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard);
114dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka            }
11562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka            mAccessibilityDelegate.setKeyboard(keyboard);
11662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        } else {
11762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka            mAccessibilityDelegate = null;
11862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        }
11963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka    }
12063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
12163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka    @Override
122bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void showMoreKeysPanel(final View parentView, final Controller controller,
123fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang            final int pointX, final int pointY, final KeyboardActionListener listener) {
124f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka        mController = controller;
125f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka        mListener = listener;
126fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        final View container = getContainerView();
127bcbaffd9dd950c6b82c682a742c93f9319d908f5Tadashi G. Takaoka        // The coordinates of panel's left-top corner in parentView's coordinate system.
1282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // We need to consider background drawable paddings.
1292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int x = pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft();
1302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom()
1312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                + getPaddingBottom();
132ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
133bcbaffd9dd950c6b82c682a742c93f9319d908f5Tadashi G. Takaoka        parentView.getLocationInWindow(mCoordinates);
1342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // Ensure the horizontal position of the panel does not extend past the parentView edges.
135fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
136be68b41ccb1a16201c3b91f1576a731f7715aaf6Tom Ouyang        final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates);
137fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        final int panelY = y + CoordinateUtils.y(mCoordinates);
138fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        container.setX(panelX);
139fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        container.setY(panelY);
140ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka
141bcbaffd9dd950c6b82c682a742c93f9319d908f5Tadashi G. Takaoka        mOriginX = x + container.getPaddingLeft();
142bcbaffd9dd950c6b82c682a742c93f9319d908f5Tadashi G. Takaoka        mOriginY = y + container.getPaddingTop();
143fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        controller.onShowMoreKeysPanel(this);
14462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
14504cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka        if (accessibilityDelegate != null
14604cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka                && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
14762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka            accessibilityDelegate.onShowMoreKeysKeyboard();
14862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        }
149f3f3189adbce15d609d5e06632f8e0630db890d1Tadashi G. Takaoka    }
150f3f3189adbce15d609d5e06632f8e0630db890d1Tadashi G. Takaoka
15135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    /**
15235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang     * Returns the default x coordinate for showing this panel.
15335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang     */
15435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    protected int getDefaultCoordX() {
15535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        return ((MoreKeysKeyboard)getKeyboard()).getDefaultCoordX();
15635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
15735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
15835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    @Override
15935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) {
16035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        mActivePointerId = pointerId;
161adba09b54ed1b30bf9b24d632165229a0752b144Tadashi G. Takaoka        mCurrentKey = detectKey(x, y);
16235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
16335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
16435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    @Override
165fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka    public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime) {
166ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (mActivePointerId != pointerId) {
167ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            return;
168ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
169ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        final boolean hasOldKey = (mCurrentKey != null);
170adba09b54ed1b30bf9b24d632165229a0752b144Tadashi G. Takaoka        mCurrentKey = detectKey(x, y);
171ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (hasOldKey && mCurrentKey == null) {
172fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            // A more keys keyboard is canceled when detecting no key.
1731f215a58c99f0eb2f536e9cccd51371f2883e201Tadashi G. Takaoka            mController.onCancelMoreKeysPanel();
174ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
17535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
17635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
17735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    @Override
17835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) {
179fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        if (mActivePointerId != pointerId) {
180fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            return;
181fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        }
182fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        // Calling {@link #detectKey(int,int,int)} here is harmless because the last move event and
183fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        // the following up event share the same coordinates.
184adba09b54ed1b30bf9b24d632165229a0752b144Tadashi G. Takaoka        mCurrentKey = detectKey(x, y);
185fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        if (mCurrentKey != null) {
18635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            updateReleaseKeyGraphics(mCurrentKey);
1875fc1510bc1cbe9bcd1105a403e07b5400470ae96Tadashi G. Takaoka            onKeyInput(mCurrentKey, x, y);
18835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mCurrentKey = null;
18935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
19035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
19135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
19235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    /**
19335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang     * Performs the specific action for this panel when the user presses a key on the panel.
19435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang     */
1955fc1510bc1cbe9bcd1105a403e07b5400470ae96Tadashi G. Takaoka    protected void onKeyInput(final Key key, final int x, final int y) {
1965fc1510bc1cbe9bcd1105a403e07b5400470ae96Tadashi G. Takaoka        final int code = key.getCode();
19735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (code == Constants.CODE_OUTPUT_TEXT) {
19835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            mListener.onTextInput(mCurrentKey.getOutputText());
19935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        } else if (code != Constants.CODE_UNSPECIFIED) {
200a91dfff5e54f68c4003327eeca47286084c35a2fJean Chalard            if (getKeyboard().hasProximityCharsCorrection(code)) {
2012282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                mListener.onCodeInput(code, x, y, false /* isKeyRepeat */);
202a91dfff5e54f68c4003327eeca47286084c35a2fJean Chalard            } else {
2032282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                mListener.onCodeInput(code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
2042282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                        false /* isKeyRepeat */);
205a91dfff5e54f68c4003327eeca47286084c35a2fJean Chalard            }
20635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
20735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
20835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
209adba09b54ed1b30bf9b24d632165229a0752b144Tadashi G. Takaoka    private Key detectKey(int x, int y) {
21035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final Key oldKey = mCurrentKey;
21135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final Key newKey = mKeyDetector.detectHitKey(x, y);
212fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        if (newKey == oldKey) {
213fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            return newKey;
214fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        }
215fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        // A new key is detected.
216fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        if (oldKey != null) {
217fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            updateReleaseKeyGraphics(oldKey);
218fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            invalidateKey(oldKey);
219fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        }
220fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        if (newKey != null) {
221fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            updatePressKeyGraphics(newKey);
222fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            invalidateKey(newKey);
22335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
224fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        return newKey;
22535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
22635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
22735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void updateReleaseKeyGraphics(final Key key) {
22835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        key.onReleased();
22935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        invalidateKey(key);
23035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
23135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
23235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void updatePressKeyGraphics(final Key key) {
23335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        key.onPressed();
23435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        invalidateKey(key);
23535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
23635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
237ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka    @Override
23809e4530ca75d4664eb72708face83b466839c522Tadashi G. Takaoka    public void dismissMoreKeysPanel() {
23909e4530ca75d4664eb72708face83b466839c522Tadashi G. Takaoka        if (!isShowingInParent()) {
24009e4530ca75d4664eb72708face83b466839c522Tadashi G. Takaoka            return;
24109e4530ca75d4664eb72708face83b466839c522Tadashi G. Takaoka        }
242a2e365da0acf80c1bcd8413d72f697a0da374779Tadashi G. Takaoka        final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
24304cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka        if (accessibilityDelegate != null
24404cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka                && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
245a2e365da0acf80c1bcd8413d72f697a0da374779Tadashi G. Takaoka            accessibilityDelegate.onDismissMoreKeysKeyboard();
246a2e365da0acf80c1bcd8413d72f697a0da374779Tadashi G. Takaoka        }
2471f215a58c99f0eb2f536e9cccd51371f2883e201Tadashi G. Takaoka        mController.onDismissMoreKeysPanel();
2489ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    }
2499ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
2509ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    @Override
251bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public int translateX(final int x) {
25263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return x - mOriginX;
25363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka    }
25463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
25563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka    @Override
256bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public int translateY(final int y) {
25763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return y - mOriginY;
258ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka    }
259fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang
260fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    @Override
26135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    public boolean onTouchEvent(final MotionEvent me) {
26235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int action = me.getActionMasked();
26335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final long eventTime = me.getEventTime();
26435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int index = me.getActionIndex();
26535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int x = (int)me.getX(index);
26635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int y = (int)me.getY(index);
26735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int pointerId = me.getPointerId(index);
26835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        switch (action) {
26935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        case MotionEvent.ACTION_DOWN:
27035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        case MotionEvent.ACTION_POINTER_DOWN:
27135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            onDownEvent(x, y, pointerId, eventTime);
27235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            break;
27335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        case MotionEvent.ACTION_UP:
27435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        case MotionEvent.ACTION_POINTER_UP:
27535580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            onUpEvent(x, y, pointerId, eventTime);
27635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            break;
27735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        case MotionEvent.ACTION_MOVE:
27835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            onMoveEvent(x, y, pointerId, eventTime);
27935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            break;
28035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        }
28133482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka        return true;
28235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
28335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
28462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka    /**
28562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka     * {@inheritDoc}
28662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka     */
28762316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka    @Override
28862316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka    public boolean onHoverEvent(final MotionEvent event) {
28962316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
29004cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka        if (accessibilityDelegate != null
29104cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka                && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
29262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka            return accessibilityDelegate.onHoverEvent(event);
29362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        }
29462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        return super.onHoverEvent(event);
29562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka    }
29662316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka
2977f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka    private View getContainerView() {
298fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        return (View)getParent();
299fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    }
300fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang
301fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    @Override
3027f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka    public void showInParent(final ViewGroup parentView) {
3037f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka        removeFromParent();
3047f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka        parentView.addView(getContainerView());
3057f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka    }
3067f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka
3077f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka    @Override
3087f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka    public void removeFromParent() {
3097f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka        final View containerView = getContainerView();
3107f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka        final ViewGroup currentParent = (ViewGroup)containerView.getParent();
3117f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka        if (currentParent != null) {
3127f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka            currentParent.removeView(containerView);
3137f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka        }
3147f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka    }
3157f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka
3167f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka    @Override
317fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    public boolean isShowingInParent() {
318fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        return (getContainerView().getParent() != null);
319fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    }
320ff082d081f3ea18ff0b9b22126ee4a86504cf83cTadashi G. Takaoka}
321