14a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka/*
24a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * Copyright (C) 2013 The Android Open Source Project
34a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka *
44a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
54a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * you may not use this file except in compliance with the License.
64a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * You may obtain a copy of the License at
74a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka *
84a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
94a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka *
104a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
114a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
124a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * See the License for the specific language governing permissions and
144a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * limitations under the License.
154a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka */
164a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
174a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaokapackage com.android.inputmethod.keyboard.internal;
184a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
199342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport com.android.inputmethod.latin.common.Constants;
2036799b2aa2982ec17341cd2c5ed81e608bcee8c6Jean Chalardimport com.android.inputmethod.latin.common.InputPointers;
214a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
224a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka/**
234a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * This class arbitrates batch input.
244a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * An instance of this class holds a {@link GestureStrokeRecognitionPoints}.
254a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * And it arbitrates multiple strokes gestured by multiple fingers and aggregates those gesture
264a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka * points into one batch input.
274a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka */
284a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaokapublic class BatchInputArbiter {
294a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public interface BatchInputArbiterListener {
304a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        public void onStartBatchInput();
314a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        public void onUpdateBatchInput(
324a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                final InputPointers aggregatedPointers, final long moveEventTime);
334a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        public void onStartUpdateBatchInputTimer();
344a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        public void onEndBatchInput(final InputPointers aggregatedPointers, final long upEventTime);
354a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
364a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
374a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    // The starting time of the first stroke of a gesture input.
384a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    private static long sGestureFirstDownTime;
394a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    // The {@link InputPointers} that includes all events of a gesture input.
404a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    private static final InputPointers sAggregatedPointers = new InputPointers(
414a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
424a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregatedPointers
434a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    private static long sLastRecognitionTime = 0; // synchronized using sAggregatedPointers
444a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
454a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    private final GestureStrokeRecognitionPoints mRecognitionPoints;
464a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
474a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public BatchInputArbiter(final int pointerId, final GestureStrokeRecognitionParams params) {
484a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mRecognitionPoints = new GestureStrokeRecognitionPoints(pointerId, params);
494a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
504a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
514a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
524a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mRecognitionPoints.setKeyboardGeometry(keyWidth, keyboardHeight);
534a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
544a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
554a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    /**
564a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * Calculate elapsed time since the first gesture down.
574a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param eventTime the time of this event.
584a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @return the elapsed time in millisecond from the first gesture down.
594a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     */
604a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public int getElapsedTimeSinceFirstDown(final long eventTime) {
614a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        return (int)(eventTime - sGestureFirstDownTime);
624a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
634a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
644a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    /**
654a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * Add a down event point.
664a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param x the x-coordinate of this down event.
674a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param y the y-coordinate of this down event.
684a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param downEventTime the time of this down event.
694a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param lastLetterTypingTime the last typing input time.
704a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param activePointerCount the number of active pointers when this pointer down event occurs.
714a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     */
724a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void addDownEventPoint(final int x, final int y, final long downEventTime,
734a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            final long lastLetterTypingTime, final int activePointerCount) {
744a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        if (activePointerCount == 1) {
754a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            sGestureFirstDownTime = downEventTime;
764a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        }
774a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        final int elapsedTimeSinceFirstDown = getElapsedTimeSinceFirstDown(downEventTime);
784a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        final int elapsedTimeSinceLastTyping = (int)(downEventTime - lastLetterTypingTime);
794a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mRecognitionPoints.addDownEventPoint(
804a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                x, y, elapsedTimeSinceFirstDown, elapsedTimeSinceLastTyping);
814a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
824a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
834a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    /**
844a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * Add a move event point.
854a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param x the x-coordinate of this move event.
864a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param y the y-coordinate of this move event.
874a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param moveEventTime the time of this move event.
884a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param isMajorEvent false if this is a historical move event.
894a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param listener {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} of this
904a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     *     <code>listener</code> may be called if enough move points have been added.
914a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @return true if this move event occurs on the valid gesture area.
924a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     */
934a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public boolean addMoveEventPoint(final int x, final int y, final long moveEventTime,
944a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            final boolean isMajorEvent, final BatchInputArbiterListener listener) {
954a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        final int beforeLength = mRecognitionPoints.getLength();
964a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        final boolean onValidArea = mRecognitionPoints.addEventPoint(
974a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                x, y, getElapsedTimeSinceFirstDown(moveEventTime), isMajorEvent);
984a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        if (mRecognitionPoints.getLength() > beforeLength) {
994a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            listener.onStartUpdateBatchInputTimer();
1004a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        }
1014a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        return onValidArea;
1024a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
1034a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
1044a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    /**
1054a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * Determine whether the batch input has started or not.
1064a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param listener {@link BatchInputArbiterListener#onStartBatchInput()} of this
1074a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     *     <code>listener</code> will be called when the batch input has started successfully.
1084a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @return true if the batch input has started successfully.
1094a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     */
1104a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public boolean mayStartBatchInput(final BatchInputArbiterListener listener) {
1114a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        if (!mRecognitionPoints.isStartOfAGesture()) {
1124a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            return false;
1134a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        }
1144a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        synchronized (sAggregatedPointers) {
1154a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            sAggregatedPointers.reset();
1164a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            sLastRecognitionPointSize = 0;
1174a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            sLastRecognitionTime = 0;
1184a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            listener.onStartBatchInput();
1194a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        }
1204a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        return true;
1214a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
1224a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
1234a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    /**
1244a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * Add synthetic move event point. After adding the point,
1254a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * {@link #updateBatchInput(long,BatchInputArbiterListener)} will be called internally.
1264a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param syntheticMoveEventTime the synthetic move event time.
1274a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param listener the listener to be passed to
1284a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     *     {@link #updateBatchInput(long,BatchInputArbiterListener)}.
1294a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     */
1304a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void updateBatchInputByTimer(final long syntheticMoveEventTime,
1314a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            final BatchInputArbiterListener listener) {
1324a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mRecognitionPoints.duplicateLastPointWith(
1334a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                getElapsedTimeSinceFirstDown(syntheticMoveEventTime));
1344a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        updateBatchInput(syntheticMoveEventTime, listener);
1354a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
1364a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
1374a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    /**
1384a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * Determine whether we have enough gesture points to lookup dictionary.
1394a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param moveEventTime the time of this move event.
1404a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param listener {@link BatchInputArbiterListener#onUpdateBatchInput(InputPointers,long)} of
1414a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     *     this <code>listener</code> will be called when enough event points we have. Also
1424a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     *     {@link BatchInputArbiterListener#onStartUpdateBatchInputTimer()} will be called to have
1434a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     *     possible future synthetic move event.
1444a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     */
1454a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void updateBatchInput(final long moveEventTime,
1464a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            final BatchInputArbiterListener listener) {
1474a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        synchronized (sAggregatedPointers) {
1484a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            mRecognitionPoints.appendIncrementalBatchPoints(sAggregatedPointers);
1494a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            final int size = sAggregatedPointers.getPointerSize();
1504a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            if (size > sLastRecognitionPointSize && mRecognitionPoints.hasRecognitionTimePast(
1514a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                    moveEventTime, sLastRecognitionTime)) {
1524a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                listener.onUpdateBatchInput(sAggregatedPointers, moveEventTime);
1534a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                listener.onStartUpdateBatchInputTimer();
1544a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                // The listener may change the size of the pointers (when auto-committing
1554a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                // for example), so we need to get the size from the pointers again.
1564a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                sLastRecognitionPointSize = sAggregatedPointers.getPointerSize();
1574a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                sLastRecognitionTime = moveEventTime;
1584a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            }
1594a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        }
1604a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
1614a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
1624a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    /**
1634a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * Determine whether the batch input has ended successfully or continues.
1644a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param upEventTime the time of this up event.
1654a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param activePointerCount the number of active pointers when this pointer up event occurs.
1664a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @param listener {@link BatchInputArbiterListener#onEndBatchInput(InputPointers,long)} of this
1674a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     *     <code>listener</code> will be called when the batch input has started successfully.
1684a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     * @return true if the batch input has ended successfully.
1694a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka     */
1704a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public boolean mayEndBatchInput(final long upEventTime, final int activePointerCount,
1714a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            final BatchInputArbiterListener listener) {
1724a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        synchronized (sAggregatedPointers) {
1734a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            mRecognitionPoints.appendAllBatchPoints(sAggregatedPointers);
1744a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            if (activePointerCount == 1) {
1754a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                listener.onEndBatchInput(sAggregatedPointers, upEventTime);
1764a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                return true;
1774a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            }
1784a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        }
1794a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        return false;
1804a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
1814a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka}
182