SuddenJumpingTouchEventHandler.java revision 0ce98cbf98c6409ac18fa341f467703d78352a4c
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project *
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
7923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0
9923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project *
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
17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
194fc510a7890976d9968d73ceacf3983e77f489d2satokimport com.android.inputmethod.latin.BaseKeyboard.Key;
204fc510a7890976d9968d73ceacf3983e77f489d2satok
21923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
22923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.graphics.Canvas;
231b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasaniimport android.graphics.Paint;
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Handler;
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Message;
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.SystemClock;
2775c23ced94979a6b3f7c59e95dd46385e9702e2dKen Wakasaimport android.text.TextUtils;
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.AttributeSet;
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.MotionEvent;
30d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka
31d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaokaimport java.util.List;
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
33979f8690967ff5409fe18f5085858ccdb8e0ccf1satokpublic class LatinKeyboardView extends LatinKeyboardBaseView {
34923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
354189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    public static final int KEYCODE_OPTIONS = -100;
364189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    public static final int KEYCODE_OPTIONS_LONGPRESS = -101;
374189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    public static final int KEYCODE_VOICE = -102;
384189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    public static final int KEYCODE_F1 = -103;
394189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    public static final int KEYCODE_NEXT_LANGUAGE = -104;
404189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    public static final int KEYCODE_PREV_LANGUAGE = -105;
414189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka    public static final int KEYCODE_CAPSLOCK = -106;
428eb2e34d5b2def57c9548f88e37e5c9e5a0bea59Amith Yamasani
430ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    private LatinKeyboard mPhoneKeyboard;
44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
450fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /** Whether we've started dropping move events because we found a big jump */
460fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean mDroppingEvents;
47e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka    /**
48d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka     * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has
49d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka     * occured
500fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     */
510fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean mDisableDisambiguation;
520fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /** The distance threshold at which we start treating the touch session as a multi-touch */
530fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private int mJumpThresholdSquare = Integer.MAX_VALUE;
543e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani    /** The y coordinate of the last row */
553e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani    private int mLastRowY;
563f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani
57923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public LatinKeyboardView(Context context, AttributeSet attrs) {
58a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka        this(context, attrs, 0);
59923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
60923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
61923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
62923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super(context, attrs, defStyle);
63923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
64230cd6f7f27300e2688b5e5a22a5361f446b80e7Amith Yamasani
650ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    public void setPhoneKeyboard(LatinKeyboard phoneKeyboard) {
66923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mPhoneKeyboard = phoneKeyboard;
67923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
68230cd6f7f27300e2688b5e5a22a5361f446b80e7Amith Yamasani
69923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
703a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public void setPreviewEnabled(boolean previewEnabled) {
710ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        if (getLatinKeyboard() == mPhoneKeyboard) {
723a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka            // Phone keyboard never shows popup preview (except language switch).
733a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka            super.setPreviewEnabled(false);
743a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka        } else {
753a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka            super.setPreviewEnabled(previewEnabled);
763a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka        }
773a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    }
783a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka
790ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    public void setLatinKeyboard(LatinKeyboard k) {
800fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        super.setKeyboard(k);
810fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // One-seventh of the keyboard width seems like a reasonable threshold
820fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mJumpThresholdSquare = k.getMinWidth() / 7;
830fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mJumpThresholdSquare *= mJumpThresholdSquare;
843e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani        // Assuming there are 4 rows, this is the coordinate of the last row
853e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani        mLastRowY = (k.getHeight() * 3) / 4;
860fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        setKeyboardLocal(k);
870fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    }
880fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
890ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    public LatinKeyboard getLatinKeyboard() {
900ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        BaseKeyboard keyboard = getKeyboard();
910ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        if (keyboard instanceof LatinKeyboard) {
920ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa            return (LatinKeyboard)keyboard;
930ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        } else {
940ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa            return null;
950ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        }
960ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    }
970ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa
980fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    @Override
99923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    protected boolean onLongPress(Key key) {
100e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka        int primaryCode = key.codes[0];
101e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka        if (primaryCode == KEYCODE_OPTIONS) {
102a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka            return invokeOnKey(KEYCODE_OPTIONS_LONGPRESS);
1030ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        } else if (primaryCode == '0' && getLatinKeyboard() == mPhoneKeyboard) {
104923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // Long pressing on 0 in phone number keypad gives you a '+'.
105a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka            return invokeOnKey('+');
106923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return super.onLongPress(key);
108923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
109923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
110923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
111a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka    private boolean invokeOnKey(int primaryCode) {
112a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka        getOnKeyboardActionListener().onKey(primaryCode, null,
113a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka                LatinKeyboardBaseView.NOT_A_TOUCH_COORDINATE,
114a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka                LatinKeyboardBaseView.NOT_A_TOUCH_COORDINATE);
115a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka        return true;
116a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka    }
117a1cc4f0a8d9a70ff1515d1ddb1476f6ce630afe2Tadashi G. Takaoka
118979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
119979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    protected CharSequence adjustCase(CharSequence label) {
1200ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        LatinKeyboard keyboard = getLatinKeyboard();
1210ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        if (keyboard.isAlphaKeyboard()
12266e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka                && keyboard.isShifted()
12375c23ced94979a6b3f7c59e95dd46385e9702e2dKen Wakasa                && !TextUtils.isEmpty(label) && label.length() < 3
124979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                && Character.isLowerCase(label.charAt(0))) {
125979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            label = label.toString().toUpperCase();
126979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
127979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return label;
128979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
129979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
130979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public boolean setShiftLocked(boolean shiftLocked) {
1310ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        LatinKeyboard keyboard = getLatinKeyboard();
1320ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        keyboard.setShiftLocked(shiftLocked);
1330ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        invalidateAllKeys();
1340ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        return true;
135979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
136979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1370fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /**
1380fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * This function checks to see if we need to handle any sudden jumps in the pointer location
1390fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * that could be due to a multi-touch being treated as a move by the firmware or hardware.
1400fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * Once a sudden jump is detected, all subsequent move events are discarded
1410fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * until an UP is received.<P>
142e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka     * When a sudden jump is detected, an UP event is simulated at the last position and when
1430fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * the sudden moves subside, a DOWN event is simulated for the second key.
1440fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * @param me the motion event
145e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka     * @return true if the event was consumed, so that it doesn't continue to be handled by
1460fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * KeyboardView.
1470fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     */
1480fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean handleSuddenJump(MotionEvent me) {
1490fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int action = me.getAction();
1500fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int x = (int) me.getX();
1510fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int y = (int) me.getY();
1520fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        boolean result = false;
1530fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
1540fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // Real multi-touch event? Stop looking for sudden jumps
1550fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        if (me.getPointerCount() > 1) {
1560fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDisableDisambiguation = true;
1570fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
1580fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        if (mDisableDisambiguation) {
1590fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // If UP, reset the multi-touch flag
1600fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false;
1610fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            return false;
1620fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
1630fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
1640fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        switch (action) {
1650fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_DOWN:
1660fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // Reset the "session"
1670fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDroppingEvents = false;
1680fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDisableDisambiguation = false;
1690fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1700fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_MOVE:
1710fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // Is this a big jump?
1720fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
1733e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            // Check the distance and also if the move is not entirely within the bottom row
1743e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            // If it's only in the bottom row, it might be an intentional slide gesture
1753e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            // for language switching
1763e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani            if (distanceSquare > mJumpThresholdSquare
1773e0c82ec80a69c4adbd60546c3c56c83c43ec7ebAmith Yamasani                    && (mLastY < mLastRowY || y < mLastRowY)) {
1780fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // If we're not yet dropping events, start dropping and send an UP event
1790fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                if (!mDroppingEvents) {
1800fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    mDroppingEvents = true;
1810fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    // Send an up event
1820fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
1830fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                            MotionEvent.ACTION_UP,
1840fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                            mLastX, mLastY, me.getMetaState());
1850fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    super.onTouchEvent(translated);
1860fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    translated.recycle();
1870fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                }
1880fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                result = true;
1890fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            } else if (mDroppingEvents) {
1900fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // If moves are small and we're already dropping events, continue dropping
1910fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                result = true;
1920fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            }
1930fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1940fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_UP:
1950fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            if (mDroppingEvents) {
1960fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // Send a down event first, as we dropped a bunch of sudden jumps and assume that
1970fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // the user is releasing the touch on the second key.
1980fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
1990fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                        MotionEvent.ACTION_DOWN,
2000fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                        x, y, me.getMetaState());
2010fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                super.onTouchEvent(translated);
2020fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                translated.recycle();
2030fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                mDroppingEvents = false;
2040fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // Let the up event get processed as well, result = false
2050fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            }
2060fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
2070fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
2080fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // Track the previous coordinate
2090fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mLastX = x;
2100fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mLastY = y;
2110fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        return result;
2120fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    }
2130fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
214358e485b14938fbcb5af5be75aa29f2b73674100Amith Yamasani    @Override
215358e485b14938fbcb5af5be75aa29f2b73674100Amith Yamasani    public boolean onTouchEvent(MotionEvent me) {
2160ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        LatinKeyboard keyboard = getLatinKeyboard();
2171b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        if (DEBUG_LINE) {
2181b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            mLastX = (int) me.getX();
2191b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            mLastY = (int) me.getY();
2201b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            invalidate();
2211b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        }
222938c178215d38c6f085b32b0994598f9e8bc5ab5Amith Yamasani
223d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka        // If there was a sudden jump, return without processing the actual motion event.
224d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka        if (handleSuddenJump(me))
225d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka            return true;
226d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka
2273f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        // Reset any bounding box controls in the keyboard
2283f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        if (me.getAction() == MotionEvent.ACTION_DOWN) {
2293f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            keyboard.keyReleased();
2303f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        }
2313f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani
2323f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        if (me.getAction() == MotionEvent.ACTION_UP) {
2333f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            int languageDirection = keyboard.getLanguageChangeDirection();
2343f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            if (languageDirection != 0) {
2353f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                getOnKeyboardActionListener().onKey(
2363f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                        languageDirection == 1 ? KEYCODE_NEXT_LANGUAGE : KEYCODE_PREV_LANGUAGE,
237542f057ef64ae16cf2bc528880cf32bfd073edadsatok                        null, mLastX, mLastY);
2383f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                me.setAction(MotionEvent.ACTION_CANCEL);
2393f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                keyboard.keyReleased();
2403f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani                return super.onTouchEvent(me);
2413f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani            }
2423f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        }
2433f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani
244d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka        return super.onTouchEvent(me);
245bad436e93b49116f9005433845bf53126452a839Amith Yamasani    }
246bad436e93b49116f9005433845bf53126452a839Amith Yamasani
247923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /****************************  INSTRUMENTATION  *******************************/
248923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
249923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    static final boolean DEBUG_AUTO_PLAY = false;
2501b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani    static final boolean DEBUG_LINE = false;
251923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int MSG_TOUCH_DOWN = 1;
252923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int MSG_TOUCH_UP = 2;
253e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
254923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    Handler mHandler2;
255e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
256923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private String mStringToPlay;
257923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mStringIndex;
258923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private boolean mDownDelivered;
259923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private Key[] mAsciiKeys = new Key[256];
260923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private boolean mPlaying;
2611b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani    private int mLastX;
2621b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani    private int mLastY;
2631b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani    private Paint mPaint;
264923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2650ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    private void setKeyboardLocal(LatinKeyboard k) {
266923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (DEBUG_AUTO_PLAY) {
267923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            findKeys();
268923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (mHandler2 == null) {
269923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mHandler2 = new Handler() {
270923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    @Override
271923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    public void handleMessage(Message msg) {
272923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        removeMessages(MSG_TOUCH_DOWN);
273923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        removeMessages(MSG_TOUCH_UP);
274923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        if (mPlaying == false) return;
275e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
276923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        switch (msg.what) {
277923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                            case MSG_TOUCH_DOWN:
278923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                if (mStringIndex >= mStringToPlay.length()) {
279923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    mPlaying = false;
280923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    return;
281923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                }
282923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                char c = mStringToPlay.charAt(mStringIndex);
283e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                while (c > 255 || mAsciiKeys[c] == null) {
284923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    mStringIndex++;
285923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    if (mStringIndex >= mStringToPlay.length()) {
286923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        mPlaying = false;
287923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        return;
288923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    }
289923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                    c = mStringToPlay.charAt(mStringIndex);
290923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                }
291923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int x = mAsciiKeys[c].x + 10;
292923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int y = mAsciiKeys[c].y + 26;
293e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(),
294e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                        SystemClock.uptimeMillis(),
295923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        MotionEvent.ACTION_DOWN, x, y, 0);
296923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                LatinKeyboardView.this.dispatchTouchEvent(me);
297923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                me.recycle();
298923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                sendEmptyMessageDelayed(MSG_TOUCH_UP, 500); // Deliver up in 500ms if nothing else
299923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                // happens
300923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                mDownDelivered = true;
301923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                break;
302923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                            case MSG_TOUCH_UP:
303923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                char cUp = mStringToPlay.charAt(mStringIndex);
304923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int x2 = mAsciiKeys[cUp].x + 10;
305923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                int y2 = mAsciiKeys[cUp].y + 26;
306923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                mStringIndex++;
307e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka
308e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                MotionEvent me2 = MotionEvent.obtain(SystemClock.uptimeMillis(),
309e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka                                        SystemClock.uptimeMillis(),
310923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                        MotionEvent.ACTION_UP, x2, y2, 0);
311923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                LatinKeyboardView.this.dispatchTouchEvent(me2);
312923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                me2.recycle();
313923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 500); // Deliver up in 500ms if nothing else
314923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                // happens
315923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                mDownDelivered = false;
316923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                                break;
317923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                        }
318923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                    }
319923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                };
320923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
321923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
322923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
323923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
324923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
325923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void findKeys() {
3260ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa        List<Key> keys = getLatinKeyboard().getKeys();
327923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Get the keys on this keyboard
328923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        for (int i = 0; i < keys.size(); i++) {
329923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            int code = keys.get(i).codes[0];
330e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            if (code >= 0 && code <= 255) {
331923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                mAsciiKeys[code] = keys.get(i);
332923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
333923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
334923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
335979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
336979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void startPlaying(String s) {
337979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG_AUTO_PLAY) {
338979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (s == null) return;
339979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mStringToPlay = s.toLowerCase();
340979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mPlaying = true;
341979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mDownDelivered = false;
342979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mStringIndex = 0;
343979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10);
344979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
345923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
346923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
347923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
348923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void draw(Canvas c) {
349979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinIMEUtil.GCUtils.getInstance().reset();
350979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        boolean tryGC = true;
351979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
352979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            try {
353979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                super.draw(c);
354979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = false;
355979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            } catch (OutOfMemoryError e) {
356979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e);
357979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
358979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
359979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG_AUTO_PLAY) {
360979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (mPlaying) {
361979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mHandler2.removeMessages(MSG_TOUCH_DOWN);
362979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mHandler2.removeMessages(MSG_TOUCH_UP);
363979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if (mDownDelivered) {
364979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20);
365979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                } else {
366979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20);
367979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
3701b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        if (DEBUG_LINE) {
3711b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            if (mPaint == null) {
3721b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani                mPaint = new Paint();
3731b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani                mPaint.setColor(0x80FFFFFF);
3741b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani                mPaint.setAntiAlias(false);
3751b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            }
3761b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            c.drawLine(mLastX, 0, mLastX, getHeight(), mPaint);
3771b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani            c.drawLine(0, mLastY, getWidth(), mLastY, mPaint);
3781b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani        }
379923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
380923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
381