Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2013 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 com.android.inputmethod.latin.common.Constants;
     20 import com.android.inputmethod.latin.common.InputPointers;
     21 
     22 /**
     23  * This class arbitrates batch input.
     24  * An instance of this class holds a {@link GestureStrokeRecognitionPoints}.
     25  * And it arbitrates multiple strokes gestured by multiple fingers and aggregates those gesture
     26  * points into one batch input.
     27  */
     28 public class BatchInputArbiter {
     29     public interface BatchInputArbiterListener {
     30         public void onStartBatchInput();
     31         public void onUpdateBatchInput(
     32                 final InputPointers aggregatedPointers, final long moveEventTime);
     33         public void onStartUpdateBatchInputTimer();
     34         public void onEndBatchInput(final InputPointers aggregatedPointers, final long upEventTime);
     35     }
     36 
     37     // The starting time of the first stroke of a gesture input.
     38     private static long sGestureFirstDownTime;
     39     // The {@link InputPointers} that includes all events of a gesture input.
     40     private static final InputPointers sAggregatedPointers = new InputPointers(
     41             Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
     42     private static int sLastRecognitionPointSize = 0; // synchronized using sAggregatedPointers
     43     private static long sLastRecognitionTime = 0; // synchronized using sAggregatedPointers
     44 
     45     private final GestureStrokeRecognitionPoints mRecognitionPoints;
     46 
     47     public BatchInputArbiter(final int pointerId, final GestureStrokeRecognitionParams params) {
     48         mRecognitionPoints = new GestureStrokeRecognitionPoints(pointerId, params);
     49     }
     50 
     51     public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
     52         mRecognitionPoints.setKeyboardGeometry(keyWidth, keyboardHeight);
     53     }
     54 
     55     /**
     56      * Calculate elapsed time since the first gesture down.
     57      * @param eventTime the time of this event.
     58      * @return the elapsed time in millisecond from the first gesture down.
     59      */
     60     public int getElapsedTimeSinceFirstDown(final long eventTime) {
     61         return (int)(eventTime - sGestureFirstDownTime);
     62     }
     63 
     64     /**
     65      * Add a down event point.
     66      * @param x the x-coordinate of this down event.
     67      * @param y the y-coordinate of this down event.
     68      * @param downEventTime the time of this down event.
     69      * @param lastLetterTypingTime the last typing input time.
     70      * @param activePointerCount the number of active pointers when this pointer down event occurs.
     71      */
     72     public void addDownEventPoint(final int x, final int y, final long downEventTime,
     73             final long lastLetterTypingTime, final int activePointerCount) {
     74         if (activePointerCount == 1) {
     75             sGestureFirstDownTime = downEventTime;
     76         }
     77         final int elapsedTimeSinceFirstDown = getElapsedTimeSinceFirstDown(downEventTime);
     78         final int elapsedTimeSinceLastTyping = (int)(downEventTime - lastLetterTypingTime);
     79         mRecognitionPoints.addDownEventPoint(
     80                 x, y, elapsedTimeSinceFirstDown, elapsedTimeSinceLastTyping);
     81     }
     82 
     83     /**
     84      * Add a move event point.
     85      * @param x the x-coordinate of this move event.
     86      * @param y the y-coordinate of this move event.
     87      * @param moveEventTime the time of this move event.
     88      * @param isMajorEvent false if this is a historical move event.
     89      * @param listener {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} of this
     90      *     <code>listener</code> may be called if enough move points have been added.
     91      * @return true if this move event occurs on the valid gesture area.
     92      */
     93     public boolean addMoveEventPoint(final int x, final int y, final long moveEventTime,
     94             final boolean isMajorEvent, final BatchInputArbiterListener listener) {
     95         final int beforeLength = mRecognitionPoints.getLength();
     96         final boolean onValidArea = mRecognitionPoints.addEventPoint(
     97                 x, y, getElapsedTimeSinceFirstDown(moveEventTime), isMajorEvent);
     98         if (mRecognitionPoints.getLength() > beforeLength) {
     99             listener.onStartUpdateBatchInputTimer();
    100         }
    101         return onValidArea;
    102     }
    103 
    104     /**
    105      * Determine whether the batch input has started or not.
    106      * @param listener {@link BatchInputArbiterListener#onStartBatchInput()} of this
    107      *     <code>listener</code> will be called when the batch input has started successfully.
    108      * @return true if the batch input has started successfully.
    109      */
    110     public boolean mayStartBatchInput(final BatchInputArbiterListener listener) {
    111         if (!mRecognitionPoints.isStartOfAGesture()) {
    112             return false;
    113         }
    114         synchronized (sAggregatedPointers) {
    115             sAggregatedPointers.reset();
    116             sLastRecognitionPointSize = 0;
    117             sLastRecognitionTime = 0;
    118             listener.onStartBatchInput();
    119         }
    120         return true;
    121     }
    122 
    123     /**
    124      * Add synthetic move event point. After adding the point,
    125      * {@link #updateBatchInput(long,BatchInputArbiterListener)} will be called internally.
    126      * @param syntheticMoveEventTime the synthetic move event time.
    127      * @param listener the listener to be passed to
    128      *     {@link #updateBatchInput(long,BatchInputArbiterListener)}.
    129      */
    130     public void updateBatchInputByTimer(final long syntheticMoveEventTime,
    131             final BatchInputArbiterListener listener) {
    132         mRecognitionPoints.duplicateLastPointWith(
    133                 getElapsedTimeSinceFirstDown(syntheticMoveEventTime));
    134         updateBatchInput(syntheticMoveEventTime, listener);
    135     }
    136 
    137     /**
    138      * Determine whether we have enough gesture points to lookup dictionary.
    139      * @param moveEventTime the time of this move event.
    140      * @param listener {@link BatchInputArbiterListener#onUpdateBatchInput(InputPointers,long)} of
    141      *     this <code>listener</code> will be called when enough event points we have. Also
    142      *     {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} will be called to have
    143      *     possible future synthetic move event.
    144      */
    145     public void updateBatchInput(final long moveEventTime,
    146             final BatchInputArbiterListener listener) {
    147         synchronized (sAggregatedPointers) {
    148             mRecognitionPoints.appendIncrementalBatchPoints(sAggregatedPointers);
    149             final int size = sAggregatedPointers.getPointerSize();
    150             if (size > sLastRecognitionPointSize && mRecognitionPoints.hasRecognitionTimePast(
    151                     moveEventTime, sLastRecognitionTime)) {
    152                 listener.onUpdateBatchInput(sAggregatedPointers, moveEventTime);
    153                 listener.onStartUpdateBatchInputTimer();
    154                 // The listener may change the size of the pointers (when auto-committing
    155                 // for example), so we need to get the size from the pointers again.
    156                 sLastRecognitionPointSize = sAggregatedPointers.getPointerSize();
    157                 sLastRecognitionTime = moveEventTime;
    158             }
    159         }
    160     }
    161 
    162     /**
    163      * Determine whether the batch input has ended successfully or continues.
    164      * @param upEventTime the time of this up event.
    165      * @param activePointerCount the number of active pointers when this pointer up event occurs.
    166      * @param listener {@link BatchInputArbiterListener#onEndBatchInput(InputPointers,long)} of this
    167      *     <code>listener</code> will be called when the batch input has started successfully.
    168      * @return true if the batch input has ended successfully.
    169      */
    170     public boolean mayEndBatchInput(final long upEventTime, final int activePointerCount,
    171             final BatchInputArbiterListener listener) {
    172         synchronized (sAggregatedPointers) {
    173             mRecognitionPoints.appendAllBatchPoints(sAggregatedPointers);
    174             if (activePointerCount == 1) {
    175                 listener.onEndBatchInput(sAggregatedPointers, upEventTime);
    176                 return true;
    177             }
    178         }
    179         return false;
    180     }
    181 }
    182