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