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 { onStartBatchInput()30 public void onStartBatchInput(); onUpdateBatchInput( final InputPointers aggregatedPointers, final long moveEventTime)31 public void onUpdateBatchInput( 32 final InputPointers aggregatedPointers, final long moveEventTime); onStartUpdateBatchInputTimer()33 public void onStartUpdateBatchInputTimer(); onEndBatchInput(final InputPointers aggregatedPointers, final long upEventTime)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 BatchInputArbiter(final int pointerId, final GestureStrokeRecognitionParams params)47 public BatchInputArbiter(final int pointerId, final GestureStrokeRecognitionParams params) { 48 mRecognitionPoints = new GestureStrokeRecognitionPoints(pointerId, params); 49 } 50 setKeyboardGeometry(final int keyWidth, final int keyboardHeight)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 */ getElapsedTimeSinceFirstDown(final long eventTime)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 */ addDownEventPoint(final int x, final int y, final long downEventTime, final long lastLetterTypingTime, final int activePointerCount)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 */ addMoveEventPoint(final int x, final int y, final long moveEventTime, final boolean isMajorEvent, final BatchInputArbiterListener listener)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 */ mayStartBatchInput(final BatchInputArbiterListener listener)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 */ updateBatchInputByTimer(final long syntheticMoveEventTime, final BatchInputArbiterListener listener)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 */ updateBatchInput(final long moveEventTime, final BatchInputArbiterListener listener)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 */ mayEndBatchInput(final long upEventTime, final int activePointerCount, final BatchInputArbiterListener listener)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