16a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2010 The Android Open Source Project
36a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
76a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
96a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka *
106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
156a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka */
166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
186a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
19536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaokaimport android.content.res.Resources;
205509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport android.content.res.TypedArray;
21e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaokaimport android.os.SystemClock;
22c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport android.util.Log;
238ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaokaimport android.view.MotionEvent;
24c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
254a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.BatchInputArbiter;
264a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.BatchInputArbiter.BatchInputArbiterListener;
27c986d16e8aa97c042e1791cabb0a79bb9aaf04a1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.BogusMoveEventDetector;
2834a9e2ae87c6718d439b294d9536b6587a4f89f8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.DrawingProxy;
29a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureEnabler;
30e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeDrawingParams;
31e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeDrawingPoints;
32e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureStrokeRecognitionParams;
3372934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
3434a9e2ae87c6718d439b294d9536b6587a4f89f8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.TimerProxy;
355045103d6174a059bceb5ab9c7c03ef48e263c95Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.TypingTimeRecorder;
365509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaokaimport com.android.inputmethod.latin.R;
379342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport com.android.inputmethod.latin.common.Constants;
385b91b551e5ffaf2c2e691dfbd434f21c82293986Jean Chalardimport com.android.inputmethod.latin.common.CoordinateUtils;
3936799b2aa2982ec17341cd2c5ed81e608bcee8c6Jean Chalardimport com.android.inputmethod.latin.common.InputPointers;
402dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaokaimport com.android.inputmethod.latin.define.DebugFlags;
41212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaokaimport com.android.inputmethod.latin.settings.Settings;
42536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaokaimport com.android.inputmethod.latin.utils.ResourceUtils;
436a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
445c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaokaimport java.util.ArrayList;
45dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
46051f8b144232f724386960cf6781a6d4665e4f02Tadashi G. Takaokaimport javax.annotation.Nonnull;
47051f8b144232f724386960cf6781a6d4665e4f02Tadashi G. Takaokaimport javax.annotation.Nullable;
48051f8b144232f724386960cf6781a6d4665e4f02Tadashi G. Takaoka
494a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaokapublic final class PointerTracker implements PointerTrackerQueue.Element,
504a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        BatchInputArbiterListener {
51dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final String TAG = PointerTracker.class.getSimpleName();
52dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_EVENT = false;
53dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_MOVE_EVENT = false;
54dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    private static final boolean DEBUG_LISTENER = false;
552dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaoka    private static boolean DEBUG_MODE = DebugFlags.DEBUG_ENABLED || DEBUG_EVENT;
5640a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka
57a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    static final class PointerTrackerParams {
582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        public final boolean mKeySelectionByDraggingFinger;
595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
60b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        public final int mTouchNoiseThresholdDistance;
613623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka        public final int mSuppressKeyPreviewAfterBatchInputDuration;
628126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        public final int mKeyRepeatStartTimeout;
638126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        public final int mKeyRepeatInterval;
64212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        public final int mLongPressShiftLockTimeout;
655509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
6680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mKeySelectionByDraggingFinger = mainKeyboardViewAttr.getBoolean(
682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                    R.styleable.MainKeyboardView_keySelectionByDraggingFinger, false);
695509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
705509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
71b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
725509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
733623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka            mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
743623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka                    R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
758126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
768126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
778126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
788126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatInterval, 0);
79212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt(
80212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0);
815509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        }
825509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    }
835509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
84a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka    private static GestureEnabler sGestureEnabler = new GestureEnabler();
85a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka
86160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    // Parameters for pointer handling.
875509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka    private static PointerTrackerParams sParams;
88e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka    private static GestureStrokeRecognitionParams sGestureStrokeRecognitionParams;
89e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka    private static GestureStrokeDrawingParams sGestureStrokeDrawingParams;
90d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka    private static boolean sNeedsPhantomSuddenMoveEventHack;
91b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // Move this threshold to resource.
92b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    // TODO: Device specific parameter would be better for device specific hack?
93b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
95a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private static final ArrayList<PointerTracker> sTrackers = new ArrayList<>();
9693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka    private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
975c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
985c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    public final int mPointerId;
996a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static DrawingProxy sDrawingProxy;
1012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static TimerProxy sTimerProxy;
1022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static KeyboardActionListener sListener = KeyboardActionListener.EMPTY_LISTENER;
103baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
104921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka    // The {@link KeyDetector} is set whenever the down event is processed. Also this is updated
105921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka    // when new {@link Keyboard} is set by {@link #setKeyDetector(KeyDetector)}.
106ef8a03685f46c7a3ce02ad4a568217c9ef7fe677Tadashi G. Takaoka    private KeyDetector mKeyDetector = new KeyDetector();
1075a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    private Keyboard mKeyboard;
1082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private int mPhantomSuddenMoveThreshold;
109b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
1106a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1116c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private boolean mIsDetectingGesture = false; // per PointerTracker.
1126c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private static boolean sInGesture = false;
1135045103d6174a059bceb5ab9c7c03ef48e263c95Tadashi G. Takaoka    private static TypingTimeRecorder sTypingTimeRecorder;
1149580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
1158a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // The position and time at which first down event occurred.
1168a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mDownTime;
117be708c4e59ba16cf735440b99aa682baa3319ec5Tadashi G. Takaoka    @Nonnull
118547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    private int[] mDownCoordinates = CoordinateUtils.newInstance();
1198a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private long mUpTime;
1208a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
121e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The current key where this pointer is.
122e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mCurrentKey = null;
123e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    // The position where the current key was recognized for the first time.
1248a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyX;
1258a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mKeyY;
1268a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
1278a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    // Last pointer position.
1288a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastX;
1298a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    private int mLastY;
1306a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1311a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // true if keyboard layout has been changed.
1321a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    private boolean mKeyboardLayoutHasBeenChanged;
1331a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka
13413d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    // true if this pointer is no longer triggering any action because it has been canceled.
13513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    private boolean mIsTrackingForActionDisabled;
136c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
13735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    // the more keys panel currently being shown. equals null if no panel is active.
13835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private MoreKeysPanel mMoreKeysPanel;
1399ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka
140212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    private static final int MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT = 3;
1412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // true if this pointer is in the dragging finger mode.
1422fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    boolean mIsInDraggingFinger;
1432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // true if this pointer is sliding from a modifier key and in the sliding key input mode,
144f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // so that further modifier keys should be ignored.
1452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    boolean mIsInSlidingKeyInput;
146a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka    // if not a NOT_A_CODE, the key of this code is repeating
147a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka    private int mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
148cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
1492fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // true if dragging finger is allowed.
1502fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private boolean mIsAllowedDraggingFinger;
15167a4ecacc7525c9343cded13fc93e9a2381ea2d8Tadashi G. Takaoka
1524a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    private final BatchInputArbiter mBatchInputArbiter;
153e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka    private final GestureStrokeDrawingPoints mGestureStrokeDrawingPoints;
154f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
1552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: Add PointerTrackerFactory singleton and move some class static methods into it.
1562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public static void init(final TypedArray mainKeyboardViewAttr, final TimerProxy timerProxy,
157921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka            final DrawingProxy drawingProxy) {
1582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sParams = new PointerTrackerParams(mainKeyboardViewAttr);
159e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka        sGestureStrokeRecognitionParams = new GestureStrokeRecognitionParams(mainKeyboardViewAttr);
160e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka        sGestureStrokeDrawingParams = new GestureStrokeDrawingParams(mainKeyboardViewAttr);
1615045103d6174a059bceb5ab9c7c03ef48e263c95Tadashi G. Takaoka        sTypingTimeRecorder = new TypingTimeRecorder(
162e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka                sGestureStrokeRecognitionParams.mStaticTimeThresholdAfterFastTyping,
1635045103d6174a059bceb5ab9c7c03ef48e263c95Tadashi G. Takaoka                sParams.mSuppressKeyPreviewAfterBatchInputDuration);
1642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
1652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Resources res = mainKeyboardViewAttr.getResources();
166536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
167bd9887a57485c602e66a8f0eaebe40313ffa89cdTadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(res,
168bd9887a57485c602e66a8f0eaebe40313ffa89cdTadashi G. Takaoka                        R.array.phantom_sudden_move_event_device_list, Boolean.FALSE.toString()));
169c986d16e8aa97c042e1791cabb0a79bb9aaf04a1Tadashi G. Takaoka        BogusMoveEventDetector.init(res);
17093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
1712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sTimerProxy = timerProxy;
1722fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sDrawingProxy = drawingProxy;
1736a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
1746a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1758335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
176694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
177a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka        sGestureEnabler.setMainDictionaryAvailability(mainDictionaryAvailable);
1788335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
1798335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
180694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
181a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka        sGestureEnabler.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
182918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka    }
183918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka
1842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public static PointerTracker getPointerTracker(final int id) {
185b4fbbe57f574ce6e6a5827156f875fe7d3eb5089Tadashi G. Takaoka        final ArrayList<PointerTracker> trackers = sTrackers;
1865c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1875c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
1885c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        for (int i = trackers.size(); i <= id; i++) {
1892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final PointerTracker tracker = new PointerTracker(i);
1905c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            trackers.add(tracker);
1915c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
1925c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1935c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return trackers.get(id);
1945c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1955c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
1962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public static boolean isAnyInDraggingFinger() {
1972fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return sPointerTrackerQueue.isAnyInDraggingFinger();
1985c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
1995c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
2005c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public static void cancelAllPointerTrackers() {
2015c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        sPointerTrackerQueue.cancelAllPointerTrackers();
2025c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    }
2035c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka
204694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyboardActionListener(final KeyboardActionListener listener) {
2052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sListener = listener;
2065c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2075c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
208694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public static void setKeyDetector(final KeyDetector keyDetector) {
2099fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
2109fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka        if (keyboard == null) {
2119fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka            return;
2129fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka        }
213afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
214afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
215afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
2165c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            tracker.setKeyDetectorInner(keyDetector);
2175c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
218a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka        sGestureEnabler.setPasswordMode(keyboard.mId.passwordInput());
2195c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2205c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
221dde36ef34329164cf8b8a3985c578dab0343b3ebTadashi G. Takaoka    public static void setReleasedKeyGraphicsToAllKeys() {
222afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        final int trackersSize = sTrackers.size();
223afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa        for (int i = 0; i < trackersSize; ++i) {
224afed0567e91b9411fa61b03f5ac17812db56fd18Ken Wakasa            final PointerTracker tracker = sTrackers.get(i);
22553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            tracker.setReleasedKeyGraphics(tracker.getKey(), true /* withAnimation */);
2265c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        }
2275c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka    }
2285c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka
229ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    public static void dismissAllMoreKeysPanels() {
230ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        final int trackersSize = sTrackers.size();
231ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        for (int i = 0; i < trackersSize; ++i) {
232ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            final PointerTracker tracker = sTrackers.get(i);
2332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            tracker.dismissMoreKeysPanel();
234ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
235ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    }
236ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
2372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private PointerTracker(final int id) {
2385c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        mPointerId = id;
2394a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mBatchInputArbiter = new BatchInputArbiter(id, sGestureStrokeRecognitionParams);
240c3fe1425a5941e4801caa681dd53fb742d4489d9Tadashi G. Takaoka        mGestureStrokeDrawingPoints = new GestureStrokeDrawingPoints(sGestureStrokeDrawingParams);
2416a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
2426a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
2431a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka    // Returns true if keyboard has been changed by this callback.
2448126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key,
245ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            final int repeatCount) {
24613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // While gesture input is going on, this method should be a no-operation. But when gesture
24713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
24813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // are set to false. To keep this method is a no-operation,
24913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // <code>mIsTrackingForActionDisabled</code> should also be taken account of.
25013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
251eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return false;
252eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
2532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
254e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
2558126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onPress    : %s%s%s%s", mPointerId,
2569fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka                    (key == null ? "none" : Constants.printableCode(key.getCode())),
25758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    ignoreModifierKey ? " ignoreModifier" : "",
2588126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka                    key.isEnabled() ? "" : " disabled",
259ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka                    repeatCount > 0 ? " repeatCount=" + repeatCount : ""));
260e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
26193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (ignoreModifierKey) {
262996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return false;
26393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
264e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka        if (key.isEnabled()) {
2652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            sListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1);
266690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
267690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            mKeyboardLayoutHasBeenChanged = false;
2682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            sTimerProxy.startTypingStateTimer(key);
269690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka            return keyboardLayoutHasBeenChanged;
270690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        }
271690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka        return false;
272dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
273dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
274690b1360bfda3cbaae896de65dcc3cd347dc8329Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may in shifted state and the
27529881854bc5a625d01bad8c678ac10e6d1877e5fJean Chalard    // primaryCode is different from {@link Key#mKeyCode}.
276694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
2772282e8520a2c1984989a14fb09896536f5033b26Jean Chalard            final int y, final long eventTime, final boolean isKeyRepeat) {
2782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
2792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState();
28029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int code = altersCode ? key.getAltCode() : primaryCode;
2812013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (DEBUG_LISTENER) {
282240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            final String output = code == Constants.CODE_OUTPUT_TEXT
283240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    ? key.getOutputText() : Constants.printableCode(code);
28458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
28558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    output, ignoreModifierKey ? " ignoreModifier" : "",
28658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
2872013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
288e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
289996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
290e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
2916bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
2926bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.isEnabled() || altersCode) {
2935045103d6174a059bceb5ab9c7c03ef48e263c95Tadashi G. Takaoka            sTypingTimeRecorder.onCodeInput(code, eventTime);
294240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
2952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                sListener.onTextInput(key.getOutputText());
296240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            } else if (code != Constants.CODE_UNSPECIFIED) {
297a91dfff5e54f68c4003327eeca47286084c35a2fJean Chalard                if (mKeyboard.hasProximityCharsCorrection(code)) {
2982282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                    sListener.onCodeInput(code, x, y, isKeyRepeat);
299a91dfff5e54f68c4003327eeca47286084c35a2fJean Chalard                } else {
300a91dfff5e54f68c4003327eeca47286084c35a2fJean Chalard                    sListener.onCodeInput(code,
3012282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                            Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, isKeyRepeat);
302a91dfff5e54f68c4003327eeca47286084c35a2fJean Chalard                }
3032013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
30493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
305dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
306dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
307f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    // Note that we need primaryCode argument because the keyboard may be in shifted state and the
30829881854bc5a625d01bad8c678ac10e6d1877e5fJean Chalard    // primaryCode is different from {@link Key#mKeyCode}.
309694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void callListenerOnRelease(final Key key, final int primaryCode,
310694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final boolean withSliding) {
31113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}.
31213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
313eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
314eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
3152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
316e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        if (DEBUG_LISTENER) {
31758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
318240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                    Constants.printableCode(primaryCode),
31958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
32058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    key.isEnabled() ?  "": " disabled"));
321e742436a29f46764441e83aaebf3ec25283bff1bTadashi G. Takaoka        }
322e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (ignoreModifierKey) {
323996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka            return;
324e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
32593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        if (key.isEnabled()) {
3262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            sListener.onReleaseKey(primaryCode, withSliding);
32793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
328dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
329dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
33041016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    private void callListenerOnFinishSlidingInput() {
33141016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        if (DEBUG_LISTENER) {
33241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
33341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
3342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sListener.onFinishSlidingInput();
33541016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    }
33641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka
3378aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    private void callListenerOnCancelInput() {
338694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_LISTENER) {
33958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
340694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
3412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sListener.onCancelInput();
342dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
343dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
344694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void setKeyDetectorInner(final KeyDetector keyDetector) {
34558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final Keyboard keyboard = keyDetector.getKeyboard();
3469fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka        if (keyboard == null) {
3479fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka            return;
3489fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka        }
34958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
35058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return;
35158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
352a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = keyDetector;
3539fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka        mKeyboard = keyboard;
3542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // Mark that keyboard layout has been changed.
3552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyboardLayoutHasBeenChanged = true;
356d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyWidth = mKeyboard.mMostCommonKeyWidth;
357d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        final int keyHeight = mKeyboard.mMostCommonKeyHeight;
3584a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mBatchInputArbiter.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
359075158ccd039e4cc7e78d9db0470500e60c3fa1bTadashi G. Takaoka        // Keep {@link #mCurrentKey} that comes from previous keyboard. The key preview of
360075158ccd039e4cc7e78d9db0470500e60c3fa1bTadashi G. Takaoka        // {@link #mCurrentKey} will be dismissed by {@setReleasedKeyGraphics(Key)} via
361075158ccd039e4cc7e78d9db0470500e60c3fa1bTadashi G. Takaoka        // {@link onMoveEventInternal(int,int,long)} or {@link #onUpEventInternal(int,int,long)}.
3622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPhantomSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
363d631778e1c1ffcdf28129894239e7ee7d6f399fcTadashi G. Takaoka        mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
3645a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    }
3655a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka
3660cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
3672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public boolean isInDraggingFinger() {
3682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return mIsInDraggingFinger;
369cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
370cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
371051f8b144232f724386960cf6781a6d4665e4f02Tadashi G. Takaoka    @Nullable
372e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    public Key getKey() {
373e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey;
374dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
375dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka
3760cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
3772aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    public boolean isModifier() {
378e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return mCurrentKey != null && mCurrentKey.isModifier();
3792aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3802aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
381694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public Key getKeyOn(final int x, final int y) {
382723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
3832aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka    }
3842aa8078df86029dab394d8dd616f4f6decb39035Tadashi G. Takaoka
38553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka    private void setReleasedKeyGraphics(@Nullable final Key key, final boolean withAnimation) {
3866bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
387faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
388faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
389faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
39053b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        sDrawingProxy.onKeyReleased(key, withAnimation);
391faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
392faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
393faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
394faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
39553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                    sDrawingProxy.onKeyReleased(shiftKey, false /* withAnimation */);
3962013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
3972013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
398faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
3992013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
400faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.altCodeWhileTyping()) {
40129d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
402faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
403faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
40453b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                sDrawingProxy.onKeyReleased(altKey, false /* withAnimation */);
405faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
406faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
40729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
40853b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                    sDrawingProxy.onKeyReleased(k, false /* withAnimation */);
4092013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4102013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
4116a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
4126a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
4136a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
41429d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
415a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka        if (!sGestureEnabler.shouldHandleGesture()) return false;
4165045103d6174a059bceb5ab9c7c03ef48e263c95Tadashi G. Takaoka        return sTypingTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
41729d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka    }
41829d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka
41953b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka    private void setPressedKeyGraphics(@Nullable final Key key, final long eventTime) {
4206bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key == null) {
4216bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka            return;
4226bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        }
4236bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka
4246bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
4252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState();
4266bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
4276bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (!needsToUpdateGraphics) {
428faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            return;
429faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
430faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
43153b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        final boolean noKeyPreview = sInGesture || needsToSuppressKeyPreviewPopup(eventTime);
43253b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        sDrawingProxy.onKeyPressed(key, !noKeyPreview);
433faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka
434faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        if (key.isShift()) {
435faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key shiftKey : mKeyboard.mShiftKeys) {
436faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka                if (shiftKey != key) {
43753b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                    sDrawingProxy.onKeyPressed(shiftKey, false /* withPreview */);
4382013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4392013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
440faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka        }
4412013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
442aa7a68a3e0211a9e6c6da20c0f76aace3b3ba011Tadashi G. Takaoka        if (altersCode) {
44329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka            final int altCode = key.getAltCode();
444faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            final Key altKey = mKeyboard.getKey(altCode);
445faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            if (altKey != null) {
44653b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                sDrawingProxy.onKeyPressed(altKey, false /* withPreview */);
447faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            }
448faad353feabef17e3bea6d5d9b4cb8bacf94788cTadashi G. Takaoka            for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
44929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                if (k != key && k.getAltCode() == altCode) {
45053b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                    sDrawingProxy.onKeyPressed(k, false /* withPreview */);
4512013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                }
4522013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
453d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka        }
454c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka    }
455c5d33b16521de56ad01b0b6308217efb009078b7Tadashi G. Takaoka
456e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka    public GestureStrokeDrawingPoints getGestureStrokeDrawingPoints() {
457e2a6253cb581f9ab70cfb723d32b14f9ac7d2ab7Tadashi G. Takaoka        return mGestureStrokeDrawingPoints;
4582f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang    }
4592f81757c3a5eb50d41ce19fb534f72cbf607a997Tom Ouyang
460be708c4e59ba16cf735440b99aa682baa3319ec5Tadashi G. Takaoka    public void getLastCoordinates(@Nonnull final int[] outCoords) {
461547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(outCoords, mLastX, mLastY);
4628a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4638a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
4648a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    public long getDownTime() {
4658a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return mDownTime;
4668a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4678a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
468be708c4e59ba16cf735440b99aa682baa3319ec5Tadashi G. Takaoka    public void getDownCoordinates(@Nonnull final int[] outCoords) {
469547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.copy(outCoords, mDownCoordinates);
470547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka    }
471547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka
472694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onDownKey(final int x, final int y, final long eventTime) {
4738a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mDownTime = eventTime;
474547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        CoordinateUtils.set(mDownCoordinates, x, y);
475b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onDownKey();
4768a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
4778a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4788a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
479c986d16e8aa97c042e1791cabb0a79bb9aaf04a1Tadashi G. Takaoka    private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
480b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        return (int)Math.hypot(x1 - x2, y1 - y2);
481b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    }
482b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka
483694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKeyInternal(final int x, final int y) {
484b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
4858a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastX = x;
4868a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mLastY = y;
487723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        return mKeyDetector.detectHitKey(x, y);
4888a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4898a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
490694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveKey(final int x, final int y) {
4918a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        return onMoveKeyInternal(x, y);
4928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
4938a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
494694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
495e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        mCurrentKey = newKey;
4968a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyX = x;
4978a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        mKeyY = y;
498e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        return newKey;
4998a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka    }
5008a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka
5012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    /* package */ static int getActivePointerTrackerCount() {
50293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        return sPointerTrackerQueue.size();
5036c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
5046c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
505212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    private boolean isOldestTrackerInQueue() {
506ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka        return sPointerTrackerQueue.getOldestElement() == this;
50713ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    }
50813ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka
5094a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    // Implements {@link BatchInputArbiterListener}.
5104a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    @Override
5114a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void onStartBatchInput() {
5129c5d165e9c5797f16d3b07b043a5525353ad0d4fTadashi G. Takaoka        if (DEBUG_LISTENER) {
51358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
5149580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
5154a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        sListener.onStartBatchInput();
5164a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        dismissAllMoreKeysPanels();
51734a9e2ae87c6718d439b294d9536b6587a4f89f8Tadashi G. Takaoka        sTimerProxy.cancelLongPressTimersOf(this);
51872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
51972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
520904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka    private void showGestureTrail() {
52113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
5226f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka            return;
5236f0a60d10de056cbb89cf7984c9f8f64bb95db9dTadashi G. Takaoka        }
524212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        // A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
5252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sDrawingProxy.showGestureTrail(
526212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka                this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
52772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
52872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
529904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka    public void updateBatchInputByTimer(final long syntheticMoveEventTime) {
5304a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mBatchInputArbiter.updateBatchInputByTimer(syntheticMoveEventTime, this);
5314a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
5324a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
5334a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    // Implements {@link BatchInputArbiterListener}.
5344a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    @Override
5354a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void onUpdateBatchInput(final InputPointers aggregatedPointers, final long eventTime) {
5364a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        if (DEBUG_LISTENER) {
5374a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
5384a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                    aggregatedPointers.getPointerSize()));
5399580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
5404a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        sListener.onUpdateBatchInput(aggregatedPointers);
5419580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
5429580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
5434a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    // Implements {@link BatchInputArbiterListener}.
5444a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    @Override
5454a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void onStartUpdateBatchInputTimer() {
5464a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        sTimerProxy.startUpdateBatchInputTimer(this);
5474a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    }
5484a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka
5494a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    // Implements {@link BatchInputArbiterListener}.
5504a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    @Override
5514a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka    public void onEndBatchInput(final InputPointers aggregatedPointers, final long eventTime) {
5524a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        sTypingTimeRecorder.onEndBatchInput(eventTime);
5534a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        sTimerProxy.cancelAllUpdateBatchInputTimers();
5544a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
5554a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            return;
5564a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        }
5574a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        if (DEBUG_LISTENER) {
5584a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
5594a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                    mPointerId, aggregatedPointers.getPointerSize()));
5609580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
5614a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        sListener.onEndBatchInput(aggregatedPointers);
5629580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
5639580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
5648e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    private void cancelBatchInput() {
5655c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
5668353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        mIsDetectingGesture = false;
5677c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        if (!sInGesture) {
5687c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka            return;
5697c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        }
5708e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sInGesture = false;
5718e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        if (DEBUG_LISTENER) {
5728e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
5738e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        }
5742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sListener.onCancelBatchInput();
5758e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka    }
5768e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka
5772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) {
578e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int action = me.getActionMasked();
579e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final long eventTime = me.getEventTime();
580e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
5812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // When this pointer is the only active pointer and is showing a more keys panel,
5822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // we should ignore other pointers' motion event.
5832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final boolean shouldIgnoreOtherPointers =
5842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                    isShowingMoreKeysPanel() && getActivePointerTrackerCount() == 1;
585e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            final int pointerCount = me.getPointerCount();
586e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            for (int index = 0; index < pointerCount; index++) {
587e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                final int id = me.getPointerId(index);
5882fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                if (shouldIgnoreOtherPointers && id != mPointerId) {
5892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                    continue;
5902fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                }
591e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                final int x = (int)me.getX(index);
592e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                final int y = (int)me.getY(index);
5932fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                final PointerTracker tracker = getPointerTracker(id);
594e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka                tracker.onMoveEvent(x, y, eventTime, me);
595e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            }
596e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka            return;
597e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        }
598e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int index = me.getActionIndex();
599e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int x = (int)me.getX(index);
600e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int y = (int)me.getY(index);
6018ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        switch (action) {
6028ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
6038ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
6042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            onDownEvent(x, y, eventTime, keyDetector);
6058ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6068ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
6078ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
6088ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
6098ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6108ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
6118ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            onCancelEvent(x, y, eventTime);
6128ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka            break;
6138ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka        }
6148ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka    }
6158ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka
616e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onDownEvent(final int x, final int y, final long eventTime,
6172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final KeyDetector keyDetector) {
6187a750ca50d0ebe6bb5f395479ae2bef355c0a5ddTadashi G. Takaoka        setKeyDetectorInner(keyDetector);
619694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
620dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onDownEvent:", x, y, eventTime);
621694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
622baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        // Naive up-to-down noise filter.
6238a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final long deltaT = eventTime - mUpTime;
624160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        if (deltaT < sParams.mTouchNoiseThresholdTime) {
625b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final int distance = getDistance(x, y, mLastX, mLastY);
626b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            if (distance < sParams.mTouchNoiseThresholdDistance) {
627faf437b5078e882b630706cd315c335f204ab861Tadashi G. Takaoka                if (DEBUG_MODE)
628b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                    Log.w(TAG, String.format("[%d] onDownEvent:"
629b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            + " ignore potential noise: time=%d distance=%d",
630b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka                            mPointerId, deltaT, distance));
63113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
632baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka                return;
633baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka            }
634baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        }
635baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
636eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final Key key = getKeyOn(x, y);
637b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka        mBogusMoveEventDetector.onActualDownEvent(x, y);
63893b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (key != null && key.isModifier()) {
63993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // Before processing a down event of modifier key, all pointers already being
64093b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            // tracked should be released.
64193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            sPointerTrackerQueue.releaseAllPointers(eventTime);
6421d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
64393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.add(this);
6441d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
645a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka        if (!sGestureEnabler.shouldHandleGesture()) {
6466c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
6476c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
6480938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka        // A gesture should start only from a non-modifier key. Note that the gesture detection is
6490938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka        // disabled when the key is repeating.
650b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
6510938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka                && key != null && !key.isModifier();
652b305e6775a214f1cc16e584484e26a47eb8baa52Tadashi G. Takaoka        if (mIsDetectingGesture) {
6534a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            mBatchInputArbiter.addDownEventPoint(x, y, eventTime,
6544a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                    sTypingTimeRecorder.getLastLetterTypingTime(), getActivePointerTrackerCount());
6554a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            mGestureStrokeDrawingPoints.onDownEvent(
6564a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                    x, y, mBatchInputArbiter.getElapsedTimeSinceFirstDown(eventTime));
657eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
6581d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
6591d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
6602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    /* package */ boolean isShowingMoreKeysPanel() {
66135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        return (mMoreKeysPanel != null);
66235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    }
66335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
6642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void dismissMoreKeysPanel() {
6652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (isShowingMoreKeysPanel()) {
6662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mMoreKeysPanel.dismissMoreKeysPanel();
6672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mMoreKeysPanel = null;
6682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
6692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
6702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
671694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void onDownEventInternal(final int x, final int y, final long eventTime) {
672e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        Key key = onDownKey(x, y, eventTime);
6732fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // Key selection by dragging finger is allowed when 1) key selection by dragging finger is
6742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // enabled by configuration, 2) this pointer starts dragging from modifier key, or 3) this
6752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // pointer's KeyDetector always allows key selection by dragging finger, such as
6762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // {@link MoreKeysKeyboard}.
6772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mIsAllowedDraggingFinger = sParams.mKeySelectionByDraggingFinger
678c9f203805ca23276fcdcdc79b9298bc1d413ad98Tadashi G. Takaoka                || (key != null && key.isModifier())
6792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                || mKeyDetector.alwaysAllowsKeySelectionByDraggingFinger();
6801a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka        mKeyboardLayoutHasBeenChanged = false;
68113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = false;
6822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        resetKeySelectionByDraggingFinger();
683e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        if (key != null) {
6841a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // This onPress call may have changed keyboard layout. Those cases are detected at
685e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            // {@link #setKeyboard}. In those cases, we should update key according to the new
6861a6fba570260ca9f837e5a6874274f39a3c0a734Tadashi G. Takaoka            // keyboard layout.
687ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
688e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                key = onDownKey(x, y, eventTime);
689e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            }
690996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
691e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startRepeatKey(key);
692e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka            startLongPressTimer(key);
69329d2d4818266e76930b9f5376b1a7ebacd0c9f25Tadashi G. Takaoka            setPressedKeyGraphics(key, eventTime);
6946a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
6956a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
6966a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
6972fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void startKeySelectionByDraggingFinger(final Key key) {
6982fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (!mIsInDraggingFinger) {
6992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mIsInSlidingKeyInput = key.isModifier();
700e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        }
7012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mIsInDraggingFinger = true;
702996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka    }
703996db15d3c018ed2a7b4eee96ea94b9f80d8e379Tadashi G. Takaoka
7042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void resetKeySelectionByDraggingFinger() {
7052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mIsInDraggingFinger = false;
706f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mIsInSlidingKeyInput = false;
707051f8b144232f724386960cf6781a6d4665e4f02Tadashi G. Takaoka        sDrawingProxy.showSlidingKeyInputPreview(null /* tracker */);
708f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka    }
709f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka
7106c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onGestureMoveEvent(final int x, final int y, final long eventTime,
71102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final boolean isMajorEvent, final Key key) {
712904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        if (!mIsDetectingGesture) {
713904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            return;
714904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        }
7154a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        final boolean onValidArea = mBatchInputArbiter.addMoveEventPoint(
7164a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                x, y, eventTime, isMajorEvent, this);
717904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        // If the move event goes out from valid batch input area, cancel batch input.
718904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        if (!onValidArea) {
719904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            cancelBatchInput();
720904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            return;
721904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        }
7224a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka        mGestureStrokeDrawingPoints.onMoveEvent(
7234a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                x, y, mBatchInputArbiter.getElapsedTimeSinceFirstDown(eventTime));
724904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        // If the MoreKeysPanel is showing then do not attempt to enter gesture mode. However,
725904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        // the gestured touch points are still being recorded in case the panel is dismissed.
726904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
727904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            return;
728904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        }
729904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        if (!sInGesture && key != null && Character.isLetter(key.getCode())
7304a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                && mBatchInputArbiter.mayStartBatchInput(this)) {
731904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            sInGesture = true;
732904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        }
733904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka        if (sInGesture) {
734904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            if (key != null) {
7354a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                mBatchInputArbiter.updateBatchInput(eventTime, this);
7369580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka            }
737904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            showGestureTrail();
7389580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka        }
7399580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka    }
7409580c467f96c542c66af86a2c376612ba4d91434Tadashi G. Takaoka
741e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
742694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_MOVE_EVENT) {
743dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onMoveEvent:", x, y, eventTime);
744694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
74513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
746e8f45ab56f3e6f358953dede794a63fc5901961dTadashi G. Takaoka            return;
747694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
748baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka
749a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka        if (sGestureEnabler.shouldHandleGesture() && me != null) {
750eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            // Add historical points to gesture path.
751eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int pointerIndex = me.findPointerIndex(mPointerId);
752eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            final int historicalSize = me.getHistorySize();
753eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            for (int h = 0; h < historicalSize; h++) {
754eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
755eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
756eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final long historicalTime = me.getHistoricalEventTime(h);
7576c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                onGestureMoveEvent(historicalX, historicalY, historicalTime,
75802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                        false /* isMajorEvent */, null);
759eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
760eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
761ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
762ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        if (isShowingMoreKeysPanel()) {
763ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedX = mMoreKeysPanel.translateX(x);
764ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            final int translatedY = mMoreKeysPanel.translateY(y);
765ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
766ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            onMoveKey(x, y);
7672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            if (mIsInSlidingKeyInput) {
7682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                sDrawingProxy.showSlidingKeyInputPreview(this);
769212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            }
770ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang            return;
771ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        }
7726c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        onMoveEventInternal(x, y, eventTime);
7736c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    }
7746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka
7752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void processDraggingFingerInToNewKey(final Key newKey, final int x, final int y,
7763c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime) {
7773c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // This onPress call may have changed keyboard layout. Those cases are detected
7783c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // at {@link #setKeyboard}. In those cases, we should update key according
7793c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        // to the new keyboard layout.
7803c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        Key key = newKey;
781ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
7823c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            key = onMoveKey(x, y);
7833c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
7843c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onMoveToNewKey(key, x, y);
78513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
78613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            return;
78713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        }
7883c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        startLongPressTimer(key);
7893c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        setPressedKeyGraphics(key, eventTime);
7903c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
7913c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
7923c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
7933c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
7943c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
7953c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
7963c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " phantom sudden move event (distance=%d) is translated to "
7973c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
7983c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    getDistance(x, y, lastX, lastY),
7997dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.getCode()),
8007dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    x, y, Constants.printableCode(key.getCode())));
8013c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
80235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
8033c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
8043c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
8053c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
8063c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
8073c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
8083c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        if (DEBUG_MODE) {
8093c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float keyDiagonal = (float)Math.hypot(
8103c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
8113c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            final float radiusRatio =
8123c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
8133c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    / keyDiagonal;
8143c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            Log.w(TAG, String.format("[%d] onMoveEvent:"
8153c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " bogus down-move-up event (raidus=%.2f key diagonal) is "
8163c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
8173c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka                    mPointerId, radiusRatio,
8187dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    lastX, lastY, Constants.printableCode(oldKey.getCode()),
8197dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                    x, y, Constants.printableCode(key.getCode())));
8203c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        }
82135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
8223c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka        onDownEventInternal(x, y, eventTime);
8233c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka    }
8243c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka
8252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void processDraggingFingerOutFromOldKey(final Key oldKey) {
82653b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        setReleasedKeyGraphics(oldKey, true /* withAnimation */);
8277dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        callListenerOnRelease(oldKey, oldKey.getCode(), true /* withSliding */);
8282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        startKeySelectionByDraggingFinger(oldKey);
8292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sTimerProxy.cancelKeyTimersOf(this);
830831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka    }
831831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka
8322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void dragFingerFromOldKeyToNewKey(final Key key, final int x, final int y,
8338b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            final long eventTime, final Key oldKey, final int lastX, final int lastY) {
8348b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid in to the new key from the previous key, we must call
8358b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onRelease() first to notify that the previous key has been released, then call
8368b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // onPress() to notify that the new key is being pressed.
8372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        processDraggingFingerOutFromOldKey(oldKey);
8388b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        startRepeatKey(key);
8392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (mIsAllowedDraggingFinger) {
8402fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            processDraggingFingerInToNewKey(key, x, y, eventTime);
8412a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
8422a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive touches may be reported as a sudden move by
8432a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // touch panel firmware. This hack detects such cases and translates the move event to
8442a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // successive up and down events.
8452a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // TODO: Should find a way to balance gesture detection and this hack.
8462a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        else if (sNeedsPhantomSuddenMoveEventHack
8472fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                && getDistance(x, y, lastX, lastY) >= mPhantomSuddenMoveThreshold) {
8482a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
8492a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
8502a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: On some devices, quick successive proximate touches may be reported as a bogus
8512a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // down-move-up event by touch panel firmware. This hack detects such cases and breaks
8522a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // these events into separate up and down events.
853c986d16e8aa97c042e1791cabb0a79bb9aaf04a1Tadashi G. Takaoka        else if (sTypingTimeRecorder.isInFastTyping(eventTime)
8542a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
8552a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
8562a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        }
8572a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // HACK: If there are currently multiple touches, register the key even if the finger
8582a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // slides off the key. This defends against noise from some touch panels when there are
8592a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // close multiple touches.
8602a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        // Caveat: When in chording input mode with a modifier key, we don't use this hack.
86193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        else if (getActivePointerTrackerCount() > 1
8622a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
8632a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (DEBUG_MODE) {
8642a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                Log.w(TAG, String.format("[%d] onMoveEvent:"
8652a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka                        + " detected sliding finger while multi touching", mPointerId));
8663c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            }
8672a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            onUpEvent(x, y, eventTime);
86813d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            cancelTrackingForAction();
86953b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey, true /* withAnimation */);
8702a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka        } else {
8712a3b15b267f4a4c43a2d3f47241c489b9cd94d41Tadashi G. Takaoka            if (!mIsDetectingGesture) {
87213d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
8738b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
87453b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            setReleasedKeyGraphics(oldKey, true /* withAnimation */);
8758b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
8768b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
8778b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
8782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void dragFingerOutFromOldKey(final Key oldKey, final int x, final int y) {
8798b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // The pointer has been slid out from the previous key, we must call onRelease() to
8808b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        // notify that the previous key has been released.
8812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        processDraggingFingerOutFromOldKey(oldKey);
8822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (mIsAllowedDraggingFinger) {
8838b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onMoveToNewKey(null, x, y);
8848b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        } else {
8858b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (!mIsDetectingGesture) {
88613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                cancelTrackingForAction();
8878b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            }
8888b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        }
8898b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka    }
8908b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka
8916c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka    private void onMoveEventInternal(final int x, final int y, final long eventTime) {
8928a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastX = mLastX;
8938a995157f37365cf79b893e9106d1830d70c39dcTadashi G. Takaoka        final int lastY = mLastY;
894e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka        final Key oldKey = mCurrentKey;
8958b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        final Key newKey = onMoveKey(x, y);
896eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
897a00838b6e8d7b3ebb55b26cccc84a1763f88f0a9Tadashi G. Takaoka        if (sGestureEnabler.shouldHandleGesture()) {
8986c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            // Register move event on gesture tracker.
8998b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
9006c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (sInGesture) {
9016c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mCurrentKey = null;
90253b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                setReleasedKeyGraphics(oldKey, true /* withAnimation */);
9036c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                return;
9046c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            }
905eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
906eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
9078b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka        if (newKey != null) {
9083c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
9092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                dragFingerFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
9103c6d3a4df8b23b091c34ee3590268d469d46cbeeTadashi G. Takaoka            } else if (oldKey == null) {
911831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // The pointer has been slid in to the new key, but the finger was not on any keys.
912831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka                // In this case, we must call onPress() to notify that the new key is being pressed.
9132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                processDraggingFingerInToNewKey(newKey, x, y, eventTime);
914c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka            }
915831198a4e4295bcdae44b7d00686b8a93aed720bTadashi G. Takaoka        } else { // newKey == null
9168b449c6dda88174ec19bfc366baf048a72857215Tadashi G. Takaoka            if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
9172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                dragFingerOutFromOldKey(oldKey, x, y);
91807221a4ad11fa5ae6275c107f1f86260691bd505Tadashi G. Takaoka            }
9196a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
9202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (mIsInSlidingKeyInput) {
9212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            sDrawingProxy.showSlidingKeyInputPreview(this);
922212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
9236a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
9246a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
925e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onUpEvent(final int x, final int y, final long eventTime) {
926694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
927dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onUpEvent  :", x, y, eventTime);
928694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
9291d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
9302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sTimerProxy.cancelUpdateBatchInputTimer(this);
93193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        if (!sInGesture) {
93293b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            if (mCurrentKey != null && mCurrentKey.isModifier()) {
93393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // Before processing an up event of modifier key, all pointers already being
93493b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                // tracked should be released.
93593b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
93693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka            } else {
93793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
9381d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka            }
9391d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka        }
94035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(x, y, eventTime);
94193b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
9421d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9431d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
944d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
945d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
946d9786ce2e389c8c02af7773b53b5c44fe4fa0b0cTadashi G. Takaoka    // "virtual" up event.
9470cc425bd9c476d3cb6708554282a3242019eb317Tadashi G. Takaoka    @Override
948694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    public void onPhantomUpEvent(final long eventTime) {
949694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
950547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka            printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
951694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
95235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        onUpEventInternal(mLastX, mLastY, eventTime);
95313d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
9541d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
9551d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
95635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang    private void onUpEventInternal(final int x, final int y, final long eventTime) {
9572fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sTimerProxy.cancelKeyTimersOf(this);
9582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final boolean isInDraggingFinger = mIsInDraggingFinger;
959e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
9602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        resetKeySelectionByDraggingFinger();
9616c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mIsDetectingGesture = false;
9626c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final Key currentKey = mCurrentKey;
9636c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        mCurrentKey = null;
964a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        final int currentRepeatingKeyCode = mCurrentRepeatingKeyCode;
965a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
9665a40dcaf8b6250eeea241471e54e8fe856cdf19bTadashi G. Takaoka        // Release the last pressed key.
96753b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        setReleasedKeyGraphics(currentKey, true /* withAnimation */);
96835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
96935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        if (isShowingMoreKeysPanel()) {
97013d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka            if (!mIsTrackingForActionDisabled) {
97135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedX = mMoreKeysPanel.translateX(x);
97235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int translatedY = mMoreKeysPanel.translateY(y);
97335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
97435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            }
975fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka            dismissMoreKeysPanel();
97635580bad6f3da3b204653825bbb6871563e70728Tom Ouyang            return;
9779ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        }
978eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
9796c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (sInGesture) {
9806c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            if (currentKey != null) {
9817dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                callListenerOnRelease(currentKey, currentKey.getCode(), true /* withSliding */);
982eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            }
9834a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka            if (mBatchInputArbiter.mayEndBatchInput(
9844a4b6d42a79779fe2a1eaf9c251cf98ab6fdccb5Tadashi G. Takaoka                    eventTime, getActivePointerTrackerCount(), this)) {
985904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka                sInGesture = false;
986904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            }
987904866c701cd1708f1a2a31e53dfc262ec3eb2b5Tadashi G. Takaoka            showGestureTrail();
988eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang            return;
989eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        }
99058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
99113d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        if (mIsTrackingForActionDisabled) {
992d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka            return;
993694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
994a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        if (currentKey != null && currentKey.isRepeatable()
9952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                && (currentKey.getCode() == currentRepeatingKeyCode) && !isInDraggingFinger) {
996e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka            return;
9976a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
998e3b1bdc4f18f77f54b33776ad698d57970acd722Tadashi G. Takaoka        detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
9992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (isInSlidingKeyInput) {
100041016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka            callListenerOnFinishSlidingInput();
100141016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        }
10026a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10036a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1004b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
100513d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka    public void cancelTrackingForAction() {
10068353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
10078353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka            return;
10088353e751cae4a26d186fb645e9d3d40e1bc5d14bTadashi G. Takaoka        }
100913d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        mIsTrackingForActionDisabled = true;
1010b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1011b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
101262316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka    public boolean isInOperation() {
101362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka        return !mIsTrackingForActionDisabled;
101462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka    }
101562316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka
101653b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka    public void onLongPressed() {
101734a9e2ae87c6718d439b294d9536b6587a4f89f8Tadashi G. Takaoka        sTimerProxy.cancelLongPressTimersOf(this);
101853b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
101953b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            return;
102053b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        }
102153b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        final Key key = getKey();
102253b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        if (key == null) {
102353b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            return;
102453b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        }
102553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        if (key.hasNoPanelAutoMoreKey()) {
102653b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            cancelKeyTracking();
102753b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            final int moreKeyCode = key.getMoreKeys()[0].mCode;
102853b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            sListener.onPressKey(moreKeyCode, 0 /* repeatCont */, true /* isSinglePointer */);
102953b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            sListener.onCodeInput(moreKeyCode, Constants.NOT_A_COORDINATE,
103053b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                    Constants.NOT_A_COORDINATE, false /* isKeyRepeat */);
103153b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            sListener.onReleaseKey(moreKeyCode, false /* withSliding */);
103253b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            return;
103353b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        }
103453b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        final int code = key.getCode();
103553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
103653b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
103753b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            if (sListener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
103853b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                cancelKeyTracking();
103953b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                sListener.onReleaseKey(code, false /* withSliding */);
104053b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka                return;
104153b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            }
104253b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        }
104353b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka
104453b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        setReleasedKeyGraphics(key, false /* withAnimation */);
104553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = sDrawingProxy.showMoreKeysKeyboard(key, this);
104653b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        if (moreKeysPanel == null) {
104753b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka            return;
104853b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        }
104953b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(mLastX);
105053b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(mLastY);
105153b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        moreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, SystemClock.uptimeMillis());
105253b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        mMoreKeysPanel = moreKeysPanel;
105362316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka    }
105462316d7e821fa3a1ed052eb1ac2e8c0d08931d3eTadashi G. Takaoka
105553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka    private void cancelKeyTracking() {
10562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        resetKeySelectionByDraggingFinger();
105713d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka        cancelTrackingForAction();
105853b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey, true /* withAnimation */);
105993b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        sPointerTrackerQueue.remove(this);
1060d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka    }
1061d2c2b4d112ee17750c1a49ff223b9410aa9e4ec6Tadashi G. Takaoka
1062e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka    private void onCancelEvent(final int x, final int y, final long eventTime) {
1063694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        if (DEBUG_EVENT) {
1064dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            printTouchEvent("onCancelEvt:", x, y, eventTime);
1065694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        }
10661d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
10677c1e853387f71235fd0bd8051246f7a95be5ed53Tadashi G. Takaoka        cancelBatchInput();
10685c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllPointerTrackers();
10698e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        sPointerTrackerQueue.releaseAllPointers(eventTime);
1070baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka        onCancelEventInternal();
10711d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka    }
10721d7d9664a9850a7c8043651e4b7a055ec034f571Tadashi G. Takaoka
1073baf83886be975d804eda3e1519b7255026e5163eTadashi G. Takaoka    private void onCancelEventInternal() {
10742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sTimerProxy.cancelKeyTimersOf(this);
107553b6d627e7fe66ce47ee6ae01254abc070558e77Tadashi G. Takaoka        setReleasedKeyGraphics(mCurrentKey, true /* withAnimation */);
10762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        resetKeySelectionByDraggingFinger();
1077fa9b9578d44748de512c947651010e703c663936Tadashi G. Takaoka        dismissMoreKeysPanel();
10786a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
10796a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1080b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka    private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
1081b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            final Key newKey) {
1082694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka        final Key curKey = mCurrentKey;
10836a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        if (newKey == curKey) {
1084e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return false;
1085a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1086a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (curKey == null /* && newKey != null */) {
1087a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1088a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1089a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        // Here curKey points to the different key from newKey.
1090a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
10912fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mIsInSlidingKeyInput);
1092a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
1093a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
1094a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1095a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
1096a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        / mKeyboard.mMostCommonKeyWidth;
1097a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1098a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
1099b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1100a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            return true;
1101a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        }
1102c986d16e8aa97c042e1791cabb0a79bb9aaf04a1Tadashi G. Takaoka        if (!mIsAllowedDraggingFinger && sTypingTimeRecorder.isInFastTyping(eventTime)
1103a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
1104a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka            if (DEBUG_MODE) {
1105a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float keyDiagonal = (float)Math.hypot(
1106a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
1107a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                final float lengthFromDownRatio =
1108c986d16e8aa97c042e1791cabb0a79bb9aaf04a1Tadashi G. Takaoka                        mBogusMoveEventDetector.getAccumulatedDistanceFromDownKey() / keyDiagonal;
1109a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
1110a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        + " %.2f key diagonal from virtual down point",
1111a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka                        mPointerId, lengthFromDownRatio));
1112b0952888ebabc5c1fe7c3149d38e4350c890a0a0Tadashi G. Takaoka            }
1113e6cb8fc234940700ae97af787e62962a98d332e5Tadashi G. Takaoka            return true;
11146a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka        }
1115a232930d9cfb4a9710eb57735b4a3fe722d64a2bTadashi G. Takaoka        return false;
11166a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11176a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
1118694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void startLongPressTimer(final Key key) {
11191b5b0a3ebc80c24120a8fd5ba45497127b93d238Tadashi G. Takaoka        // Note that we need to cancel all active long press shift key timers if any whenever we
11201b5b0a3ebc80c24120a8fd5ba45497127b93d238Tadashi G. Takaoka        // start a new long press timer for both non-shift and shift keys.
112134a9e2ae87c6718d439b294d9536b6587a4f89f8Tadashi G. Takaoka        sTimerProxy.cancelLongPressShiftKeyTimer();
112243ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (sInGesture) return;
112343ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (key == null) return;
112443ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        if (!key.isLongPressEnabled()) return;
112543ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // Caveat: Please note that isLongPressEnabled() can be true even if the current key
11262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // doesn't have its more keys. (e.g. spacebar, globe key) If we are in the dragging finger
11272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // mode, we will disable long press timer of such key.
112843ad100d8bc20fc1327a462f24b5f8c99849ffbeTadashi G. Takaoka        // We always need to start the long press timer if the key has its more keys regardless of
11292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // whether or not we are in the dragging finger mode.
11302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (mIsInDraggingFinger && key.getMoreKeys() == null) return;
11312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
11322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int delay = getLongPressTimeout(key.getCode());
11331b5b0a3ebc80c24120a8fd5ba45497127b93d238Tadashi G. Takaoka        if (delay <= 0) return;
11342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sTimerProxy.startLongPressTimerOf(this, delay);
11352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
11362fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
11372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private int getLongPressTimeout(final int code) {
11382fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (code == Constants.CODE_SHIFT) {
11392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return sParams.mLongPressShiftLockTimeout;
11402fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
11412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
11422fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (mIsInSlidingKeyInput) {
11432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // We use longer timeout for sliding finger input started from the modifier key.
11442fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
1145212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
11462fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return longpressTimeout;
114766e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka    }
114866e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
11493623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka    private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
115083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        if (key == null) {
11518aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka            callListenerOnCancelInput();
1152dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka            return;
1153dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka        }
115466e306d01c6820d4f4d8b2209438ec086b48ac51Tadashi G. Takaoka
11557dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
11562282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        callListenerOnCodeInput(key, code, x, y, eventTime, false /* isKeyRepeat */);
115741016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        callListenerOnRelease(key, code, false /* withSliding */);
11586a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka    }
11596a1514a0deac7f3d8ec33430403b2caea05bc8b9Tadashi G. Takaoka
11608126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    private void startRepeatKey(final Key key) {
11618126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        if (sInGesture) return;
11628126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        if (key == null) return;
11638126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        if (!key.isRepeatable()) return;
11642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // Don't start key repeat when we are in the dragging finger mode.
11652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (mIsInDraggingFinger) return;
1166ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        final int startRepeatCount = 1;
11672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        startKeyRepeatTimer(startRepeatCount);
11688126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    }
11698126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka
1170ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka    public void onKeyRepeat(final int code, final int repeatCount) {
11718126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        final Key key = getKey();
11727dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (key == null || key.getCode() != code) {
1173a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka            mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
11748126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            return;
11758126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        }
1176a456e3f659e03c5a36b87e318a10d469520cf72bSatoshi Kataoka        mCurrentRepeatingKeyCode = code;
11770938fb6913c10d32f0a870210fc208c7bbfe282cSatoshi Kataoka        mIsDetectingGesture = false;
1178ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        final int nextRepeatCount = repeatCount + 1;
11792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        startKeyRepeatTimer(nextRepeatCount);
1180ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka        callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
11812282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis(),
11822282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                true /* isKeyRepeat */);
11838126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka    }
11848126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka
11852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private void startKeyRepeatTimer(final int repeatCount) {
11862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int delay =
11872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                (repeatCount == 1) ? sParams.mKeyRepeatStartTimeout : sParams.mKeyRepeatInterval;
11882fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        sTimerProxy.startKeyRepeatTimerOf(this, repeatCount, delay);
11892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
11902fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
1191694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka    private void printTouchEvent(final String title, final int x, final int y,
1192694ccb5ba1bb61caea379487c6bdfae7c64c2643Tadashi G. Takaoka            final long eventTime) {
1193723aaa2eebcfea0d285f11fc265941057332664dTadashi G. Takaoka        final Key key = mKeyDetector.detectHitKey(x, y);
11949fd7447d6c814aa48cd0b41acf9ff5fc0f62abb8Tadashi G. Takaoka        final String code = (key == null ? "none" : Constants.printableCode(key.getCode()));
119558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
119613d5da8b8819866bd2ef1aac6eadc74305bf8d55Tadashi G. Takaoka                (mIsTrackingForActionDisabled ? "-" : " "), title, x, y, eventTime, code));
1197dbc44989a5be68679c889ae45cde17002b748fdaTadashi G. Takaoka    }
11986e5a3986854549a45c95770b5a88ae5577e93299Tadashi G. Takaoka}
1199