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