Home | History | Annotate | Download | only in keyboard
      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.os.Build;
     21 import android.util.Log;
     22 import android.view.MotionEvent;
     23 
     24 import com.android.inputmethod.latin.LatinImeLogger;
     25 import com.android.inputmethod.latin.R;
     26 
     27 public 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.HARDWARE, 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 LatinKeyboardView}.
     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