MoreKeysKeyboardView.java revision 18e21d58f7c0b5c5c001835c093221c573a841c4
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.inputmethod.keyboard;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.util.AttributeSet;
22import android.view.MotionEvent;
23import android.view.View;
24
25import com.android.inputmethod.latin.Constants;
26import com.android.inputmethod.latin.CoordinateUtils;
27import com.android.inputmethod.latin.R;
28
29/**
30 * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and
31 * detecting key presses and touch movements.
32 */
33public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
34    private final int[] mCoordinates = CoordinateUtils.newInstance();
35
36    private final KeyDetector mKeyDetector;
37    private Controller mController;
38    protected KeyboardActionListener mListener;
39    private int mOriginX;
40    private int mOriginY;
41    private Key mCurrentKey;
42
43    private int mActivePointerId;
44
45    public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) {
46        this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
47    }
48
49    public MoreKeysKeyboardView(final Context context, final AttributeSet attrs,
50            final int defStyle) {
51        super(context, attrs, defStyle);
52
53        final Resources res = context.getResources();
54        mKeyDetector = new MoreKeysDetector(
55                res.getDimension(R.dimen.more_keys_keyboard_slide_allowance));
56    }
57
58    @Override
59    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
60        final Keyboard keyboard = getKeyboard();
61        if (keyboard != null) {
62            final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
63            final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
64            setMeasuredDimension(width, height);
65        } else {
66            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
67        }
68    }
69
70    @Override
71    public void setKeyboard(final Keyboard keyboard) {
72        super.setKeyboard(keyboard);
73        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
74                -getPaddingTop() + mVerticalCorrection);
75    }
76
77    @Override
78    public void showMoreKeysPanel(final View parentView, final Controller controller,
79            final int pointX, final int pointY, final KeyboardActionListener listener) {
80        mController = controller;
81        mListener = listener;
82        final View container = getContainerView();
83        // The coordinates of panel's left-top corner in parentView's coordinate system.
84        final int x = pointX - getDefaultCoordX() - container.getPaddingLeft();
85        final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom();
86
87        parentView.getLocationInWindow(mCoordinates);
88        // Ensure the horizontal position of the panel does not extend past the screen edges.
89        final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
90        final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates);
91        final int panelY = y + CoordinateUtils.y(mCoordinates);
92        container.setX(panelX);
93        container.setY(panelY);
94
95        mOriginX = x + container.getPaddingLeft();
96        mOriginY = y + container.getPaddingTop();
97        controller.onShowMoreKeysPanel(this);
98    }
99
100    /**
101     * Returns the default x coordinate for showing this panel.
102     */
103    protected int getDefaultCoordX() {
104        return ((MoreKeysKeyboard)getKeyboard()).getDefaultCoordX();
105    }
106
107    @Override
108    public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) {
109        mActivePointerId = pointerId;
110        onMoveKeyInternal(x, y, pointerId);
111    }
112
113    @Override
114    public void onMoveEvent(int x, int y, final int pointerId, long eventTime) {
115        if (mActivePointerId != pointerId) {
116            return;
117        }
118        final boolean hasOldKey = (mCurrentKey != null);
119        onMoveKeyInternal(x, y, pointerId);
120        if (hasOldKey && mCurrentKey == null) {
121            // If the pointer has moved too far away from any target then cancel the panel.
122            mController.onCancelMoreKeysPanel();
123        }
124    }
125
126    @Override
127    public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) {
128        if (mCurrentKey != null && mActivePointerId == pointerId) {
129            updateReleaseKeyGraphics(mCurrentKey);
130            onCodeInput(mCurrentKey.mCode, x, y);
131            mCurrentKey = null;
132        }
133    }
134
135    /**
136     * Performs the specific action for this panel when the user presses a key on the panel.
137     */
138    protected void onCodeInput(final int code, final int x, final int y) {
139        if (code == Constants.CODE_OUTPUT_TEXT) {
140            mListener.onTextInput(mCurrentKey.getOutputText());
141        } else if (code != Constants.CODE_UNSPECIFIED) {
142            mListener.onCodeInput(code, x, y);
143        }
144    }
145
146    private void onMoveKeyInternal(int x, int y, int pointerId) {
147        if (mActivePointerId != pointerId) {
148            // Ignore old pointers when newer pointer is active.
149            return;
150        }
151        final Key oldKey = mCurrentKey;
152        final Key newKey = mKeyDetector.detectHitKey(x, y);
153        if (newKey != oldKey) {
154            mCurrentKey = newKey;
155            invalidateKey(mCurrentKey);
156            if (oldKey != null) {
157                updateReleaseKeyGraphics(oldKey);
158            }
159            if (newKey != null) {
160                updatePressKeyGraphics(newKey);
161            }
162        }
163    }
164
165    private void updateReleaseKeyGraphics(final Key key) {
166        key.onReleased();
167        invalidateKey(key);
168    }
169
170    private void updatePressKeyGraphics(final Key key) {
171        key.onPressed();
172        invalidateKey(key);
173    }
174
175    @Override
176    public boolean dismissMoreKeysPanel() {
177        if (mController == null) return false;
178        return mController.onDismissMoreKeysPanel();
179    }
180
181    @Override
182    public int translateX(final int x) {
183        return x - mOriginX;
184    }
185
186    @Override
187    public int translateY(final int y) {
188        return y - mOriginY;
189    }
190
191    @Override
192    public boolean onTouchEvent(final MotionEvent me) {
193        final int action = me.getActionMasked();
194        final long eventTime = me.getEventTime();
195        final int index = me.getActionIndex();
196        final int x = (int)me.getX(index);
197        final int y = (int)me.getY(index);
198        final int pointerId = me.getPointerId(index);
199        processMotionEvent(action, x, y, pointerId, eventTime);
200        return true;
201    }
202
203    public void processMotionEvent(final int action, final int x, final int y,
204            final int pointerId, final long eventTime) {
205        switch (action) {
206        case MotionEvent.ACTION_DOWN:
207        case MotionEvent.ACTION_POINTER_DOWN:
208            onDownEvent(x, y, pointerId, eventTime);
209            break;
210        case MotionEvent.ACTION_UP:
211        case MotionEvent.ACTION_POINTER_UP:
212            onUpEvent(x, y, pointerId, eventTime);
213            break;
214        case MotionEvent.ACTION_MOVE:
215            onMoveEvent(x, y, pointerId, eventTime);
216            break;
217        }
218    }
219
220    @Override
221    public View getContainerView() {
222        return (View)getParent();
223    }
224
225    @Override
226    public boolean isShowingInParent() {
227        return (getContainerView().getParent() != null);
228    }
229}
230