SuddenJumpingTouchEventHandler.java revision 5a309f57155fb95667c2ccdda730eaf175de8876
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
35a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of
6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at
75a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0
95a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under
14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License.
15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIMEUtil;
201fb28137f43ae083c773c32440981ac61e83fa5dsatokimport com.android.inputmethod.voice.VoiceIMEConnector;
214fc510a7890976d9968d73ceacf3983e77f489d2satok
22923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.graphics.Canvas;
241b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasaniimport android.graphics.Paint;
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Handler;
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Message;
27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.SystemClock;
2875c23ced94979a6b3f7c59e95dd46385e9702e2dKen Wakasaimport android.text.TextUtils;
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.AttributeSet;
30923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.MotionEvent;
31d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka
32d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaokaimport java.util.List;
33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
345a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapublic class LatinKeyboardView extends KeyboardView {
358eb2e34d5b2def57c9548f88e37e5c9e5a0bea59Amith Yamasani
360fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /** Whether we've started dropping move events because we found a big jump */
370fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean mDroppingEvents;
38e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka    /**
39d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka     * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has
40d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka     * occured
410fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     */
420fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean mDisableDisambiguation;
430fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /** The distance threshold at which we start treating the touch session as a multi-touch */
440fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private int mJumpThresholdSquare = Integer.MAX_VALUE;
453e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani    /** The y coordinate of the last row */
463e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani    private int mLastRowY;
473f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani
48923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public LatinKeyboardView(Context context, AttributeSet attrs) {
49a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka        this(context, attrs, 0);
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
51923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
52923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
53923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super(context, attrs, defStyle);
54923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
55230cd6f7f27300e2688b5e5a22a5361f446b80e7Amith Yamasani
56923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
573a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public void setPreviewEnabled(boolean previewEnabled) {
582c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
592c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka        if (latinKeyboard != null
602c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) {
612c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            // Phone and number keyboard never shows popup preview (except language switch).
623a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka            super.setPreviewEnabled(false);
633a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka        } else {
643a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka            super.setPreviewEnabled(previewEnabled);
653a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka        }
663a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    }
673a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka
680ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    public void setLatinKeyboard(LatinKeyboard k) {
690fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        super.setKeyboard(k);
700fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // One-seventh of the keyboard width seems like a reasonable threshold
710fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mJumpThresholdSquare = k.getMinWidth() / 7;
720fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mJumpThresholdSquare *= mJumpThresholdSquare;
733e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani        // Assuming there are 4 rows, this is the coordinate of the last row
743e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani        mLastRowY = (k.getHeight() * 3) / 4;
750fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        setKeyboardLocal(k);
760fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    }
770fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
780ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    public LatinKeyboard getLatinKeyboard() {
795a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka        Keyboard keyboard = getKeyboard();
800ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        if (keyboard instanceof LatinKeyboard) {
810ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa            return (LatinKeyboard)keyboard;
820ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        } else {
830ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa            return null;
840ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        }
850ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    }
860ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa
870fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    @Override
88923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    protected boolean onLongPress(Key key) {
89e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka        int primaryCode = key.codes[0];
905a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka        if (primaryCode == LatinKeyboard.KEYCODE_OPTIONS) {
915a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            return invokeOnKey(LatinKeyboard.KEYCODE_OPTIONS_LONGPRESS);
922c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka        } else if (primaryCode == '0' && getLatinKeyboard().isPhoneKeyboard()) {
93923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // Long pressing on 0 in phone number keypad gives you a '+'.
94a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka            return invokeOnKey('+');
95923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
96923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return super.onLongPress(key);
97923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
98923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
99923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
100a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka    private boolean invokeOnKey(int primaryCode) {
101a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka        getOnKeyboardActionListener().onKey(primaryCode, null,
1025a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka                KeyboardView.NOT_A_TOUCH_COORDINATE,
1035a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka                KeyboardView.NOT_A_TOUCH_COORDINATE);
104a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka        return true;
105a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka    }
106a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka
107979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
108979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    protected CharSequence adjustCase(CharSequence label) {
1090ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        LatinKeyboard keyboard = getLatinKeyboard();
1100ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        if (keyboard.isAlphaKeyboard()
111f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                && keyboard.isShiftedOrShiftLocked()
11275c23ced94979a6b3f7c59e95dd46385e9702e2dKen Wakasa                && !TextUtils.isEmpty(label) && label.length() < 3
113979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                && Character.isLowerCase(label.charAt(0))) {
114979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            label = label.toString().toUpperCase();
115979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
116979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return label;
117979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
118979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1190fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /**
1200fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * This function checks to see if we need to handle any sudden jumps in the pointer location
1210fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * that could be due to a multi-touch being treated as a move by the firmware or hardware.
1220fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * Once a sudden jump is detected, all subsequent move events are discarded
1230fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * until an UP is received.<P>
124e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka     * When a sudden jump is detected, an UP event is simulated at the last position and when
1250fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * the sudden moves subside, a DOWN event is simulated for the second key.
1260fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * @param me the motion event
127e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka     * @return true if the event was consumed, so that it doesn't continue to be handled by
1280fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * KeyboardView.
1290fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     */
1300fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean handleSuddenJump(MotionEvent me) {
1310fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int action = me.getAction();
1320fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int x = (int) me.getX();
1330fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int y = (int) me.getY();
1340fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        boolean result = false;
1350fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
1360fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // Real multi-touch event? Stop looking for sudden jumps
1370fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        if (me.getPointerCount() > 1) {
1380fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDisableDisambiguation = true;
1390fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
1400fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        if (mDisableDisambiguation) {
1410fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // If UP, reset the multi-touch flag
1420fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false;
1430fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            return false;
1440fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
1450fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
1460fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        switch (action) {
1470fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_DOWN:
1480fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // Reset the "session"
1490fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDroppingEvents = false;
1500fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDisableDisambiguation = false;
1510fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1520fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_MOVE:
1530fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // Is this a big jump?
1540fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
1553e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            // Check the distance and also if the move is not entirely within the bottom row
1563e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            // If it's only in the bottom row, it might be an intentional slide gesture
1573e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            // for language switching
1583e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            if (distanceSquare > mJumpThresholdSquare
1593e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani                    && (mLastY < mLastRowY || y < mLastRowY)) {
1600fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // If we're not yet dropping events, start dropping and send an UP event
1610fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                if (!mDroppingEvents) {
1620fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    mDroppingEvents = true;
1630fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    // Send an up event
1640fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
1650fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                            MotionEvent.ACTION_UP,
1660fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                            mLastX, mLastY, me.getMetaState());
1670fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    super.onTouchEvent(translated);
1680fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    translated.recycle();
1690fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                }
1700fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                result = true;
1710fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            } else if (mDroppingEvents) {
1720fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // If moves are small and we're already dropping events, continue dropping
1730fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                result = true;
1740fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            }
1750fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1760fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_UP:
1770fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            if (mDroppingEvents) {
1780fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // Send a down event first, as we dropped a bunch of sudden jumps and assume that
1790fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // the user is releasing the touch on the second key.
1800fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
1810fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                        MotionEvent.ACTION_DOWN,
1820fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                        x, y, me.getMetaState());
1830fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                super.onTouchEvent(translated);
1840fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                translated.recycle();
1850fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                mDroppingEvents = false;
1860fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // Let the up event get processed as well, result = false
1870fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            }
1880fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1890fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
1900fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // Track the previous coordinate
1910fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mLastX = x;
1920fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mLastY = y;
1930fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        return result;
1940fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    }
1950fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
196358e485b14938fbcb5af5be75aa29f2b73674100Amith Yamasani    @Override
197358e485b14938fbcb5af5be75aa29f2b73674100Amith Yamasani    public boolean onTouchEvent(MotionEvent me) {
1980ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        LatinKeyboard keyboard = getLatinKeyboard();
1991b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        if (DEBUG_LINE) {
2001b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            mLastX = (int) me.getX();
2011b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            mLastY = (int) me.getY();
2021b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            invalidate();
2031b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        }
204938c178215d38c6f085b32b0994598f9e8bc5ab5Amith Yamasani
205d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka        // If there was a sudden jump, return without processing the actual motion event.
206d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka        if (handleSuddenJump(me))
207d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka            return true;
208d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka
2093f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        // Reset any bounding box controls in the keyboard
2103f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        if (me.getAction() == MotionEvent.ACTION_DOWN) {
2113f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            keyboard.keyReleased();
2123f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        }
2133f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani
2143f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        if (me.getAction() == MotionEvent.ACTION_UP) {
2153f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            int languageDirection = keyboard.getLanguageChangeDirection();
2163f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            if (languageDirection != 0) {
2173f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                getOnKeyboardActionListener().onKey(
2185a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka                        languageDirection == 1
2195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka                        ? LatinKeyboard.KEYCODE_NEXT_LANGUAGE : LatinKeyboard.KEYCODE_PREV_LANGUAGE,
220542f057ef64ae16cf2bc528880cf32bfd073edadsatok                        null, mLastX, mLastY);
2213f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                me.setAction(MotionEvent.ACTION_CANCEL);
2223f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                keyboard.keyReleased();
2233f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                return super.onTouchEvent(me);
2243f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            }
2253f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        }
2263f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani
227d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka        return super.onTouchEvent(me);
228bad436e93b49116f9005433845bf53126452a839Amith Yamasani    }
229bad436e93b49116f9005433845bf53126452a839Amith Yamasani
230923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /****************************  INSTRUMENTATION  *******************************/
231923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2325a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    public static final boolean DEBUG_AUTO_PLAY = false;
2335a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    public static final boolean DEBUG_LINE = false;
234923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int MSG_TOUCH_DOWN = 1;
235923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int MSG_TOUCH_UP = 2;
236e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
237923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    Handler mHandler2;
238e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
239923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private String mStringToPlay;
240923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mStringIndex;
241923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private boolean mDownDelivered;
242923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private Key[] mAsciiKeys = new Key[256];
243923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private boolean mPlaying;
2441b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani    private int mLastX;
2451b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani    private int mLastY;
2461b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani    private Paint mPaint;
247923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2480ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    private void setKeyboardLocal(LatinKeyboard k) {
249923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (DEBUG_AUTO_PLAY) {
250923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            findKeys();
251923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (mHandler2 == null) {
252923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mHandler2 = new Handler() {
253923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    @Override
254923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    public void handleMessage(Message msg) {
255923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        removeMessages(MSG_TOUCH_DOWN);
256923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        removeMessages(MSG_TOUCH_UP);
257923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        if (mPlaying == false) return;
258e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
259923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        switch (msg.what) {
260923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                            case MSG_TOUCH_DOWN:
261923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                if (mStringIndex >= mStringToPlay.length()) {
262923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    mPlaying = false;
263923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    return;
264923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                }
265923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                char c = mStringToPlay.charAt(mStringIndex);
266e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                while (c > 255 || mAsciiKeys[c] == null) {
267923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    mStringIndex++;
268923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    if (mStringIndex >= mStringToPlay.length()) {
269923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        mPlaying = false;
270923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        return;
271923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    }
272923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    c = mStringToPlay.charAt(mStringIndex);
273923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                }
274923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int x = mAsciiKeys[c].x + 10;
275923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int y = mAsciiKeys[c].y + 26;
276e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(),
277e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                        SystemClock.uptimeMillis(),
278923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        MotionEvent.ACTION_DOWN, x, y, 0);
279923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                LatinKeyboardView.this.dispatchTouchEvent(me);
280923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                me.recycle();
281923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                sendEmptyMessageDelayed(MSG_TOUCH_UP, 500); // Deliver up in 500ms if nothing else
282923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                // happens
283923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                mDownDelivered = true;
284923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                break;
285923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                            case MSG_TOUCH_UP:
286923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                char cUp = mStringToPlay.charAt(mStringIndex);
287923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int x2 = mAsciiKeys[cUp].x + 10;
288923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int y2 = mAsciiKeys[cUp].y + 26;
289923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                mStringIndex++;
290e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
291e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                MotionEvent me2 = MotionEvent.obtain(SystemClock.uptimeMillis(),
292e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                        SystemClock.uptimeMillis(),
293923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        MotionEvent.ACTION_UP, x2, y2, 0);
294923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                LatinKeyboardView.this.dispatchTouchEvent(me2);
295923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                me2.recycle();
296923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 500); // Deliver up in 500ms if nothing else
297923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                // happens
298923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                mDownDelivered = false;
299923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                break;
300923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        }
301923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    }
302923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                };
303923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
304923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
305923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
306923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
307923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
308923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void findKeys() {
3090ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        List<Key> keys = getLatinKeyboard().getKeys();
310923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Get the keys on this keyboard
311923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        for (int i = 0; i < keys.size(); i++) {
312923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            int code = keys.get(i).codes[0];
313e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            if (code >= 0 && code <= 255) {
314923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mAsciiKeys[code] = keys.get(i);
315923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
316923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
317923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
318979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
319979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void startPlaying(String s) {
320979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG_AUTO_PLAY) {
321979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (s == null) return;
322979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mStringToPlay = s.toLowerCase();
323979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mPlaying = true;
324979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mDownDelivered = false;
325979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mStringIndex = 0;
326979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10);
327979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
328923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
329923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
330923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
331923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void draw(Canvas c) {
332979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinIMEUtil.GCUtils.getInstance().reset();
333979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        boolean tryGC = true;
334979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
335979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            try {
336979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                super.draw(c);
337979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = false;
338979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            } catch (OutOfMemoryError e) {
339979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e);
340979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
341979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
342979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG_AUTO_PLAY) {
343979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (mPlaying) {
344979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mHandler2.removeMessages(MSG_TOUCH_DOWN);
345979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mHandler2.removeMessages(MSG_TOUCH_UP);
346979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if (mDownDelivered) {
347979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20);
348979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                } else {
349979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20);
350979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
351923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
352923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
3531b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        if (DEBUG_LINE) {
3541b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            if (mPaint == null) {
3551b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani                mPaint = new Paint();
3561b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani                mPaint.setColor(0x80FFFFFF);
3571b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani                mPaint.setAntiAlias(false);
3581b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            }
3591b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            c.drawLine(mLastX, 0, mLastX, getHeight(), mPaint);
3601b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            c.drawLine(0, mLastY, getWidth(), mLastY, mPaint);
3611b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        }
362923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
3631fb28137f43ae083c773c32440981ac61e83fa5dsatok
3641fb28137f43ae083c773c32440981ac61e83fa5dsatok    @Override
3651fb28137f43ae083c773c32440981ac61e83fa5dsatok    protected void onAttachedToWindow() {
3661fb28137f43ae083c773c32440981ac61e83fa5dsatok        // Token is available from here.
3671fb28137f43ae083c773c32440981ac61e83fa5dsatok        VoiceIMEConnector.getInstance().onAttachedToWindow();
3681fb28137f43ae083c773c32440981ac61e83fa5dsatok    }
369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
370