MainKeyboardView.java revision bb476be4e62b3bed7848d37df42f8fa7363b58d1
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;
3113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.preference.PreferenceManager;
325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
33375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport android.util.DisplayMetrics;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
38b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
39f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
400e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport android.widget.TextView;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
4415f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.ExternallyReferenced;
452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.keyboard.internal.DrawingHandler;
46cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView;
47cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview;
48cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview;
4949d8af8a4e900f3c68c333aba7fde0a11fd368b1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
50bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewChoreographer;
510e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
52b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
53cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview;
542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.keyboard.internal.TimerHandler;
554daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport com.android.inputmethod.latin.Constants;
5615d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
58375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords;
59c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
60a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.settings.DebugSettings;
61e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
62e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CoordinateUtils;
63a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaokaimport com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
64ccf4a310279b13bbf0b6aac76a0878178c1dfb7dTadashi G. Takaokaimport com.android.inputmethod.latin.utils.TypefaceUtils;
654be6198cb73cc24e10834153c4e049644ed187e3Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.UsabilityStudyLogUtils;
66ccf4a310279b13bbf0b6aac76a0878178c1dfb7dTadashi G. Takaokaimport com.android.inputmethod.latin.utils.ViewLayoutUtils;
676b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
74dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
75dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio
772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor
782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor
792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_spacebarBackground
80dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
81dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
82dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
84dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
85dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
86dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger
88dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
89dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
90dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
91dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
92dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
93375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLayout
940e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset
950e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight
960e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout
97d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout
98f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha
99dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
100375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout
1013623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
1023623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
1033623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
1043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
1053623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
1063623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
1073623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
1083623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
1093623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
1103623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
1113623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
1125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
1132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy,
1142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks {
115c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
117bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
118bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
119bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
1202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    /* Space key and its icon and background. */
1214112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
1222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private Drawable mSpacebarIcon;
1232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final Drawable mSpacebarBackground;
124bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
12531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
12631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
127bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
128fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1294daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final float mLanguageOnSpacebarTextRatio;
1312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private float mLanguageOnSpacebarTextSize;
1322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final int mLanguageOnSpacebarTextColor;
1332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final int mLanguageOnSpacebarTextShadowColor;
134bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
135bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
136bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
137bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
138bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
139bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
140bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
14115d4793911fa305e0a58aced925961e948582979satok
14273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
1432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
1442fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1454daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
14673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
147cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    // Drawing preview placer view
148cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final DrawingPreviewPlacerView mDrawingPreviewPlacerView;
149375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int[] mOriginCoords = CoordinateUtils.newInstance();
150cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final GestureFloatingTextDrawingPreview mGestureFloatingTextDrawingPreview;
151cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final GestureTrailsDrawingPreview mGestureTrailsDrawingPreview;
152cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final SlidingKeyInputDrawingPreview mSlidingKeyInputDrawingPreview;
153375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1540e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Key preview
1552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static final boolean FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED = false;
156992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka    private final KeyPreviewDrawParams mKeyPreviewDrawParams;
157bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka    private final KeyPreviewChoreographer mKeyPreviewChoreographer;
1580e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
1592affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
160f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private final Paint mBackgroundDimAlphaPaint = new Paint();
161f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private boolean mNeedsToDimEntireKeyboard;
162a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final View mMoreKeysKeyboardContainer;
163a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache =
164a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            CollectionUtils.newWeakHashMap();
1652affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
166d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // More keys panel (used by both more keys keyboard and more suggestions view)
167d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // TODO: Consider extending to support multiple more keys panels
168d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
170375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Gesture floating preview text
171375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // TODO: Make this parameter customizable by user via settings.
172375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private int mGestureFloatingPreviewTextLingerTimeout;
173375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final KeyDetector mKeyDetector;
175b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka    private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
1765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final TimerHandler mKeyTimerHandler;
178741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private final int mLanguageOnSpacebarHorizontalMargin;
1795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final DrawingHandler mDrawingHandler =
1812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            new DrawingHandler(this);
182375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
183dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
184c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
1855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
1865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
187dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
1885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
1895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
190cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView = new DrawingPreviewPlacerView(context, attrs);
1912fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
1922fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
1932fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
1942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
1952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
1962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
1972fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
1982fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyTimerHandler = new TimerHandler(
1992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime);
2002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
2012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
2022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
2032fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
2042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
2052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyDetector = new KeyDetector(
2062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
2072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
208921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka        PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */);
2092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
21013ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
21113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean forceNonDistinctMultitouch = prefs.getBoolean(
212a5ff9f0c77005769f92ca1131882bb4e3ca18980Tadashi G. Takaoka                DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
21313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean hasDistinctMultitouch = context.getPackageManager()
214b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
215b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                && !forceNonDistinctMultitouch;
216b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null
217921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka                : new NonDistinctMultitouchHelper();
21822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
219f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final int backgroundDimAlpha = mainKeyboardViewAttr.getInt(
220f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka                R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
221f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setColor(Color.BLACK);
222f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
2232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mSpacebarBackground = mainKeyboardViewAttr.getDrawable(
2242fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_spacebarBackground);
225d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean(
226c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
227d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable(
228c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
2292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
2302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
2312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor(
2322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0);
2332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
2342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0);
235d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt(
236c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
2374daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
238d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
239c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
240d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
241c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
242d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
243c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
244160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
245992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka        mKeyPreviewDrawParams = new KeyPreviewDrawParams(mainKeyboardViewAttr);
246bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer = new KeyPreviewChoreographer(mKeyPreviewDrawParams);
247bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka
248a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
249d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka                R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
250d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
251c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
252375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
253375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt(
254375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
255fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
256cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview = new GestureFloatingTextDrawingPreview(
257cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka                mDrawingPreviewPlacerView, mainKeyboardViewAttr);
258cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView.addPreview(mGestureFloatingTextDrawingPreview);
259fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
260cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureTrailsDrawingPreview = new GestureTrailsDrawingPreview(
261cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka                mDrawingPreviewPlacerView, mainKeyboardViewAttr);
262cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView.addPreview(mGestureTrailsDrawingPreview);
263fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
264cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview = new SlidingKeyInputDrawingPreview(
265cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka                mDrawingPreviewPlacerView, mainKeyboardViewAttr);
266cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView.addPreview(mSlidingKeyInputDrawingPreview);
267d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mainKeyboardViewAttr.recycle();
268160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
269a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardContainer = LayoutInflater.from(getContext())
270a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                .inflate(moreKeysKeyboardLayoutId, null);
27131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
27231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
27331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
27431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
27531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
27631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
277f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka
278f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
279741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka
2802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarHorizontalMargin = (int)getResources().getDimension(
2812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.dimen.config_language_on_spacebar_horizontal_margin);
28231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
28331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
28452876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    @Override
28552876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
28652876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa        super.setHardwareAcceleratedDrawingEnabled(enabled);
287cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView.setHardwareAcceleratedDrawingEnabled(enabled);
28852876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    }
28952876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa
290dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
29120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (resId == 0) {
29257372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            // TODO: Stop returning null.
29320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return null;
29420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
29531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
29631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
297d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
29831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
29973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
30031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
30131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
30273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
3032fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
3042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final ObjectAnimator animatorToStart) {
3052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (animatorToCancel == null || animatorToStart == null) {
3062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // TODO: Stop using null as a no-operation animator.
3072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
3082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
3092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        float startFraction = 0.0f;
3102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (animatorToCancel.isStarted()) {
3112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            animatorToCancel.cancel();
3122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
3132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
3142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final long startTime = (long)(animatorToStart.getDuration() * startFraction);
3152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        animatorToStart.start();
3162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        animatorToStart.setCurrentPlayTime(startTime);
3172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
3192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHander.Callbacks} method.
3202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
3212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void startWhileTypingFadeinAnimation() {
3222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        cancelAndStartAnimators(
3232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
3242fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
3262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
3272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void startWhileTypingFadeoutAnimation() {
3282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        cancelAndStartAnimators(
3292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
3302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
33215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
33331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
33431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
335d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
336d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
33715f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
338dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
33931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
34031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
34131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
34231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
34315f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
34431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
34531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
34631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
34731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
34815f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
349dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
3505141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (mAltCodeKeyWhileTypingAnimAlpha == alpha) {
3515141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
3525141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3535141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        // Update the visual of alt-code-key-while-typing.
35431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
3555141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
3565141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (keyboard == null) {
3575141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
3585141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3595141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
3605141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            invalidateKey(key);
3615141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
364dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
3655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
3665c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
3675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: We should reconsider which coordinate system should be used to represent keyboard
3702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // event.
3712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public int getKeyX(final int x) {
3722fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return Constants.isValidCoordinate(x) ? mKeyDetector.getTouchX(x) : x;
3735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: We should reconsider which coordinate system should be used to represent keyboard
3762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // event.
3772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public int getKeyY(final int y) {
3782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return Constants.isValidCoordinate(y) ? mKeyDetector.getTouchY(y) : y;
3790efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
3800efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
3815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
3825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
3835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
3845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
3855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
3865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
3875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
3885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
389dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
3905cbda70956af14453fca7395d892c59aabbd8fd3Jean Chalard        // Remove any pending messages, except dismissing preview and key repeat.
3915cbda70956af14453fca7395d892c59aabbd8fd3Jean Chalard        mKeyTimerHandler.cancelLongPressTimers();
3925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
3935a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
39408ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
3958335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
396a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
3974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
398240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
3992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mSpacebarIcon = (mSpaceKey != null)
4004daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
4014112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
4022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio;
4039c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
40415c42454c0117d869f31ce278021f53ec74c4aa2Tadashi G. Takaoka            final int orientation = getContext().getResources().getConfiguration().orientation;
40515c42454c0117d869f31ce278021f53ec74c4aa2Tadashi G. Takaoka            ResearchLogger.mainKeyboardView_setKeyboard(keyboard, orientation);
40648a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
407f147794fd41491a3383e6aca6d49007f58124068alanv
408f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
409f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
410b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa        AccessibleKeyboardViewProxy.getInstance().setKeyboard();
4115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4130e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
4140e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Enables or disables the key feedback popup. This is a popup that shows a magnified
4150e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * version of the depressed key. By default the preview is enabled.
4160e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param previewEnabled whether or not to enable the key feedback preview
4170e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param delay the delay after which the preview is dismissed
4180e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #isKeyPreviewPopupEnabled()
4190e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
4200e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
4217d09b5681202d3df9df1d5634071091f3f45fbe5Tadashi G. Takaoka        mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay);
4220e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
4230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
424375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void locatePreviewPlacerView() {
425cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        if (mDrawingPreviewPlacerView.getParent() != null) {
426375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
427375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
428375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int width = getWidth();
429375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int height = getHeight();
430375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (width == 0 || height == 0) {
431375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
432375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
433375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
434375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        getLocationInWindow(mOriginCoords);
435375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final DisplayMetrics dm = getResources().getDisplayMetrics();
436375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (CoordinateUtils.y(mOriginCoords) < dm.heightPixels / 4) {
437375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
438375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
439375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
440375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final View rootView = getRootView();
441375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (rootView == null) {
442375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find root view");
443375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
444375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
445375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
446375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        // Note: It'd be very weird if we get null by android.R.id.content.
447375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (windowContentView == null) {
448cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka            Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView");
449375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        } else {
450cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka            windowContentView.addView(mDrawingPreviewPlacerView);
451cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka            mDrawingPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, width, height);
452375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
453375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
454375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
4550e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
4560e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Returns the enabled state of the key feedback preview
4570e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @return whether or not the key feedback preview is enabled
4580e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #setKeyPreviewPopupEnabled(boolean, int)
4590e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
4600e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public boolean isKeyPreviewPopupEnabled() {
4617d09b5681202d3df9df1d5634071091f3f45fbe5Tadashi G. Takaoka        return mKeyPreviewDrawParams.isPopupEnabled();
4620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
4630e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
4642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link DrawingHandler.Callbacks} method.
4652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
4662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissAllKeyPreviews() {
467bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer.dismissAllKeyPreviews();
468375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        PointerTracker.setReleasedKeyGraphicsToAllKeys();
469375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
470375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
4710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
4722fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void showKeyPreview(final Key key) {
4730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // If key is invalid or IME is already closed, we must not show key preview.
4740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // Trying to show key preview while root window is closed causes
4750e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // WindowManager.BadTokenException.
4760e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (key == null) {
4770e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            return;
4780e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
4790e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
4802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
4812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Keyboard keyboard = getKeyboard();
4827d09b5681202d3df9df1d5634071091f3f45fbe5Tadashi G. Takaoka        if (!previewParams.isPopupEnabled()) {
483992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka            previewParams.setVisibleOffset(-keyboard.mVerticalGap);
4842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
4852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
4862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
487bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        locatePreviewPlacerView();
488bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        final TextView previewTextView = mKeyPreviewChoreographer.getKeyPreviewTextView(
489bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka                key, mDrawingPreviewPlacerView);
4900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        getLocationInWindow(mOriginCoords);
491bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer.placeKeyPreview(key, previewTextView, keyboard.mIconsSet,
492bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka                mKeyDrawParams, getWidth(), mOriginCoords);
493bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer.showKeyPreview(key, previewTextView, isHardwareAccelerated());
4942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
4952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
4962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHandler.Callbacks} method.
4970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
4982fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissKeyPreviewWithoutDelay(final Key key) {
499bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */);
5002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // To redraw key top letter.
5012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        invalidateKey(key);
5022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
5032fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
5042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
5052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissKeyPreview(final Key key) {
5062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (!isHardwareAccelerated()) {
5072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // TODO: Implement preference option to control key preview method and duration.
5087d09b5681202d3df9df1d5634071091f3f45fbe5Tadashi G. Takaoka            mDrawingHandler.dismissKeyPreview(mKeyPreviewDrawParams.getLingerTimeout(), key);
5092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
5102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
511bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */);
5120e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
5130e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
514ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
515cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview.setPreviewEnabled(enabled);
516ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    }
517ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka
5180e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
5190e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
5200e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        locatePreviewPlacerView();
521cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker);
5220e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
5230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
5240e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
5250e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void dismissSlidingKeyInputPreview() {
526cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
5270e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
5280e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
529724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa    private void setGesturePreviewMode(final boolean isGestureTrailEnabled,
530724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureFloatingPreviewTextEnabled) {
531cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview.setPreviewEnabled(isGestureFloatingPreviewTextEnabled);
532cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureTrailsDrawingPreview.setPreviewEnabled(isGestureTrailEnabled);
533375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
534375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
5352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link DrawingHandler.Callbacks} method.
5362fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
537375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
538375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
539cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords);
540375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
541375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
542375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void dismissGestureFloatingPreviewText() {
543375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
544375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout);
545375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
546375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
547fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    @Override
548212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    public void showGestureTrail(final PointerTracker tracker,
549212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            final boolean showsFloatingPreviewText) {
550375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
551212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        if (showsFloatingPreviewText) {
552cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka            mGestureFloatingTextDrawingPreview.setPreviewPosition(tracker);
553212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
554cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureTrailsDrawingPreview.setPreviewPosition(tracker);
555375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
556375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
5578335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
558dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
5598335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
5608335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
5618335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
562724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa    public void setGestureHandlingEnabledByUser(final boolean isGestureHandlingEnabledByUser,
563724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureTrailEnabled,
564724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureFloatingPreviewTextEnabled) {
565724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        PointerTracker.setGestureHandlingEnabledByUser(isGestureHandlingEnabledByUser);
566724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        setGesturePreviewMode(isGestureHandlingEnabledByUser && isGestureTrailEnabled,
567724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa                isGestureHandlingEnabledByUser && isGestureFloatingPreviewTextEnabled);
5680657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
5690657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
5705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
5714331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
5724331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
5739c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
5749c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been attached.  This is needed to properly show the splash screen, which requires that
5759c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // the window token of the KeyboardView be non-null.
5769c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
577c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
578c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        }
579c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
580c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
581c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
582c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
583c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
584cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView.removeAllViews();
5859c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
5869c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been detached.  This is needed to invalidate the reference of {@link MainKeyboardView}
5879c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // to null.
5889c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
589c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
5904331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
5914331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
5924331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
593a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) {
5947dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (key.getMoreKeys() == null) {
5955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
59620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
597a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
598a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (moreKeysKeyboard == null) {
599a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            moreKeysKeyboard = new MoreKeysKeyboard.Builder(
600a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                    context, key, this, mKeyPreviewDrawParams).build();
601a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
60220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
6035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
604a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final View container = mMoreKeysKeyboardContainer;
6052affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
6062affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
6072affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
608b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
6092affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
6105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHandler.Callbacks} method.
6135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
614d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka     * Called when a key is long pressed.
6155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
6165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
6172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
6182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void onLongPress(final PointerTracker tracker) {
619a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
62096efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
621a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
622a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final Key key = tracker.getKey();
623a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key == null) {
62496efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
625a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
6269c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
627c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_onLongPress();
6289bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
629f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        final KeyboardActionListener listener = mKeyboardActionListener;
6309552badf3c24d2098d227b0ddca0721b928a10b1Tadashi G. Takaoka        if (key.hasNoPanelAutoMoreKey()) {
6317dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            final int moreKeyCode = key.getMoreKeys()[0].mCode;
6323708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
633ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */);
634f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            listener.onCodeInput(moreKeyCode,
635f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
636f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            listener.onReleaseKey(moreKeyCode, false /* withSliding */);
63796efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
6383708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
6397dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
640a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
6413708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
642f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
6436dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
644f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka                listener.onReleaseKey(code, false /* withSliding */);
64596efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka                return;
6466dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
6476dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
64896efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka        openMoreKeysPanel(key, tracker);
6496dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6506dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
65196efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka    private void openMoreKeysPanel(final Key key, final PointerTracker tracker) {
652a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext());
6539d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
65496efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
6555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
65663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
657547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int[] lastCoords = CoordinateUtils.newInstance();
658547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        tracker.getLastCoordinates(lastCoords);
659a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !key.noKeyPreview();
6607ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
6617ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
6627ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
6637ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
664547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                ? CoordinateUtils.x(lastCoords)
6657dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                : key.getX() + key.getWidth() / 2;
6667ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
6677ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
6687ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
66929d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
67029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
671992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka        final int pointY = key.getY() + mKeyPreviewDrawParams.getVisibleOffset();
672fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
67333482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka        tracker.onShowMoreKeysPanel(moreKeysPanel);
6742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // TODO: Implement zoom in animation of more keys panel.
6752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        dismissKeyPreviewWithoutDelay(key);
6765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public boolean isInDraggingFinger() {
679a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
68063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
6815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return PointerTracker.isAnyInDraggingFinger();
6835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
685d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
686d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
6871b6eea89037158a1e271730eacdacb8b6e550d3aTadashi G. Takaoka        locatePreviewPlacerView();
688cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        panel.showInParent(mDrawingPreviewPlacerView);
689d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mMoreKeysPanel = panel;
690a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        dimEntireKeyboard(true /* dimmed */);
691d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
692d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
693d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public boolean isShowingMoreKeysPanel() {
694a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent();
695d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
696d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
697d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
6980e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka    public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {
699d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
700d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
701d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
702d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
7030e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka    public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {
704d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        dimEntireKeyboard(false /* dimmed */);
705d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
7067f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka            mMoreKeysPanel.removeFromParent();
707d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            mMoreKeysPanel = null;
708d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        }
709d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
710d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
7115433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void startDoubleTapShiftKeyTimer() {
7125433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        mKeyTimerHandler.startDoubleTapShiftKeyTimer();
7135433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
7145433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
7155433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void cancelDoubleTapShiftKeyTimer() {
7165433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        mKeyTimerHandler.cancelDoubleTapShiftKeyTimer();
7175433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
7185433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
7195433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public boolean isInDoubleTapShiftKeyTimeout() {
7205433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout();
7215433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
7225433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
7235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
724dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    public boolean dispatchTouchEvent(MotionEvent event) {
725dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
726dc2ee7772402633817702e95c2a5b17f6dec03ebalanv            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
727dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        }
728dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        return super.dispatchTouchEvent(event);
729dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    }
730dc2ee7772402633817702e95c2a5b17f6dec03ebalanv
731dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    @Override
732dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
73346286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
73446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
73546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
736b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        if (mNonDistinctMultitouchHelper != null) {
737b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) {
738b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                // Key repeating timer will be canceled if 2 or more keys are in action.
7392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mKeyTimerHandler.cancelKeyRepeatTimers();
740b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            }
741b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            // Non distinct multitouch screen support
7422fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector);
743b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            return true;
744b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        }
745310ea9ab9058e922a562309cb0dc5d5e092000f1Tadashi G. Takaoka        return processMotionEvent(me);
746c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
747c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
748dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
74930977a151e15e30f5385a349e92fb770b987435fTadashi G. Takaoka        if (LatinImeLogger.sUsabilityStudy) {
75030977a151e15e30f5385a349e92fb770b987435fTadashi G. Takaoka            UsabilityStudyLogUtils.writeMotionEvent(me);
751d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
752fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // Currently the same "move" event is being logged twice.
7539c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
75454dd1bed528331208f15df2693bcd01004dd471aTadashi G. Takaoka            ResearchLogger.mainKeyboardView_processMotionEvent(me);
75515d4793911fa305e0a58aced925961e948582979satok        }
7565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
757e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int index = me.getActionIndex();
758e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int id = me.getPointerId(index);
7592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final PointerTracker tracker = PointerTracker.getPointerTracker(id);
7602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // When a more keys panel is showing, we should ignore other fingers' single touch events
7612fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // other than the finger that is showing the more keys panel.
7622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel()
7632fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                && PointerTracker.getActivePointerTrackerCount() == 1) {
7642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return true;
7652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
7662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        tracker.processMotionEvent(me, mKeyDetector);
7675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
7685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7705c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public void cancelAllOngoingEvents() {
771375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
772375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.cancelAllMessages();
7735c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissAllKeyPreviews();
7745c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissGestureFloatingPreviewText();
7755c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissSlidingKeyInputPreview();
7765c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
7775c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.cancelAllPointerTrackers();
778375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
779375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
7805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
7815c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllOngoingEvents();
782a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
7835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
785586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
786c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
787586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
788586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
789586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
790586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
791586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
792c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
793dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean dispatchHoverEvent(final MotionEvent event) {
7945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
7956a23b9e3d54031524445b1190cc1eba77916c5b3Tadashi G. Takaoka            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(
7966a23b9e3d54031524445b1190cc1eba77916c5b3Tadashi G. Takaoka                    event, mKeyDetector);
7975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
799586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
8005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
80222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
803dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
8047bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
80520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
80620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
80720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
808240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
80920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (shortcutKey == null) {
81020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
81120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
8127bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
8137bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
8145afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
8155afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
816dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
817dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
8184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
819fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
820fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
821d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
822aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
8234c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
824dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
8254daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
82631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
82731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
82831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
829d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
830dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
83131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
83231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
83331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
834dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
8354c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
8364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
8374112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
8384112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
839dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
84020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) {
84120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
84220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
8434112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
8444112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
84522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
84622b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
847a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private void dimEntireKeyboard(final boolean dimmed) {
848f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
849f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mNeedsToDimEntireKeyboard = dimmed;
850f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (needsRedrawing) {
851f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka            invalidateAllKeys();
852f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
853f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
854f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
855f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    @Override
856f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    protected void onDraw(final Canvas canvas) {
857f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        super.onDraw(canvas);
858f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
859f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        // Overlay a dark rectangle to dim.
860f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (mNeedsToDimEntireKeyboard) {
86108ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint);
862f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
863f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
864f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
8652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Draw key background.
8662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
8672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    protected void onDrawKeyBackground(final Key key, final Canvas canvas,
8682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final Drawable background) {
8692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (key.getCode() == Constants.CODE_SPACE) {
8702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            super.onDrawKeyBackground(key, canvas, mSpacebarBackground);
8712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
8722fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
8732fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        super.onDrawKeyBackground(key, canvas, background);
8742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
8752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
87622b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
877dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
878dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
8796bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
88073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
88173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
8822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // Don't draw key top letter when key preview is showing.
8832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED
884bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka                && mKeyPreviewChoreographer.isShowingKeyPreview(key)) {
8852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // TODO: Fade out animation for the key top letter, and fade in animation for the key
8862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // background color when the user presses the key.
8872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
8882fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
8897dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
8907dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (code == Constants.CODE_SPACE) {
891bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
8924112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
893fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
894bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
8954112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
8967dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        } else if (code == Constants.CODE_LANGUAGE_SWITCH) {
89781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
898fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
899b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
900b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
90122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
90222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
9034112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
904741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
905741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka        final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2;
906bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
9072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float textWidth = TypefaceUtils.getStringWidth(text, paint);
90820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (textWidth < width) {
90920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return true;
91020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
911bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
912741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka        final float scaleX = maxTextWidth / textWidth;
91320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
91420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
91520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
916bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
917bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
9182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth;
919bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
920bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
921bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
922741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private String layoutLanguageOnSpacebar(final Paint paint,
92308ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            final InputMethodSubtype subtype, final int width) {
924741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka
925bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
926a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype);
92720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, fullText, paint)) {
92820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return fullText;
929bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
930bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
931a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String middleText = SubtypeLocaleUtils.getMiddleDisplayName(subtype);
93220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, middleText, paint)) {
93320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return middleText;
9344112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9354112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
936a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String shortText = SubtypeLocaleUtils.getShortDisplayName(subtype);
93720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, shortText, paint)) {
93820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return shortText;
9394112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9404112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
941bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
9424112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9434112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
944dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
9457dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int width = key.getWidth();
9467dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int height = key.getHeight();
9474112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
948f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
949bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
950bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
951bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
9522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            paint.setTextSize(mLanguageOnSpacebarTextSize);
953bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
954bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
9554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
9564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
9574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
958b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
9592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            paint.setColor(mLanguageOnSpacebarTextShadowColor);
96031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
9622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            paint.setColor(mLanguageOnSpacebarTextColor);
96331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
9654112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9664112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
968bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
9694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
9704112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
9714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
9724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
973bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
9742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        } else if (mSpacebarIcon != null) {
9752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final int iconWidth = mSpacebarIcon.getIntrinsicWidth();
9762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final int iconHeight = mSpacebarIcon.getIntrinsicHeight();
9774112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
9784112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
9792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            drawIcon(canvas, mSpacebarIcon, x, y, iconWidth, iconHeight);
9804112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
982c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa
983e68d565d0f9704145adbf781105f1ac9a90f0a98Ken Wakasa    @Override
984c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    public void deallocateMemory() {
985e68d565d0f9704145adbf781105f1ac9a90f0a98Ken Wakasa        super.deallocateMemory();
986afca1ddd233c03d79433931a0b6ba97ed22663edTadashi G. Takaoka        mDrawingPreviewPlacerView.deallocateMemory();
987c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    }
9885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
989