MainKeyboardView.java revision 6dde878d515f7bf5268d16a8fe4921d8821c5ae7
15f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/*
25f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project
35f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
45f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
55f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * you may not use this file except in compliance with the License.
65f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * You may obtain a copy of the License at
75f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
85f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
95f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * See the License for the specific language governing permissions and
145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * limitations under the License.
155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.Context;
205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager;
215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.res.Resources;
225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.res.TypedArray;
236dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message;
2563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaokaimport android.os.SystemClock;
265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.GestureDetector;
295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.accessibility.AccessibilityEvent;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.widget.PopupWindow;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
386dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.deprecated.VoiceProxy;
39f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
402321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder;
426dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
456dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.Utils;
465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_verticalCorrection
545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_popupLayout
555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
56f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokapublic class LatinKeyboardBaseView extends KeyboardView implements PointerTracker.KeyEventHandler {
575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private static final String TAG = LatinKeyboardBaseView.class.getSimpleName();
585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    // Timing constants
625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private final int mKeyRepeatInterval;
635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    // XML attribute
655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private final float mVerticalCorrection;
665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private final int mPopupLayout;
675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    // Mini keyboard
695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private PopupWindow mPopupWindow;
7063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka    private PopupPanel mPopupPanel;
7163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka    private int mPopupPanelPointerTrackerId;
725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private final WeakHashMap<Key, PopupPanel> mPopupPanelCache =
735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            new WeakHashMap<Key, PopupPanel>();
745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private final boolean mHasDistinctMultitouch;
795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldKeyIndex;
815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
82a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka    protected KeyDetector mKeyDetector;
835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
84c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    // To detect double tap.
855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    protected GestureDetector mGestureDetector;
865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
87f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler = new KeyTimerHandler(this);
885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
892321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka    private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardBaseView>
902321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
91f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
92f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
9398b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        private static final int MSG_IGNORE_DOUBLE_TAP = 3;
945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        private boolean mInKeyRepeat;
965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
97f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        public KeyTimerHandler(LatinKeyboardBaseView outerInstance) {
985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            super(outerInstance);
995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
1025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void handleMessage(Message msg) {
1035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final LatinKeyboardBaseView keyboardView = getOuterInstance();
1045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
1055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
1065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
1075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                tracker.onRepeatKey(msg.arg1);
1085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                startKeyRepeatTimer(keyboardView.mKeyRepeatInterval, msg.arg1, tracker);
1095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
1105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
1115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                keyboardView.openMiniKeyboardIfRequired(msg.arg1, tracker);
1125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
1135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
1145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1162321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
1175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {
1185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mInKeyRepeat = true;
1195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay);
1205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
1235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mInKeyRepeat = false;
1245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
1255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
1285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return mInKeyRepeat;
1295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1312321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
1325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {
13398b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
1345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay);
1355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1372321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
13898b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
1395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
1405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1422321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
1435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyTimers() {
1445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            cancelKeyRepeatTimer();
14598b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
1465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_IGNORE_DOUBLE_TAP);
1475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void startIgnoringDoubleTap() {
1505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_IGNORE_DOUBLE_TAP),
1515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
1525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isIgnoringDoubleTap() {
1555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return hasMessages(MSG_IGNORE_DOUBLE_TAP);
1565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelAllMessages() {
1595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            cancelKeyTimers();
1605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
1625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
163c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
164c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        private boolean mProcessingShiftDoubleTapEvent = false;
165c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
166c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
167c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        public boolean onDoubleTap(MotionEvent firstDown) {
168c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            final Keyboard keyboard = getKeyboard();
169c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard instanceof LatinKeyboard
170c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    && ((LatinKeyboard) keyboard).isAlphaKeyboard()) {
171c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                final int pointerIndex = firstDown.getActionIndex();
172c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                final int id = firstDown.getPointerId(pointerIndex);
173c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                final PointerTracker tracker = getPointerTracker(id);
174c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                // If the first down event is on shift key.
175c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                if (tracker.isOnShiftKey((int) firstDown.getX(), (int) firstDown.getY())) {
176c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    mProcessingShiftDoubleTapEvent = true;
177c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    return true;
178c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                }
179c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            }
180c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            mProcessingShiftDoubleTapEvent = false;
181c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            return false;
182c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
183c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
184c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
185c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        public boolean onDoubleTapEvent(MotionEvent secondTap) {
186c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            if (mProcessingShiftDoubleTapEvent
187c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    && secondTap.getAction() == MotionEvent.ACTION_DOWN) {
188c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                final MotionEvent secondDown = secondTap;
189c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                final int pointerIndex = secondDown.getActionIndex();
190c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                final int id = secondDown.getPointerId(pointerIndex);
191c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                final PointerTracker tracker = getPointerTracker(id);
192c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                // If the second down event is also on shift key.
193c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                if (tracker.isOnShiftKey((int) secondDown.getX(), (int) secondDown.getY())) {
194c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    // Detected a double tap on shift key. If we are in the ignoring double tap
195c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    // mode, it means we have already turned off caps lock in
196c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    // {@link KeyboardSwitcher#onReleaseShift} .
197c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    final boolean ignoringDoubleTap = mKeyTimerHandler.isIgnoringDoubleTap();
198c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    if (!ignoringDoubleTap)
199c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                        onDoubleTapShiftKey(tracker);
200c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                    return true;
201c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                }
202c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                // Otherwise these events should not be handled as double tap.
203c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                mProcessingShiftDoubleTapEvent = false;
204c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            }
205c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka            return mProcessingShiftDoubleTapEvent;
206c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
207c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
208c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
2095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public LatinKeyboardBaseView(Context context, AttributeSet attrs) {
2105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        this(context, attrs, R.attr.keyboardViewStyle);
2115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
2125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public LatinKeyboardBaseView(Context context, AttributeSet attrs, int defStyle) {
2145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
2155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final TypedArray a = context.obtainStyledAttributes(
2175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
2185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mVerticalCorrection = a.getDimensionPixelOffset(
2195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                R.styleable.KeyboardView_verticalCorrection, 0);
2205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mPopupLayout = a.getResourceId(R.styleable.KeyboardView_popupLayout, 0);
2215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        a.recycle();
2225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final Resources res = getResources();
224a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        final float keyHysteresisDistance = res.getDimension(R.dimen.key_hysteresis_distance);
225a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka        mKeyDetector = new KeyDetector(keyHysteresisDistance);
2265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final boolean ignoreMultitouch = true;
228c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        mGestureDetector = new GestureDetector(
229c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka                getContext(), new DoubleTapListener(), null, ignoreMultitouch);
2305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mGestureDetector.setIsLongpressEnabled(false);
2315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mHasDistinctMultitouch = context.getPackageManager()
2335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
2345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
235906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka
2365c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.init(mHasDistinctMultitouch, getContext());
2375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
2385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void startIgnoringDoubleTap() {
2405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (ENABLE_CAPSLOCK_BY_DOUBLETAP)
241f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka            mKeyTimerHandler.startIgnoringDoubleTap();
2425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
2435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2445a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    public void setKeyboardActionListener(KeyboardActionListener listener) {
2455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
2465c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
2475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
2485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
2505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
2515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
2525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
2530efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
2540efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
2555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
2565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
2575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
258bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
2590efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
2600efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
2610efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
2620efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
2630efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
264f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
265f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
266f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
267f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
268f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
2690efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
2700efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
2710efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
2720efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
2736dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    @Override
2746dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
2756dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
2766dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        if (keyboard instanceof LatinKeyboard) {
2776dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard;
2786dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            if (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard()) {
2796dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                // Phone and number keyboard never shows popup preview.
2806dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                super.setKeyPreviewPopupEnabled(false, delay);
2816dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                return;
2826dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
2836dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
2846dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        super.setKeyPreviewPopupEnabled(previewEnabled, delay);
2856dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
2866dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
2875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
2885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
2895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
2905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
2915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
2925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
2935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
2945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
2955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void setKeyboard(Keyboard keyboard) {
2965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview
297f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelKeyTimers();
2985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
2995a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
3005a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
3018da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
3025c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
3035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mPopupPanelCache.clear();
3045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
3075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns whether the device has distinct multi-touch panel.
3085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the device has distinct multi-touch panel.
3095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
3105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
3115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mHasDistinctMultitouch;
3125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
3155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
3165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * codes for adjacent keys.  When disabled, only the primary key code will be
3175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * reported.
3185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param enabled whether or not the proximity correction is enabled
3195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
3205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void setProximityCorrectionEnabled(boolean enabled) {
3215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyDetector.setProximityCorrectionEnabled(enabled);
3225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
3255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns true if proximity correction is enabled.
3265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
3275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isProximityCorrectionEnabled() {
3285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyDetector.isProximityCorrectionEnabled();
3295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
3325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void cancelAllMessages() {
333f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
3345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.cancelAllMessages();
3355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private boolean openMiniKeyboardIfRequired(int keyIndex, PointerTracker tracker) {
3385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Check if we have a popup layout specified first.
3395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (mPopupLayout == 0) {
3405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
3415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
34363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        // Check if we are already displaying popup panel.
34463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        if (mPopupPanel != null)
34563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return false;
3465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final Key parentKey = tracker.getKey(keyIndex);
3475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (parentKey == null)
3485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
34963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return onLongPress(parentKey, tracker);
3505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private void onDoubleTapShiftKey(@SuppressWarnings("unused") PointerTracker tracker) {
3535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // When shift key is double tapped, the first tap is correctly processed as usual tap. And
3545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // the second tap is treated as this double tap event, so that we need not mark tracker
355906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka        // calling setAlreadyProcessed() nor remove the tracker from mPointerQueue.
3565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
3575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    // This default implementation returns a popup mini keyboard panel.
3605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    protected PopupPanel onCreatePopupPanel(Key parentKey) {
3615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (parentKey.mPopupCharacters == null)
3625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
3635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null);
3655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (container == null)
3665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            throw new NullPointerException();
3675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final PopupMiniKeyboardView miniKeyboardView =
3695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
3705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final Keyboard parentKeyboard = getKeyboard();
3715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final Keyboard miniKeyboard = new MiniKeyboardBuilder(
3728da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build();
3735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        miniKeyboardView.setKeyboard(miniKeyboard);
3745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
3765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
3775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return miniKeyboardView;
3795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
3825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    protected boolean needsToDimKeyboard() {
38363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return mPopupPanel != null;
3845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3866dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) {
3876dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
3886dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        // We should not set text fade factor to the keyboard which does not display the language on
3896dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        // its spacebar.
3906dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) {
3916dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            ((LatinKeyboard)keyboard).setSpacebarTextFadeFactor(fadeFactor, this);
3926dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
3936dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
3946dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
3955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
3965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Called when a key is long pressed. By default this will open mini keyboard associated
3975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * with this key.
3985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param parentKey the key that was long pressed
3995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
4005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the long press is handled, false otherwise. Subclasses should call the
4015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * method on the base class if the subclass doesn't wish to handle the call.
4025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
4046dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final int primaryCode = parentKey.mCode;
4056dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
4066dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        if (keyboard instanceof LatinKeyboard) {
4076dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            final LatinKeyboard latinKeyboard = (LatinKeyboard) keyboard;
4086dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.isPhoneKeyboard()) {
4096dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
4106dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                // Long pressing on 0 in phone number keypad gives you a '+'.
4116dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                return invokeOnKey(Keyboard.CODE_PLUS);
4126dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
4136dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.isAlphaKeyboard()) {
4146dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
4156dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                return invokeOnKey(Keyboard.CODE_CAPSLOCK);
4166dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
4176dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
4186dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) {
4196dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            // Both long pressing settings key and space key invoke IME switcher dialog.
4206dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            if (getKeyboardActionListener().onCustomRequest(
4216dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                    LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
4226dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
4236dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                return true;
4246dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            } else {
4256dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                return openPopupPanel(parentKey, tracker);
4266dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
4276dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        } else {
4286dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            return openPopupPanel(parentKey, tracker);
4296dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
4306dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
4316dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
4326dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    private boolean invokeOnKey(int primaryCode) {
4336dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        getKeyboardActionListener().onCodeInput(primaryCode, null,
4346dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
4356dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
4366dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        return true;
4376dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
4386dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
4396dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    private boolean openPopupPanel(Key parentKey, PointerTracker tracker) {
4405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        PopupPanel popupPanel = mPopupPanelCache.get(parentKey);
4415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (popupPanel == null) {
4425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            popupPanel = onCreatePopupPanel(parentKey);
4435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (popupPanel == null)
4445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                return false;
4455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mPopupPanelCache.put(parentKey, popupPanel);
4465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
4475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (mPopupWindow == null) {
4485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mPopupWindow = new PopupWindow(getContext());
4495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mPopupWindow.setBackgroundDrawable(null);
4505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mPopupWindow.setAnimationStyle(R.style.PopupMiniKeyboardAnimation);
4515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Allow popup window to be drawn off the screen.
4525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mPopupWindow.setClippingEnabled(false);
4535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
45463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mPopupPanel = popupPanel;
45563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        mPopupPanelPointerTrackerId = tracker.mPointerId;
45663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
4579ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        popupPanel.showPopupPanel(this, parentKey, tracker, mPopupWindow);
45863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        final int translatedX = popupPanel.translateX(tracker.getLastX());
45963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        final int translatedY = popupPanel.translateY(tracker.getLastY());
4609ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        tracker.onShowPopupPanel(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
4615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        invalidateAllKeys();
4635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
4645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private PointerTracker getPointerTracker(final int id) {
4675c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        return PointerTracker.getPointerTracker(id, this);
4685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
47163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        if (mPopupPanel != null) {
47263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
47363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
4745c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            return PointerTracker.isAnyInSlidingKeyInput();
4755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
4765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public int getPointerCount() {
4795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mOldPointerCount;
4805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
4835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean onTouchEvent(MotionEvent me) {
484f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
4855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
4865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
4875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
4885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
4895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
4915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
4925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
493f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
4945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
4955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
4965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Gesture detector must be enabled only when mini-keyboard is not on the screen.
49863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        if (mPopupPanel == null && mGestureDetector != null
4995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                && mGestureDetector.onTouchEvent(me)) {
5005c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            PointerTracker.dismissAllKeyPreviews();
501f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka            mKeyTimerHandler.cancelKeyTimers();
5025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
5035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
5065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
5075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
50863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        final int x, y;
50963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        if (mPopupPanel != null && id == mPopupPanelPointerTrackerId) {
51063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            x = mPopupPanel.translateX((int)me.getX(index));
51163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            y = mPopupPanel.translateY((int)me.getY(index));
51263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
51363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            x = (int)me.getX(index);
51463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            y = (int)me.getY(index);
5155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
517f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
5185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = getPointerTracker(id);
5195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
5205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
5215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
522f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
5235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
5245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
5255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
5285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
5295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
530f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
5315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
5325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            PointerTracker tracker = getPointerTracker(0);
5335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
5345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
5355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
5365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
5375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int newKeyIndex = tracker.getKeyIndexOn(x, y);
5385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                if (mOldKeyIndex != newKeyIndex) {
5390efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
5405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP)
541906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
5425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
5435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
5445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
5455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
5465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastX = tracker.getLastX();
5475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastY = tracker.getLastY();
5485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                mOldKeyIndex = tracker.getKeyIndexOn(lastX, lastY);
549906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                tracker.onUpEvent(lastX, lastY, eventTime);
5505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
5510efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                processMotionEvent(tracker, action, x, y, eventTime, this);
5525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
5535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
5545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
5555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
5565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
5575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
5605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
5615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
56263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                final int px, py;
56363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                if (mPopupPanel != null && tracker.mPointerId == mPopupPanelPointerTrackerId) {
56463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    px = mPopupPanel.translateX((int)me.getX(i));
56563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    py = mPopupPanel.translateY((int)me.getY(i));
56663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                } else {
56763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    px = (int)me.getX(i);
56863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    py = (int)me.getY(i);
56963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                }
57063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                tracker.onMoveEvent(px, py, eventTime);
5715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
5725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
5730efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka            processMotionEvent(getPointerTracker(id), action, x, y, eventTime, this);
5745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
5775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5793fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka    private static void processMotionEvent(PointerTracker tracker, int action, int x, int y,
580f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka            long eventTime, PointerTracker.KeyEventHandler handler) {
5813fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka        switch (action) {
5823fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
5833fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
584f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka            tracker.onDownEvent(x, y, eventTime, handler);
5853fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka            break;
5863fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka        case MotionEvent.ACTION_UP:
5873fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
5883fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka            tracker.onUpEvent(x, y, eventTime);
58904dec7f84de798482a5ddf9700e23ab561fe18fbTadashi G. Takaoka            break;
59004dec7f84de798482a5ddf9700e23ab561fe18fbTadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
59104dec7f84de798482a5ddf9700e23ab561fe18fbTadashi G. Takaoka            tracker.onMoveEvent(x, y, eventTime);
5923fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka            break;
5933fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka        case MotionEvent.ACTION_CANCEL:
5943fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka            tracker.onCancelEvent(x, y, eventTime);
5953fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka            break;
5963fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka        }
5973fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka    }
5983fdae97417b8ca33ae199c7817de7a80a3aeddaeTadashi G. Takaoka
5995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
6005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
6015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.closing();
6029ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        dismissPopupPanel();
6035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mPopupPanelCache.clear();
6045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6069ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    @Override
6079ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    public boolean dismissPopupPanel() {
6085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (mPopupWindow != null && mPopupWindow.isShowing()) {
6095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mPopupWindow.dismiss();
61063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            mPopupPanel = null;
61163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            mPopupPanelPointerTrackerId = -1;
6125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            invalidateAllKeys();
6135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
6145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
6165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean handleBack() {
6199ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka        return dismissPopupPanel();
6205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
6236dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    public void draw(Canvas c) {
6246dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
6256dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        boolean tryGC = true;
6266dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
6276dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            try {
6286dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                super.draw(c);
6296dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tryGC = false;
6306dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            } catch (OutOfMemoryError e) {
6316dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e);
6326dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
6336dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
6346dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6356dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
6366dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    @Override
6376dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    protected void onAttachedToWindow() {
6386dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        // Token is available from here.
6396dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        VoiceProxy.getInstance().onAttachedToWindow();
6406dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6416dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
6426dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    @Override
6435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean dispatchTouchEvent(MotionEvent event) {
64465a898d9ef63e321deb06d1ede835c182e7bcce1Alan Viverette        // Drop non-hover touch events when touch exploration is enabled.
6455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
64665a898d9ef63e321deb06d1ede835c182e7bcce1Alan Viverette            return false;
6475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return super.dispatchTouchEvent(event);
6505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
6535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
6545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
6555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = getPointerTracker(0);
6565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent(
6575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    event, tracker) || super.dispatchPopulateAccessibilityEvent(event);
6585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return super.dispatchPopulateAccessibilityEvent(event);
6615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
663586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
664586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * Receives hover events from the input framework. This method overrides
665586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
666586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * lower SDK versions, this method is never called.
667586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
668586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
669586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
670586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
671586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
672586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    public boolean dispatchHoverEvent(MotionEvent event) {
6735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
6745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = getPointerTracker(0);
675586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
6765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
678586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
6795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
6805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
682