• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.inputmethod.keyboard;
18 
19 import android.content.Context;
20 import android.os.Build;
21 import android.util.Log;
22 import android.view.MotionEvent;
23 
24 import com.android.inputmethod.latin.LatinImeLogger;
25 import com.android.inputmethod.latin.R;
26 
27 public class SuddenJumpingTouchEventHandler {
28     private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
29     private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
30 
31     public interface ProcessMotionEvent {
processMotionEvent(MotionEvent me)32         public boolean processMotionEvent(MotionEvent me);
33     }
34 
35     private final ProcessMotionEvent mView;
36     private final boolean mNeedsSuddenJumpingHack;
37 
38     /** Whether we've started dropping move events because we found a big jump */
39     private boolean mDroppingEvents;
40     /**
41      * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has
42      * occured
43      */
44     private boolean mDisableDisambiguation;
45     /** The distance threshold at which we start treating the touch session as a multi-touch */
46     private int mJumpThresholdSquare = Integer.MAX_VALUE;
47     private int mLastX;
48     private int mLastY;
49 
SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view)50     public SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view) {
51         mView = view;
52         final String[] deviceList = context.getResources().getStringArray(
53                 R.array.sudden_jumping_touch_event_device_list);
54         mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.HARDWARE, deviceList);
55     }
56 
needsSuddenJumpingHack(String deviceName, String[] deviceList)57     private static boolean needsSuddenJumpingHack(String deviceName, String[] deviceList) {
58         for (String device : deviceList) {
59             if (device.equalsIgnoreCase(deviceName)) {
60                 return true;
61             }
62         }
63         return false;
64     }
65 
setKeyboard(Keyboard newKeyboard)66     public void setKeyboard(Keyboard newKeyboard) {
67         // One-seventh of the keyboard width seems like a reasonable threshold
68         final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
69         mJumpThresholdSquare = jumpThreshold * jumpThreshold;
70     }
71 
72     /**
73      * This function checks to see if we need to handle any sudden jumps in the pointer location
74      * that could be due to a multi-touch being treated as a move by the firmware or hardware.
75      * Once a sudden jump is detected, all subsequent move events are discarded
76      * until an UP is received.<P>
77      * When a sudden jump is detected, an UP event is simulated at the last position and when
78      * the sudden moves subside, a DOWN event is simulated for the second key.
79      * @param me the motion event
80      * @return true if the event was consumed, so that it doesn't continue to be handled by
81      * {@link LatinKeyboardView}.
82      */
handleSuddenJumping(MotionEvent me)83     private boolean handleSuddenJumping(MotionEvent me) {
84         if (!mNeedsSuddenJumpingHack)
85             return false;
86         final int action = me.getAction();
87         final int x = (int) me.getX();
88         final int y = (int) me.getY();
89         boolean result = false;
90 
91         // Real multi-touch event? Stop looking for sudden jumps
92         if (me.getPointerCount() > 1) {
93             mDisableDisambiguation = true;
94         }
95         if (mDisableDisambiguation) {
96             // If UP, reset the multi-touch flag
97             if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false;
98             return false;
99         }
100 
101         switch (action) {
102         case MotionEvent.ACTION_DOWN:
103             // Reset the "session"
104             mDroppingEvents = false;
105             mDisableDisambiguation = false;
106             break;
107         case MotionEvent.ACTION_MOVE:
108             // Is this a big jump?
109             final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
110             // Check the distance.
111             if (distanceSquare > mJumpThresholdSquare) {
112                 // If we're not yet dropping events, start dropping and send an UP event
113                 if (!mDroppingEvents) {
114                     mDroppingEvents = true;
115                     // Send an up event
116                     MotionEvent translated = MotionEvent.obtain(
117                             me.getEventTime(), me.getEventTime(),
118                             MotionEvent.ACTION_UP,
119                             mLastX, mLastY, me.getMetaState());
120                     mView.processMotionEvent(translated);
121                     translated.recycle();
122                 }
123                 result = true;
124             } else if (mDroppingEvents) {
125                 // If moves are small and we're already dropping events, continue dropping
126                 result = true;
127             }
128             break;
129         case MotionEvent.ACTION_UP:
130             if (mDroppingEvents) {
131                 // Send a down event first, as we dropped a bunch of sudden jumps and assume that
132                 // the user is releasing the touch on the second key.
133                 MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
134                         MotionEvent.ACTION_DOWN,
135                         x, y, me.getMetaState());
136                 mView.processMotionEvent(translated);
137                 translated.recycle();
138                 mDroppingEvents = false;
139                 // Let the up event get processed as well, result = false
140             }
141             break;
142         }
143         // Track the previous coordinate
144         mLastX = x;
145         mLastY = y;
146         return result;
147     }
148 
onTouchEvent(MotionEvent me)149     public boolean onTouchEvent(MotionEvent me) {
150         // If there was a sudden jump, return without processing the actual motion event.
151         if (handleSuddenJumping(me)) {
152             if (DEBUG_MODE)
153                 Log.w(TAG, "onTouchEvent: ignore sudden jump " + me);
154             return true;
155         }
156         return mView.processMotionEvent(me);
157     }
158 }
159