LatinIME/java/src/com/android/inputmethod/keyboard/internal/BatchInputArbiter.java
Tadashi G. Takaoka 4a4b6d42a7 Add BatchInputArbiter
Move the logic that determines how to handle gesture typing from
PointerTracker to BatchInputArbiter class.

This change must be checked in together with Ia7db8f4fb7.

Change-Id: I5e9f786de58d8a3cb0407ae562af707710ec37fb
2013-12-26 14:09:57 +09:00

181 lines
8.4 KiB
Java

/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.keyboard.internal;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.InputPointers;
/**
* This class arbitrates batch input.
* An instance of this class holds a {@link GestureStrokeRecognitionPoints}.
* And it arbitrates multiple strokes gestured by multiple fingers and aggregates those gesture
* points into one batch input.
*/
public class BatchInputArbiter {
public interface BatchInputArbiterListener {
public void onStartBatchInput();
public void onUpdateBatchInput(
final InputPointers aggregatedPointers, final long moveEventTime);
public void onStartUpdateBatchInputTimer();
public void onEndBatchInput(final InputPointers aggregatedPointers, final long upEventTime);
}
// The starting time of the first stroke of a gesture input.
private static long sGestureFirstDownTime;
// The {@link InputPointers} that includes all events of a gesture input.
private static final InputPointers sAggregatedPointers = new InputPointers(
Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
private static int sLastRecognitionPointSize = 0; // synchronized using sAggregatedPointers
private static long sLastRecognitionTime = 0; // synchronized using sAggregatedPointers
private final GestureStrokeRecognitionPoints mRecognitionPoints;
public BatchInputArbiter(final int pointerId, final GestureStrokeRecognitionParams params) {
mRecognitionPoints = new GestureStrokeRecognitionPoints(pointerId, params);
}
public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
mRecognitionPoints.setKeyboardGeometry(keyWidth, keyboardHeight);
}
/**
* Calculate elapsed time since the first gesture down.
* @param eventTime the time of this event.
* @return the elapsed time in millisecond from the first gesture down.
*/
public int getElapsedTimeSinceFirstDown(final long eventTime) {
return (int)(eventTime - sGestureFirstDownTime);
}
/**
* Add a down event point.
* @param x the x-coordinate of this down event.
* @param y the y-coordinate of this down event.
* @param downEventTime the time of this down event.
* @param lastLetterTypingTime the last typing input time.
* @param activePointerCount the number of active pointers when this pointer down event occurs.
*/
public void addDownEventPoint(final int x, final int y, final long downEventTime,
final long lastLetterTypingTime, final int activePointerCount) {
if (activePointerCount == 1) {
sGestureFirstDownTime = downEventTime;
}
final int elapsedTimeSinceFirstDown = getElapsedTimeSinceFirstDown(downEventTime);
final int elapsedTimeSinceLastTyping = (int)(downEventTime - lastLetterTypingTime);
mRecognitionPoints.addDownEventPoint(
x, y, elapsedTimeSinceFirstDown, elapsedTimeSinceLastTyping);
}
/**
* Add a move event point.
* @param x the x-coordinate of this move event.
* @param y the y-coordinate of this move event.
* @param moveEventTime the time of this move event.
* @param isMajorEvent false if this is a historical move event.
* @param listener {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} of this
* <code>listener</code> may be called if enough move points have been added.
* @return true if this move event occurs on the valid gesture area.
*/
public boolean addMoveEventPoint(final int x, final int y, final long moveEventTime,
final boolean isMajorEvent, final BatchInputArbiterListener listener) {
final int beforeLength = mRecognitionPoints.getLength();
final boolean onValidArea = mRecognitionPoints.addEventPoint(
x, y, getElapsedTimeSinceFirstDown(moveEventTime), isMajorEvent);
if (mRecognitionPoints.getLength() > beforeLength) {
listener.onStartUpdateBatchInputTimer();
}
return onValidArea;
}
/**
* Determine whether the batch input has started or not.
* @param listener {@link BatchInputArbiterListener#onStartBatchInput()} of this
* <code>listener</code> will be called when the batch input has started successfully.
* @return true if the batch input has started successfully.
*/
public boolean mayStartBatchInput(final BatchInputArbiterListener listener) {
if (!mRecognitionPoints.isStartOfAGesture()) {
return false;
}
synchronized (sAggregatedPointers) {
sAggregatedPointers.reset();
sLastRecognitionPointSize = 0;
sLastRecognitionTime = 0;
listener.onStartBatchInput();
}
return true;
}
/**
* Add synthetic move event point. After adding the point,
* {@link #updateBatchInput(long,BatchInputArbiterListener)} will be called internally.
* @param syntheticMoveEventTime the synthetic move event time.
* @param listener the listener to be passed to
* {@link #updateBatchInput(long,BatchInputArbiterListener)}.
*/
public void updateBatchInputByTimer(final long syntheticMoveEventTime,
final BatchInputArbiterListener listener) {
mRecognitionPoints.duplicateLastPointWith(
getElapsedTimeSinceFirstDown(syntheticMoveEventTime));
updateBatchInput(syntheticMoveEventTime, listener);
}
/**
* Determine whether we have enough gesture points to lookup dictionary.
* @param moveEventTime the time of this move event.
* @param listener {@link BatchInputArbiterListener#onUpdateBatchInput(InputPointers,long)} of
* this <code>listener</code> will be called when enough event points we have. Also
* {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} will be called to have
* possible future synthetic move event.
*/
public void updateBatchInput(final long moveEventTime,
final BatchInputArbiterListener listener) {
synchronized (sAggregatedPointers) {
mRecognitionPoints.appendIncrementalBatchPoints(sAggregatedPointers);
final int size = sAggregatedPointers.getPointerSize();
if (size > sLastRecognitionPointSize && mRecognitionPoints.hasRecognitionTimePast(
moveEventTime, sLastRecognitionTime)) {
listener.onUpdateBatchInput(sAggregatedPointers, moveEventTime);
listener.onStartUpdateBatchInputTimer();
// The listener may change the size of the pointers (when auto-committing
// for example), so we need to get the size from the pointers again.
sLastRecognitionPointSize = sAggregatedPointers.getPointerSize();
sLastRecognitionTime = moveEventTime;
}
}
}
/**
* Determine whether the batch input has ended successfully or continues.
* @param upEventTime the time of this up event.
* @param activePointerCount the number of active pointers when this pointer up event occurs.
* @param listener {@link BatchInputArbiterListener#onEndBatchInput(InputPointers,long)} of this
* <code>listener</code> will be called when the batch input has started successfully.
* @return true if the batch input has ended successfully.
*/
public boolean mayEndBatchInput(final long upEventTime, final int activePointerCount,
final BatchInputArbiterListener listener) {
synchronized (sAggregatedPointers) {
mRecognitionPoints.appendAllBatchPoints(sAggregatedPointers);
if (activePointerCount == 1) {
listener.onEndBatchInput(sAggregatedPointers, upEventTime);
return true;
}
}
return false;
}
}