SuddenJumpingTouchEventHandler.java revision c403a46f6d787b79768895272d53d296100677dd
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka * Copyright (C) 2011 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
19923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
20c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaokaimport android.os.Build;
21faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaokaimport android.util.Log;
22923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.MotionEvent;
23d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka
2413a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
25c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaokaimport com.android.inputmethod.latin.R;
2613a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka
27c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaokapublic class SuddenJumpingTouchEventHandler {
28c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
29faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka    private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
308eb2e34d5b2def57c9548f88e37e5c9e5a0bea59Amith Yamasani
31c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    public interface ProcessMotionEvent {
32c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        public boolean processMotionEvent(MotionEvent me);
33c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
34c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
35c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    private final ProcessMotionEvent mView;
36c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    private final boolean mNeedsSuddenJumpingHack;
37c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
380fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /** Whether we've started dropping move events because we found a big jump */
390fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean mDroppingEvents;
40e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka    /**
41d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka     * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has
42d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka     * occured
430fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     */
440fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private boolean mDisableDisambiguation;
450fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /** The distance threshold at which we start treating the touch session as a multi-touch */
460fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    private int mJumpThresholdSquare = Integer.MAX_VALUE;
47e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka    private int mLastX;
48e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka    private int mLastY;
493f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani
50c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    public SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view) {
51c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mView = view;
52c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        final String[] deviceList = context.getResources().getStringArray(
53c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka                R.array.sudden_jumping_touch_event_device_list);
54c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.DEVICE, deviceList);
55923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
56923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
57c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    private static boolean needsSuddenJumpingHack(String deviceName, String[] deviceList) {
58c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        for (String device : deviceList) {
59c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka            if (device.equalsIgnoreCase(deviceName)) {
60c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka                return true;
61c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka            }
62c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        }
63c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return false;
64923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
65230cd6f7f27300e2688b5e5a22a5361f446b80e7Amith Yamasani
66050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka    public void setKeyboard(Keyboard newKeyboard) {
670fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // One-seventh of the keyboard width seems like a reasonable threshold
688da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
69851c3267d4ab21f892b4164783bb4959c88b44ffTadashi G. Takaoka        mJumpThresholdSquare = jumpThreshold * jumpThreshold;
700ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa    }
710ce98cbf98c6409ac18fa341f467703d78352a4cKen Wakasa
720fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    /**
730fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * This function checks to see if we need to handle any sudden jumps in the pointer location
740fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * that could be due to a multi-touch being treated as a move by the firmware or hardware.
750fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * Once a sudden jump is detected, all subsequent move events are discarded
760fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * until an UP is received.<P>
77e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka     * When a sudden jump is detected, an UP event is simulated at the last position and when
780fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * the sudden moves subside, a DOWN event is simulated for the second key.
790fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     * @param me the motion event
80e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka     * @return true if the event was consumed, so that it doesn't continue to be handled by
81ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka     * {@link LatinKeyboardBaseView}.
820fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani     */
83c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    private boolean handleSuddenJumping(MotionEvent me) {
84c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        if (!mNeedsSuddenJumpingHack)
859a5b592b27158b6fb8b7a89157bb995b182899d8Tadashi G. Takaoka            return false;
860fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int action = me.getAction();
870fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int x = (int) me.getX();
880fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        final int y = (int) me.getY();
890fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        boolean result = false;
900fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
910fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // Real multi-touch event? Stop looking for sudden jumps
920fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        if (me.getPointerCount() > 1) {
930fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDisableDisambiguation = true;
940fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
950fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        if (mDisableDisambiguation) {
960fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // If UP, reset the multi-touch flag
970fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false;
980fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            return false;
990fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
1000fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
1010fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        switch (action) {
1020fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_DOWN:
1030fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // Reset the "session"
1040fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDroppingEvents = false;
1050fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            mDisableDisambiguation = false;
1060fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1070fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_MOVE:
1080fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            // Is this a big jump?
1090fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
110ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka            // Check the distance.
111ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka            if (distanceSquare > mJumpThresholdSquare) {
1120fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // If we're not yet dropping events, start dropping and send an UP event
1130fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                if (!mDroppingEvents) {
1140fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    mDroppingEvents = true;
1150fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    // Send an up event
116240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard                    MotionEvent translated = MotionEvent.obtain(
117240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard                            me.getEventTime(), me.getEventTime(),
1180fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                            MotionEvent.ACTION_UP,
1190fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                            mLastX, mLastY, me.getMetaState());
120c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka                    mView.processMotionEvent(translated);
1210fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                    translated.recycle();
1220fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                }
1230fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                result = true;
1240fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            } else if (mDroppingEvents) {
1250fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // If moves are small and we're already dropping events, continue dropping
1260fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                result = true;
1270fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            }
1280fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1290fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        case MotionEvent.ACTION_UP:
1300fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            if (mDroppingEvents) {
1310fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // Send a down event first, as we dropped a bunch of sudden jumps and assume that
1320fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // the user is releasing the touch on the second key.
1330fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
1340fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                        MotionEvent.ACTION_DOWN,
1350fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                        x, y, me.getMetaState());
136c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka                mView.processMotionEvent(translated);
1370fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                translated.recycle();
1380fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                mDroppingEvents = false;
1390fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani                // Let the up event get processed as well, result = false
1400fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            }
1410fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani            break;
1420fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        }
1430fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        // Track the previous coordinate
1440fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mLastX = x;
1450fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        mLastY = y;
1460fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani        return result;
1470fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani    }
1480fef498a07515bdd5ac1ccfa564776d72fd85a51Amith Yamasani
149358e485b14938fbcb5af5be75aa29f2b73674100Amith Yamasani    public boolean onTouchEvent(MotionEvent me) {
150d2a431efa726771dee5c7b90004a0ed670d9a129Tadashi G. Takaoka        // If there was a sudden jump, return without processing the actual motion event.
151c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        if (handleSuddenJumping(me)) {
152faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka            if (DEBUG_MODE)
153faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                Log.w(TAG, "onTouchEvent: ignore sudden jump " + me);
154faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka            return true;
155faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka        }
156c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return mView.processMotionEvent(me);
157bad436e93b49116f9005433845bf53126452a839Amith Yamasani    }
158923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
159