MainKeyboardView.java revision 4331012a9e7779ff7c8359a443dc5815ee6ea8d9
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
19d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaokaimport android.animation.AnimatorInflater;
2031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaokaimport android.animation.ObjectAnimator;
215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.Context;
225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager;
2327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport android.content.res.Resources;
244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray;
256dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
2622b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint;
274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align;
28bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface;
294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable;
305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message;
3115d4793911fa305e0a58aced925961e948582979satokimport android.text.TextUtils;
325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
38b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
39f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.widget.PopupWindow;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
44f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
452321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
464daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport com.android.inputmethod.latin.Constants;
476dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
4815d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
5127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport com.android.inputmethod.latin.StringUtils;
523bf57a5624679a20db26df912077a53b9f90ad36Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeLocale;
536dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.Utils;
5415d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
55c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
566b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport java.util.Locale;
595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_verticalCorrection
665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_popupLayout
675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
68c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokapublic class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
69c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        SuddenJumpingTouchEventHandler.ProcessMotionEvent {
70c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    private static final String TAG = LatinKeyboardView.class.getSimpleName();
715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
72d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    // TODO: Kill process when the usability study mode was changed.
73d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
74d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge
75bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
76bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
77bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
78bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /* Space key and its icons */
794112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
804112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Drawable mSpaceIcon;
81bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
8231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
8331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
84bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
85fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
864daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final float mSpacebarTextRatio;
884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private float mSpacebarTextSize;
894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextColor;
904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextShadowColor;
91bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
92bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
93bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
94bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
95bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
96bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
97bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
9815d4793911fa305e0a58aced925961e948582979satok
9973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
10031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
10131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1024daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
10373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
1042affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
1059d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private PopupWindow mMoreKeysWindow;
1069d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1079d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private int mMoreKeysPanelPointerTrackerId;
1089d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
1099d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            new WeakHashMap<Key, MoreKeysPanel>();
1102affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
1115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
112160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final PointerTrackerParams mPointerTrackerParams;
113bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
1145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
115bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    protected KeyDetector mKeyDetector;
11606b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    private boolean mHasDistinctMultitouch;
1175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
118e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mOldKey;
1195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
120160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler;
1215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
122c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
1232321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
124f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
125f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
1260ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        private static final int MSG_DOUBLE_TAP = 3;
12773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        private static final int MSG_TYPING_STATE_EXPIRED = 4;
1285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
129a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        private final KeyTimerParams mParams;
1305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
131a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) {
1325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            super(outerInstance);
133a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mParams = params;
1345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
1375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void handleMessage(Message msg) {
138c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka            final LatinKeyboardView keyboardView = getOuterInstance();
1395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
1405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
1415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
1428a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                final Key currentKey = tracker.getKey();
1438a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                if (currentKey != null && currentKey.mCode == msg.arg1) {
1448a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                    tracker.onRegisterKey(currentKey);
1458a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                    startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
1468a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                }
1475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
1485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
149a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                if (tracker != null) {
1502affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                    keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker);
151a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                } else {
152a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
153a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
1545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
15573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            case MSG_TYPING_STATE_EXPIRED:
156b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
157b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                        keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
15873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                break;
1595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
1605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
16232c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        private void startKeyRepeatTimer(PointerTracker tracker, long delay) {
1638a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            final Key key = tracker.getKey();
1648a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (key == null) return;
1658a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
16632c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        }
16732c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka
1682321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
169a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker) {
17032c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka            startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout);
1715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
1745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
1755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1770d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        // TODO: Suppress layout changes in key repeat mode
1785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
1790d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka            return hasMessages(MSG_REPEAT_KEY);
1805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1822321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
183a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code) {
18498b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
185a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            final int delay;
186a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            switch (code) {
187a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            case Keyboard.CODE_SHIFT:
18840e9012276b1df9be8c1a9069eaeb16027549a85Tadashi G. Takaoka                delay = mParams.mLongPressShiftKeyTimeout;
189a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
190a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            default:
191a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                delay = 0;
192a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
193a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
194a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            if (delay > 0) {
195a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, code, 0), delay);
196a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
197a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
198a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
199a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        @Override
200a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker) {
201a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            cancelLongPressTimer();
2027b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (tracker == null) {
2037b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                return;
2047b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2057b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final Key key = tracker.getKey();
2067b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final int delay;
2077b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            switch (key.mCode) {
2087b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            case Keyboard.CODE_SHIFT:
2097b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                delay = mParams.mLongPressShiftKeyTimeout;
2107b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2117b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            default:
2127b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
2137b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // We use longer timeout for sliding finger input started from the symbols
2147b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // mode key.
2157b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    delay = mParams.mLongPressKeyTimeout * 3;
2167b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                } else {
2177b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    delay = mParams.mLongPressKeyTimeout;
218a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2197b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2207b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2217b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (delay > 0) {
2227b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
223a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
2245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2262321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
22798b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
2285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
2295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
23145213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka        public static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
23245213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                final ObjectAnimator animatorToStart) {
23345213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            float startFraction = 0.0f;
23445213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            if (animatorToCancel.isStarted()) {
235b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToCancel.cancel();
23645213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
237b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
23845213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            final long startTime = (long)(animatorToStart.getDuration() * startFraction);
23945213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.start();
24045213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.setCurrentPlayTime(startTime);
241b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
242b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
2432321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
24473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public void startTypingStateTimer() {
24573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            final boolean isTyping = isTypingState();
246f1678ba8024606349bc184cfeaead2be059f7b5bTadashi G. Takaoka            removeMessages(MSG_TYPING_STATE_EXPIRED);
24773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            sendMessageDelayed(
24873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                    obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout);
24973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            if (isTyping) {
25073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                return;
25173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            }
252b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            final LatinKeyboardView keyboardView = getOuterInstance();
253b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
254b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
25593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
25693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
25793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
25873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState() {
25973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            return hasMessages(MSG_TYPING_STATE_EXPIRED);
26093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
26193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
26293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
2630ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer() {
2640ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP),
2655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
2665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2680ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        @Override
269beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer() {
270beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            removeMessages(MSG_DOUBLE_TAP);
271beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        }
272beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
273beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        @Override
2740ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout() {
2750ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            return hasMessages(MSG_DOUBLE_TAP);
2765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
277c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
278c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
2790ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelKeyTimers() {
2800ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyRepeatTimer();
2810ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelLongPressTimer();
282c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
283c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
2840ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelAllMessages() {
2850ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyTimers();
286c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
287c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
288c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
289160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    public static class PointerTrackerParams {
290160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
291160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
292160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public final float mTouchNoiseThresholdDistance;
293160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
294160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
295160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
296160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        private PointerTrackerParams() {
297160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
298160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mTouchNoiseThresholdTime =0;
299160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
300160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        }
301160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
302160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public PointerTrackerParams(TypedArray latinKeyboardViewAttr) {
303160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mSlidingKeyInputEnabled = latinKeyboardViewAttr.getBoolean(
304160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_slidingKeyInputEnable, false);
305a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTouchNoiseThresholdTime = latinKeyboardViewAttr.getInt(
306a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    R.styleable.LatinKeyboardView_touchNoiseThresholdTime, 0);
307a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTouchNoiseThresholdDistance = latinKeyboardViewAttr.getDimension(
308a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    R.styleable.LatinKeyboardView_touchNoiseThresholdDistance, 0);
309a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
310a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka    }
311a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
312a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka    static class KeyTimerParams {
313a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mKeyRepeatStartTimeout;
314a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mKeyRepeatInterval;
315a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mLongPressKeyTimeout;
316a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mLongPressShiftKeyTimeout;
31773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public final int mIgnoreAltCodeKeyTimeout;
318a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
319a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public KeyTimerParams(TypedArray latinKeyboardViewAttr) {
320160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mKeyRepeatStartTimeout = latinKeyboardViewAttr.getInt(
321160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_keyRepeatStartTimeout, 0);
322a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mKeyRepeatInterval = latinKeyboardViewAttr.getInt(
323a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    R.styleable.LatinKeyboardView_keyRepeatInterval, 0);
324160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mLongPressKeyTimeout = latinKeyboardViewAttr.getInt(
325160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_longPressKeyTimeout, 0);
326160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mLongPressShiftKeyTimeout = latinKeyboardViewAttr.getInt(
327160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_longPressShiftKeyTimeout, 0);
32873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            mIgnoreAltCodeKeyTimeout = latinKeyboardViewAttr.getInt(
32973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                    R.styleable.LatinKeyboardView_ignoreAltCodeKeyTimeout, 0);
330160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        }
331160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
332160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
333c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    public LatinKeyboardView(Context context, AttributeSet attrs) {
3345afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka        this(context, attrs, R.attr.latinKeyboardViewStyle);
3355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
337c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
3385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
3395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
340c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
341c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
3425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mHasDistinctMultitouch = context.getPackageManager()
3435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
344918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Resources res = getResources();
345d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
346918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka                Utils.getDeviceOverrideValue(res,
347d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        R.array.phantom_sudden_move_event_device_list, "false"));
34862b8dddb6ddb057555a1665759f9cf197e480c9fTadashi G. Takaoka        PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack);
34922b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
3504112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final TypedArray a = context.obtainStyledAttributes(
3514112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView);
3524112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
3534112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false);
3544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = a.getDrawable(
3554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon);
3564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio,
3574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                1000, 1000, 1) / 1000.0f;
3584112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0);
3594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextShadowColor = a.getColor(
3604112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
36131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = a.getInt(
3624daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha,
3634daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
364d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
365d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
36673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
36773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
36873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
36973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
370160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
371a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        final KeyTimerParams keyTimerParams = new KeyTimerParams(a);
372160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mPointerTrackerParams = new PointerTrackerParams(a);
373160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
374160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        final float keyHysteresisDistance = a.getDimension(
375160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                R.styleable.LatinKeyboardView_keyHysteresisDistance, 0);
376160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mKeyDetector = new KeyDetector(keyHysteresisDistance);
377a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams);
3782affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
3792affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
3804112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        a.recycle();
381160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
382160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        PointerTracker.setParameters(mPointerTrackerParams);
3834c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka
38431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
38531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
38631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
38731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
38831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
38931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
39031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
39131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
39231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(int resId, Object target) {
39331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        if (resId == 0) return null;
39431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
39531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
396d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
39731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
39873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
39931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
40031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
40173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
40231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    // Getter/setter methods for {@link ObjectAnimator}.
40331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
40431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
405d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
406d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
40731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(int alpha) {
40831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
40931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
41031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
41131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
41231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
41331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
41431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
41531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
41631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(int alpha) {
41731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
41831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        updateAltCodeKeyWhileTyping();
4195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4215a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    public void setKeyboardActionListener(KeyboardActionListener listener) {
4225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
4235c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
4245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
4285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
4295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4300efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
4310efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
4325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
4335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
435bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
4360efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
4370efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
4380efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4390efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4400efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
441f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
442f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
443f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
444f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
445f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
4460efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
4470efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
4480efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4490efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
4525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
4535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
4545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
4555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
4565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
4585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void setKeyboard(Keyboard keyboard) {
4598a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview and key repeat.
4608a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        mKeyTimerHandler.cancelLongPressTimer();
4615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
4625a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
4635a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
46462b8dddb6ddb057555a1665759f9cf197e480c9fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector, mGestureInputEnabled);
465c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mTouchScreenRegulator.setKeyboard(keyboard);
4669d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
4674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
4684112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
4691f2d0aa6c9b343848ee51e5bc13ccaaadf3ba4feTadashi G. Takaoka        mSpaceIcon = (mSpaceKey != null)
4704daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
4714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
4724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
47348a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
47448a7681e064ae259b840f0e757da2d716043d893Kurt Partridge            ResearchLogger.latinKeyboardView_setKeyboard(keyboard);
47548a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
476f147794fd41491a3383e6aca6d49007f58124068alanv
477f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
478f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
479f147794fd41491a3383e6aca6d49007f58124068alanv        AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
4805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns whether the device has distinct multi-touch panel.
4845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the device has distinct multi-touch panel.
4855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
4875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mHasDistinctMultitouch;
4885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
49006b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    public void setDistinctMultitouch(boolean hasDistinctMultitouch) {
49106b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka        mHasDistinctMultitouch = hasDistinctMultitouch;
49206b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    }
49306b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka
4945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
4965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * codes for adjacent keys.  When disabled, only the primary key code will be
4975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * reported.
4985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param enabled whether or not the proximity correction is enabled
4995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void setProximityCorrectionEnabled(boolean enabled) {
5015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyDetector.setProximityCorrectionEnabled(enabled);
5025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns true if proximity correction is enabled.
5065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isProximityCorrectionEnabled() {
5085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyDetector.isProximityCorrectionEnabled();
5095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
5124331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
5134331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
5144331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // Notify the research logger that the keyboard view has been attached.  This is needed
5154331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // to properly show the splash screen, which requires that the window token of the
5164331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // KeyboardView be non-null.
5174331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
5184331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge            ResearchLogger.getInstance().latinKeyboardView_onAttachedToWindow();
5194331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
5204331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
5214331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
5224331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    @Override
5235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void cancelAllMessages() {
524f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
5255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.cancelAllMessages();
5265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5282affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private boolean openMoreKeysKeyboardIfRequired(Key parentKey, PointerTracker tracker) {
5295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Check if we have a popup layout specified first.
5309d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysLayout == 0) {
5315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
5325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
53463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        // Check if we are already displaying popup panel.
5359d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null)
53663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return false;
5375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (parentKey == null)
5385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
53963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return onLongPress(parentKey, tracker);
5405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5429d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // This default implementation returns a more keys panel.
5439d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) {
5449d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (parentKey.mMoreKeys == null)
5455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
5465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5479d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null);
5485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (container == null)
5495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            throw new NullPointerException();
5505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5512affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
5522affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
5537ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(container, parentKey, this)
5547ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                .build();
5552affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
556b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
5575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5582affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
5595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5622affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka     * Called when a key is long pressed. By default this will open more keys keyboard associated
5635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * with this key.
5645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param parentKey the key that was long pressed
5655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
5665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the long press is handled, false otherwise. Subclasses should call the
5675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * method on the base class if the subclass doesn't wish to handle the call.
5685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
5709bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
5719bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinKeyboardView_onLongPress();
5729bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5736dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final int primaryCode = parentKey.mCode;
574e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka        if (parentKey.hasEmbeddedMoreKey()) {
575ed3bac91f242850c6d1833a5f8981b9cc208c5ddTadashi G. Takaoka            final int embeddedCode = parentKey.mMoreKeys[0].mCode;
5763708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
577e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka            invokeCodeInput(embeddedCode);
5783708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            invokeReleaseKey(primaryCode);
579a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode);
5803708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            return true;
5813708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
58281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        if (primaryCode == Keyboard.CODE_SPACE || primaryCode == Keyboard.CODE_LANGUAGE_SWITCH) {
5833708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
5843708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
5856dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
58642e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                invokeReleaseKey(primaryCode);
58742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                return true;
5886dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
5896dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
59017dc10724bf0db04d0a4bfb2b8be0739ad9e60c6Tadashi G. Takaoka        return openMoreKeysPanel(parentKey, tracker);
5916dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
5926dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
59342e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    private boolean invokeCustomRequest(int code) {
5942a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        return mKeyboardActionListener.onCustomRequest(code);
59542e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
59642e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
59742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    private void invokeCodeInput(int primaryCode) {
598ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        mKeyboardActionListener.onCodeInput(primaryCode,
5996dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
6006dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
60142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
60242e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
60342e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    private void invokeReleaseKey(int primaryCode) {
6042a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardActionListener.onReleaseKey(primaryCode, false);
6056dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6066dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
6079d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean openMoreKeysPanel(Key parentKey, PointerTracker tracker) {
6089d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
6099d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
6109d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            moreKeysPanel = onCreateMoreKeysPanel(parentKey);
6119d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            if (moreKeysPanel == null)
6125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                return false;
6139d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
6145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6159d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysWindow == null) {
6169d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow = new PopupWindow(getContext());
6179d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow.setBackgroundDrawable(null);
6182affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka            mMoreKeysWindow.setAnimationStyle(R.style.MoreKeysKeyboardAnimation);
6195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6209d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanel = moreKeysPanel;
6219d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelPointerTrackerId = tracker.mPointerId;
62263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
6237ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !parentKey.noKeyPreview();
6247ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
6257ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
6267ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
6277ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
6287ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                ? tracker.getLastX()
629f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka                : parentKey.mX + parentKey.mWidth / 2;
6307ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
6317ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
6327ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
6337ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointY = parentKey.mY + (keyPreviewEnabled
6347ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                ? mKeyPreviewDrawParams.mPreviewVisibleOffset
6357ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                : -parentKey.mVerticalGap);
6369d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        moreKeysPanel.showMoreKeysPanel(
6372a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener);
6389d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(tracker.getLastX());
6399d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(tracker.getLastY());
640e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
6411b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        dimEntireKeyboard(true);
6425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
6435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
6469d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null) {
64763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
64863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
6495c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            return PointerTracker.isAnyInSlidingKeyInput();
6505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public int getPointerCount() {
6545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mOldPointerCount;
6555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
6585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean onTouchEvent(MotionEvent me) {
65946286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
66046286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
66146286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
662c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return mTouchScreenRegulator.onTouchEvent(me);
663c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
664c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
665c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    @Override
666c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    public boolean processMotionEvent(MotionEvent me) {
667f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
6685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
6695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
6705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
6715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
6725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
6745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
6755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
676f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
6775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
6785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
6815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
6825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
68363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        final int x, y;
6849d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null && id == mMoreKeysPanelPointerTrackerId) {
6859d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            x = mMoreKeysPanel.translateX((int)me.getX(index));
6869d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            y = mMoreKeysPanel.translateY((int)me.getY(index));
68763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
68863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            x = (int)me.getX(index);
68963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            y = (int)me.getY(index);
6905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
691d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        if (ENABLE_USABILITY_STUDY_LOG) {
692d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            final String eventTag;
693d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            switch (action) {
694d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_UP:
695d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[Up]";
696d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
697d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_DOWN:
698d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[Down]";
699d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
700d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_POINTER_UP:
701d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[PointerUp]";
702d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
703d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_POINTER_DOWN:
704d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[PointerDown]";
705d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
706d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_MOVE: // Skip this as being logged below
707d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "";
708d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
709d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                default:
710d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[Action" + action + "]";
711d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
712d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            }
713d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            if (!TextUtils.isEmpty(eventTag)) {
714d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final float size = me.getSize(index);
715d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final float pressure = me.getPressure(index);
716d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                UsabilityStudyLogUtils.getInstance().write(
717d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        eventTag + eventTime + "," + id + "," + x + "," + y + ","
718d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        + size + "," + pressure);
719d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            }
720d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
721c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
7229bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime, index, id,
7239bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    x, y);
72415d4793911fa305e0a58aced925961e948582979satok        }
7255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
726f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
727e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
7285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
7295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
7305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
731f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
7325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
7345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
7385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
739f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
7405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
741e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
7425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
7435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
7445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
7455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
746e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                final Key newKey = tracker.getKeyOn(x, y);
747e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                if (mOldKey != newKey) {
7480efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
7495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP)
750906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
7515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
7525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
7535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
7545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
7555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastX = tracker.getLastX();
7565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastY = tracker.getLastY();
757e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                mOldKey = tracker.getKeyOn(lastX, lastY);
758906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                tracker.onUpEvent(lastX, lastY, eventTime);
7595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
7608ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka                tracker.processMotionEvent(action, x, y, eventTime, this);
7615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
7625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
7635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
7645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
7695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
770d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final int pointerId = me.getPointerId(i);
771e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                final PointerTracker tracker = PointerTracker.getPointerTracker(
772d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        pointerId, this);
77363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                final int px, py;
7743314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                final MotionEvent motionEvent;
7759d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                if (mMoreKeysPanel != null
7769d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                        && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
7779d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    px = mMoreKeysPanel.translateX((int)me.getX(i));
7789d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    py = mMoreKeysPanel.translateY((int)me.getY(i));
7793314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                    motionEvent = null;
78063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                } else {
78163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    px = (int)me.getX(i);
78263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    py = (int)me.getY(i);
7833314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                    motionEvent = me;
78463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                }
7853314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                tracker.onMoveEvent(px, py, eventTime, motionEvent);
786d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                if (ENABLE_USABILITY_STUDY_LOG) {
787d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    final float pointerSize = me.getSize(i);
788d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    final float pointerPressure = me.getPressure(i);
789d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    UsabilityStudyLogUtils.getInstance().write("[Move]"  + eventTime + ","
790d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                            + pointerId + "," + px + "," + py + ","
791d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                            + pointerSize + "," + pointerPressure);
792d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                }
793c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa                if (ProductionFlag.IS_EXPERIMENTAL) {
7949bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime,
7959bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                            i, pointerId, px, py);
79615d4793911fa305e0a58aced925961e948582979satok                }
7975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
799e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
800e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            tracker.processMotionEvent(action, x, y, eventTime, this);
8015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
8045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
8075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
8085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.closing();
8099d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        dismissMoreKeysPanel();
8109d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
8115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8139ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    @Override
8149d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public boolean dismissMoreKeysPanel() {
8159d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysWindow != null && mMoreKeysWindow.isShowing()) {
8169d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow.dismiss();
8179d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanel = null;
8189d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelPointerTrackerId = -1;
8191b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            dimEntireKeyboard(false);
8205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
8215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
8266dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    public void draw(Canvas c) {
8276dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
8286dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        boolean tryGC = true;
8296dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
8306dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            try {
8316dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                super.draw(c);
8326dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tryGC = false;
8336dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            } catch (OutOfMemoryError e) {
834dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(TAG, e);
8356dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
8366dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
8376dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
8386dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
839586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
840c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
841586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
842586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
843586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
844586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
845586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
846c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
847586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    public boolean dispatchHoverEvent(MotionEvent event) {
8485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
849e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
850586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
8515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
853586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
8545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
85622b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
8575afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    public void updateShortcutKey(boolean available) {
8587bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
8597bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        if (keyboard == null) return;
8607bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT);
8617bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        if (shortcutKey == null) return;
8627bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
8637bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
8645afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
8655afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
86673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    private void updateAltCodeKeyWhileTyping() {
86773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
86873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (keyboard == null) return;
86973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
87073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            invalidateKey(key);
87173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
87273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    }
87373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
874dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(boolean subtypeChanged,
875fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            boolean needsToDisplayLanguage, boolean hasMultipleEnabledIMEsOrSubtypes) {
8764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
877fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
878fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
879d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
880aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
8814c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
882dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
8834daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
88431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
88531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
88631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
887d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
888dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
88931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
89031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
89131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
892dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
8934c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
8944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
8954112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
8964112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
8974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    public void updateAutoCorrectionState(boolean isAutoCorrection) {
8984112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) return;
8994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
9004112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
90122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
90222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
90322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
904f9521c6f378e3f2aa13d9e382ae13708e3ae6317Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
9056bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
90673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
90773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
9084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (key.mCode == Keyboard.CODE_SPACE) {
909bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
9104112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
911fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
912bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
9134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
91481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        } else if (key.mCode == Keyboard.CODE_LANGUAGE_SWITCH) {
91581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
916fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
917b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
918b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
91922b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
92022b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
9214112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
922bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private boolean fitsTextIntoWidth(final int width, String text, Paint paint) {
923bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
924bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float textWidth = getLabelWidth(text, paint);
925bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (textWidth < width) return true;
926bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
927bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float scaleX = width / textWidth;
928bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) return false;
929bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
930bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
931bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return getLabelWidth(text, paint) < width;
932bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
933bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
934bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
935bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private String layoutLanguageOnSpacebar(Paint paint, InputMethodSubtype subtype,
936bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final int width) {
937bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
938bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        String text = getFullDisplayName(subtype, getResources());
939bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (fitsTextIntoWidth(width, text, paint)) {
940bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            return text;
941bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
942bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
943bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        text = getMiddleDisplayName(subtype);
944bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (fitsTextIntoWidth(width, text, paint)) {
945bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            return text;
9464112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9474112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
948bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        text = getShortDisplayName(subtype);
949bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (fitsTextIntoWidth(width, text, paint)) {
950bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            return text;
9514112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9524112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
953bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
9544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
956bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private void drawSpacebar(Key key, Canvas canvas, Paint paint) {
957bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        final int width = key.mWidth;
958b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        final int height = key.mHeight;
9594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
960f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
961bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
962bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
963bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
964bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextSize(mSpacebarTextSize);
965bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
966bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
9674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
9684112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
9694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
970b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
971aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextShadowColor);
97231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9734112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
974aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextColor);
97531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
9774112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9784112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9794112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
980bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
9814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
9824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
9834112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
9844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
985bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
9864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else if (mSpaceIcon != null) {
9874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = mSpaceIcon.getIntrinsicWidth();
9884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mSpaceIcon.getIntrinsicHeight();
9894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
9904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
991bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
9924112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9934112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
99427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
99527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // InputMethodSubtype's display name for spacebar text in its locale.
99627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //        isAdditionalSubtype (T=true, F=false)
99727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // locale layout | Short  Middle      Full
99827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // ------ ------ - ---- --------- ----------------------
99927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  en_US qwerty F  En  English   English (US)           exception
100027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  en_GB qwerty F  En  English   English (UK)           exception
100127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  fr    azerty F  Fr  Français  Français
100227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  fr_CA qwerty F  Fr  Français  Français (Canada)
100327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  de    qwertz F  De  Deutsch   Deutsch
100427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  zz    qwerty F      QWERTY    QWERTY
100527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  fr    qwertz T  Fr  Français  Français (QWERTZ)
100627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  de    qwerty T  De  Deutsch   Deutsch (QWERTY)
100727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  en_US azerty T  En  English   English (US) (AZERTY)
100827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  zz    azerty T      AZERTY    AZERTY
100927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
101027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's full display name in its locale.
101127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    static String getFullDisplayName(InputMethodSubtype subtype, Resources res) {
101227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
101327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
101427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
101527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
101627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return SubtypeLocale.getSubtypeDisplayName(subtype, res);
101727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
101827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
101927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's short display name in its locale.
102027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    static String getShortDisplayName(InputMethodSubtype subtype) {
102127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
102227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return "";
102327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
102427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
102527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getLanguage(), locale);
102627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
102727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
102827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's middle display name in its locale.
102927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    static String getMiddleDisplayName(InputMethodSubtype subtype) {
103027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
103127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
103227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
103327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
103427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
103527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
10365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
1037