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