MainKeyboardView.java revision d2c00f550d6b4f2452fe38cee7c1d2222a4b4b3d
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;
3013ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.preference.PreferenceManager;
315d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalardimport android.text.TextUtils;
325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
37b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
404d146d5e3e00cab1cca7d0d29fe00c0d629b5eacTadashi G. Takaokaimport com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate;
4115f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.ExternallyReferenced;
42cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView;
43cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview;
44cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview;
4549d8af8a4e900f3c68c333aba7fde0a11fd368b1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
46bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewChoreographer;
470e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
48016d6c424cf004c282db5a57e64f3d1eee672a22Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewView;
497dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper;
5003288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.MoreKeySpec;
51b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
52cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview;
532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.keyboard.internal.TimerHandler;
545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
5585ddfe1317a4475269e53f62c2338c335e02e839Jean Chalardimport com.android.inputmethod.latin.RichInputMethodSubtype;
56375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords;
579342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport com.android.inputmethod.latin.common.Constants;
584beeb9253a06482299e0c67467531d30436a02fcJean Chalardimport com.android.inputmethod.latin.common.StringUtils;
59a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.settings.DebugSettings;
60e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CoordinateUtils;
61ccf4a310279b13bbf0b6aac76a0878178c1dfb7dTadashi G. Takaokaimport com.android.inputmethod.latin.utils.TypefaceUtils;
625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
635d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalardimport java.util.Locale;
645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
662ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaokaimport javax.annotation.Nonnull;
672ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka
685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio
722fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor
7341f9f1ef703ca4d63f1ef08b51d1365a17b4065aTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowRadius
742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor
75dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
76dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
77dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
78dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
79dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
80dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
81dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
84dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
85dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
86dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
87dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
88375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLayout
890e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset
900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight
910e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout
92ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewShowUpAnimator
93ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewDismissAnimator
94d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout
950f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardForActionLayout
96f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha
97dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
98375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout
993623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
1003623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
1013623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
1023623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
1033623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
1043623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
1053623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
1063623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
1073623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
1083623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
1093623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
1105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
1112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy,
1122ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        MoreKeysPanel.Controller, TimerHandler.Callbacks {
113c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
115bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
116bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
117bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
1182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    /* Space key and its icon and background. */
1194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
120bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
12131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
12231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
1237dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka    private int mLanguageOnSpacebarFormatType;
124fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1254daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final float mLanguageOnSpacebarTextRatio;
1272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private float mLanguageOnSpacebarTextSize;
1282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final int mLanguageOnSpacebarTextColor;
12941f9f1ef703ca4d63f1ef08b51d1365a17b4065aTadashi G. Takaoka    private final float mLanguageOnSpacebarTextShadowRadius;
1302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final int mLanguageOnSpacebarTextShadowColor;
13141f9f1ef703ca4d63f1ef08b51d1365a17b4065aTadashi G. Takaoka    private static final float LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
132bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
133bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
13415d4793911fa305e0a58aced925961e948582979satok
13573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
1362fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
1372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1384daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
13973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
140cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    // Drawing preview placer view
141cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final DrawingPreviewPlacerView mDrawingPreviewPlacerView;
142375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int[] mOriginCoords = CoordinateUtils.newInstance();
143cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final GestureFloatingTextDrawingPreview mGestureFloatingTextDrawingPreview;
144cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final GestureTrailsDrawingPreview mGestureTrailsDrawingPreview;
145cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka    private final SlidingKeyInputDrawingPreview mSlidingKeyInputDrawingPreview;
146375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1470e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Key preview
148992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka    private final KeyPreviewDrawParams mKeyPreviewDrawParams;
149bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka    private final KeyPreviewChoreographer mKeyPreviewChoreographer;
1500e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
1512affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
152f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private final Paint mBackgroundDimAlphaPaint = new Paint();
153a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final View mMoreKeysKeyboardContainer;
1540f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka    private final View mMoreKeysKeyboardForActionContainer;
155a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = new WeakHashMap<>();
1562affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
157d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // More keys panel (used by both more keys keyboard and more suggestions view)
158d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // TODO: Consider extending to support multiple more keys panels
159d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
161375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Gesture floating preview text
162375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // TODO: Make this parameter customizable by user via settings.
163375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private int mGestureFloatingPreviewTextLingerTimeout;
164375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final KeyDetector mKeyDetector;
166b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka    private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
1675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1682ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka    private final TimerHandler mTimerHandler;
169741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private final int mLanguageOnSpacebarHorizontalMargin;
1705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
171d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka    private MainKeyboardAccessibilityDelegate mAccessibilityDelegate;
172bca7e4e9a2ed07d5d87f4dce9f793e40edb09691Tadashi G. Takaoka
173dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
174c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
1755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
1765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
177dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
1785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
1795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
180cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView = new DrawingPreviewPlacerView(context, attrs);
1812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
1822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
1832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
1842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
1852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
1862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
1872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
1882ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mTimerHandler = new TimerHandler(
1892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime);
1902fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
1912fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
1922fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
1932fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
1942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
1952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyDetector = new KeyDetector(
1962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
1972fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
1982ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        PointerTracker.init(mainKeyboardViewAttr, mTimerHandler, this /* DrawingProxy */);
1992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
20013ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
20113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean forceNonDistinctMultitouch = prefs.getBoolean(
202a5ff9f0c77005769f92ca1131882bb4e3ca18980Tadashi G. Takaoka                DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
20313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean hasDistinctMultitouch = context.getPackageManager()
204b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
205b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                && !forceNonDistinctMultitouch;
206b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null
207921a32f0a923ca00c4818d3869f0abd9a63dcf0eTadashi G. Takaoka                : new NonDistinctMultitouchHelper();
20822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
209f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final int backgroundDimAlpha = mainKeyboardViewAttr.getInt(
210f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka                R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
211f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setColor(Color.BLACK);
212f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
2132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
2142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
2152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor(
2162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0);
21741f9f1ef703ca4d63f1ef08b51d1365a17b4065aTadashi G. Takaoka        mLanguageOnSpacebarTextShadowRadius = mainKeyboardViewAttr.getFloat(
21841f9f1ef703ca4d63f1ef08b51d1365a17b4065aTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarTextShadowRadius,
21941f9f1ef703ca4d63f1ef08b51d1365a17b4065aTadashi G. Takaoka                LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED);
2202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
2212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0);
222d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt(
223c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
2244daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
225d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
226c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
227d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
228c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
229d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
230c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
231160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
232992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka        mKeyPreviewDrawParams = new KeyPreviewDrawParams(mainKeyboardViewAttr);
233bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer = new KeyPreviewChoreographer(mKeyPreviewDrawParams);
234bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka
235a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
236d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka                R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
2370f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka        final int moreKeysKeyboardForActionLayoutId = mainKeyboardViewAttr.getResourceId(
2380f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka                R.styleable.MainKeyboardView_moreKeysKeyboardForActionLayout,
2390f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka                moreKeysKeyboardLayoutId);
240d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
241c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
242375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
243375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt(
244375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
245fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
246cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview = new GestureFloatingTextDrawingPreview(
2477fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka                mainKeyboardViewAttr);
2487fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview.setDrawingView(mDrawingPreviewPlacerView);
249fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
2507fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka        mGestureTrailsDrawingPreview = new GestureTrailsDrawingPreview(mainKeyboardViewAttr);
2517fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka        mGestureTrailsDrawingPreview.setDrawingView(mDrawingPreviewPlacerView);
252fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
2537fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview = new SlidingKeyInputDrawingPreview(mainKeyboardViewAttr);
2547fb630b2a83983ea42108969b82ca85886e19241Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview.setDrawingView(mDrawingPreviewPlacerView);
255d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mainKeyboardViewAttr.recycle();
256160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
2570f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka        final LayoutInflater inflater = LayoutInflater.from(getContext());
2580f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka        mMoreKeysKeyboardContainer = inflater.inflate(moreKeysKeyboardLayoutId, null);
2590f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka        mMoreKeysKeyboardForActionContainer = inflater.inflate(
2600f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka                moreKeysKeyboardForActionLayoutId, null);
26131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
26231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
26331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
26431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
26531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
26631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
267f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka
268f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
269741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka
2702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarHorizontalMargin = (int)getResources().getDimension(
2712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.dimen.config_language_on_spacebar_horizontal_margin);
27231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
27331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
27452876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    @Override
27552876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
27652876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa        super.setHardwareAcceleratedDrawingEnabled(enabled);
277cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView.setHardwareAcceleratedDrawingEnabled(enabled);
27852876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    }
27952876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa
280dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
28120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (resId == 0) {
28257372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            // TODO: Stop returning null.
28320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return null;
28420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
28531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
28631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
287d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
28831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
28973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
29031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
29131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
29273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
2932fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
2942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final ObjectAnimator animatorToStart) {
2952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (animatorToCancel == null || animatorToStart == null) {
2962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // TODO: Stop using null as a no-operation animator.
2972fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
2982fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
2992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        float startFraction = 0.0f;
3002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (animatorToCancel.isStarted()) {
3012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            animatorToCancel.cancel();
3022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
3032fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
3042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final long startTime = (long)(animatorToStart.getDuration() * startFraction);
3052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        animatorToStart.start();
3062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        animatorToStart.setCurrentPlayTime(startTime);
3072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
3092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHander.Callbacks} method.
3102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
3112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void startWhileTypingFadeinAnimation() {
3122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        cancelAndStartAnimators(
3132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
3142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
3162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
3172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void startWhileTypingFadeoutAnimation() {
3182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        cancelAndStartAnimators(
3192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
3202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
32215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
32331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
32431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
325d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
326d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
32715f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
328dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
32931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
33031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
33131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
33231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
33315f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
33431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
33531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
33631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
33731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
33815f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
339dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
3405141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (mAltCodeKeyWhileTypingAnimAlpha == alpha) {
3415141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
3425141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3435141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        // Update the visual of alt-code-key-while-typing.
34431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
3455141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
3465141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (keyboard == null) {
3475141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
3485141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3495141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
3505141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            invalidateKey(key);
3515141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
354dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
3555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
3565c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
3575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: We should reconsider which coordinate system should be used to represent keyboard
3602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // event.
3612fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public int getKeyX(final int x) {
3622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return Constants.isValidCoordinate(x) ? mKeyDetector.getTouchX(x) : x;
3635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: We should reconsider which coordinate system should be used to represent keyboard
3662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // event.
3672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public int getKeyY(final int y) {
3682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return Constants.isValidCoordinate(y) ? mKeyDetector.getTouchY(y) : y;
3690efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
3700efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
3715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
3725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
3735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
3745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
3755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
3765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
3775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
3785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
379dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
3805cbda70956af14453fca7395d892c59aabbd8fd3Jean Chalard        // Remove any pending messages, except dismissing preview and key repeat.
3812ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mTimerHandler.cancelLongPressTimers();
3825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
3835a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
38408ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
3858335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
386a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
3874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
388240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
3894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
3902fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio;
391f147794fd41491a3383e6aca6d49007f58124068alanv
392d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka        if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
393dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka            if (mAccessibilityDelegate == null) {
394dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka                mAccessibilityDelegate = new MainKeyboardAccessibilityDelegate(this, mKeyDetector);
395dec599d1723f4ff52f066bd2dd1a4457d30cd33cTadashi G. Takaoka            }
396ae0fab9550f1d9159d819dbb0b7cc86c2bf2937cTadashi G. Takaoka            mAccessibilityDelegate.setKeyboard(keyboard);
397d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka        } else {
398ae0fab9550f1d9159d819dbb0b7cc86c2bf2937cTadashi G. Takaoka            mAccessibilityDelegate = null;
399d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka        }
4005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4020e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
403ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * Enables or disables the key preview popup. This is a popup that shows a magnified
4040e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * version of the depressed key. By default the preview is enabled.
4050e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param previewEnabled whether or not to enable the key feedback preview
4060e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param delay the delay after which the preview is dismissed
4070e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
4080e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
4097d09b5681202d3df9df1d5634071091f3f45fbe5Tadashi G. Takaoka        mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay);
4100e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
4110e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
412ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka    /**
413ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * Enables or disables the key preview popup animations and set animations' parameters.
414ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     *
415ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * @param hasCustomAnimationParams false to use the default key preview popup animations
416ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     *   specified by keyPreviewShowUpAnimator and keyPreviewDismissAnimator attributes.
417ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     *   true to override the default animations with the specified parameters.
418ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * @param showUpStartXScale from this x-scale the show up animation will start.
419ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * @param showUpStartYScale from this y-scale the show up animation will start.
420ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * @param showUpDuration the duration of the show up animation in milliseconds.
421ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * @param dismissEndXScale to this x-scale the dismiss animation will end.
422ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * @param dismissEndYScale to this y-scale the dismiss animation will end.
423ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     * @param dismissDuration the duration of the dismiss animation in milliseconds.
424ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka     */
425ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka    public void setKeyPreviewAnimationParams(final boolean hasCustomAnimationParams,
426ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka            final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration,
427ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka            final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) {
428ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka        mKeyPreviewDrawParams.setAnimationParams(hasCustomAnimationParams,
429ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka                showUpStartXScale, showUpStartYScale, showUpDuration,
430ac88f3d8459e17236654aa38debea749ecb631d1Tadashi G. Takaoka                dismissEndXScale, dismissEndYScale, dismissDuration);
431276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka    }
432276b1a2ebc6d5ea3fa36fa9271cdc6310db34021Tadashi G. Takaoka
433375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void locatePreviewPlacerView() {
434375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        getLocationInWindow(mOriginCoords);
4350c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka        mDrawingPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, getWidth(), getHeight());
4360c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka    }
4370c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka
4380c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka    private void installPreviewPlacerView() {
439375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final View rootView = getRootView();
440375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (rootView == null) {
441375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find root view");
442375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
443375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
444375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
445375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        // Note: It'd be very weird if we get null by android.R.id.content.
446375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (windowContentView == null) {
447cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka            Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView");
4480c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka            return;
449375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
4500c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka        windowContentView.addView(mDrawingPreviewPlacerView);
451375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
452375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
4530e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
4542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void showKeyPreview(final Key key) {
455a68e0dd437daa1387edc65082f96a00ca258a7b0Tadashi G. Takaoka        // If the key is invalid or has no key preview, we must not show key preview.
456a68e0dd437daa1387edc65082f96a00ca258a7b0Tadashi G. Takaoka        if (key == null || key.noKeyPreview()) {
4570e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            return;
4580e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
4592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Keyboard keyboard = getKeyboard();
460a68e0dd437daa1387edc65082f96a00ca258a7b0Tadashi G. Takaoka        if (keyboard == null) {
461a68e0dd437daa1387edc65082f96a00ca258a7b0Tadashi G. Takaoka            return;
462a68e0dd437daa1387edc65082f96a00ca258a7b0Tadashi G. Takaoka        }
463a68e0dd437daa1387edc65082f96a00ca258a7b0Tadashi G. Takaoka        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
4647d09b5681202d3df9df1d5634071091f3f45fbe5Tadashi G. Takaoka        if (!previewParams.isPopupEnabled()) {
465992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka            previewParams.setVisibleOffset(-keyboard.mVerticalGap);
4662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
4672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
4682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
469bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        locatePreviewPlacerView();
4700e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        getLocationInWindow(mOriginCoords);
47184405d2a6815a99992849e821e073835f2d892e3Tadashi G. Takaoka        mKeyPreviewChoreographer.placeAndShowKeyPreview(key, keyboard.mIconsSet, mKeyDrawParams,
4723ff72dd0daa9baaeb4d6b8ade59d65c1f0e8b34fTadashi G. Takaoka                getWidth(), mOriginCoords, mDrawingPreviewPlacerView, isHardwareAccelerated());
4732fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
4742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
4752ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka    // Implements {@link TimerHandler.Callbacks#dismissKeyPreviewWithoutDelay(Key)}.
4760e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
4772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissKeyPreviewWithoutDelay(final Key key) {
478bb476be4e62b3bed7848d37df42f8fa7363b58d1Tadashi G. Takaoka        mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */);
4792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        invalidateKey(key);
4802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
4812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
4822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
4832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissKeyPreview(final Key key) {
4842ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        if (isHardwareAccelerated()) {
485d2c00f550d6b4f2452fe38cee7c1d2222a4b4b3dTadashi G. Takaoka            mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */);
486d2c00f550d6b4f2452fe38cee7c1d2222a4b4b3dTadashi G. Takaoka            return;
4872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
4882ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        // TODO: Implement preference option to control key preview method and duration.
4892ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mTimerHandler.postDismissKeyPreview(key, mKeyPreviewDrawParams.getLingerTimeout());
4900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
4910e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
492ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
493cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview.setPreviewEnabled(enabled);
494ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    }
495ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka
4960e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
4970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
4980e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        locatePreviewPlacerView();
499cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker);
5000e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
5010e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
5020e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
5030e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void dismissSlidingKeyInputPreview() {
504cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview();
5050e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
5060e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
507724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa    private void setGesturePreviewMode(final boolean isGestureTrailEnabled,
508724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureFloatingPreviewTextEnabled) {
509cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview.setPreviewEnabled(isGestureFloatingPreviewTextEnabled);
510cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureTrailsDrawingPreview.setPreviewEnabled(isGestureTrailEnabled);
511375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
512375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
5132ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka    public void showGestureFloatingPreviewText(@Nonnull final SuggestedWords suggestedWords,
5142ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka            final boolean dismissDelayed) {
515375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
5162ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        final GestureFloatingTextDrawingPreview gestureFloatingTextDrawingPreview =
5172ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka                mGestureFloatingTextDrawingPreview;
5182ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        gestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords);
5192ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        if (dismissDelayed) {
5202ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka            mTimerHandler.postDismissGestureFloatingPreviewText(
5212ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka                    mGestureFloatingPreviewTextLingerTimeout);
5222ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        }
523375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
524375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
5252ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka    // Implements {@link TimerHandler.Callbacks#dismissGestureFloatingPreviewTextWithoutDelay()}.
5262ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka    @Override
5272ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka    public void dismissGestureFloatingPreviewTextWithoutDelay() {
5282ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText();
529375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
530375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
531fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    @Override
532212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    public void showGestureTrail(final PointerTracker tracker,
533212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            final boolean showsFloatingPreviewText) {
534375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
535212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        if (showsFloatingPreviewText) {
536cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka            mGestureFloatingTextDrawingPreview.setPreviewPosition(tracker);
537212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
538cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mGestureTrailsDrawingPreview.setPreviewPosition(tracker);
539375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
540375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
5418335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
542c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka    @SuppressWarnings("static-method")
543dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
5448335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
5458335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
5468335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
547724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa    public void setGestureHandlingEnabledByUser(final boolean isGestureHandlingEnabledByUser,
548724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureTrailEnabled,
549724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureFloatingPreviewTextEnabled) {
550724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        PointerTracker.setGestureHandlingEnabledByUser(isGestureHandlingEnabledByUser);
551724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        setGesturePreviewMode(isGestureHandlingEnabledByUser && isGestureTrailEnabled,
552724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa                isGestureHandlingEnabledByUser && isGestureFloatingPreviewTextEnabled);
5530657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
5540657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
5555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
5564331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
5574331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
5580c01fc6f1c01a2009546a2982818e68c08012ab3Tadashi G. Takaoka        installPreviewPlacerView();
559c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
560c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
561c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
562c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
563c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
564cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        mDrawingPreviewPlacerView.removeAllViews();
5654331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
5664331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
567a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) {
56803288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka        final MoreKeySpec[] moreKeys = key.getMoreKeys();
56903288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka        if (moreKeys == null) {
5705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
57120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
572a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
573a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (moreKeysKeyboard == null) {
57403288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka            // {@link KeyPreviewDrawParams#mPreviewVisibleWidth} should have been set at
57503288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka            // {@link KeyPreviewChoreographer#placeKeyPreview(Key,TextView,KeyboardIconsSet,KeyDrawParams,int,int[]},
57603288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka            // though there may be some chances that the value is zero. <code>width == 0</code>
57703288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka            // will cause zero-division error at
57803288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka            // {@link MoreKeysKeyboardParams#setParameters(int,int,int,int,int,int,boolean,int)}.
57984405d2a6815a99992849e821e073835f2d892e3Tadashi G. Takaoka            final boolean isSingleMoreKeyWithPreview = mKeyPreviewDrawParams.isPopupEnabled()
58003288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka                    && !key.noKeyPreview() && moreKeys.length == 1
58103288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka                    && mKeyPreviewDrawParams.getVisibleWidth() > 0;
58203288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka            final MoreKeysKeyboard.Builder builder = new MoreKeysKeyboard.Builder(
58384405d2a6815a99992849e821e073835f2d892e3Tadashi G. Takaoka                    context, key, getKeyboard(), isSingleMoreKeyWithPreview,
58403288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka                    mKeyPreviewDrawParams.getVisibleWidth(),
58503288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka                    mKeyPreviewDrawParams.getVisibleHeight(), newLabelPaint(key));
58603288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka            moreKeysKeyboard = builder.build();
587a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
58820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
5895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5900f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka        final View container = key.isActionKey() ? mMoreKeysKeyboardForActionContainer
5910f33e614c0799fa3a14a2c32db4bf0654df039daTadashi G. Takaoka                : mMoreKeysKeyboardContainer;
5922affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
5932affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
5942affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
595b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
5962affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
5975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHandler.Callbacks} method.
6005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
601d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka     * Called when a key is long pressed.
6025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
6035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
6042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
6052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void onLongPress(final PointerTracker tracker) {
606a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
60796efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
608a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
609a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final Key key = tracker.getKey();
610a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key == null) {
61196efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
612a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
613f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        final KeyboardActionListener listener = mKeyboardActionListener;
6149552badf3c24d2098d227b0ddca0721b928a10b1Tadashi G. Takaoka        if (key.hasNoPanelAutoMoreKey()) {
6157dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            final int moreKeyCode = key.getMoreKeys()[0].mCode;
6163708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
617ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */);
6182282e8520a2c1984989a14fb09896536f5033b26Jean Chalard            listener.onCodeInput(moreKeyCode, Constants.NOT_A_COORDINATE,
6192282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                    Constants.NOT_A_COORDINATE, false /* isKeyRepeat */);
620f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            listener.onReleaseKey(moreKeyCode, false /* withSliding */);
62196efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
6223708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
6237dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
624a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
6253708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
626f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
6276dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
628f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka                listener.onReleaseKey(code, false /* withSliding */);
62996efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka                return;
6306dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
6316dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
63296efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka        openMoreKeysPanel(key, tracker);
6336dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6346dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
63596efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka    private void openMoreKeysPanel(final Key key, final PointerTracker tracker) {
636a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext());
6379d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
63896efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
6395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
64063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
641547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int[] lastCoords = CoordinateUtils.newInstance();
642547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        tracker.getLastCoordinates(lastCoords);
64303288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka        final boolean keyPreviewEnabled = mKeyPreviewDrawParams.isPopupEnabled()
64403288ef47fd93758b5665e19fe9b892ece6e586fTadashi G. Takaoka                && !key.noKeyPreview();
6457ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
6467ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
6477ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
6487ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
649547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                ? CoordinateUtils.x(lastCoords)
6507dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                : key.getX() + key.getWidth() / 2;
6517ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
6527ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
6537ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
65429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
65529d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
656992c5219092ccafe66363451fbf4ca7c5a43fb20Tadashi G. Takaoka        final int pointY = key.getY() + mKeyPreviewDrawParams.getVisibleOffset();
657fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
65833482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka        tracker.onShowMoreKeysPanel(moreKeysPanel);
6592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // TODO: Implement zoom in animation of more keys panel.
6602ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */);
6615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6632fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public boolean isInDraggingFinger() {
664a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
66563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
6665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return PointerTracker.isAnyInDraggingFinger();
6685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
670d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
671d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
6721b6eea89037158a1e271730eacdacb8b6e550d3aTadashi G. Takaoka        locatePreviewPlacerView();
673ca6e5dfedbb57f6ec31880f2799b40a6b2adadbcTadashi G. Takaoka        // Dismiss another {@link MoreKeysPanel} that may be being showed.
674ca6e5dfedbb57f6ec31880f2799b40a6b2adadbcTadashi G. Takaoka        onDismissMoreKeysPanel();
675ca6e5dfedbb57f6ec31880f2799b40a6b2adadbcTadashi G. Takaoka        // Dismiss all key previews that may be being showed.
676ca6e5dfedbb57f6ec31880f2799b40a6b2adadbcTadashi G. Takaoka        PointerTracker.setReleasedKeyGraphicsToAllKeys();
677ca6e5dfedbb57f6ec31880f2799b40a6b2adadbcTadashi G. Takaoka        // Dismiss  sliding key input preview that may be being showed.
678ca6e5dfedbb57f6ec31880f2799b40a6b2adadbcTadashi G. Takaoka        dismissSlidingKeyInputPreview();
679cf915ddc878699909365dd599a0e154552e244e2Tadashi G. Takaoka        panel.showInParent(mDrawingPreviewPlacerView);
680d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mMoreKeysPanel = panel;
681d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
682d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
683d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public boolean isShowingMoreKeysPanel() {
684a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent();
685d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
686d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
687d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
6881f215a58c99f0eb2f536e9cccd51371f2883e201Tadashi G. Takaoka    public void onCancelMoreKeysPanel() {
689d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
690d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
691d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
692d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
6931f215a58c99f0eb2f536e9cccd51371f2883e201Tadashi G. Takaoka    public void onDismissMoreKeysPanel() {
694d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
6957f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka            mMoreKeysPanel.removeFromParent();
696d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            mMoreKeysPanel = null;
697d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        }
698d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
699d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
7005433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void startDoubleTapShiftKeyTimer() {
7012ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mTimerHandler.startDoubleTapShiftKeyTimer();
7025433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
7035433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
7045433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void cancelDoubleTapShiftKeyTimer() {
7052ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mTimerHandler.cancelDoubleTapShiftKeyTimer();
7065433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
7075433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
7085433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public boolean isInDoubleTapShiftKeyTimeout() {
7092ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        return mTimerHandler.isInDoubleTapShiftKeyTimeout();
7105433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
7115433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
7125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
713dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
71446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
71546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
71646286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
717b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        if (mNonDistinctMultitouchHelper != null) {
7182ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka            if (me.getPointerCount() > 1 && mTimerHandler.isInKeyRepeat()) {
719b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                // Key repeating timer will be canceled if 2 or more keys are in action.
7202ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka                mTimerHandler.cancelKeyRepeatTimers();
721b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            }
722b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            // Non distinct multitouch screen support
7232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector);
724b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            return true;
725b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        }
726310ea9ab9058e922a562309cb0dc5d5e092000f1Tadashi G. Takaoka        return processMotionEvent(me);
727c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
728c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
729dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
730e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int index = me.getActionIndex();
731e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int id = me.getPointerId(index);
7322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final PointerTracker tracker = PointerTracker.getPointerTracker(id);
7332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // When a more keys panel is showing, we should ignore other fingers' single touch events
7342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // other than the finger that is showing the more keys panel.
7352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel()
7362fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                && PointerTracker.getActivePointerTrackerCount() == 1) {
7372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return true;
7382fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
7392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        tracker.processMotionEvent(me, mKeyDetector);
7405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
7415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7435c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public void cancelAllOngoingEvents() {
7442ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mTimerHandler.cancelAllMessages();
745ca6e5dfedbb57f6ec31880f2799b40a6b2adadbcTadashi G. Takaoka        PointerTracker.setReleasedKeyGraphicsToAllKeys();
7462ad6d9cda33825fa265cd17372f115bb48f49d19Tadashi G. Takaoka        mGestureFloatingTextDrawingPreview.dismissGestureFloatingPreviewText();
7475c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissSlidingKeyInputPreview();
7485c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
7495c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.cancelAllPointerTrackers();
750375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
751375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
7525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
7535c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllOngoingEvents();
754a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
7555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
757bca7e4e9a2ed07d5d87f4dce9f793e40edb09691Tadashi G. Takaoka    public void onHideWindow() {
758a74719c934ceb30d2afb7ae999c909f5c6fd5325Tadashi G. Takaoka        onDismissMoreKeysPanel();
759d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka        final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
76004cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka        if (accessibilityDelegate != null
76104cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka                && AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
762d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka            accessibilityDelegate.onHideWindow();
7631ead609e6fd64fd5ac2979ac3802d8bada7d7f49Tadashi G. Takaoka        }
764bca7e4e9a2ed07d5d87f4dce9f793e40edb09691Tadashi G. Takaoka    }
765bca7e4e9a2ed07d5d87f4dce9f793e40edb09691Tadashi G. Takaoka
766586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
7679647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka     * {@inheritDoc}
768586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
769c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
7709647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka    public boolean onHoverEvent(final MotionEvent event) {
771d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka        final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
77204cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka        if (accessibilityDelegate != null
77304cd8794e04b14c2b7e7eed036b83075792134a1Tadashi G. Takaoka                && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
774d78b586976af9b6b8f6e00c04747facfac313abcTadashi G. Takaoka            return accessibilityDelegate.onHoverEvent(event);
7755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7769647d7fbee4cbd72876e949e6544dc43fadbd148Tadashi G. Takaoka        return super.onHoverEvent(event);
7775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
77822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
779dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
7807bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
78120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
78220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
78320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
784240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
78520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (shortcutKey == null) {
78620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
78720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
7887bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
7897bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
7905afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
7915afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
792dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
7937dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka            final int languageOnSpacebarFormatType,
7947dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka            final boolean hasMultipleEnabledIMEsOrSubtypes) {
795016d6c424cf004c282db5a57e64f3d1eee672a22Tadashi G. Takaoka        if (subtypeChanged) {
796016d6c424cf004c282db5a57e64f3d1eee672a22Tadashi G. Takaoka            KeyPreviewView.clearTextCache();
797016d6c424cf004c282db5a57e64f3d1eee672a22Tadashi G. Takaoka        }
7987dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka        mLanguageOnSpacebarFormatType = languageOnSpacebarFormatType;
799fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
800fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
801d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
8027dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka            mLanguageOnSpacebarFormatType = LanguageOnSpacebarHelper.FORMAT_TYPE_NONE;
8034c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
8047dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka            if (subtypeChanged
8057dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka                    && languageOnSpacebarFormatType != LanguageOnSpacebarHelper.FORMAT_TYPE_NONE) {
8064daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
80731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
80831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
80931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
810d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
811dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
81231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
81331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
81431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
815dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
8164c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
8174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
8184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
8194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
82022b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
821dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
822dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
8236bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
82473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
82573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
82641808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        super.onDrawKeyTopVisuals(key, canvas, paint, params);
8277dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
8287dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (code == Constants.CODE_SPACE) {
82941808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka            // If input language are explicitly selected.
83041808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka            if (mLanguageOnSpacebarFormatType != LanguageOnSpacebarHelper.FORMAT_TYPE_NONE) {
83141808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka                drawLanguageOnSpacebar(key, canvas, paint);
83241808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka            }
8334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
834fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
835bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
8364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
8377dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        } else if (code == Constants.CODE_LANGUAGE_SWITCH) {
838fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
83922b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
84022b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
8414112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
842741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
843741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka        final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2;
844bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
8452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float textWidth = TypefaceUtils.getStringWidth(text, paint);
84620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (textWidth < width) {
84720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return true;
84820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
849bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
850741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka        final float scaleX = maxTextWidth / textWidth;
85120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
85220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
85320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
854bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
855bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
8562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth;
857bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
858bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
859bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
860741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private String layoutLanguageOnSpacebar(final Paint paint,
86185ddfe1317a4475269e53f62c2338c335e02e839Jean Chalard            final RichInputMethodSubtype subtype, final int width) {
862bc051a561f1766e5cea2866468c769f5cb346ffbJean Chalard        if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarHelper.FORMAT_TYPE_MULTIPLE) {
8635d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalard            final Locale[] locales = subtype.getLocales();
8645d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalard            final String[] languages = new String[locales.length];
8655d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalard            for (int i = 0; i < locales.length; ++i) {
8665d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalard                languages[i] = StringUtils.toUpperCaseOfStringForLocale(
8675d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalard                        locales[i].getLanguage(), true /* needsToUpperCase */, Locale.ROOT);
8685d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalard            }
8695d2d852eb0caaf7416c159f83d51e07b1ca87e30Jean Chalard            return TextUtils.join(" / ", languages);
870bc051a561f1766e5cea2866468c769f5cb346ffbJean Chalard        }
871bc051a561f1766e5cea2866468c769f5cb346ffbJean Chalard
872bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
8737dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka        if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarHelper.FORMAT_TYPE_FULL_LOCALE) {
87485ddfe1317a4475269e53f62c2338c335e02e839Jean Chalard            final String fullText = subtype.getFullDisplayName();
8757dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka            if (fitsTextIntoWidth(width, fullText, paint)) {
8767dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka                return fullText;
8777dd32dfc065c13e946c0d42a0e000f4ce7298ed7Tadashi G. Takaoka            }
878bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
879bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
88085ddfe1317a4475269e53f62c2338c335e02e839Jean Chalard        final String middleText = subtype.getMiddleDisplayName();
88120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, middleText, paint)) {
88220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return middleText;
8834112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
8844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
885bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
8864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
8874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
88841808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka    private void drawLanguageOnSpacebar(final Key key, final Canvas canvas, final Paint paint) {
8897dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int width = key.getWidth();
8907dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int height = key.getHeight();
89141808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        paint.setTextAlign(Align.CENTER);
89241808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        paint.setTypeface(Typeface.DEFAULT);
89341808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        paint.setTextSize(mLanguageOnSpacebarTextSize);
89485ddfe1317a4475269e53f62c2338c335e02e839Jean Chalard        final RichInputMethodSubtype subtype = getKeyboard().mId.mSubtype;
89541808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        final String language = layoutLanguageOnSpacebar(paint, subtype, width);
89641808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        // Draw language text with shadow
89741808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        final float descent = paint.descent();
89841808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        final float textHeight = -paint.ascent() + descent;
89941808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        final float baseline = height / 2 + textHeight / 2;
90041808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        if (mLanguageOnSpacebarTextShadowRadius > 0.0f) {
90141808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka            paint.setShadowLayer(mLanguageOnSpacebarTextShadowRadius, 0, 0,
90241808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka                    mLanguageOnSpacebarTextShadowColor);
90341808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        } else {
90441f9f1ef703ca4d63f1ef08b51d1365a17b4065aTadashi G. Takaoka            paint.clearShadowLayer();
9054112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
90641808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        paint.setColor(mLanguageOnSpacebarTextColor);
90741808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
90841808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        canvas.drawText(language, width / 2, baseline - descent, paint);
90941808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        paint.clearShadowLayer();
91041808192d3a64d3e823e13ace27e3ce80b1c5bdaTadashi G. Takaoka        paint.setTextScaleX(1.0f);
9114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
912c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa
913e68d565d0f9704145adbf781105f1ac9a90f0a98Ken Wakasa    @Override
914c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    public void deallocateMemory() {
915e68d565d0f9704145adbf781105f1ac9a90f0a98Ken Wakasa        super.deallocateMemory();
916afca1ddd233c03d79433931a0b6ba97ed22663edTadashi G. Takaoka        mDrawingPreviewPlacerView.deallocateMemory();
917c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    }
9185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
919