SuddenJumpingTouchEventHandler.java revision c403a46f6d787b79768895272d53d296100677dd
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 17package com.android.inputmethod.keyboard; 18 19import android.content.Context; 20import android.os.Build; 21import android.util.Log; 22import android.view.MotionEvent; 23 24import com.android.inputmethod.latin.LatinImeLogger; 25import com.android.inputmethod.latin.R; 26 27public class SuddenJumpingTouchEventHandler { 28 private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName(); 29 private static boolean DEBUG_MODE = LatinImeLogger.sDBG; 30 31 public interface ProcessMotionEvent { 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 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.DEVICE, deviceList); 55 } 56 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 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 LatinKeyboardBaseView}. 82 */ 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 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