1b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka/* 2b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * Copyright (C) 2013 The Android Open Source Project 3b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * 4b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 5b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * you may not use this file except in compliance with the License. 6b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * You may obtain a copy of the License at 7b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * 8b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * 10b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 11b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 12b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * See the License for the specific language governing permissions and 14b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka * limitations under the License. 15b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka */ 16b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 17b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokapackage com.android.inputmethod.keyboard.internal; 18b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 19b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport android.util.Log; 20b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport android.view.MotionEvent; 21b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 22b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Key; 232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.keyboard.KeyDetector; 24b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker; 25b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.CoordinateUtils; 26b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 27b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokapublic final class NonDistinctMultitouchHelper { 28b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName(); 29b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 30921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka private static final int MAIN_POINTER_TRACKER_ID = 0; 31b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka private int mOldPointerCount = 1; 32b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka private Key mOldKey; 33e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka private int[] mLastCoords = CoordinateUtils.newInstance(); 34b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) { 36b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka final int pointerCount = me.getPointerCount(); 37b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka final int oldPointerCount = mOldPointerCount; 38b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka mOldPointerCount = pointerCount; 39e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Ignore continuous multi-touch events because we can't trust the coordinates 40e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // in multi-touch events. 41b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka if (pointerCount > 1 && oldPointerCount > 1) { 42b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka return; 43b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka } 44b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 45921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka // Use only main pointer tracker. 46921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka final PointerTracker mainTracker = PointerTracker.getPointerTracker( 47921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka MAIN_POINTER_TRACKER_ID); 48b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka final int action = me.getActionMasked(); 49b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka final int index = me.getActionIndex(); 50b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka final long eventTime = me.getEventTime(); 51e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka final long downTime = me.getDownTime(); 52b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 53e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // In single-touch. 54b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka if (oldPointerCount == 1 && pointerCount == 1) { 55e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka if (me.getPointerId(index) == mainTracker.mPointerId) { 562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa mainTracker.processMotionEvent(me, keyDetector); 57e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka return; 58e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka } 59e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Inject a copied event. 60e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka injectMotionEvent(action, me.getX(index), me.getY(index), downTime, eventTime, 612fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa mainTracker, keyDetector); 62b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka return; 63b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka } 64b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 65b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka // Single-touch to multi-touch transition. 66b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka if (oldPointerCount == 1 && pointerCount == 2) { 67e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Send an up event for the last pointer, be cause we can't trust the coordinates of 68e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // this multi-touch event. 69e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka mainTracker.getLastCoordinates(mLastCoords); 70e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka final int x = CoordinateUtils.x(mLastCoords); 71e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka final int y = CoordinateUtils.y(mLastCoords); 72e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka mOldKey = mainTracker.getKeyOn(x, y); 73e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Inject an artifact up event for the old key. 74e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime, 752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa mainTracker, keyDetector); 76b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka return; 77b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka } 78b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 79e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Multi-touch to single-touch transition. 80b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka if (oldPointerCount == 2 && pointerCount == 1) { 81e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Send a down event for the latest pointer if the key is different from the previous 82e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // key. 83e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka final int x = (int)me.getX(index); 84e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka final int y = (int)me.getY(index); 85b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka final Key newKey = mainTracker.getKeyOn(x, y); 86b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka if (mOldKey != newKey) { 87e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Inject an artifact down event for the new key. 88e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // An artifact up event for the new key will usually be injected as a single-touch. 89e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka injectMotionEvent(MotionEvent.ACTION_DOWN, x, y, downTime, eventTime, 902fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa mainTracker, keyDetector); 91b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka if (action == MotionEvent.ACTION_UP) { 92e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka // Inject an artifact up event for the new key also. 93e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime, 942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa mainTracker, keyDetector); 95b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka } 96b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka } 97b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka return; 98b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka } 99b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka 100b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka Log.w(TAG, "Unknown touch panel behavior: pointer count is " 101b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka + pointerCount + " (previously " + oldPointerCount + ")"); 102b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka } 103e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka 104e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka private static void injectMotionEvent(final int action, final float x, final float y, 105e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka final long downTime, final long eventTime, final PointerTracker tracker, 1062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final KeyDetector keyDetector) { 107e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka final MotionEvent me = MotionEvent.obtain( 108e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka downTime, eventTime, action, x, y, 0 /* metaState */); 109e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka try { 1102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa tracker.processMotionEvent(me, keyDetector); 111e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka } finally { 112e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka me.recycle(); 113e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka } 114e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka } 115b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka} 116