159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma/*
259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * Licensed under the Apache License, Version 2.0 (the "License");
559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * you may not use this file except in compliance with the License.
659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * You may obtain a copy of the License at
759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *      http://www.apache.org/licenses/LICENSE-2.0
959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
1059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * Unless required by applicable law or agreed to in writing, software
1159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * distributed under the License is distributed on an "AS IS" BASIS,
1259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * See the License for the specific language governing permissions and
1459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * limitations under the License.
1559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma */
1659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma/* This file is porting from Android framework.
1759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *   frameworks/base/core/java/android/inputmethodservice/KeyboardView.java
1859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *
1959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma *package android.inputmethodservice;
2059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma */
2159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
2259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmapackage jp.co.omronsoft.openwnn;
2359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
2459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.content.Context;
2559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.content.res.TypedArray;
2659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.Bitmap;
2759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.Canvas;
2859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.Paint;
2959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.PorterDuff;
3059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.Rect;
3159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.Typeface;
3259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.Paint.Align;
3359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.Region.Op;
3459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.graphics.drawable.Drawable;
3559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.os.Handler;
3659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.os.Message;
3759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.util.AttributeSet;
3859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.util.TypedValue;
3959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.view.GestureDetector;
4059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.view.Gravity;
4159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.view.LayoutInflater;
4259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.view.MotionEvent;
4359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.view.View;
4459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.view.ViewConfiguration;
4559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.view.ViewGroup.LayoutParams;
4659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.widget.PopupWindow;
4759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport android.widget.TextView;
4859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
4959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport jp.co.omronsoft.openwnn.Keyboard;
5059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport jp.co.omronsoft.openwnn.Keyboard.Key;
5159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
5259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.util.Arrays;
5359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.util.HashMap;
5459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.util.List;
5559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmaimport java.util.Map;
5659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
5759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma/**
5859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * A view that renders a virtual {@link Keyboard}. It handles rendering of keys and
5959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma * detecting key presses and touch movements.
6059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma */
6159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monmapublic class KeyboardView extends View implements View.OnClickListener {
6259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
6359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
6459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Listener for virtual keyboard events.
6559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
6659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public interface OnKeyboardActionListener {
6759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
6859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
6959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Called when the user presses a key. This is sent before the {@link #onKey} is called.
7059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * For keys that repeat, this is only called once.
7159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid
7259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * key, the value will be zero.
7359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
7459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void onPress(int primaryCode);
7559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
7659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
7759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Called when the user releases a key. This is sent after the {@link #onKey} is called.
7859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * For keys that repeat, this is only called once.
7959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param primaryCode the code of the key that was released
8059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
8159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void onRelease(int primaryCode);
8259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
8359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
8459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Send a key press to the listener.
8559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param primaryCode this is the key that was pressed
8659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param keyCodes the codes for all the possible alternative keys
8759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * with the primary code being the first. If the primary key code is
8859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * a single character such as an alphabet or number or symbol, the alternatives
8959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * will include other characters that may be on the same key or adjacent keys.
9059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * These codes are useful to correct for accidental presses of a key adjacent to
9159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * the intended key.
9259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
9359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void onKey(int primaryCode, int[] keyCodes);
9459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
9559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
9659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Sends a sequence of characters to the listener.
9759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param text the sequence of characters to be displayed.
9859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
9959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void onText(CharSequence text);
10059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
10159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
10259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Called when the user quickly moves the finger from right to left.
10359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
10459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void swipeLeft();
10559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
10659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
10759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Called when the user quickly moves the finger from left to right.
10859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
10959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void swipeRight();
11059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
11159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
11259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Called when the user quickly moves the finger from up to down.
11359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
11459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void swipeDown();
11559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
11659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
11759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Called when the user quickly moves the finger from down to up.
11859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
11959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        void swipeUp();
12059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
12159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        /**
12259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * Called when the user long presses a key.
12359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @param popupKey the key that was long pressed
12459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         * @return true if the long press is handled, false otherwise.
12559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma         */
12659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean onLongPress(Keyboard.Key key);
12759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
12859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
12959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int NOT_A_KEY = -1;
13059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
13159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int[] LONG_PRESSABLE_STATE_SET = {
13259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        android.R.attr.state_long_pressable
13359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    };
13459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
13559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Keyboard mKeyboard;
13659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mCurrentKeyIndex = NOT_A_KEY;
13759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mLabelTextSize;
13859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mKeyTextSize;
13959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mKeyTextColor;
14059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mKeyTextColor2nd;
14159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private float mShadowRadius;
14259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mShadowColor;
14359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private float mBackgroundDimAmount;
14459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
14559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private TextView mPreviewText;
14659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private PopupWindow mPreviewPopup;
14759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPreviewTextSizeLarge;
14859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPreviewOffset;
14959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPreviewHeight;
15059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int[] mOffsetInWindow;
15159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
15259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private PopupWindow mPopupKeyboard;
15359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private View mMiniKeyboardContainer;
15459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private KeyboardView mMiniKeyboard;
15559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mMiniKeyboardOnScreen;
15659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private View mPopupParent;
15759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mMiniKeyboardOffsetX;
15859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mMiniKeyboardOffsetY;
15959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Map<Key,View> mMiniKeyboardCache;
16059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int[] mWindowOffset;
16159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Key[] mKeys;
16259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
16359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Listener for {@link OnKeyboardActionListener}. */
16459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private OnKeyboardActionListener mKeyboardActionListener;
16559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
16659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int MSG_SHOW_PREVIEW = 1;
16759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int MSG_REMOVE_PREVIEW = 2;
16859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int MSG_REPEAT = 3;
16959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int MSG_LONGPRESS = 4;
17059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
17159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int DELAY_BEFORE_PREVIEW = 0;
17259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int DELAY_AFTER_PREVIEW = 70;
17359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int DEBOUNCE_TIME = 70;
17459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
17559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mVerticalCorrection;
17659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mProximityThreshold;
17759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
17859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mPreviewCentered = false;
17959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mShowPreview = true;
18059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mShowTouchPoints = true;
18159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPopupPreviewX;
18259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPopupPreviewY;
18359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mWindowY;
18459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
18559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mLastX;
18659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mLastY;
18759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mStartX;
18859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mStartY;
18959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
19059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mProximityCorrectOn;
19159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
19259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Paint mPaint;
19359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Rect mPadding;
19459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
19559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private long mDownTime;
19659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private long mLastMoveTime;
19759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mLastKey;
19859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mLastCodeX;
19959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mLastCodeY;
20059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mCurrentKey = NOT_A_KEY;
20159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mDownKey = NOT_A_KEY;
20259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private long mLastKeyTime;
20359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private long mCurrentKeyTime;
20459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int[] mKeyIndices = new int[12];
20559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private GestureDetector mGestureDetector;
20659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPopupX;
20759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPopupY;
20859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mRepeatKeyIndex = NOT_A_KEY;
20959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mPopupLayout;
21059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mAbortKey;
21159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Key mInvalidatedKey;
21259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Rect mClipRegion = new Rect(0, 0, 0, 0);
21359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mPossiblePoly;
21459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private SwipeTracker mSwipeTracker = new SwipeTracker();
21559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mSwipeThreshold;
21659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mDisambiguateSwipe;
21759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
21859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mOldPointerCount = 1;
21959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private float mOldPointerX;
22059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private float mOldPointerY;
22159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
22259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Drawable mKeyBackground;
22359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Drawable mKeyBackground2nd;
22459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
22559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int REPEAT_INTERVAL = 50;
22659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int REPEAT_START_DELAY = 400;
22759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
22859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
22959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static int MAX_NEARBY_KEYS = 12;
23059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int[] mDistances = new int[MAX_NEARBY_KEYS];
23159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
23259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mLastSentIndex;
23359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int mTapCount;
23459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private long mLastTapTime;
23559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mInMultiTap;
23659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static final int MULTITAP_INTERVAL = 800;
23759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private StringBuilder mPreviewLabel = new StringBuilder(1);
23859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
23959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
24059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mDrawPending;
24159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** The dirty region in the keyboard bitmap */
24259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Rect mDirtyRect = new Rect();
24359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** The keyboard bitmap for faster updates */
24459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Bitmap mBuffer;
24559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
24659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mKeyboardChanged;
24759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** The canvas for the above mutable keyboard bitmap */
24859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private Canvas mCanvas;
24959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
25059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    Handler mHandler = new Handler() {
25159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        @Override
25259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public void handleMessage(Message msg) {
25359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            switch (msg.what) {
25459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                case MSG_SHOW_PREVIEW:
25559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    showKey(msg.arg1);
25659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    break;
25759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                case MSG_REMOVE_PREVIEW:
25859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mPreviewText.setVisibility(INVISIBLE);
25959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    break;
26059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                case MSG_REPEAT:
26159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (repeatKey()) {
26259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        Message repeat = Message.obtain(this, MSG_REPEAT);
26359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        sendMessageDelayed(repeat, REPEAT_INTERVAL);
26459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
26559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    break;
26659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                case MSG_LONGPRESS:
26759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    openPopupIfRequired((MotionEvent) msg.obj);
26859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    break;
26959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
27059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
27159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    };
27259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
27359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Constructor */
27459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public KeyboardView(Context context, AttributeSet attrs) {
27559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        this(context, attrs, 0);
27659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
27759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
27859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /** Constructor */
27959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public KeyboardView(Context context, AttributeSet attrs, int defStyle) {
28059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        super(context, attrs, defStyle);
28159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
28259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        TypedArray a =
28359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            context.obtainStyledAttributes(
28459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                attrs, android.R.styleable.KeyboardView, defStyle, R.style.WnnKeyboardView);
28559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
28659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        LayoutInflater inflate =
28759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                (LayoutInflater) context
28859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
28959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
29059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int previewLayout = 0;
29159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int keyTextSize = 0;
29259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
29359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int n = a.getIndexCount();
29459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
29559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        for (int i = 0; i < n; i++) {
29659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int attr = a.getIndex(i);
29759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
29859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            switch (attr) {
29959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_keyBackground:
30059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyBackground = a.getDrawable(attr);
30159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
30259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_verticalCorrection:
30359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
30459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
30559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_keyPreviewLayout:
30659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                previewLayout = a.getResourceId(attr, 0);
30759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
30859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_keyPreviewOffset:
30959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
31059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
31159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_keyPreviewHeight:
31259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPreviewHeight = a.getDimensionPixelSize(attr, 80);
31359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
31459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_keyTextSize:
31559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyTextSize = a.getDimensionPixelSize(attr, 18);
31659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
31759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_keyTextColor:
31859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyTextColor = a.getColor(attr, 0xFF000000);
31959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
32059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_labelTextSize:
32159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mLabelTextSize = a.getDimensionPixelSize(attr, 14);
32259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
32359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_popupLayout:
32459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPopupLayout = a.getResourceId(attr, 0);
32559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
32659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_shadowColor:
32759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mShadowColor = a.getColor(attr, 0);
32859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
32959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case android.R.styleable.KeyboardView_shadowRadius:
33059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mShadowRadius = a.getFloat(attr, 0f);
33159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
33259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
33359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
33459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
33559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        a.recycle();
33659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        a = context.obtainStyledAttributes(attrs, R.styleable.WnnKeyboardView, 0, 0);
33759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyBackground2nd = a.getDrawable(R.styleable.WnnKeyboardView_keyBackground2nd);
33859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyTextColor2nd = a.getColor(R.styleable.WnnKeyboardView_keyTextColor2nd, 0xFF000000);
33959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
34059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        a.recycle();
34159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        a = mContext.obtainStyledAttributes(
34259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                android.R.styleable.Theme);
34359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mBackgroundDimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
34459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
34559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPreviewPopup = new PopupWindow(context);
34659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (previewLayout != 0) {
34759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewText = (TextView) inflate.inflate(previewLayout, null);
34859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewTextSizeLarge = (int) mPreviewText.getTextSize();
34959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewPopup.setContentView(mPreviewText);
35059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewPopup.setBackgroundDrawable(null);
35159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
35259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mShowPreview = false;
35359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
35459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
35559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPreviewPopup.setTouchable(false);
35659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
35759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPopupKeyboard = new PopupWindow(context);
35859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPopupKeyboard.setBackgroundDrawable(null);
35959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
36059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPopupParent = this;
36159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
36259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPaint = new Paint();
36359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPaint.setAntiAlias(true);
36459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPaint.setTextSize(keyTextSize);
36559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPaint.setTextAlign(Align.CENTER);
36659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPaint.setAlpha(255);
36759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
36859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPadding = new Rect(0, 0, 0, 0);
36959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mMiniKeyboardCache = new HashMap<Key,View>();
37059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyBackground.getPadding(mPadding);
37159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
37259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density);
37359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
37459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDisambiguateSwipe = true;
37559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
37659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        resetMultiTap();
37759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        initGestureDetector();
37859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
37959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
38059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void initGestureDetector() {
38159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
38259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            @Override
38359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            public boolean onFling(MotionEvent me1, MotionEvent me2,
38459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    float velocityX, float velocityY) {
38559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (mPossiblePoly) return false;
38659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final float absX = Math.abs(velocityX);
38759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final float absY = Math.abs(velocityY);
38859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                float deltaX = me2.getX() - me1.getX();
38959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                float deltaY = me2.getY() - me1.getY();
39059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int travelX = getWidth() / 2;
39159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int travelY = getHeight() / 2;
39259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mSwipeTracker.computeCurrentVelocity(1000);
39359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final float endingVelocityX = mSwipeTracker.getXVelocity();
39459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final float endingVelocityY = mSwipeTracker.getYVelocity();
39559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                boolean sendDownKey = false;
39659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) {
39759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) {
39859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        sendDownKey = true;
39959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
40059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        swipeRight();
40159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        return true;
40259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
40359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) {
40459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) {
40559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        sendDownKey = true;
40659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
40759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        swipeLeft();
40859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        return true;
40959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
41059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) {
41159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) {
41259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        sendDownKey = true;
41359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
41459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        swipeUp();
41559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        return true;
41659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
41759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
41859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) {
41959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        sendDownKey = true;
42059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
42159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        swipeDown();
42259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        return true;
42359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
42459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
42559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
42659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (sendDownKey) {
42759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime());
42859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
42959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return false;
43059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
43159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        });
43259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
43359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mGestureDetector.setIsLongpressEnabled(false);
43459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
43559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
43659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
43759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Set the {@link OnKeyboardActionListener} object.
43859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param listener  The OnKeyboardActionListener to set.
43959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
44059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
44159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboardActionListener = listener;
44259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
44359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
44459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
44559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the {@link OnKeyboardActionListener} object.
44659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return the listener attached to this keyboard
44759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
44859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected OnKeyboardActionListener getOnKeyboardActionListener() {
44959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mKeyboardActionListener;
45059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
45159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
45259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
45359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
45459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * view will re-layout itself to accommodate the keyboard.
45559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see Keyboard
45659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see #getKeyboard()
45759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param keyboard the keyboard to display in this view
45859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
45959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void setKeyboard(Keyboard keyboard) {
46059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (!keyboard.equals(mKeyboard)) {
46159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            clearWindowInfo();
46259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
46359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int oldRepeatKeyCode = NOT_A_KEY;
46459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeyboard != null) {
46559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            showPreview(NOT_A_KEY);
46659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if ((mRepeatKeyIndex != NOT_A_KEY) && (mRepeatKeyIndex < mKeys.length)) {
46759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                oldRepeatKeyCode = mKeys[mRepeatKeyIndex].codes[0];
46859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
46959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
47059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        removeMessages();
47159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboard = keyboard;
47259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        List<Key> keys = mKeyboard.getKeys();
47359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeys = keys.toArray(new Key[keys.size()]);
47459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        requestLayout();
47559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboardChanged = true;
47659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        invalidateAllKeys();
47759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        computeProximityThreshold(keyboard);
47859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mMiniKeyboardCache.clear();
47959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean abort = true;
48059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (oldRepeatKeyCode != NOT_A_KEY) {
48159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int keyIndex = getKeyIndices(mStartX, mStartY, null);
48259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if ((keyIndex != NOT_A_KEY)
48359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                && (keyIndex < mKeys.length)
48459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                && (oldRepeatKeyCode == mKeys[keyIndex].codes[0])) {
48559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                abort = false;
48659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mRepeatKeyIndex = keyIndex;
48759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
48859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
48959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (abort) {
49059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mHandler.removeMessages(MSG_REPEAT);
49159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
49259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mAbortKey = abort;
49359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
49459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
49559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
49659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the current keyboard being displayed by this view.
49759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return the currently attached keyboard
49859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see #setKeyboard(Keyboard)
49959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
50059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public Keyboard getKeyboard() {
50159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mKeyboard;
50259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
50359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
50459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
50559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Sets the state of the shift key of the keyboard, if any.
50659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param shifted whether or not to enable the state of the shift key
50759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return true if the shift key state changed, false if there was no change
50859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see KeyboardView#isShifted()
50959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
51059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean setShifted(boolean shifted) {
51159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeyboard != null) {
51259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (mKeyboard.setShifted(shifted)) {
51359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                invalidateAllKeys();
51459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return true;
51559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
51659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
51759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return false;
51859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
51959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
52059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
52159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the state of the shift key of the keyboard, if any.
52259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return true if the shift is in a pressed state, false otherwise. If there is
52359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * no shift key on the keyboard or there is no keyboard attached, it returns false.
52459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see KeyboardView#setShifted(boolean)
52559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
52659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean isShifted() {
52759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeyboard != null) {
52859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return mKeyboard.isShifted();
52959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
53059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return false;
53159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
53259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
53359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
53459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Enables or disables the key feedback popup. This is a popup that shows a magnified
53559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * version of the depressed key. By default the preview is enabled.
53659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param previewEnabled whether or not to enable the key feedback popup
53759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see #isPreviewEnabled()
53859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
53959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void setPreviewEnabled(boolean previewEnabled) {
54059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mShowPreview = previewEnabled;
54159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
54259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
54359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
54459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the enabled state of the key feedback popup.
54559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return whether or not the key feedback popup is enabled
54659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see #setPreviewEnabled(boolean)
54759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
54859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean isPreviewEnabled() {
54959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mShowPreview;
55059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
55159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
55259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
55359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns the root parent has the enabled state of the key feedback popup.
55459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return whether or not the key feedback popup is enabled
55559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see #setPreviewEnabled(boolean)
55659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
55759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean isParentPreviewEnabled() {
55859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if ((mPopupParent != null) && (mPopupParent != this)
55959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                && (mPopupParent instanceof KeyboardView)) {
56059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return ((KeyboardView)mPopupParent).isParentPreviewEnabled();
56159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
56259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return mShowPreview;
56359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
56459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
56559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
56659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void setVerticalCorrection(int verticalOffset) {
56759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
56859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
56959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
57059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
57159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Set View on the PopupParent.
57259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param v  The View to set.
57359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
57459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void setPopupParent(View v) {
57559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPopupParent = v;
57659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
57759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
57859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
57959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Set parameters on the KeyboardOffset.
58059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param x  The value of KeyboardOffset.
58159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param y  The value of KeyboardOffset.
58259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
58359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void setPopupOffset(int x, int y) {
58459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mMiniKeyboardOffsetX = x;
58559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mMiniKeyboardOffsetY = y;
58659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mPreviewPopup.isShowing()) {
58759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewPopup.dismiss();
58859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
58959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
59059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
59159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
59259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * When enabled, calls to {@link OnKeyboardActionListener#onKey} will include key
59359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * codes for adjacent keys.  When disabled, only the primary key code will be
59459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * reported.
59559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param enabled whether or not the proximity correction is enabled
59659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
59759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void setProximityCorrectionEnabled(boolean enabled) {
59859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mProximityCorrectOn = enabled;
59959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
60059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
60159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
60259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Returns true if proximity correction is enabled.
60359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
60459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean isProximityCorrectionEnabled() {
60559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return mProximityCorrectOn;
60659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
60759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
60859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
60959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Popup keyboard close button clicked.
61059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @hide
61159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
61259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void onClick(View v) {
61359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        dismissPopupKeyboard();
61459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
61559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
61659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private CharSequence adjustCase(CharSequence label) {
61759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeyboard.isShifted() && label != null && label.length() < 3
61859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                && Character.isLowerCase(label.charAt(0))) {
61959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            label = label.toString().toUpperCase();
62059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
62159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return label;
62259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
62359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
62459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    @Override
62559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
62659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeyboard == null) {
62759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            setMeasuredDimension(mPaddingLeft + mPaddingRight, mPaddingTop + mPaddingBottom);
62859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
62959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int width = mKeyboard.getMinWidth() + mPaddingLeft + mPaddingRight;
63059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
63159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                width = MeasureSpec.getSize(widthMeasureSpec);
63259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
63359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            setMeasuredDimension(width, mKeyboard.getHeight() + mPaddingTop + mPaddingBottom);
63459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
63559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
63659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
63759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
63859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Compute the average distance between adjacent keys (horizontally and vertically)
63959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * and square it to get the proximity threshold. We use a square here and in computing
64059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * the touch distance from a key's center to avoid taking a square root.
64159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param keyboard
64259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
64359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void computeProximityThreshold(Keyboard keyboard) {
64459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (keyboard == null) return;
64559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Key[] keys = mKeys;
64659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (keys == null) return;
64759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int length = keys.length;
64859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int dimensionSum = 0;
64959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        for (int i = 0; i < length; i++) {
65059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            Key key = keys[i];
65159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            dimensionSum += Math.min(key.width, key.height) + key.gap;
65259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
65359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (dimensionSum < 0 || length == 0) return;
65459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mProximityThreshold = (int) (dimensionSum * 1.4f / length);
65559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mProximityThreshold *= mProximityThreshold;
65659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
65759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
65859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    @Override
65959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void onSizeChanged(int w, int h, int oldw, int oldh) {
66059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        super.onSizeChanged(w, h, oldw, oldh);
66159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mBuffer = null;
66259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
66359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
66459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    @Override
66559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void onDraw(Canvas canvas) {
66659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        super.onDraw(canvas);
66759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mDrawPending || mBuffer == null || mKeyboardChanged) {
66859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            onBufferDraw();
66959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
67059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        canvas.drawBitmap(mBuffer, 0, 0, null);
67159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
67259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
67359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void onBufferDraw() {
67459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean isBufferNull = (mBuffer == null);
67559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (isBufferNull || mKeyboardChanged) {
67659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (isBufferNull || mKeyboardChanged &&
67759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
67859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final int width = Math.max(1, getWidth());
67959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final int height = Math.max(1, getHeight());
68059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
68159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mCanvas = new Canvas(mBuffer);
68259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
68359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            invalidateAllKeys();
68459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mKeyboardChanged = false;
68559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
68659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Canvas canvas = mCanvas;
68759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        canvas.clipRect(mDirtyRect, Op.REPLACE);
68859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
68959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeyboard == null) return;
69059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
69159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Paint paint = mPaint;
69259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Rect clipRegion = mClipRegion;
69359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Rect padding = mPadding;
69459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int kbdPaddingLeft = mPaddingLeft;
69559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int kbdPaddingTop = mPaddingTop;
69659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Key[] keys = mKeys;
69759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Key invalidKey = mInvalidatedKey;
69859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
69959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        paint.setColor(mKeyTextColor);
70059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean drawSingleKey = false;
70159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
70259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma          if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left &&
70359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                  invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top &&
70459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                  invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right &&
70559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                  invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) {
70659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma              drawSingleKey = true;
70759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma          }
70859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
70959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
71059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int keyCount = keys.length;
71159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        for (int i = 0; i < keyCount; i++) {
71259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final Key key = keys[i];
71359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (drawSingleKey && invalidKey != key) {
71459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                continue;
71559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
71659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
71759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            paint.setColor(key.isSecondKey ? mKeyTextColor2nd : mKeyTextColor);
71859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            Drawable keyBackground = key.isSecondKey ? mKeyBackground2nd : mKeyBackground;
71959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int[] drawableState = key.getCurrentDrawableState();
72059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            keyBackground.setState(drawableState);
72159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
72259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            String label = key.label == null? null : adjustCase(key.label).toString();
72359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
72459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final Rect bounds = keyBackground.getBounds();
72559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (key.width != bounds.right ||
72659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    key.height != bounds.bottom) {
72759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                keyBackground.setBounds(0, 0, key.width, key.height);
72859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
72959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
73059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            keyBackground.draw(canvas);
73159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
73259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (label != null) {
73359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (OpenWnn.isXLarge()) {
73459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (label.length() > 1 && key.codes.length < 2) {
73559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTextSize(mLabelTextSize);
73659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTypeface(Typeface.DEFAULT);
73759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
73859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTextSize(mKeyTextSize);
73959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTypeface(Typeface.DEFAULT_BOLD);
74059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
74159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
74259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (label.length() > 1 && key.codes.length < 2) {
74359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTextSize(mLabelTextSize);
74459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTypeface(Typeface.DEFAULT_BOLD);
74559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
74659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTextSize(mKeyTextSize);
74759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint.setTypeface(Typeface.DEFAULT_BOLD);
74859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
74959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
75059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
75159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (OpenWnn.isXLarge()) {
75259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    canvas.drawText(label,
75359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        (key.width - padding.left + 7 - padding.right) / 2
75459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                + padding.left,
75559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        (key.height - padding.top + 7 - padding.bottom) / 2
75659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                + (paint.getTextSize() - paint.descent()) / 2 + padding.top,
75759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint);
75859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
75959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    canvas.drawText(label,
76059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        (key.width - padding.left - padding.right) / 2
76159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                + padding.left,
76259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        (key.height - padding.top - padding.bottom) / 2
76359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                + (paint.getTextSize() - paint.descent()) / 2 + padding.top,
76459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        paint);
76559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
76659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                paint.setShadowLayer(0, 0, 0, 0);
76759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else if (key.icon != null) {
76859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int drawableX;
76959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int drawableY;
77059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (OpenWnn.isXLarge()) {
77159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    drawableX = (key.width - padding.left + 12 - padding.right
77259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                    - key.icon.getIntrinsicWidth()) / 2 + padding.left;
77359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    drawableY = (key.height - padding.top + 9 - padding.bottom
77459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            - key.icon.getIntrinsicHeight()) / 2 + padding.top;
77559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
77659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    drawableX = (key.width - padding.left - padding.right
77759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                    - key.icon.getIntrinsicWidth()) / 2 + padding.left;
77859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    drawableY = (key.height - padding.top - padding.bottom
77959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            - key.icon.getIntrinsicHeight()) / 2 + padding.top;
78059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
78159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                canvas.translate(drawableX, drawableY);
78259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                key.icon.setBounds(0, 0,
78359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
78459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                key.icon.draw(canvas);
78559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                canvas.translate(-drawableX, -drawableY);
78659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
78759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop);
78859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
78959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mInvalidatedKey = null;
79059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mMiniKeyboardOnScreen) {
79159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
79259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
79359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
79459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
79559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDrawPending = false;
79659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDirtyRect.setEmpty();
79759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
79859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
79959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private int getKeyIndices(int x, int y, int[] allKeys) {
80059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Key[] keys = mKeys;
80159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int primaryIndex = NOT_A_KEY;
80259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int closestKey = NOT_A_KEY;
80359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int closestKeyDist = mProximityThreshold + 1;
80459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        java.util.Arrays.fill(mDistances, Integer.MAX_VALUE);
80559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y);
80659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int keyCount = nearestKeyIndices.length;
80759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        for (int i = 0; i < keyCount; i++) {
80859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final Key key = keys[nearestKeyIndices[i]];
80959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int dist = 0;
81059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            boolean isInside = key.isInside(x,y);
81159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (isInside) {
81259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                primaryIndex = nearestKeyIndices[i];
81359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
81459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
81559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (((mProximityCorrectOn
81659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold)
81759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    || isInside)
81859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && key.codes[0] > 32) {
81959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final int nCodes = key.codes.length;
82059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (dist < closestKeyDist) {
82159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    closestKeyDist = dist;
82259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    closestKey = nearestKeyIndices[i];
82359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
82459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
82559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (allKeys == null) continue;
82659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
82759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                for (int j = 0; j < mDistances.length; j++) {
82859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mDistances[j] > dist) {
82959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        System.arraycopy(mDistances, j, mDistances, j + nCodes,
83059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                mDistances.length - j - nCodes);
83159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        System.arraycopy(allKeys, j, allKeys, j + nCodes,
83259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                allKeys.length - j - nCodes);
83359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        for (int c = 0; c < nCodes; c++) {
83459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            allKeys[j + c] = key.codes[c];
83559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mDistances[j + c] = dist;
83659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        }
83759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        break;
83859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
83959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
84059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
84159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
84259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (primaryIndex == NOT_A_KEY) {
84359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            primaryIndex = closestKey;
84459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
84559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return primaryIndex;
84659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
84759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
84859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void detectAndSendKey(int index, int x, int y, long eventTime) {
84959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (index != NOT_A_KEY && index < mKeys.length) {
85059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final Key key = mKeys[index];
85159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (key.text != null) {
85259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyboardActionListener.onText(key.text);
85359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyboardActionListener.onRelease(NOT_A_KEY);
85459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
85559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int code = key.codes[0];
85659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                int[] codes = new int[MAX_NEARBY_KEYS];
85759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                Arrays.fill(codes, NOT_A_KEY);
85859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                getKeyIndices(x, y, codes);
85959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (mInMultiTap) {
86059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mTapCount != -1) {
86159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE);
86259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
86359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mTapCount = 0;
86459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
86559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    code = key.codes[mTapCount];
86659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
86759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyboardActionListener.onKey(code, codes);
86859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyboardActionListener.onRelease(code);
86959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
87059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mLastSentIndex = index;
87159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mLastTapTime = eventTime;
87259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
87359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
87459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
87559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
87659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Handle multi-tap keys by producing the key label for the current multi-tap state.
87759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
87859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private CharSequence getPreviewText(Key key) {
87959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mInMultiTap) {
88059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewLabel.setLength(0);
88159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]);
88259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return adjustCase(mPreviewLabel);
88359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
88459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return adjustCase(key.label);
88559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
88659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
88759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
88859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void showPreview(int keyIndex) {
88959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int oldKeyIndex = mCurrentKeyIndex;
89059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final PopupWindow previewPopup = mPreviewPopup;
89159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
89259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mCurrentKeyIndex = keyIndex;
89359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Key[] keys = mKeys;
89459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (oldKeyIndex != mCurrentKeyIndex) {
89559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) {
89659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY);
89759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                invalidateKey(oldKeyIndex);
89859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
89959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) {
90059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                keys[mCurrentKeyIndex].onPressed();
90159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                invalidateKey(mCurrentKeyIndex);
90259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
90359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
90459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (oldKeyIndex != mCurrentKeyIndex && mShowPreview && isParentPreviewEnabled()) {
90559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mHandler.removeMessages(MSG_SHOW_PREVIEW);
90659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (previewPopup.isShowing()) {
90759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (keyIndex == NOT_A_KEY) {
90859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mHandler.sendMessageDelayed(mHandler
90959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            .obtainMessage(MSG_REMOVE_PREVIEW),
91059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            DELAY_AFTER_PREVIEW);
91159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
91259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
91359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (keyIndex != NOT_A_KEY) {
91459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (previewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
91559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    showKey(keyIndex);
91659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
91759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mHandler.sendMessageDelayed(
91859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0),
91959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            DELAY_BEFORE_PREVIEW);
92059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
92159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
92259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
92359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
92459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
92559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void showKey(final int keyIndex) {
92659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final PopupWindow previewPopup = mPreviewPopup;
92759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Key[] keys = mKeys;
92859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (keyIndex < 0 || keyIndex >= mKeys.length) return;
92959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Key key = keys[keyIndex];
93059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
93159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPreviewText.setBackgroundDrawable(getContext().getResources().getDrawable(R.drawable.keyboard_key_feedback));
93259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
93359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (key.icon != null) {
93459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewText.setCompoundDrawables(null, null, null,
93559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    key.iconPreview != null ? key.iconPreview : key.icon);
93659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewText.setText(null);
93759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewText.setPadding(5, 0, 5, 20);
93859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
93959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewText.setCompoundDrawables(null, null, null, null);
94059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewText.setText(getPreviewText(key));
94159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (key.label.length() > 1 && key.codes.length < 2) {
94259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize);
94359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPreviewText.setTypeface(Typeface.DEFAULT_BOLD);
94459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
94559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge);
94659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPreviewText.setTypeface(Typeface.DEFAULT);
94759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
94859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewText.setPadding(0, 0, 0, 10);
94959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
95059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
95159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
95259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width
95359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
95459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int popupHeight = mPreviewHeight;
95559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        LayoutParams lp = mPreviewText.getLayoutParams();
95659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (lp != null) {
95759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            lp.width = popupWidth;
95859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            lp.height = popupHeight;
95959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
96059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (!mPreviewCentered) {
96159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupPreviewX = key.x - (Math.abs(popupWidth - key.width) / 2 );
96259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupPreviewY = key.y - popupHeight + mPreviewOffset;
96359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
96459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupPreviewX = 160 - mPreviewText.getMeasuredWidth() / 2;
96559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupPreviewY = - mPreviewText.getMeasuredHeight();
96659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
96759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPopupPreviewY = mPopupPreviewY + 20;
96859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mHandler.removeMessages(MSG_REMOVE_PREVIEW);
96959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mOffsetInWindow == null) {
97059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mOffsetInWindow = new int[2];
97159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            getLocationInWindow(mOffsetInWindow);
97259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mOffsetInWindow[0] += mMiniKeyboardOffsetX;
97359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mOffsetInWindow[1] += mMiniKeyboardOffsetY;
97459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int[] mWindowLocation = new int[2];
97559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            getLocationOnScreen(mWindowLocation);
97659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mWindowY = mWindowLocation[1];
97759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
97859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPreviewText.getBackground().setState(
97959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
98059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPopupPreviewX += mOffsetInWindow[0];
98159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPopupPreviewY += mOffsetInWindow[1];
98259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
98359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mPopupPreviewY + mWindowY < 0) {
98459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (key.x + key.width <= getWidth() / 2) {
98559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPopupPreviewX += (int) (key.width * 2.5);
98659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
98759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mPopupPreviewX -= (int) (key.width * 2.5);
98859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
98959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupPreviewY += popupHeight;
99059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
99159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
99259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (previewPopup.isShowing()) {
99359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            previewPopup.update(mPopupPreviewX, mPopupPreviewY,
99459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    popupWidth, popupHeight);
99559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
99659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            previewPopup.setWidth(popupWidth);
99759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            previewPopup.setHeight(popupHeight);
99859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
99959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mPopupPreviewX, mPopupPreviewY);
100059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
100159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPreviewText.setVisibility(VISIBLE);
100259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
100359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
100459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
100559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
100659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
100759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * draws the cached buffer.
100859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see #invalidateKey(int)
100959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
101059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void invalidateAllKeys() {
101159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDirtyRect.union(0, 0, getWidth(), getHeight());
101259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDrawPending = true;
101359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        invalidate();
101459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
101559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
101659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
101759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only
101859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * one key is changing it's content. Any changes that affect the position or size of the key
101959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * may not be honored.
102059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param keyIndex the index of the key in the attached {@link Keyboard}.
102159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @see #invalidateAllKeys
102259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
102359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void invalidateKey(int keyIndex) {
102459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeys == null) return;
102559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (keyIndex < 0 || keyIndex >= mKeys.length) {
102659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return;
102759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
102859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final Key key = mKeys[keyIndex];
102959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mInvalidatedKey = key;
103059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mDirtyRect.union(key.x + mPaddingLeft, key.y + mPaddingTop,
103159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
103259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        onBufferDraw();
103359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        invalidate(key.x + mPaddingLeft, key.y + mPaddingTop,
103459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
103559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
103659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
103759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean openPopupIfRequired(MotionEvent me) {
103859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mPopupLayout == 0) {
103959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return false;
104059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
104159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mCurrentKey < 0 || mCurrentKey >= mKeys.length) {
104259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return false;
104359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
104459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
104559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Key popupKey = mKeys[mCurrentKey];
104659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean result = onLongPress(popupKey);
104759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (result) {
104859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mAbortKey = true;
104959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            showPreview(NOT_A_KEY);
105059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
105159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return result;
105259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
105359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
105459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
105559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Called when a key is long pressed. By default this will open any popup keyboard associated
105659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * with this key through the attributes popupLayout and popupCharacters.
105759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @param popupKey the key that was long pressed
105859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * @return true if the long press is handled, false otherwise. Subclasses should call the
105959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * method on the base class if the subclass doesn't wish to handle the call.
106059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
106159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected boolean onLongPress(Key popupKey) {
106259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mKeyboardActionListener.onLongPress(popupKey)) {
106359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return true;
106459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
106559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int popupKeyboardId = popupKey.popupResId;
106659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (popupKeyboardId != 0) {
106759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mMiniKeyboardContainer = mMiniKeyboardCache.get(popupKey);
106859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (mMiniKeyboardContainer == null) {
106959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
107059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        Context.LAYOUT_INFLATER_SERVICE);
107159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboardContainer = inflater.inflate(mPopupLayout, null);
107259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboard = (KeyboardView) mMiniKeyboardContainer.findViewById(R.id.keyboardView);
107359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                View closeButton = mMiniKeyboardContainer.findViewById(R.id.closeButton);
107459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (closeButton != null) closeButton.setOnClickListener(this);
107559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() {
107659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void onKey(int primaryCode, int[] keyCodes) {
107759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mKeyboardActionListener.onKey(primaryCode, keyCodes);
107859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        dismissPopupKeyboard();
107959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
108059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
108159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void onText(CharSequence text) {
108259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mKeyboardActionListener.onText(text);
108359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        dismissPopupKeyboard();
108459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
108559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
108659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void swipeLeft() { }
108759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void swipeRight() { }
108859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void swipeUp() { }
108959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void swipeDown() { }
109059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void onPress(int primaryCode) {
109159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mKeyboardActionListener.onPress(primaryCode);
109259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
109359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public void onRelease(int primaryCode) {
109459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mKeyboardActionListener.onRelease(primaryCode);
109559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
109659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    public boolean onLongPress(Keyboard.Key key) {
109759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        return false;
109859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
109959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                });
110059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                Keyboard keyboard;
110159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (popupKey.popupCharacters != null) {
110259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    keyboard = new Keyboard(getContext(), popupKeyboardId,
110359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight());
110459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
110559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    keyboard = new Keyboard(getContext(), popupKeyboardId);
110659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
110759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboard.setKeyboard(keyboard);
110859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboard.setPopupParent(this);
110959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboardContainer.measure(
111059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
111159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
111259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
111359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer);
111459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
111559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mMiniKeyboard = (KeyboardView) mMiniKeyboardContainer.findViewById(R.id.keyboardView);
111659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
111759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (mWindowOffset == null) {
111859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mWindowOffset = new int[2];
111959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                getLocationInWindow(mWindowOffset);
112059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
112159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupX = popupKey.x + mPaddingLeft;
112259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupY = popupKey.y + mPaddingTop;
112359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupX = mPopupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth();
112459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupY = mPopupY - mMiniKeyboardContainer.getMeasuredHeight();
112559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final int x = mPopupX + mMiniKeyboardContainer.getPaddingRight() + mWindowOffset[0];
112659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final int y = mPopupY + mMiniKeyboardContainer.getPaddingBottom() + mWindowOffset[1];
112759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y);
112859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mMiniKeyboard.setShifted(isShifted());
112959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupKeyboard.setContentView(mMiniKeyboardContainer);
113059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupKeyboard.setWidth(mMiniKeyboardContainer.getMeasuredWidth());
113159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupKeyboard.setHeight(mMiniKeyboardContainer.getMeasuredHeight());
113259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
113359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mMiniKeyboardOnScreen = true;
113459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            invalidateAllKeys();
113559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return true;
113659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
113759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return false;
113859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
113959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
114059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private long mOldEventTime;
114159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean mUsedVelocity;
114259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
114359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    @Override
114459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean onTouchEvent(MotionEvent me) {
114559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int pointerCount = me.getPointerCount();
114659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int action = me.getAction();
114759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        boolean result = false;
114859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final long now = me.getEventTime();
114959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final boolean isPointerCountOne = (pointerCount == 1);
115059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
115159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (pointerCount != mOldPointerCount) {
115259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (isPointerCountOne) {
115359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
115459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        me.getX(), me.getY(), me.getMetaState());
115559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                result = onModifiedTouchEvent(down, false);
115659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                down.recycle();
115759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (action == MotionEvent.ACTION_UP) {
115859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    result = onModifiedTouchEvent(me, true);
115959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
116059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
116159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP,
116259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mOldPointerX, mOldPointerY, me.getMetaState());
116359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                result = onModifiedTouchEvent(up, true);
116459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                up.recycle();
116559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
116659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        } else {
116759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (isPointerCountOne) {
116859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                result = onModifiedTouchEvent(me, false);
116959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mOldPointerX = me.getX();
117059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mOldPointerY = me.getY();
117159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
117259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                result = true;
117359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
117459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
117559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mOldPointerCount = pointerCount;
117659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
117759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return result;
117859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
117959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
118059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) {
118159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int touchX = (int) me.getX() - mPaddingLeft;
118259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int touchY = (int) me.getY() + mVerticalCorrection - mPaddingTop;
118359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final int action = me.getAction();
118459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final long eventTime = me.getEventTime();
118559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mOldEventTime = eventTime;
118659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        int keyIndex = getKeyIndices(touchX, touchY, null);
118759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mPossiblePoly = possiblePoly;
118859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
118959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear();
119059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mSwipeTracker.addMovement(me);
119159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
119259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mAbortKey
119359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                && action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_CANCEL) {
119459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return true;
119559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
119659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
119759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mGestureDetector.onTouchEvent(me)) {
119859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            showPreview(NOT_A_KEY);
119959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mHandler.removeMessages(MSG_REPEAT);
120059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mHandler.removeMessages(MSG_LONGPRESS);
120159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return true;
120259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
120359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
120459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) {
120559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return true;
120659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
120759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
120859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        switch (action) {
120959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case MotionEvent.ACTION_DOWN:
121059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mAbortKey = false;
121159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mStartX = touchX;
121259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mStartY = touchY;
121359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mLastCodeX = touchX;
121459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mLastCodeY = touchY;
121559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mLastKeyTime = 0;
121659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mCurrentKeyTime = 0;
121759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mLastKey = NOT_A_KEY;
121859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mCurrentKey = keyIndex;
121959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mDownKey = keyIndex;
122059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mDownTime = me.getEventTime();
122159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mLastMoveTime = mDownTime;
122259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                checkMultiTap(eventTime, keyIndex);
122359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ?
122459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mKeys[keyIndex].codes[0] : 0);
122559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) {
122659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mRepeatKeyIndex = mCurrentKey;
122759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    Message msg = mHandler.obtainMessage(MSG_REPEAT);
122859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY);
122959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    repeatKey();
123059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mAbortKey) {
123159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mRepeatKeyIndex = NOT_A_KEY;
123259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        break;
123359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
123459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
123559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (mCurrentKey != NOT_A_KEY) {
123659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me);
123759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
123859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
123959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                showPreview(keyIndex);
124059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
124159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
124259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case MotionEvent.ACTION_MOVE:
124359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                boolean continueLongPress = false;
124459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (keyIndex != NOT_A_KEY) {
124559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (mCurrentKey == NOT_A_KEY) {
124659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mCurrentKey = keyIndex;
124759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mCurrentKeyTime = eventTime - mDownTime;
124859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    } else {
124959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        if (keyIndex == mCurrentKey) {
125059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mCurrentKeyTime += eventTime - mLastMoveTime;
125159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            continueLongPress = true;
125259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        } else if (mRepeatKeyIndex == NOT_A_KEY) {
125359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            resetMultiTap();
125459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mLastKey = mCurrentKey;
125559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mLastCodeX = mLastX;
125659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mLastCodeY = mLastY;
125759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mLastKeyTime =
125859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                                    mCurrentKeyTime + eventTime - mLastMoveTime;
125959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mCurrentKey = keyIndex;
126059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                            mCurrentKeyTime = 0;
126159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        }
126259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
126359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
126459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (!continueLongPress) {
126559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mHandler.removeMessages(MSG_LONGPRESS);
126659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    if (keyIndex != NOT_A_KEY) {
126759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me);
126859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
126959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    }
127059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
127159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                showPreview(mCurrentKey);
127259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mLastMoveTime = eventTime;
127359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
127459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
127559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case MotionEvent.ACTION_UP:
127659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                removeMessages();
127759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (keyIndex == mCurrentKey) {
127859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mCurrentKeyTime += eventTime - mLastMoveTime;
127959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else {
128059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    resetMultiTap();
128159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mLastKey = mCurrentKey;
128259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime;
128359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mCurrentKey = keyIndex;
128459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mCurrentKeyTime = 0;
128559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
128659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < DEBOUNCE_TIME
128759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        && mLastKey != NOT_A_KEY) {
128859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    mCurrentKey = mLastKey;
128959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    touchX = mLastCodeX;
129059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    touchY = mLastCodeY;
129159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
129259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                showPreview(NOT_A_KEY);
129359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                Arrays.fill(mKeyIndices, NOT_A_KEY);
129459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (mRepeatKeyIndex == NOT_A_KEY && !mMiniKeyboardOnScreen && !mAbortKey) {
129559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    detectAndSendKey(mCurrentKey, touchX, touchY, eventTime);
129659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
129759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                invalidateKey(keyIndex);
129859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mRepeatKeyIndex = NOT_A_KEY;
129959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
130059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            case MotionEvent.ACTION_CANCEL:
130159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                removeMessages();
130259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                dismissPopupKeyboard();
130359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mAbortKey = true;
130459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                showPreview(NOT_A_KEY);
130559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                invalidateKey(mCurrentKey);
130659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                break;
130759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
130859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mLastX = touchX;
130959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mLastY = touchY;
131059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return true;
131159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
131259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
131359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private boolean repeatKey() {
131459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Key key = mKeys[mRepeatKeyIndex];
131559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
131659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return true;
131759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
131859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
131959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void swipeRight() {
132059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboardActionListener.swipeRight();
132159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
132259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
132359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void swipeLeft() {
132459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboardActionListener.swipeLeft();
132559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
132659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
132759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void swipeUp() {
132859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboardActionListener.swipeUp();
132959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
133059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
133159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    protected void swipeDown() {
133259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mKeyboardActionListener.swipeDown();
133359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
133459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
133559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void closing() {
133659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mPreviewPopup.isShowing()) {
133759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPreviewPopup.dismiss();
133859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
133959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        removeMessages();
134059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
134159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        dismissPopupKeyboard();
134259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mBuffer = null;
134359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mCanvas = null;
134459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mMiniKeyboardCache.clear();
134559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
134659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
134759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void removeMessages() {
134859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mHandler.removeMessages(MSG_REPEAT);
134959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mHandler.removeMessages(MSG_LONGPRESS);
135059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mHandler.removeMessages(MSG_SHOW_PREVIEW);
135159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
135259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
135359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    @Override
135459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void onDetachedFromWindow() {
135559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        super.onDetachedFromWindow();
135659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        closing();
135759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
135859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
135959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void dismissPopupKeyboard() {
136059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mPopupKeyboard.isShowing()) {
136159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPopupKeyboard.dismiss();
136259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mMiniKeyboardOnScreen = false;
136359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            invalidateAllKeys();
136459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
136559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
136659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
136759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public boolean handleBack() {
136859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (mPopupKeyboard.isShowing()) {
136959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            dismissPopupKeyboard();
137059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return true;
137159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
137259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        return false;
137359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
137459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
137559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void resetMultiTap() {
137659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mLastSentIndex = NOT_A_KEY;
137759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mTapCount = 0;
137859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mLastTapTime = -1;
137959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mInMultiTap = false;
138059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
138159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
138259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private void checkMultiTap(long eventTime, int keyIndex) {
138359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (keyIndex == NOT_A_KEY) return;
138459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        Key key = mKeys[keyIndex];
138559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (key.codes.length > 1) {
138659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mInMultiTap = true;
138759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (eventTime < mLastTapTime + MULTITAP_INTERVAL
138859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    && keyIndex == mLastSentIndex) {
138959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mTapCount = (mTapCount + 1) % key.codes.length;
139059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return;
139159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            } else {
139259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                mTapCount = -1;
139359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                return;
139459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
139559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
139659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) {
139759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            resetMultiTap();
139859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
139959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
140059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
140159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    private static class SwipeTracker {
140259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
140359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        static final int NUM_PAST = 4;
140459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        static final int LONGEST_PAST_TIME = 200;
140559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
140659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final float mPastX[] = new float[NUM_PAST];
140759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final float mPastY[] = new float[NUM_PAST];
140859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        final long mPastTime[] = new long[NUM_PAST];
140959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
141059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        float mYVelocity;
141159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        float mXVelocity;
141259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
141359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public void clear() {
141459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mPastTime[0] = 0;
141559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
141659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
141759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public void addMovement(MotionEvent ev) {
141859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            long time = ev.getEventTime();
141959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final int N = ev.getHistorySize();
142059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            for (int i=0; i<N; i++) {
142159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
142259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                        ev.getHistoricalEventTime(i));
142359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
142459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            addPoint(ev.getX(), ev.getY(), time);
142559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
142659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
142759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        private void addPoint(float x, float y, long time) {
142859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int drop = -1;
142959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int i;
143059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final long[] pastTime = mPastTime;
143159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            for (i=0; i<NUM_PAST; i++) {
143259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (pastTime[i] == 0) {
143359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    break;
143459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
143559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    drop = i;
143659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
143759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
143859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (i == NUM_PAST && drop < 0) {
143959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                drop = 0;
144059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
144159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (drop == i) drop--;
144259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final float[] pastX = mPastX;
144359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final float[] pastY = mPastY;
144459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (drop >= 0) {
144559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final int start = drop+1;
144659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final int count = NUM_PAST-drop-1;
144759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                System.arraycopy(pastX, start, pastX, 0, count);
144859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                System.arraycopy(pastY, start, pastY, 0, count);
144959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                System.arraycopy(pastTime, start, pastTime, 0, count);
145059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                i -= (drop+1);
145159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
145259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            pastX[i] = x;
145359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            pastY[i] = y;
145459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            pastTime[i] = time;
145559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            i++;
145659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            if (i < NUM_PAST) {
145759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                pastTime[i] = 0;
145859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
145959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
146059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
146159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public void computeCurrentVelocity(int units) {
146259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            computeCurrentVelocity(units, Float.MAX_VALUE);
146359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
146459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
146559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public void computeCurrentVelocity(int units, float maxVelocity) {
146659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final float[] pastX = mPastX;
146759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final float[] pastY = mPastY;
146859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final long[] pastTime = mPastTime;
146959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
147059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final float oldestX = pastX[0];
147159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final float oldestY = pastY[0];
147259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            final long oldestTime = pastTime[0];
147359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            float accumX = 0;
147459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            float accumY = 0;
147559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            int N=0;
147659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            while (N < NUM_PAST) {
147759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (pastTime[N] == 0) {
147859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    break;
147959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                }
148059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                N++;
148159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
148259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
148359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            for (int i=1; i < N; i++) {
148459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                final int dur = (int)(pastTime[i] - oldestTime);
148559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (dur == 0) continue;
148659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                float dist = pastX[i] - oldestX;
148759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                float vel = (dist/dur) * units;
148859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (accumX == 0) accumX = vel;
148959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                else accumX = (accumX + vel) * .5f;
149059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
149159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                dist = pastY[i] - oldestY;
149259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                vel = (dist/dur) * units;
149359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                if (accumY == 0) accumY = vel;
149459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                else accumY = (accumY + vel) * .5f;
149559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            }
149659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
149759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    : Math.min(accumX, maxVelocity);
149859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
149959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma                    : Math.min(accumY, maxVelocity);
150059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
150159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
150259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public float getXVelocity() {
150359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return mXVelocity;
150459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
150559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
150659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        public float getYVelocity() {
150759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma            return mYVelocity;
150859aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        }
150959aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
151059aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma
151159aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    /**
151259aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     * Clear window info.
151359aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma     */
151459aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    public void clearWindowInfo() {
151559aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma        mOffsetInWindow = null;
151659aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma    }
151759aefa242169b7a51c2381daee58ff22fd1834ceJunichi Monma}
1518