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