MainKeyboardView.java revision 57372fd3da2e879f190e4ccd41a00813774c9019
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;
2427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport android.content.res.Resources;
254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray;
266dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
27f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaokaimport android.graphics.Color;
2822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint;
294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align;
30bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface;
314112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable;
325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message;
3372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaokaimport android.os.SystemClock;
3413ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.preference.PreferenceManager;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
36375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport android.util.DisplayMetrics;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
38375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport android.util.SparseArray;
390e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport android.util.TypedValue;
405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
44b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
45f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
460e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport android.widget.TextView;
475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
5015f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.ExternallyReferenced;
51f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
522321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
53fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText;
54fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureTrailsPreview;
5549d8af8a4e900f3c68c333aba7fde0a11fd368b1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
560e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
57375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PreviewPlacerView;
58fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview;
59081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.TouchScreenRegulator;
60375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.latin.CollectionUtils;
614daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport com.android.inputmethod.latin.Constants;
62547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaokaimport com.android.inputmethod.latin.CoordinateUtils;
6313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport com.android.inputmethod.latin.DebugSettings;
646dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
6515d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
679879f65651a748e4c0a45715eb7d5663652f1127Tadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
68cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaokaimport com.android.inputmethod.latin.Settings;
695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
7027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport com.android.inputmethod.latin.StringUtils;
713bf57a5624679a20db26df912077a53b9f90ad36Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeLocale;
72375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords;
7315d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
74c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
756b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport java.util.Locale;
785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
84dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
85dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
86dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
87dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
88dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
89dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
90dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
91dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
92dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
93dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
94dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
95dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
96dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
97dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
98dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
99dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
100dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
101375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLayout
1020e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset
1030e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight
1040e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout
105d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout
106f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha
107dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
108375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout
1093623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
1103623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
1113623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
1123623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
1133623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
1143623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
1153623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
1163623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
1173623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
1183623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
1193623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
1205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
121a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
1220e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        PointerTracker.DrawingProxy, MoreKeysPanel.Controller,
1230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        TouchScreenRegulator.ProcessMotionEvent {
124c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
126d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    // TODO: Kill process when the usability study mode was changed.
127d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
128d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge
129bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
130bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
131bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
132bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /* Space key and its icons */
1334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
1344112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Drawable mSpaceIcon;
135bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
13631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
13731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
138bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
139fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1404daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1414112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final float mSpacebarTextRatio;
1424112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private float mSpacebarTextSize;
1434112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextColor;
1444112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextShadowColor;
145bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
146bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
147bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
148bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
149bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
150bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
151bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
15215d4793911fa305e0a58aced925961e948582979satok
15373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
15431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
15531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1564daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
15773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
158375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Preview placer view
159375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final PreviewPlacerView mPreviewPlacerView;
160375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int[] mOriginCoords = CoordinateUtils.newInstance();
161fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final GestureFloatingPreviewText mGestureFloatingPreviewText;
162fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final GestureTrailsPreview mGestureTrailsPreview;
163fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final SlidingKeyInputPreview mSlidingKeyInputPreview;
164375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Key preview
1660e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int PREVIEW_ALPHA = 240;
167375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int mKeyPreviewLayoutId;
1680e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final int mKeyPreviewOffset;
1690e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final int mKeyPreviewHeight;
170375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
1710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
1720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private boolean mShowKeyPreviewPopup = true;
1730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private int mKeyPreviewLingerTimeout;
1740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
1752affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
176f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private final Paint mBackgroundDimAlphaPaint = new Paint();
177f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private boolean mNeedsToDimEntireKeyboard;
178a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final View mMoreKeysKeyboardContainer;
179a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache =
180a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            CollectionUtils.newWeakHashMap();
1812affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
182d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // More keys panel (used by both more keys keyboard and more suggestions view)
183d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // TODO: Consider extending to support multiple more keys panels
184d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
186375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Gesture floating preview text
187375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // TODO: Make this parameter customizable by user via settings.
188375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private int mGestureFloatingPreviewTextLingerTimeout;
189375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
190081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka    private final TouchScreenRegulator mTouchScreenRegulator;
1915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
192d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    private KeyDetector mKeyDetector;
19313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    private final boolean mHasDistinctMultitouch;
1945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
195e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mOldKey;
1965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
197160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler;
1985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
199a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
2002321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
20127e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_TYPING_STATE_EXPIRED = 0;
202f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
203f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
2040ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        private static final int MSG_DOUBLE_TAP = 3;
20572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        private static final int MSG_UPDATE_BATCH_INPUT = 4;
2065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2075509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatStartTimeout;
2085509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatInterval;
209cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka        private final int mLongPressShiftLockTimeout;
2105509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mIgnoreAltCodeKeyTimeout;
21172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        private final int mGestureRecognitionUpdateTime;
2125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2135509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public KeyTimerHandler(final MainKeyboardView outerInstance,
2145509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                final TypedArray mainKeyboardViewAttr) {
2155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            super(outerInstance);
2165509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
2175509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
2185509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
2195509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
2205509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatInterval, 0);
221cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka            mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt(
222cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0);
2235509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
2245509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
22572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
22672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
2275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
230dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void handleMessage(final Message msg) {
231c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
23248d8d8d0ae573605f938b3859bf58e1972f0d737Tadashi G. Takaoka            if (keyboardView == null) {
23348d8d8d0ae573605f938b3859bf58e1972f0d737Tadashi G. Takaoka                return;
23448d8d8d0ae573605f938b3859bf58e1972f0d737Tadashi G. Takaoka            }
2355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
2365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
23727e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            case MSG_TYPING_STATE_EXPIRED:
23827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
23927e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                break;
2405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
2418a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                final Key currentKey = tracker.getKey();
2428a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                if (currentKey != null && currentKey.mCode == msg.arg1) {
2438a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                    tracker.onRegisterKey(currentKey);
2445509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    startKeyRepeatTimer(tracker, mKeyRepeatInterval);
2458a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                }
2465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
2475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
248a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                if (tracker != null) {
249a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                    keyboardView.onLongPress(tracker);
250a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                } else {
251a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
252a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
25472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            case MSG_UPDATE_BATCH_INPUT:
25572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
25672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                startUpdateBatchInputTimer(tracker);
25772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                break;
2585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
2595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
261dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) {
2628a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            final Key key = tracker.getKey();
26320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            if (key == null) {
26420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka                return;
26520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            }
2668a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
26732c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        }
26832c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka
2692321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
270dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startKeyRepeatTimer(final PointerTracker tracker) {
2715509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout);
2725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
2755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
2765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2780d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        // TODO: Suppress layout changes in key repeat mode
2795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
2800d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka            return hasMessages(MSG_REPEAT_KEY);
2815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2832321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
284dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final int code) {
28598b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
286a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            final int delay;
287a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            switch (code) {
288240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
289cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka                delay = mLongPressShiftLockTimeout;
290a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
291a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            default:
292a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                delay = 0;
293a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
294a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
295a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            if (delay > 0) {
296a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, code, 0), delay);
297a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
298a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
299a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
300a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        @Override
301dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final PointerTracker tracker) {
302a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            cancelLongPressTimer();
3037b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (tracker == null) {
3047b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                return;
3057b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
3067b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final Key key = tracker.getKey();
3077b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final int delay;
3087b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            switch (key.mCode) {
309240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
310cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka                delay = mLongPressShiftLockTimeout;
3117b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
3127b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            default:
313cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka                final int longpressTimeout =
314cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka                        Settings.getInstance().getCurrent().mKeyLongpressTimeout;
3157b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
3167b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // We use longer timeout for sliding finger input started from the symbols
3177b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // mode key.
318cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka                    delay = longpressTimeout * 3;
3197b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                } else {
320cc5e4a444524f73a112d7223d7940d41cf60d1f4Tadashi G. Takaoka                    delay = longpressTimeout;
321a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
3227b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
3237b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
3247b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (delay > 0) {
3257b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
326a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
3275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3292321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
33098b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
3315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
3325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
334d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
33545213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                final ObjectAnimator animatorToStart) {
33657372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            if (animatorToCancel == null || animatorToStart == null) {
33757372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka                // TODO: Stop using null as a no-operation animator.
33857372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka                return;
33957372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            }
34045213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            float startFraction = 0.0f;
34145213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            if (animatorToCancel.isStarted()) {
342b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToCancel.cancel();
34345213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
344b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
34545213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            final long startTime = (long)(animatorToStart.getDuration() * startFraction);
34645213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.start();
34745213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.setCurrentPlayTime(startTime);
348b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
349b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
350d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
351d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
352d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
353d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
354d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
355d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
356d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
357d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
358d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
359d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
3602321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
361dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startTypingStateTimer(final Key typedKey) {
362d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
363d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
364d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
365d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
36673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            final boolean isTyping = isTypingState();
367f1678ba8024606349bc184cfeaead2be059f7b5bTadashi G. Takaoka            removeMessages(MSG_TYPING_STATE_EXPIRED);
368d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
369d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
370d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            // When user hits the space or the enter key, just cancel the while-typing timer.
371d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final int typedCode = typedKey.mCode;
372240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
373d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
374d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
375d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
376d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
37773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            sendMessageDelayed(
3785509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
37973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            if (isTyping) {
38073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                return;
38173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            }
382d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            startWhileTypingFadeoutAnimation(keyboardView);
38393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
38493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
38593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
38673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState() {
38773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            return hasMessages(MSG_TYPING_STATE_EXPIRED);
38893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
38993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3909f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        @Override
3910ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer() {
3920ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP),
3935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
3945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3960ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        @Override
397beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer() {
398beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            removeMessages(MSG_DOUBLE_TAP);
399beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        }
400beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
401beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        @Override
4020ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout() {
4030ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            return hasMessages(MSG_DOUBLE_TAP);
4045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
405c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
406c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
4070ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelKeyTimers() {
4080ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyRepeatTimer();
4090ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelLongPressTimer();
410c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
411c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
41272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        @Override
41372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        public void startUpdateBatchInputTimer(final PointerTracker tracker) {
41472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (mGestureRecognitionUpdateTime <= 0) {
41572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                return;
41672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            }
41772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
41872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker),
41972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    mGestureRecognitionUpdateTime);
42072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
42172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
42272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        @Override
423915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        public void cancelUpdateBatchInputTimer(final PointerTracker tracker) {
424915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
425915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        }
426915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka
427915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        @Override
4282db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka        public void cancelAllUpdateBatchInputTimers() {
42972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT);
43072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
43172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
4320ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelAllMessages() {
4330ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyTimers();
4342db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka            cancelAllUpdateBatchInputTimers();
435c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
436c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
437c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
438375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
439375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
440375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public static class DrawingHandler extends StaticInnerHandlerWrapper<MainKeyboardView> {
441375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        private static final int MSG_DISMISS_KEY_PREVIEW = 0;
442375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
443375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
444375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public DrawingHandler(final MainKeyboardView outerInstance) {
445375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            super(outerInstance);
446375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
447375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
448375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        @Override
449375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void handleMessage(final Message msg) {
450375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final MainKeyboardView mainKeyboardView = getOuterInstance();
451375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            if (mainKeyboardView == null) return;
452375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
453375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            switch (msg.what) {
454375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            case MSG_DISMISS_KEY_PREVIEW:
455375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                final TextView previewText = mainKeyboardView.mKeyPreviewTexts.get(
456375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                        tracker.mPointerId);
457375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                if (previewText != null) {
458375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                    previewText.setVisibility(INVISIBLE);
459375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                }
460375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                break;
461375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
462fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mainKeyboardView.showGestureFloatingPreviewText(SuggestedWords.EMPTY);
463375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                break;
464375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            }
465375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
466375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
467375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void dismissKeyPreview(final long delay, final PointerTracker tracker) {
468375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
469375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
470375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
471375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void cancelDismissKeyPreview(final PointerTracker tracker) {
472375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
473375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
474375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
475375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        private void cancelAllDismissKeyPreviews() {
476375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            removeMessages(MSG_DISMISS_KEY_PREVIEW);
477375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
478375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
479375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void dismissGestureFloatingPreviewText(final long delay) {
480375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay);
481375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
482375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
483375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        public void cancelAllMessages() {
484375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            cancelAllDismissKeyPreviews();
485375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
486375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
487375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
488dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
489c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
4905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
492dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
4935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
4945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
495081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        mTouchScreenRegulator = new TouchScreenRegulator(context, this);
496c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
49713ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
49813ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean forceNonDistinctMultitouch = prefs.getBoolean(
499a5ff9f0c77005769f92ca1131882bb4e3ca18980Tadashi G. Takaoka                DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
50013ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean hasDistinctMultitouch = context.getPackageManager()
5015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
50213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch;
503918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Resources res = getResources();
504d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
505fa7f08b37327c825baf0a6455d6f7fdf8b37da57Tadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(
506fa7f08b37327c825baf0a6455d6f7fdf8b37da57Tadashi G. Takaoka                        res, R.array.phantom_sudden_move_event_device_list));
50793b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        PointerTracker.init(needsPhantomSuddenMoveEventHack);
508fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView = new PreviewPlacerView(context, attrs);
50922b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
510d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
511c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
512f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final int backgroundDimAlpha = mainKeyboardViewAttr.getInt(
513f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka                R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
514f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setColor(Color.BLACK);
515f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
516d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean(
517c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
518d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable(
519c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
520d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
521ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f);
522d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mSpacebarTextColor = mainKeyboardViewAttr.getColor(
523d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextColor, 0);
524d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
525c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
526d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt(
527c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
5284daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
529d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
530c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
531d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
532c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
533d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
534c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
535160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
536d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
53708ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
538d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
53908ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
540f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mKeyDetector = new KeyDetector(
541f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
542d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mKeyTimerHandler = new KeyTimerHandler(this, mainKeyboardViewAttr);
5430e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
5440e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewOffset, 0);
5450e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
5460e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewHeight, 0);
5470e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewLingerTimeout = mainKeyboardViewAttr.getInt(
5480e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
549375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyPreviewLayoutId = mainKeyboardViewAttr.getResourceId(
550375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewLayout, 0);
5510e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (mKeyPreviewLayoutId == 0) {
5520e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            mShowKeyPreviewPopup = false;
5530e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
554a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
555d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka                R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
556d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
557c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
558375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
559375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt(
560375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
561d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        PointerTracker.setParameters(mainKeyboardViewAttr);
562fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
563fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText = new GestureFloatingPreviewText(
564fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
565fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mGestureFloatingPreviewText);
566fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
567fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureTrailsPreview = new GestureTrailsPreview(
568fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
569fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mGestureTrailsPreview);
570fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
571fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview = new SlidingKeyInputPreview(
572fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
573fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mSlidingKeyInputPreview);
574d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mainKeyboardViewAttr.recycle();
575160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
576a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardContainer = LayoutInflater.from(getContext())
577a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                .inflate(moreKeysKeyboardLayoutId, null);
57831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
57931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
58031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
58131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
58231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
58331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
58431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
58531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
586dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
58720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (resId == 0) {
58857372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            // TODO: Stop returning null.
58920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return null;
59020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
59131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
59231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
593d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
59431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
59573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
59631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
59731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
59873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
59915f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
60031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
60131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
602d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
603d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
60415f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
605dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
60631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
60731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
60831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
60931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
61015f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
61131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
61231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
61331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
61431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
61515f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
616dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
61731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
61831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        updateAltCodeKeyWhileTyping();
6195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
621dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
6225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
6235c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
6245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
6275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
6285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
6295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
6300efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
6310efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
6325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
6335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
635bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
6360efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
6370efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
6380efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
6390efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
6400efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
641f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
642f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
643f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
644f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
645f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
6460efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
6470efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
6480efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
6490efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
6505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
6515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
6525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
6535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
6545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
6555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
6565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
6575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
658dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
6598a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview and key repeat.
6608a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        mKeyTimerHandler.cancelLongPressTimer();
6615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
6625a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
66308ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
6648335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
665081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        mTouchScreenRegulator.setKeyboardGeometry(keyboard.mOccupiedWidth);
666a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
6674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
668240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
6691f2d0aa6c9b343848ee51e5bc13ccaaadf3ba4feTadashi G. Takaoka        mSpaceIcon = (mSpaceKey != null)
6704daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
6714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
6724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
6739c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
674c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
67548a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
676f147794fd41491a3383e6aca6d49007f58124068alanv
677f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
678f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
679b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa        AccessibleKeyboardViewProxy.getInstance().setKeyboard();
6805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6820e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
6830e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Enables or disables the key feedback popup. This is a popup that shows a magnified
6840e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * version of the depressed key. By default the preview is enabled.
6850e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param previewEnabled whether or not to enable the key feedback preview
6860e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param delay the delay after which the preview is dismissed
6870e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #isKeyPreviewPopupEnabled()
6880e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
6890e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
6900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mShowKeyPreviewPopup = previewEnabled;
6910e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewLingerTimeout = delay;
6920e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
6930e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
694375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
695375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void locatePreviewPlacerView() {
696375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (mPreviewPlacerView.getParent() != null) {
697375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
698375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
699375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int width = getWidth();
700375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int height = getHeight();
701375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (width == 0 || height == 0) {
702375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
703375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
704375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
705375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        getLocationInWindow(mOriginCoords);
706375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final DisplayMetrics dm = getResources().getDisplayMetrics();
707375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (CoordinateUtils.y(mOriginCoords) < dm.heightPixels / 4) {
708375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
709375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
710375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
711375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final View rootView = getRootView();
712375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (rootView == null) {
713375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find root view");
714375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
715375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
716375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
717375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        // Note: It'd be very weird if we get null by android.R.id.content.
718375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (windowContentView == null) {
719375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView");
720375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        } else {
721375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            windowContentView.addView(mPreviewPlacerView);
722375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            mPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, width, height);
723375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
724375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
725375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
7260e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
7270e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Returns the enabled state of the key feedback preview
7280e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @return whether or not the key feedback preview is enabled
7290e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #setKeyPreviewPopupEnabled(boolean, int)
7300e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
7310e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public boolean isKeyPreviewPopupEnabled() {
7320e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        return mShowKeyPreviewPopup;
7330e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
7340e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
735375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void addKeyPreview(final TextView keyPreview) {
736375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
737375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mPreviewPlacerView.addView(
738375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
739375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
740375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
741375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private TextView getKeyPreviewText(final int pointerId) {
742375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        TextView previewText = mKeyPreviewTexts.get(pointerId);
743375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (previewText != null) {
744375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return previewText;
745375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
746375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final Context context = getContext();
747375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (mKeyPreviewLayoutId != 0) {
748375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
749375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        } else {
750375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            previewText = new TextView(context);
751375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
752375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyPreviewTexts.put(pointerId, previewText);
753375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        return previewText;
754375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
755375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
756375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void dismissAllKeyPreviews() {
757375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int pointerCount = mKeyPreviewTexts.size();
758375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        for (int id = 0; id < pointerCount; id++) {
759375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final TextView previewText = mKeyPreviewTexts.get(id);
760375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            if (previewText != null) {
761375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                previewText.setVisibility(INVISIBLE);
762375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            }
763375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
764375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        PointerTracker.setReleasedKeyGraphicsToAllKeys();
765375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
766375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
7670e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Background state set
7680e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
7690e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_MIDDLE
7700e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            EMPTY_STATE_SET,
7710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_has_morekeys }
7720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        },
7730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_LEFT
7740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_left_edge },
7750e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_left_edge, R.attr.state_has_morekeys }
7760e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        },
7770e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_RIGHT
7780e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_right_edge },
7790e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_right_edge, R.attr.state_has_morekeys }
7800e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7810e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    };
7820e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_MIDDLE = 0;
7830e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_LEFT = 1;
7840e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_RIGHT = 2;
7850e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_NORMAL = 0;
7860e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_HAS_MOREKEYS = 1;
7870e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int[] KEY_PREVIEW_BACKGROUND_DEFAULT_STATE =
7880e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            KEY_PREVIEW_BACKGROUND_STATE_TABLE[STATE_MIDDLE][STATE_NORMAL];
7890e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
7910e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void showKeyPreview(final PointerTracker tracker) {
7920e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
7930e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
7940e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (!mShowKeyPreviewPopup) {
7950e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap;
7960e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            return;
7970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
7980e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7990e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final TextView previewText = getKeyPreviewText(tracker.mPointerId);
8000e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // If the key preview has no parent view yet, add it to the ViewGroup which can place
8010e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // key preview absolutely in SoftInputWindow.
8020e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (previewText.getParent() == null) {
8030e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            addKeyPreview(previewText);
8040e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
8050e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8060e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mDrawingHandler.cancelDismissKeyPreview(tracker);
8070e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final Key key = tracker.getKey();
8080e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // If key is invalid or IME is already closed, we must not show key preview.
8090e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // Trying to show key preview while root window is closed causes
8100e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // WindowManager.BadTokenException.
8110e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (key == null) {
8120e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            return;
8130e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
8140e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8150e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final KeyDrawParams drawParams = mKeyDrawParams;
8160e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewText.setTextColor(drawParams.mPreviewTextColor);
8170e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final Drawable background = previewText.getBackground();
8180e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (background != null) {
8190e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            background.setState(KEY_PREVIEW_BACKGROUND_DEFAULT_STATE);
8200e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            background.setAlpha(PREVIEW_ALPHA);
8210e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
822914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka        final String label = key.getPreviewLabel();
8230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // What we show as preview should match what we show on a key top in onDraw().
8240e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (label != null) {
8250e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            // TODO Should take care of temporaryShiftLabel here.
8260e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setCompoundDrawables(null, null, null, null);
827914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka            previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
828914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka                    key.selectPreviewTextSize(drawParams));
829914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka            previewText.setTypeface(key.selectPreviewTypeface(drawParams));
8300e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setText(label);
8310e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else {
8320e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setCompoundDrawables(null, null, null,
8330e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                    key.getPreviewIcon(keyboard.mIconsSet));
8340e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewText.setText(null);
8350e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
8360e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8370e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewText.measure(
8380e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
8390e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int keyDrawWidth = key.getDrawWidth();
8400e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int previewWidth = previewText.getMeasuredWidth();
8410e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int previewHeight = mKeyPreviewHeight;
8420e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The width and height of visible part of the key preview background. The content marker
8430e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // of the background 9-patch have to cover the visible part of the background.
8440e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
8450e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                - previewText.getPaddingRight();
8460e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewParams.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop()
8470e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                - previewText.getPaddingBottom();
8480e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The distance between the top edge of the parent key and the bottom of the visible part
8490e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // of the key preview background.
8500e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewParams.mPreviewVisibleOffset = mKeyPreviewOffset - previewText.getPaddingBottom();
8510e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        getLocationInWindow(mOriginCoords);
8520e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The key preview is horizontally aligned with the center of the visible part of the
8530e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
8540e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // the left/right background is used if such background is specified.
8550e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int statePosition;
8560e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
8570e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                + CoordinateUtils.x(mOriginCoords);
8580e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (previewX < 0) {
8590e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewX = 0;
8600e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_LEFT;
8610e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else if (previewX > getWidth() - previewWidth) {
8620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewX = getWidth() - previewWidth;
8630e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_RIGHT;
8640e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else {
8650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_MIDDLE;
8660e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
8670e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The key preview is placed vertically above the top edge of the parent key with an
8680e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // arbitrary offset.
8690e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int previewY = key.mY - previewHeight + mKeyPreviewOffset
8700e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                + CoordinateUtils.y(mOriginCoords);
8710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (background != null) {
8730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            final int hasMoreKeys = (key.mMoreKeys != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
8740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
8750e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
8760e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        ViewLayoutUtils.placeViewAt(
8770e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                previewText, previewX, previewY, previewWidth, previewHeight);
8780e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        previewText.setVisibility(VISIBLE);
8790e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
8800e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8810e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
8820e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void dismissKeyPreview(final PointerTracker tracker) {
8830e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, tracker);
8840e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
8850e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
886ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
887ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        mSlidingKeyInputPreview.setPreviewEnabled(enabled);
888ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    }
889ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka
8900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
8910e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
8920e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        locatePreviewPlacerView();
893fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview.setPreviewPosition(tracker);
8940e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
8950e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
8960e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
8970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void dismissSlidingKeyInputPreview() {
898fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview.dismissSlidingKeyInputPreview();
8990e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
9000e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
901375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
902375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            final boolean drawsGestureFloatingPreviewText) {
903fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText.setPreviewEnabled(drawsGestureFloatingPreviewText);
904fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureTrailsPreview.setPreviewEnabled(drawsGesturePreviewTrail);
905375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
906375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
907375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
908375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
909fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText.setSuggetedWords(suggestedWords);
910375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
911375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
912375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void dismissGestureFloatingPreviewText() {
913375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
914375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout);
915375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
916375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
917fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    @Override
918ab334eb64669e909f0a401fddffa891962002602Tadashi G. Takaoka    public void showGesturePreviewTrail(final PointerTracker tracker) {
919375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
920fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText.setPreviewPosition(tracker);
921fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureTrailsPreview.setPreviewPosition(tracker);
922375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
923375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
9248335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
925dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
9268335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
9278335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
9288335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
929dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
9308335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
9310657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
9320657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
9335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
9344331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
9354331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
9369c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
9379c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been attached.  This is needed to properly show the splash screen, which requires that
9389c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // the window token of the KeyboardView be non-null.
9399c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
940c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
941c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        }
942c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
943c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
944c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
945c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
946c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
947375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mPreviewPlacerView.removeAllViews();
9489c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
9499c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been detached.  This is needed to invalidate the reference of {@link MainKeyboardView}
9509c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // to null.
9519c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
952c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
9534331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
9544331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
9554331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
956a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) {
957a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key.mMoreKeys == null) {
9585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
95920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
960a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
961a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (moreKeysKeyboard == null) {
962a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            moreKeysKeyboard = new MoreKeysKeyboard.Builder(
963a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                    context, key, this, mKeyPreviewDrawParams).build();
964a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
96520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
9665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
967a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final View container = mMoreKeysKeyboardContainer;
9682affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
9692affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
9702affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
971b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
9722affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
9735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
9745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
9755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
976d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka     * Called when a key is long pressed.
9775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
9785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the long press is handled, false otherwise. Subclasses should call the
9795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * method on the base class if the subclass doesn't wish to handle the call.
9805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
981a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private boolean onLongPress(final PointerTracker tracker) {
982a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
983a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            return false;
984a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
985a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final Key key = tracker.getKey();
986a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key == null) {
987a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            return false;
988a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
9899c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
990c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_onLongPress();
9919bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
992a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int code = key.mCode;
993a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key.hasEmbeddedMoreKey()) {
994a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            final int embeddedCode = key.mMoreKeys[0].mCode;
9953708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
996e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka            invokeCodeInput(embeddedCode);
997a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            invokeReleaseKey(code);
998a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            KeyboardSwitcher.getInstance().hapticAndAudioFeedback(code);
9993708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            return true;
10003708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
1001a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
10023708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
10033708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
10046dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
1005a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                invokeReleaseKey(code);
100642e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                return true;
10076dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
10086dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
1009a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        return openMoreKeysPanel(key, tracker);
10106dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
10116dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
1012a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private boolean invokeCustomRequest(final int requestCode) {
1013a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        return mKeyboardActionListener.onCustomRequest(requestCode);
101442e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
101542e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
1016a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private void invokeCodeInput(final int code) {
1017ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka        mKeyboardActionListener.onCodeInput(
1018a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
101942e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
102042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
1021a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private void invokeReleaseKey(final int code) {
1022a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mKeyboardActionListener.onReleaseKey(code, false);
10236dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
10246dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
1025a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private boolean openMoreKeysPanel(final Key key, final PointerTracker tracker) {
1026a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext());
10279d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
1028a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            return false;
10295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
103063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
1031547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int[] lastCoords = CoordinateUtils.newInstance();
1032547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        tracker.getLastCoordinates(lastCoords);
1033a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !key.noKeyPreview();
10347ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
10357ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
10367ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
10377ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
1038547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                ? CoordinateUtils.x(lastCoords)
1039a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                : key.mX + key.mWidth / 2;
10407ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
10417ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
10427ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
104329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
104429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
1045a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int pointY = key.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
1046fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
1047547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(CoordinateUtils.x(lastCoords));
1048547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(CoordinateUtils.y(lastCoords));
1049e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
10505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
10515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
10525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
10535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
1054a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
105563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
10565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
105720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        return PointerTracker.isAnyInSlidingKeyInput();
10585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
10595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1060d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
1061d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
10621b6eea89037158a1e271730eacdacb8b6e550d3aTadashi G. Takaoka        locatePreviewPlacerView();
1063d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
1064d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            onDismissMoreKeysPanel();
1065d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        }
1066a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mPreviewPlacerView.addView(panel.getContainerView());
1067d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mMoreKeysPanel = panel;
1068a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        dimEntireKeyboard(true /* dimmed */);
1069d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
1070d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
1071d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public boolean isShowingMoreKeysPanel() {
1072a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent();
1073d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
1074d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
1075d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
1076d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public void onCancelMoreKeysPanel() {
1077d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
1078d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
1079d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
1080d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
1081d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public boolean onDismissMoreKeysPanel() {
1082d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        dimEntireKeyboard(false /* dimmed */);
1083d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
1084d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            mPreviewPlacerView.removeView(mMoreKeysPanel.getContainerView());
1085d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            mMoreKeysPanel = null;
1086d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            return true;
1087d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        }
1088d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        return false;
1089d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
1090d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
10915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public int getPointerCount() {
10925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mOldPointerCount;
10935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
10945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
10955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
1096dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    public boolean dispatchTouchEvent(MotionEvent event) {
1097dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
1098dc2ee7772402633817702e95c2a5b17f6dec03ebalanv            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
1099dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        }
1100dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        return super.dispatchTouchEvent(event);
1101dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    }
1102dc2ee7772402633817702e95c2a5b17f6dec03ebalanv
1103dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    @Override
1104dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
110546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
110646286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
110746286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
1108c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return mTouchScreenRegulator.onTouchEvent(me);
1109c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
1110c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
1111c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    @Override
1112dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
1113f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
11145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
11155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
11165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
11175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
11185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
11195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
11205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
11215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
1122f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
11235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
11245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
11255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
11265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
11275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
11285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
112935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int x = (int)me.getX(index);
113035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int y = (int)me.getY(index);
113135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
1132fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This might be moved to the tracker.processMotionEvent() call below.
1133fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        if (ENABLE_USABILITY_STUDY_LOG && action != MotionEvent.ACTION_MOVE) {
1134fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            writeUsabilityStudyLog(me, action, eventTime, index, id, x, y);
1135d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
1136fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This should be moved to the tracker.processMotionEvent() call below.
1137fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // Currently the same "move" event is being logged twice.
11389c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
1139fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            ResearchLogger.mainKeyboardView_processMotionEvent(
1140fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    me, action, eventTime, index, id, x, y);
114115d4793911fa305e0a58aced925961e948582979satok        }
11425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1143f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
1144e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
11455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
11465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
11475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
1148f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
11495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
11505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
11515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
11525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
11535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
11545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
11555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
1156f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
11575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
1158e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
11595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
11605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
11615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
11625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
1163e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                final Key newKey = tracker.getKeyOn(x, y);
1164e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                if (mOldKey != newKey) {
11650efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
1166fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP) {
1167906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
1168fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    }
11695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
11705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
11715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
11725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
1173547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                final int[] lastCoords = CoordinateUtils.newInstance();
1174547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                mOldKey = tracker.getKeyOn(
1175547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords));
1176547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                tracker.onUpEvent(
1177547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords), eventTime);
11785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
11798ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka                tracker.processMotionEvent(action, x, y, eventTime, this);
11805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
11815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
11825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
11835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
11845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
11855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
11865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
11875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
11885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
1189d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final int pointerId = me.getPointerId(i);
1190e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                final PointerTracker tracker = PointerTracker.getPointerTracker(
1191d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        pointerId, this);
119235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int px = (int)me.getX(i);
119335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int py = (int)me.getY(i);
119435580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                tracker.onMoveEvent(px, py, eventTime, me);
1195d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                if (ENABLE_USABILITY_STUDY_LOG) {
1196fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py);
1197d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                }
11981bf47582a050a682154ea2bb3822219e41e4258eKurt Partridge                // TODO: This seems to be no longer necessary, and confusing because it leads to
11991bf47582a050a682154ea2bb3822219e41e4258eKurt Partridge                // duplicate MotionEvents being recorded.
12001bf47582a050a682154ea2bb3822219e41e4258eKurt Partridge                // if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
12011bf47582a050a682154ea2bb3822219e41e4258eKurt Partridge                //     ResearchLogger.mainKeyboardView_processMotionEvent(
12021bf47582a050a682154ea2bb3822219e41e4258eKurt Partridge                //             me, action, eventTime, i, pointerId, px, py);
12031bf47582a050a682154ea2bb3822219e41e4258eKurt Partridge                // }
12045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
12055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
1206e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
1207e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            tracker.processMotionEvent(action, x, y, eventTime, this);
12085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
12095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
12105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
12115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
12125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1213fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka    private static void writeUsabilityStudyLog(final MotionEvent me, final int action,
1214fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            final long eventTime, final int index, final int id, final int x, final int y) {
1215fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final String eventTag;
1216fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        switch (action) {
1217fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
1218fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Up]";
1219fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
1220fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
1221fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Down]";
1222fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
1223fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
1224fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[PointerUp]";
1225fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
1226fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
1227fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[PointerDown]";
1228fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
1229fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
1230fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Move]";
1231fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
1232fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        default:
1233fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Action" + action + "]";
1234fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
1235fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        }
1236fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final float size = me.getSize(index);
1237fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final float pressure = me.getPressure(index);
1238fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        UsabilityStudyLogUtils.getInstance().write(
1239fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                eventTag + eventTime + "," + id + "," + x + "," + y + "," + size + "," + pressure);
1240fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka    }
1241fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka
1242375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void cancelAllMessages() {
1243375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
1244375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.cancelAllMessages();
1245375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
1246375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
12475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
1248375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        dismissAllKeyPreviews();
1249375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        cancelAllMessages();
1250a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        onDismissMoreKeysPanel();
1251a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
12525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
12535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1254586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
1255c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
1256586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
1257586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
1258586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
1259586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
1260586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
1261c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
1262dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean dispatchHoverEvent(final MotionEvent event) {
12635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
1264e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
1265586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
12665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
12675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1268586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
12695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
12705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
127122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
1272dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
12737bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
127420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
127520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
127620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1277240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
127820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (shortcutKey == null) {
127920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
128020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
12817bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
12827bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
12835afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
12845afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
128573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    private void updateAltCodeKeyWhileTyping() {
128673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
128720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
128820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
128920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
129073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
129173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            invalidateKey(key);
129273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
129373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    }
129473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
1295dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
1296dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
12974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
1298fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
1299fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
1300d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
1301aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
13024c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
1303dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
13044daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
130531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
130631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
130731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
1308d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
1309dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
131031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
131131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
131231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
1313dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
13144c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
13154112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
13164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
13174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1318dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
131920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) {
132020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
132120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
13224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
13234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
132422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
132522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
1326a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private void dimEntireKeyboard(final boolean dimmed) {
1327f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
1328f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mNeedsToDimEntireKeyboard = dimmed;
1329f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (needsRedrawing) {
1330f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka            invalidateAllKeys();
1331f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
1332f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
1333f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
1334f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    @Override
1335f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    protected void onDraw(final Canvas canvas) {
1336f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        super.onDraw(canvas);
1337f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
1338f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        // Overlay a dark rectangle to dim.
1339f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (mNeedsToDimEntireKeyboard) {
134008ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint);
1341f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
1342f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
1343f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
134422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
1345dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
1346dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
13476bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
134873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
134973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
1350240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (key.mCode == Constants.CODE_SPACE) {
1351bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
13524112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
1353fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
1354bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
13554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
1356240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        } else if (key.mCode == Constants.CODE_LANGUAGE_SWITCH) {
135781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
1358fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
1359b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
1360b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
136122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
136222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
13634112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
136408ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka    private static boolean fitsTextIntoWidth(final int width, final String text,
136508ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            final Paint paint) {
1366bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
136708ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka        final float textWidth = TypefaceUtils.getLabelWidth(text, paint);
136820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (textWidth < width) {
136920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return true;
137020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1371bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1372bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float scaleX = width / textWidth;
137320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
137420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
137520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1376bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1377bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
137808ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka        return TypefaceUtils.getLabelWidth(text, paint) < width;
1379bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
1380bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1381bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
138208ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka    private static String layoutLanguageOnSpacebar(final Paint paint,
138308ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            final InputMethodSubtype subtype, final int width) {
1384bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
13854b3cae9b0cbd5bf30a1c8da383ff247f9c2afc5aTadashi G. Takaoka        final String fullText = getFullDisplayName(subtype);
138620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, fullText, paint)) {
138720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return fullText;
1388bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
1389bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
139020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String middleText = getMiddleDisplayName(subtype);
139120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, middleText, paint)) {
139220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return middleText;
13934112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
13944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
139520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String shortText = getShortDisplayName(subtype);
139620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, shortText, paint)) {
139720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return shortText;
13984112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
13994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1400bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
14014112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
14024112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1403dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
1404bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        final int width = key.mWidth;
1405b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        final int height = key.mHeight;
14064112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1407f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
1408bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
1409bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
1410bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
1411bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextSize(mSpacebarTextSize);
1412bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
1413bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
14144112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
14154112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
14164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
1417b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
1418aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextShadowColor);
141931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
14204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
1421aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextColor);
142231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
14234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
14244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
14254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
14264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
1427bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
14284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
14294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
14304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
14314112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1432bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
14334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else if (mSpaceIcon != null) {
14344112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = mSpaceIcon.getIntrinsicWidth();
14354112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mSpaceIcon.getIntrinsicHeight();
14364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
14374112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1438bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
14394112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
14404112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
144127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
144227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // InputMethodSubtype's display name for spacebar text in its locale.
144327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //        isAdditionalSubtype (T=true, F=false)
144476d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    // locale layout  | Short  Middle      Full
144576d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    // ------ ------- - ---- --------- ----------------------
144676d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_US qwerty  F  En  English   English (US)           exception
144776d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_GB qwerty  F  En  English   English (UK)           exception
144876d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  es_US spanish F  Es  Español   Español (EE.UU.)       exception
144976d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr    azerty  F  Fr  Français  Français
145076d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr_CA qwerty  F  Fr  Français  Français (Canada)
145176d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  de    qwertz  F  De  Deutsch   Deutsch
145276d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  zz    qwerty  F      QWERTY    QWERTY
14538630cd9d1708d42b3ed5d07d89ade2f3c1f85e47Tadashi G. Takaoka    //  fr    qwertz  T  Fr  Français  Français
14548630cd9d1708d42b3ed5d07d89ade2f3c1f85e47Tadashi G. Takaoka    //  de    qwerty  T  De  Deutsch   Deutsch
14558630cd9d1708d42b3ed5d07d89ade2f3c1f85e47Tadashi G. Takaoka    //  en_US azerty  T  En  English   English (US)
145676d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  zz    azerty  T      AZERTY    AZERTY
145727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
145827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's full display name in its locale.
14594b3cae9b0cbd5bf30a1c8da383ff247f9c2afc5aTadashi G. Takaoka    static String getFullDisplayName(final InputMethodSubtype subtype) {
146027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
146127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
146227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
14638630cd9d1708d42b3ed5d07d89ade2f3c1f85e47Tadashi G. Takaoka        return SubtypeLocale.getSubtypeLocaleDisplayName(subtype.getLocale());
146427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
146527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
146627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's short display name in its locale.
1467dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getShortDisplayName(final InputMethodSubtype subtype) {
146827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
146927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return "";
147027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
147127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
147299b93d17d53c2d587c45373831b327f7851ec0a8Jean Chalard        return StringUtils.capitalizeFirstCodePoint(locale.getLanguage(), locale);
147327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
147427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
147527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's middle display name in its locale.
1476dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getMiddleDisplayName(final InputMethodSubtype subtype) {
147727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
147827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
147927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
148027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
14818630cd9d1708d42b3ed5d07d89ade2f3c1f85e47Tadashi G. Takaoka        return SubtypeLocale.getSubtypeLocaleDisplayName(locale.getLanguage());
148227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
14835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
1484