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.KeyDetector;
24 import com.android.inputmethod.keyboard.PointerTracker;
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 static final int MAIN_POINTER_TRACKER_ID = 0;
31     private int mOldPointerCount = 1;
32     private Key mOldKey;
33     private int[] mLastCoords = CoordinateUtils.newInstance();
34 
processMotionEvent(final MotionEvent me, final KeyDetector keyDetector)35     public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) {
36         final int pointerCount = me.getPointerCount();
37         final int oldPointerCount = mOldPointerCount;
38         mOldPointerCount = pointerCount;
39         // Ignore continuous multi-touch events because we can't trust the coordinates
40         // in multi-touch events.
41         if (pointerCount > 1 && oldPointerCount > 1) {
42             return;
43         }
44 
45         // Use only main pointer tracker.
46         final PointerTracker mainTracker = PointerTracker.getPointerTracker(
47                 MAIN_POINTER_TRACKER_ID);
48         final int action = me.getActionMasked();
49         final int index = me.getActionIndex();
50         final long eventTime = me.getEventTime();
51         final long downTime = me.getDownTime();
52 
53         // In single-touch.
54         if (oldPointerCount == 1 && pointerCount == 1) {
55             if (me.getPointerId(index) == mainTracker.mPointerId) {
56                 mainTracker.processMotionEvent(me, keyDetector);
57                 return;
58             }
59             // Inject a copied event.
60             injectMotionEvent(action, me.getX(index), me.getY(index), downTime, eventTime,
61                     mainTracker, keyDetector);
62             return;
63         }
64 
65         // Single-touch to multi-touch transition.
66         if (oldPointerCount == 1 && pointerCount == 2) {
67             // Send an up event for the last pointer, be cause we can't trust the coordinates of
68             // this multi-touch event.
69             mainTracker.getLastCoordinates(mLastCoords);
70             final int x = CoordinateUtils.x(mLastCoords);
71             final int y = CoordinateUtils.y(mLastCoords);
72             mOldKey = mainTracker.getKeyOn(x, y);
73             // Inject an artifact up event for the old key.
74             injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime,
75                     mainTracker, keyDetector);
76             return;
77         }
78 
79         // Multi-touch to single-touch transition.
80         if (oldPointerCount == 2 && pointerCount == 1) {
81             // Send a down event for the latest pointer if the key is different from the previous
82             // key.
83             final int x = (int)me.getX(index);
84             final int y = (int)me.getY(index);
85             final Key newKey = mainTracker.getKeyOn(x, y);
86             if (mOldKey != newKey) {
87                 // Inject an artifact down event for the new key.
88                 // An artifact up event for the new key will usually be injected as a single-touch.
89                 injectMotionEvent(MotionEvent.ACTION_DOWN, x, y, downTime, eventTime,
90                         mainTracker, keyDetector);
91                 if (action == MotionEvent.ACTION_UP) {
92                     // Inject an artifact up event for the new key also.
93                     injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime,
94                             mainTracker, keyDetector);
95                 }
96             }
97             return;
98         }
99 
100         Log.w(TAG, "Unknown touch panel behavior: pointer count is "
101                 + pointerCount + " (previously " + oldPointerCount + ")");
102     }
103 
injectMotionEvent(final int action, final float x, final float y, final long downTime, final long eventTime, final PointerTracker tracker, final KeyDetector keyDetector)104     private static void injectMotionEvent(final int action, final float x, final float y,
105             final long downTime, final long eventTime, final PointerTracker tracker,
106             final KeyDetector keyDetector) {
107         final MotionEvent me = MotionEvent.obtain(
108                 downTime, eventTime, action, x, y, 0 /* metaState */);
109         try {
110             tracker.processMotionEvent(me, keyDetector);
111         } finally {
112             me.recycle();
113         }
114     }
115 }
116