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