1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under 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.MainKeyboardView; 24 import com.android.inputmethod.latin.LatinImeLogger; 25 import com.android.inputmethod.latin.R; 26 import com.android.inputmethod.latin.ResourceUtils; 27 import com.android.inputmethod.latin.define.ProductionFlag; 28 import com.android.inputmethod.research.ResearchLogger; 29 30 public final class TouchScreenRegulator { 31 private static final String TAG = TouchScreenRegulator.class.getSimpleName(); 32 private static boolean DEBUG_MODE = LatinImeLogger.sDBG; 33 34 public interface ProcessMotionEvent { 35 public boolean processMotionEvent(MotionEvent me); 36 } 37 38 private final ProcessMotionEvent mView; 39 private final boolean mNeedsSuddenJumpingHack; 40 41 /** Whether we've started dropping move events because we found a big jump */ 42 private boolean mDroppingEvents; 43 /** 44 * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has 45 * occured 46 */ 47 private boolean mDisableDisambiguation; 48 /** The distance threshold at which we start treating the touch session as a multi-touch */ 49 private int mJumpThresholdSquare = Integer.MAX_VALUE; 50 private int mLastX; 51 private int mLastY; 52 // One-seventh of the keyboard width seems like a reasonable threshold 53 private static final float JUMP_THRESHOLD_RATIO_TO_KEYBOARD_WIDTH = 1.0f / 7.0f; 54 55 public TouchScreenRegulator(final Context context, final ProcessMotionEvent view) { 56 mView = view; 57 mNeedsSuddenJumpingHack = Boolean.parseBoolean(ResourceUtils.getDeviceOverrideValue( 58 context.getResources(), R.array.sudden_jumping_touch_event_device_list)); 59 } 60 61 public void setKeyboardGeometry(final int keyboardWidth) { 62 final float jumpThreshold = keyboardWidth * JUMP_THRESHOLD_RATIO_TO_KEYBOARD_WIDTH; 63 mJumpThresholdSquare = (int)(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 */ 77 private boolean handleSuddenJumping(final 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 143 public boolean onTouchEvent(final 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.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 149 ResearchLogger.suddenJumpingTouchEventHandler_onTouchEvent(me); 150 } 151 return true; 152 } 153 return mView.processMotionEvent(me); 154 } 155 } 156