MainKeyboardView.java revision 5433ce64c0f141bc4ee82912844f47f575654af5
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;
2213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.content.SharedPreferences;
235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager;
244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray;
256dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
26f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaokaimport android.graphics.Color;
2722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint;
284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align;
29bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface;
304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable;
315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message;
3272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaokaimport android.os.SystemClock;
3313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.preference.PreferenceManager;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
35375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport android.util.DisplayMetrics;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
37375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport android.util.SparseArray;
380e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport android.util.TypedValue;
395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
43b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
44f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
450e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport android.widget.TextView;
465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
4915f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.ExternallyReferenced;
50f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
512321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
52fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText;
53fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureTrailsPreview;
5449d8af8a4e900f3c68c333aba7fde0a11fd368b1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
550e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
56375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PreviewPlacerView;
57fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview;
584daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport com.android.inputmethod.latin.Constants;
5915d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
61375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords;
62c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
63a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.settings.DebugSettings;
64e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
65e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CoordinateUtils;
66e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
67a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaokaimport com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
68ccf4a310279b13bbf0b6aac76a0878178c1dfb7dTadashi G. Takaokaimport com.android.inputmethod.latin.utils.TypefaceUtils;
694be6198cb73cc24e10834153c4e049644ed187e3Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.UsabilityStudyLogUtils;
70ccf4a310279b13bbf0b6aac76a0878178c1dfb7dTadashi G. Takaokaimport com.android.inputmethod.latin.utils.ViewLayoutUtils;
716b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
78dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
79dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
80dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
81dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
82dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
84dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
85dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
86dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
87dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
88dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
89dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
90dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
91dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
92dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
93dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
94dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
95dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
96375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLayout
970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset
980e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight
990e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout
100d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout
101f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha
102dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
103375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout
1043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
1053623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
1063623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
1073623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
1083623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
1093623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
1103623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
1113623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
1123623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
1133623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
1143623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
1155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
116a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
117310ea9ab9058e922a562309cb0dc5d5e092000f1Tadashi G. Takaoka        PointerTracker.DrawingProxy, MoreKeysPanel.Controller {
118c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
120bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
121bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
122bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
123bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /* Space key and its icons */
1244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
1254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Drawable mSpaceIcon;
126bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
12731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
12831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
129bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
130fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1314daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1324112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final float mSpacebarTextRatio;
1334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private float mSpacebarTextSize;
1344112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextColor;
1354112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextShadowColor;
136bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
137bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
138bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
139bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
140bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
141bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
142bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
14315d4793911fa305e0a58aced925961e948582979satok
14473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
14531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
14631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1474daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
14873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
149375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Preview placer view
150375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final PreviewPlacerView mPreviewPlacerView;
151375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int[] mOriginCoords = CoordinateUtils.newInstance();
152fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final GestureFloatingPreviewText mGestureFloatingPreviewText;
153fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final GestureTrailsPreview mGestureTrailsPreview;
154fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final SlidingKeyInputPreview mSlidingKeyInputPreview;
155375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1560e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Key preview
1570e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int PREVIEW_ALPHA = 240;
158375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int mKeyPreviewLayoutId;
1590e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final int mKeyPreviewOffset;
1600e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final int mKeyPreviewHeight;
161375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
1620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
1630e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private boolean mShowKeyPreviewPopup = true;
1640e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private int mKeyPreviewLingerTimeout;
1650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
1662affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
167f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private final Paint mBackgroundDimAlphaPaint = new Paint();
168f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private boolean mNeedsToDimEntireKeyboard;
169a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final View mMoreKeysKeyboardContainer;
170a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache =
171a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            CollectionUtils.newWeakHashMap();
1722affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
173d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // More keys panel (used by both more keys keyboard and more suggestions view)
174d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // TODO: Consider extending to support multiple more keys panels
175d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
177375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Gesture floating preview text
178375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // TODO: Make this parameter customizable by user via settings.
179375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private int mGestureFloatingPreviewTextLingerTimeout;
180375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
181d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    private KeyDetector mKeyDetector;
18213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    private final boolean mHasDistinctMultitouch;
1835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
184e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mOldKey;
1855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
186160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler;
1875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
188a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
1892321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
19027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_TYPING_STATE_EXPIRED = 0;
191f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
192f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
1932a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 3;
19472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        private static final int MSG_UPDATE_BATCH_INPUT = 4;
1955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1965509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mIgnoreAltCodeKeyTimeout;
19772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        private final int mGestureRecognitionUpdateTime;
1985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1995509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public KeyTimerHandler(final MainKeyboardView outerInstance,
2005509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                final TypedArray mainKeyboardViewAttr) {
2015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            super(outerInstance);
2025509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
2035509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
2045509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
20572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
20672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
2075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
210dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void handleMessage(final Message msg) {
211c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
21248d8d8d0ae573605f938b3859bf58e1972f0d737Tadashi G. Takaoka            if (keyboardView == null) {
21348d8d8d0ae573605f938b3859bf58e1972f0d737Tadashi G. Takaoka                return;
21448d8d8d0ae573605f938b3859bf58e1972f0d737Tadashi G. Takaoka            }
2155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
2165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
21727e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            case MSG_TYPING_STATE_EXPIRED:
21827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
21927e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                break;
2205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
2218126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka                tracker.onKeyRepeat(msg.arg1);
2225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
2235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
2249552badf3c24d2098d227b0ddca0721b928a10b1Tadashi G. Takaoka                keyboardView.onLongPress(tracker);
2255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
22672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            case MSG_UPDATE_BATCH_INPUT:
22772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
22872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                startUpdateBatchInputTimer(tracker);
22972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                break;
2305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
2315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2338126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        @Override
2348126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka        public void startKeyRepeatTimer(final PointerTracker tracker, final int delay) {
2358a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            final Key key = tracker.getKey();
2368126e79e085c75f0eadaaf71e0a4ed1a2b83d892Tadashi G. Takaoka            if (key == null || delay == 0) {
23720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka                return;
23820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            }
2398a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
24032c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        }
24132c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka
2425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
2435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
2445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2460d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        // TODO: Suppress layout changes in key repeat mode
2475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
2480d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka            return hasMessages(MSG_REPEAT_KEY);
2495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2512321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
252212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        public void startLongPressTimer(final PointerTracker tracker, final int delay) {
253a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            cancelLongPressTimer();
254212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            if (delay <= 0) return;
255212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
2565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2582321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
25998b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
2605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
2615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
263d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
26445213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                final ObjectAnimator animatorToStart) {
26557372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            if (animatorToCancel == null || animatorToStart == null) {
26657372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka                // TODO: Stop using null as a no-operation animator.
26757372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka                return;
26857372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            }
26945213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            float startFraction = 0.0f;
27045213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            if (animatorToCancel.isStarted()) {
271b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToCancel.cancel();
27245213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
273b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
27445213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            final long startTime = (long)(animatorToStart.getDuration() * startFraction);
27545213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.start();
27645213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.setCurrentPlayTime(startTime);
277b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
278b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
279d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
280d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
281d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
282d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
283d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
284d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
285d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
286d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
287d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
288d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
2892321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
290dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startTypingStateTimer(final Key typedKey) {
291d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
292d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
293d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
294d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
29573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            final boolean isTyping = isTypingState();
296f1678ba8024606349bc184cfeaead2be059f7b5bTadashi G. Takaoka            removeMessages(MSG_TYPING_STATE_EXPIRED);
297d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
298d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
299d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            // When user hits the space or the enter key, just cancel the while-typing timer.
300d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final int typedCode = typedKey.mCode;
301240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
3025141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka                if (isTyping) {
3035141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka                    startWhileTypingFadeinAnimation(keyboardView);
3045141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka                }
305d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
306d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
307d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
30873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            sendMessageDelayed(
3095509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
31073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            if (isTyping) {
31173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                return;
31273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            }
313d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            startWhileTypingFadeoutAnimation(keyboardView);
31493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
31593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
31693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
31773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState() {
31873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            return hasMessages(MSG_TYPING_STATE_EXPIRED);
31993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
32093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3219f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        @Override
3222a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public void startDoubleTapShiftKeyTimer() {
3232a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP_SHIFT_KEY),
3245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
3255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3270ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        @Override
3282a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public void cancelDoubleTapShiftKeyTimer() {
3292a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            removeMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
330beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        }
331beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
332beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        @Override
3332a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka        public boolean isInDoubleTapShiftKeyTimeout() {
3342a9882a433e2372ac32fbc0def578d4d9a97a676Tadashi G. Takaoka            return hasMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
3355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
336c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
337c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
3380ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelKeyTimers() {
3390ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyRepeatTimer();
3400ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelLongPressTimer();
341c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
342c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
34372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        @Override
34472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        public void startUpdateBatchInputTimer(final PointerTracker tracker) {
34572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (mGestureRecognitionUpdateTime <= 0) {
34672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                return;
34772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            }
34872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
34972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker),
35072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    mGestureRecognitionUpdateTime);
35172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
35272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
35372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        @Override
354915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        public void cancelUpdateBatchInputTimer(final PointerTracker tracker) {
355915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
356915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        }
357915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka
358915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        @Override
3592db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka        public void cancelAllUpdateBatchInputTimers() {
36072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT);
36172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
36272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
3630ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelAllMessages() {
3640ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyTimers();
3652db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka            cancelAllUpdateBatchInputTimers();
366c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
367c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
368c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
369375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
370375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
371375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public static class DrawingHandler extends StaticInnerHandlerWrapper<MainKeyboardView> {
372375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        private static final int MSG_DISMISS_KEY_PREVIEW = 0;
373375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
374375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
375375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public DrawingHandler(final MainKeyboardView outerInstance) {
376375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            super(outerInstance);
377375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
378375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
379375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        @Override
380375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void handleMessage(final Message msg) {
381375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final MainKeyboardView mainKeyboardView = getOuterInstance();
382375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            if (mainKeyboardView == null) return;
383375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
384375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            switch (msg.what) {
385375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            case MSG_DISMISS_KEY_PREVIEW:
386375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                final TextView previewText = mainKeyboardView.mKeyPreviewTexts.get(
387375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                        tracker.mPointerId);
388375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                if (previewText != null) {
389375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                    previewText.setVisibility(INVISIBLE);
390375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                }
391375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                break;
392375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
393fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mainKeyboardView.showGestureFloatingPreviewText(SuggestedWords.EMPTY);
394375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                break;
395375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            }
396375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
397375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
398375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void dismissKeyPreview(final long delay, final PointerTracker tracker) {
399375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
400375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
401375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
402375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void cancelDismissKeyPreview(final PointerTracker tracker) {
403375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
404375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
405375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
406375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        private void cancelAllDismissKeyPreviews() {
407375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            removeMessages(MSG_DISMISS_KEY_PREVIEW);
408375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
409375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
410375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void dismissGestureFloatingPreviewText(final long delay) {
411375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay);
412375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
413375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
414375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void cancelAllMessages() {
415375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            cancelAllDismissKeyPreviews();
416375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
417375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
418375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
419dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
420c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
4215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
423dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
4245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
4255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
42613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
42713ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean forceNonDistinctMultitouch = prefs.getBoolean(
428a5ff9f0c77005769f92ca1131882bb4e3ca18980Tadashi G. Takaoka                DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
42913ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean hasDistinctMultitouch = context.getPackageManager()
4305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
43113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch;
432536438a45e5dc9d75c6c1a7d75262c41ce8f953cTadashi G. Takaoka        PointerTracker.init(getResources());
433fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView = new PreviewPlacerView(context, attrs);
43422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
435d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
436c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
437f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final int backgroundDimAlpha = mainKeyboardViewAttr.getInt(
438f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka                R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
439f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setColor(Color.BLACK);
440f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
441d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean(
442c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
443d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable(
444c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
445d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
446ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f);
447d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mSpacebarTextColor = mainKeyboardViewAttr.getColor(
448d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextColor, 0);
449d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
450c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
451d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt(
452c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
4534daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
454d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
455c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
456d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
457c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
458d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
459c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
460160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
461d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
46208ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
463d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
46408ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
465f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mKeyDetector = new KeyDetector(
466f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
467d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mKeyTimerHandler = new KeyTimerHandler(this, mainKeyboardViewAttr);
4680e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
4690e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewOffset, 0);
4700e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
4710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewHeight, 0);
4720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewLingerTimeout = mainKeyboardViewAttr.getInt(
4730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
474375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyPreviewLayoutId = mainKeyboardViewAttr.getResourceId(
475375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewLayout, 0);
4760e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (mKeyPreviewLayoutId == 0) {
4770e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            mShowKeyPreviewPopup = false;
4780e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
479a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
480d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka                R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
481d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
482c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
483375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
484375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt(
485375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
486d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        PointerTracker.setParameters(mainKeyboardViewAttr);
487fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
488fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText = new GestureFloatingPreviewText(
489fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
490fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mGestureFloatingPreviewText);
491fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
492fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureTrailsPreview = new GestureTrailsPreview(
493fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
494fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mGestureTrailsPreview);
495fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
496fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview = new SlidingKeyInputPreview(
497fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
498fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mSlidingKeyInputPreview);
499d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mainKeyboardViewAttr.recycle();
500160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
501a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardContainer = LayoutInflater.from(getContext())
502a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                .inflate(moreKeysKeyboardLayoutId, null);
50331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
50431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
50531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
50631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
50731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
50831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
509f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka
510f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
51131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
51231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
513dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
51420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (resId == 0) {
51557372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            // TODO: Stop returning null.
51620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return null;
51720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
51831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
51931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
520d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
52131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
52273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
52331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
52431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
52573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
52615f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
52731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
52831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
529d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
530d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
53115f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
532dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
53331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
53431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
53531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
53631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
53715f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
53831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
53931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
54031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
54131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
54215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
543dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
5445141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (mAltCodeKeyWhileTypingAnimAlpha == alpha) {
5455141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
5465141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
5475141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        // Update the visual of alt-code-key-while-typing.
54831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
5495141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
5505141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (keyboard == null) {
5515141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
5525141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
5535141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
5545141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            invalidateKey(key);
5555141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
5565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
558dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
5595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
5605c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
5615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
5655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
5665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5670efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
5680efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
5695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
5705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
572bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
5730efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
5740efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
5750efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
5760efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
5770efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
578f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
579f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
580f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
581f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
582f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
5830efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
5840efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
5850efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
5860efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
5875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
5895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
5905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
5915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
5925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
5935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
595dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
5968a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview and key repeat.
5978a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        mKeyTimerHandler.cancelLongPressTimer();
5985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
5995a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
60008ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
6018335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
602a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
6034112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
604240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
6051f2d0aa6c9b343848ee51e5bc13ccaaadf3ba4feTadashi G. Takaoka        mSpaceIcon = (mSpaceKey != null)
6064daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
6074112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
6084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
6099c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
610c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
61148a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
612f147794fd41491a3383e6aca6d49007f58124068alanv
613f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
614f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
615b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa        AccessibleKeyboardViewProxy.getInstance().setKeyboard();
6165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6180e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
6190e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Enables or disables the key feedback popup. This is a popup that shows a magnified
6200e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * version of the depressed key. By default the preview is enabled.
6210e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param previewEnabled whether or not to enable the key feedback preview
6220e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param delay the delay after which the preview is dismissed
6230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #isKeyPreviewPopupEnabled()
6240e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
6250e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
6260e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mShowKeyPreviewPopup = previewEnabled;
6270e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewLingerTimeout = delay;
6280e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
6290e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
630375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
631375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void locatePreviewPlacerView() {
632375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (mPreviewPlacerView.getParent() != null) {
633375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
634375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
635375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int width = getWidth();
636375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int height = getHeight();
637375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (width == 0 || height == 0) {
638375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
639375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
640375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
641375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        getLocationInWindow(mOriginCoords);
642375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final DisplayMetrics dm = getResources().getDisplayMetrics();
643375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (CoordinateUtils.y(mOriginCoords) < dm.heightPixels / 4) {
644375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
645375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
646375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
647375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final View rootView = getRootView();
648375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (rootView == null) {
649375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find root view");
650375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
651375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
652375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
653375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        // Note: It'd be very weird if we get null by android.R.id.content.
654375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (windowContentView == null) {
655375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView");
656375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        } else {
657375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            windowContentView.addView(mPreviewPlacerView);
658375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            mPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, width, height);
659375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
660375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
661375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
6620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
6630e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Returns the enabled state of the key feedback preview
6640e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @return whether or not the key feedback preview is enabled
6650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #setKeyPreviewPopupEnabled(boolean, int)
6660e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
6670e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public boolean isKeyPreviewPopupEnabled() {
6680e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        return mShowKeyPreviewPopup;
6690e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
6700e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
671375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void addKeyPreview(final TextView keyPreview) {
672375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
673375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mPreviewPlacerView.addView(
674375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
675375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
676375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
677375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private TextView getKeyPreviewText(final int pointerId) {
678375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        TextView previewText = mKeyPreviewTexts.get(pointerId);
679375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (previewText != null) {
680375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return previewText;
681375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
682375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final Context context = getContext();
683375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (mKeyPreviewLayoutId != 0) {
684375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
685375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        } else {
686375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            previewText = new TextView(context);
687375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
688375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyPreviewTexts.put(pointerId, previewText);
689375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        return previewText;
690375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
691375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
692375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void dismissAllKeyPreviews() {
693375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int pointerCount = mKeyPreviewTexts.size();
694375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        for (int id = 0; id < pointerCount; id++) {
695375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final TextView previewText = mKeyPreviewTexts.get(id);
696375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            if (previewText != null) {
697375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                previewText.setVisibility(INVISIBLE);
698375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            }
699375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
700375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        PointerTracker.setReleasedKeyGraphicsToAllKeys();
701375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
702375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
7030e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Background state set
7040e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
7050e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_MIDDLE
7060e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            EMPTY_STATE_SET,
7070e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_has_morekeys }
7080e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        },
7090e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_LEFT
7100e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_left_edge },
7110e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_left_edge, R.attr.state_has_morekeys }
7120e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        },
7130e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_RIGHT
7140e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_right_edge },
7150e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_right_edge, R.attr.state_has_morekeys }
7160e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7170e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    };
7180e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_MIDDLE = 0;
7190e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_LEFT = 1;
7200e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_RIGHT = 2;
7210e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_NORMAL = 0;
7220e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_HAS_MOREKEYS = 1;
7230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7240e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
7250e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void showKeyPreview(final PointerTracker tracker) {
7260e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
7270e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
7280e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (!mShowKeyPreviewPopup) {
7290e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap;
7300e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            return;
7310e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7320e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7330e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final TextView previewText = getKeyPreviewText(tracker.mPointerId);
7340e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // If the key preview has no parent view yet, add it to the ViewGroup which can place
7350e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // key preview absolutely in SoftInputWindow.
7360e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (previewText.getParent() == null) {
7370e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            addKeyPreview(previewText);
7380e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7390e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7400e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mDrawingHandler.cancelDismissKeyPreview(tracker);
7410e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final Key key = tracker.getKey();
7420e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // If key is invalid or IME is already closed, we must not show key preview.
7430e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // Trying to show key preview while root window is closed causes
7440e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // WindowManager.BadTokenException.
7450e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (key == null) {
7460e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            return;
7470e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7480e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7490e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final KeyDrawParams drawParams = mKeyDrawParams;
7500e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewText.setTextColor(drawParams.mPreviewTextColor);
7510e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final Drawable background = previewText.getBackground();
752914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka        final String label = key.getPreviewLabel();
7530e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // What we show as preview should match what we show on a key top in onDraw().
7540e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (label != null) {
7550e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            // TODO Should take care of temporaryShiftLabel here.
7560e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setCompoundDrawables(null, null, null, null);
757914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka            previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
758914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka                    key.selectPreviewTextSize(drawParams));
759914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka            previewText.setTypeface(key.selectPreviewTypeface(drawParams));
7600e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setText(label);
7610e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else {
7620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setCompoundDrawables(null, null, null,
7630e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                    key.getPreviewIcon(keyboard.mIconsSet));
7640e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setText(null);
7650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7660e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7670e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewText.measure(
7680e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
7690e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int keyDrawWidth = key.getDrawWidth();
7700e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int previewWidth = previewText.getMeasuredWidth();
7710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int previewHeight = mKeyPreviewHeight;
7720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The width and height of visible part of the key preview background. The content marker
7730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // of the background 9-patch have to cover the visible part of the background.
7740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
7750e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                - previewText.getPaddingRight();
7760e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewParams.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop()
7770e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                - previewText.getPaddingBottom();
7780e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The distance between the top edge of the parent key and the bottom of the visible part
7790e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // of the key preview background.
7800e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewParams.mPreviewVisibleOffset = mKeyPreviewOffset - previewText.getPaddingBottom();
7810e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        getLocationInWindow(mOriginCoords);
7820e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The key preview is horizontally aligned with the center of the visible part of the
7830e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
7840e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // the left/right background is used if such background is specified.
7850e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int statePosition;
7860e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
7870e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                + CoordinateUtils.x(mOriginCoords);
7880e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (previewX < 0) {
7890e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewX = 0;
7900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_LEFT;
7910e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else if (previewX > getWidth() - previewWidth) {
7920e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewX = getWidth() - previewWidth;
7930e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_RIGHT;
7940e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else {
7950e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_MIDDLE;
7960e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The key preview is placed vertically above the top edge of the parent key with an
7980e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // arbitrary offset.
7990e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int previewY = key.mY - previewHeight + mKeyPreviewOffset
8000e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                + CoordinateUtils.y(mOriginCoords);
8010e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8020e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (background != null) {
8030e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            final int hasMoreKeys = (key.mMoreKeys != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
8040e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
8053201e986473f18ed13e1e3dad87c2ecde1d3edc8Tadashi G. Takaoka            background.setAlpha(PREVIEW_ALPHA);
8060e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
8070e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        ViewLayoutUtils.placeViewAt(
8080e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                previewText, previewX, previewY, previewWidth, previewHeight);
8090e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewText.setVisibility(VISIBLE);
8100e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
8110e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8120e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
8130e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void dismissKeyPreview(final PointerTracker tracker) {
8140e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, tracker);
8150e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
8160e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
817ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
818ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        mSlidingKeyInputPreview.setPreviewEnabled(enabled);
819ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    }
820ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka
8210e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
8220e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
8230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        locatePreviewPlacerView();
824fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview.setPreviewPosition(tracker);
8250e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
8260e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8270e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
8280e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void dismissSlidingKeyInputPreview() {
829fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview.dismissSlidingKeyInputPreview();
8300e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
8310e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
832cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa    public void setGesturePreviewMode(final boolean drawsGestureTrail,
833375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final boolean drawsGestureFloatingPreviewText) {
834fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText.setPreviewEnabled(drawsGestureFloatingPreviewText);
835cb3bba3c4ee4652e12c81185ab9a648db20bb0ddKen Wakasa        mGestureTrailsPreview.setPreviewEnabled(drawsGestureTrail);
836375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
837375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
838375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
839375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
840fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText.setSuggetedWords(suggestedWords);
841375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
842375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
843375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void dismissGestureFloatingPreviewText() {
844375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
845375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout);
846375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
847375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
848fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    @Override
849212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    public void showGestureTrail(final PointerTracker tracker,
850212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            final boolean showsFloatingPreviewText) {
851375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
852212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        if (showsFloatingPreviewText) {
853212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mGestureFloatingPreviewText.setPreviewPosition(tracker);
854212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
855fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureTrailsPreview.setPreviewPosition(tracker);
856375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
857375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
8588335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
859dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
8608335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
8618335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
8628335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
863dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
8648335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
8650657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
8660657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
8675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
8684331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
8694331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
8709c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
8719c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been attached.  This is needed to properly show the splash screen, which requires that
8729c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // the window token of the KeyboardView be non-null.
8739c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
874c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
875c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        }
876c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
877c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
878c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
879c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
880c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
881375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mPreviewPlacerView.removeAllViews();
8829c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
8839c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been detached.  This is needed to invalidate the reference of {@link MainKeyboardView}
8849c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // to null.
8859c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
886c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
8874331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
8884331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
8894331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
890a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) {
891a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key.mMoreKeys == null) {
8925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
89320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
894a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
895a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (moreKeysKeyboard == null) {
896a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            moreKeysKeyboard = new MoreKeysKeyboard.Builder(
897a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                    context, key, this, mKeyPreviewDrawParams).build();
898a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
89920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
9005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
901a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final View container = mMoreKeysKeyboardContainer;
9022affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
9032affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
9042affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
905b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
9062affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
9075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
9085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
9095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
910d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka     * Called when a key is long pressed.
9115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
9125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
91396efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka    private void onLongPress(final PointerTracker tracker) {
914a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
91596efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
916a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
917a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final Key key = tracker.getKey();
918a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key == null) {
91996efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
920a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
9219c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
922c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_onLongPress();
9239bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
924f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        final KeyboardActionListener listener = mKeyboardActionListener;
9259552badf3c24d2098d227b0ddca0721b928a10b1Tadashi G. Takaoka        if (key.hasNoPanelAutoMoreKey()) {
926f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            final int moreKeyCode = key.mMoreKeys[0].mCode;
9273708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
9286455172a707a1137eb15db8073774982db9dd1faTadashi G. Takaoka            listener.onPressKey(moreKeyCode, false /* isRepeatKey */, true /* isSinglePointer */);
929f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            listener.onCodeInput(moreKeyCode,
930f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
931f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            listener.onReleaseKey(moreKeyCode, false /* withSliding */);
93296efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
9333708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
934f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        final int code = key.mCode;
935a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
9363708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
937f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
9386dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
939f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka                listener.onReleaseKey(code, false /* withSliding */);
94096efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka                return;
9416dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
9426dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
94396efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka        openMoreKeysPanel(key, tracker);
9446dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
9456dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
94696efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka    private void openMoreKeysPanel(final Key key, final PointerTracker tracker) {
947a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext());
9489d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
94996efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
9505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
95163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
952547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int[] lastCoords = CoordinateUtils.newInstance();
953547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        tracker.getLastCoordinates(lastCoords);
954a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !key.noKeyPreview();
9557ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
9567ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
9577ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
9587ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
959547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                ? CoordinateUtils.x(lastCoords)
960a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                : key.mX + key.mWidth / 2;
9617ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
9627ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
9637ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
96429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
96529d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
966a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int pointY = key.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
967fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
968547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(CoordinateUtils.x(lastCoords));
969547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(CoordinateUtils.y(lastCoords));
970e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
9715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
9725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
9735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
974a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
97563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
9765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
97720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        return PointerTracker.isAnyInSlidingKeyInput();
9785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
9795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
980d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
981d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
9821b6eea89037158a1e271730eacdacb8b6e550d3aTadashi G. Takaoka        locatePreviewPlacerView();
9830e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka        // TODO: Remove this check
9840e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka        if (panel.isShowingInParent()) {
9850e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka            panel.dismissMoreKeysPanel();
986d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        }
987a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mPreviewPlacerView.addView(panel.getContainerView());
988d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mMoreKeysPanel = panel;
989a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        dimEntireKeyboard(true /* dimmed */);
990d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
991d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
992d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public boolean isShowingMoreKeysPanel() {
993a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent();
994d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
995d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
996d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
9970e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka    public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {
998d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
999d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
1000d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
1001d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
10020e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka    public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {
1003d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        dimEntireKeyboard(false /* dimmed */);
1004d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
1005d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            mPreviewPlacerView.removeView(mMoreKeysPanel.getContainerView());
1006d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            mMoreKeysPanel = null;
1007d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        }
1008d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
1009d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
10105433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void startDoubleTapShiftKeyTimer() {
10115433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        mKeyTimerHandler.startDoubleTapShiftKeyTimer();
10125433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
10135433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
10145433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void cancelDoubleTapShiftKeyTimer() {
10155433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        mKeyTimerHandler.cancelDoubleTapShiftKeyTimer();
10165433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
10175433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
10185433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public boolean isInDoubleTapShiftKeyTimeout() {
10195433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout();
10205433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
10215433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
10225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
1023dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    public boolean dispatchTouchEvent(MotionEvent event) {
1024dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
1025dc2ee7772402633817702e95c2a5b17f6dec03ebalanv            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
1026dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        }
1027dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        return super.dispatchTouchEvent(event);
1028dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    }
1029dc2ee7772402633817702e95c2a5b17f6dec03ebalanv
1030dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    @Override
1031dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
103246286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
103346286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
103446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
1035310ea9ab9058e922a562309cb0dc5d5e092000f1Tadashi G. Takaoka        // TODO: Add multi-touch to single-touch event converter for non-distinct multi-touch
1036310ea9ab9058e922a562309cb0dc5d5e092000f1Tadashi G. Takaoka        // device.
1037310ea9ab9058e922a562309cb0dc5d5e092000f1Tadashi G. Takaoka        return processMotionEvent(me);
1038c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
1039c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
1040dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
1041f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
10425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
10435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
10445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
10455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
10465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
10475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
10485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
10495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
1050f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
10515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
10525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
10535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
10545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
10555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
10565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
105735580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int x = (int)me.getX(index);
105835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int y = (int)me.getY(index);
105935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
1060fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This might be moved to the tracker.processMotionEvent() call below.
106130977a151e15e30f5385a349e92fb770b987435fTadashi G. Takaoka        if (LatinImeLogger.sUsabilityStudy) {
106230977a151e15e30f5385a349e92fb770b987435fTadashi G. Takaoka            UsabilityStudyLogUtils.writeMotionEvent(me);
1063d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
1064fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This should be moved to the tracker.processMotionEvent() call below.
1065fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // Currently the same "move" event is being logged twice.
10669c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
1067fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            ResearchLogger.mainKeyboardView_processMotionEvent(
1068fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    me, action, eventTime, index, id, x, y);
106915d4793911fa305e0a58aced925961e948582979satok        }
10705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1071f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
1072e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
10735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
10745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
10755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
1076f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
10775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
10785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
10795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
10805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
10815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
10825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
10835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
1084f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
10855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
1086e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
10875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
10885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
10895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
10905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
1091e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                final Key newKey = tracker.getKeyOn(x, y);
1092e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                if (mOldKey != newKey) {
10930efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
1094fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP) {
1095906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
1096fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    }
10975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
10985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
10995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
11005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
1101547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                final int[] lastCoords = CoordinateUtils.newInstance();
1102547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                mOldKey = tracker.getKeyOn(
1103547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords));
1104547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                tracker.onUpEvent(
1105547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords), eventTime);
11065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
11078ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka                tracker.processMotionEvent(action, x, y, eventTime, this);
11085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
11095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
11105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
11115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
11125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
11135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
11145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
11155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
11165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
1117d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final int pointerId = me.getPointerId(i);
1118e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                final PointerTracker tracker = PointerTracker.getPointerTracker(
1119d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        pointerId, this);
112035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int px = (int)me.getX(i);
112135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int py = (int)me.getY(i);
112235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                tracker.onMoveEvent(px, py, eventTime, me);
11235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
11245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
1125e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
1126e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            tracker.processMotionEvent(action, x, y, eventTime, this);
11275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
11285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
11295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
11305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
11315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
11325c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public void cancelAllOngoingEvents() {
1133375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
1134375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.cancelAllMessages();
11355c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissAllKeyPreviews();
11365c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissGestureFloatingPreviewText();
11375c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissSlidingKeyInputPreview();
11385c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
11395c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.cancelAllPointerTrackers();
1140375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
1141375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
11425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
11435c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllOngoingEvents();
1144a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
11455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
11465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1147586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
1148c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
1149586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
1150586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
1151586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
1152586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
1153586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
1154c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
1155dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean dispatchHoverEvent(final MotionEvent event) {
11565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
1157e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
1158586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
11595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
11605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1161586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
11625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
11635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
116422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
1165dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
11667bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
116720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
116820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
116920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1170240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
117120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (shortcutKey == null) {
117220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
117320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
11747bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
11757bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
11765afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
11775afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
1178dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
1179dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
11804112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
1181fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
1182fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
1183d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
1184aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
11854c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
1186dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
11874daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
118831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
118931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
119031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
1191d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
1192dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
119331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
119431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
119531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
1196dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
11974c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
11984112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
11994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
12004112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1201dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
120220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) {
120320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
120420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
12054112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
12064112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
120722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
120822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
1209a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private void dimEntireKeyboard(final boolean dimmed) {
1210f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
1211f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mNeedsToDimEntireKeyboard = dimmed;
1212f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (needsRedrawing) {
1213f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka            invalidateAllKeys();
1214f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
1215f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
1216f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
1217f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    @Override
1218f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    protected void onDraw(final Canvas canvas) {
1219f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        super.onDraw(canvas);
1220f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
1221f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        // Overlay a dark rectangle to dim.
1222f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (mNeedsToDimEntireKeyboard) {
122308ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint);
1224f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
1225f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
1226f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
122722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
1228dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
1229dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
12306bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
123173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
123273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
1233240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (key.mCode == Constants.CODE_SPACE) {
1234bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
12354112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
1236fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
1237bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
12384112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
1239240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        } else if (key.mCode == Constants.CODE_LANGUAGE_SWITCH) {
124081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
1241fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
1242b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
1243b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
124422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
124522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
12464112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
124708ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka    private static boolean fitsTextIntoWidth(final int width, final String text,
124808ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            final Paint paint) {
1249bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
125008ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka        final float textWidth = TypefaceUtils.getLabelWidth(text, paint);
125120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (textWidth < width) {
125220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return true;
125320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1254bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1255bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float scaleX = width / textWidth;
125620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
125720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
125820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1259bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1260bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
126108ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka        return TypefaceUtils.getLabelWidth(text, paint) < width;
1262bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
1263bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1264bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
126508ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka    private static String layoutLanguageOnSpacebar(final Paint paint,
126608ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            final InputMethodSubtype subtype, final int width) {
1267bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
1268a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype);
126920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, fullText, paint)) {
127020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return fullText;
1271bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
1272bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1273a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String middleText = SubtypeLocaleUtils.getMiddleDisplayName(subtype);
127420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, middleText, paint)) {
127520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return middleText;
12764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
12774112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1278a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String shortText = SubtypeLocaleUtils.getShortDisplayName(subtype);
127920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, shortText, paint)) {
128020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return shortText;
12814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
12824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1283bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
12844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
12854112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1286dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
1287bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        final int width = key.mWidth;
1288b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        final int height = key.mHeight;
12894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1290f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
1291bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
1292bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
1293bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
1294bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextSize(mSpacebarTextSize);
1295bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
1296bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
12974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
12984112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
12994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
1300b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
1301aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextShadowColor);
130231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
13034112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
1304aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextColor);
130531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
13064112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
13074112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
13084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
13094112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
1310bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
13114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
13124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
13134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
13144112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1315bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
13164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else if (mSpaceIcon != null) {
13174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = mSpaceIcon.getIntrinsicWidth();
13184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mSpaceIcon.getIntrinsicHeight();
13194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
13204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1321bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
13224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
13234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
1324c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa
1325c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    public void deallocateMemory() {
1326c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa        mGestureTrailsPreview.deallocateMemory();
1327c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    }
13285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
1329