MainKeyboardView.java revision 6a23b9e3d54031524445b1190cc1eba77916c5b3
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
192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport android.animation.Animator;
20d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaokaimport android.animation.AnimatorInflater;
212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport android.animation.AnimatorListenerAdapter;
222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport android.animation.AnimatorSet;
2331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaokaimport android.animation.ObjectAnimator;
245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.Context;
2513ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.content.SharedPreferences;
265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager;
274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray;
286dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
29f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaokaimport android.graphics.Color;
3022b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint;
314112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align;
32bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface;
334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable;
3413ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.preference.PreferenceManager;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
36375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport android.util.DisplayMetrics;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
380e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport android.util.TypedValue;
395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
42b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport android.view.animation.AccelerateInterpolator;
442fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport android.view.animation.DecelerateInterpolator;
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;
512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.keyboard.internal.DrawingHandler;
52fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText;
53fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.GestureTrailsPreview;
5449d8af8a4e900f3c68c333aba7fde0a11fd368b1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
550e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
56b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
57375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.PreviewPlacerView;
58fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview;
592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.keyboard.internal.TimerHandler;
604daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport com.android.inputmethod.latin.Constants;
6115d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
63375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaokaimport com.android.inputmethod.latin.SuggestedWords;
64c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
65a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.settings.DebugSettings;
66e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
67e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CoordinateUtils;
68a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaokaimport com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
69ccf4a310279b13bbf0b6aac76a0878178c1dfb7dTadashi G. Takaokaimport com.android.inputmethod.latin.utils.TypefaceUtils;
704be6198cb73cc24e10834153c4e049644ed187e3Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.UsabilityStudyLogUtils;
71ccf4a310279b13bbf0b6aac76a0878178c1dfb7dTadashi G. Takaokaimport com.android.inputmethod.latin.utils.ViewLayoutUtils;
726b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport java.util.ArrayDeque;
752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport java.util.HashMap;
762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport java.util.HashSet;
775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
82dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio
852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor
862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor
872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_spacebarBackground
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
952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger
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 */
1212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy,
1222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks {
123c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
125bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
126bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
127bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
1282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    /* Space key and its icon and background. */
1294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
1302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private Drawable mSpacebarIcon;
1312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final Drawable mSpacebarBackground;
132bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
13331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
13431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
135bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
136fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1374daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1382fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final float mLanguageOnSpacebarTextRatio;
1392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private float mLanguageOnSpacebarTextSize;
1402fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final int mLanguageOnSpacebarTextColor;
1412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final int mLanguageOnSpacebarTextShadowColor;
142bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
143bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
144bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
145bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
146bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
147bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
148bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
14915d4793911fa305e0a58aced925961e948582979satok
15073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
1512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
1522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1534daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
15473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
155375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Preview placer view
156375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final PreviewPlacerView mPreviewPlacerView;
157375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int[] mOriginCoords = CoordinateUtils.newInstance();
158fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final GestureFloatingPreviewText mGestureFloatingPreviewText;
159fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final GestureTrailsPreview mGestureTrailsPreview;
160fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    private final SlidingKeyInputPreview mSlidingKeyInputPreview;
161375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Key preview
1632fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static final boolean FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED = false;
164375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private final int mKeyPreviewLayoutId;
1650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final int mKeyPreviewOffset;
1660e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final int mKeyPreviewHeight;
1672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Free {@link TextView} pool that can be used for key preview.
1682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque();
1692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview.
1702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap();
1710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
1720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private boolean mShowKeyPreviewPopup = true;
1730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private int mKeyPreviewLingerTimeout;
1742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private int mKeyPreviewZoomInDuration;
1752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private int mKeyPreviewZoomOutDuration;
1762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static final float KEY_PREVIEW_START_ZOOM_IN_SCALE = 0.7f;
1772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static final float KEY_PREVIEW_END_ZOOM_IN_SCALE = 1.0f;
1782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static final float KEY_PREVIEW_END_ZOOM_OUT_SCALE = 0.7f;
1792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
1802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            new AccelerateInterpolator();
1812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
1822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            new DecelerateInterpolator();
1830e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
1842affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
185f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private final Paint mBackgroundDimAlphaPaint = new Paint();
186f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    private boolean mNeedsToDimEntireKeyboard;
187a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final View mMoreKeysKeyboardContainer;
188a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache =
189a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            CollectionUtils.newWeakHashMap();
1902affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
191d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // More keys panel (used by both more keys keyboard and more suggestions view)
192d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    // TODO: Consider extending to support multiple more keys panels
193d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
195375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // Gesture floating preview text
196375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    // TODO: Make this parameter customizable by user via settings.
197375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private int mGestureFloatingPreviewTextLingerTimeout;
198375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
1992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final KeyDetector mKeyDetector;
200b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka    private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
2015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final TimerHandler mKeyTimerHandler;
203741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private final int mLanguageOnSpacebarHorizontalMargin;
2045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final DrawingHandler mDrawingHandler =
2062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            new DrawingHandler(this);
207375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
208dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
209c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
2105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
2115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
212dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
2135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
2145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviewPlacerView = new PreviewPlacerView(context, attrs);
2162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
2172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
2182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
2192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
2202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
2212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
2222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
2232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyTimerHandler = new TimerHandler(
2242fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime);
2252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
2262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
2272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
2282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
2292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
2302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyDetector = new KeyDetector(
2312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
2322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
2332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */,
2342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mKeyDetector);
2352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
23613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
23713ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean forceNonDistinctMultitouch = prefs.getBoolean(
238a5ff9f0c77005769f92ca1131882bb4e3ca18980Tadashi G. Takaoka                DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
23913ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean hasDistinctMultitouch = context.getPackageManager()
240b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
241b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                && !forceNonDistinctMultitouch;
242b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null
2432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                : new NonDistinctMultitouchHelper(PointerTracker.getPointerTracker(0));
24422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
245f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final int backgroundDimAlpha = mainKeyboardViewAttr.getInt(
246f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka                R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
247f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setColor(Color.BLACK);
248f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
2492fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mSpacebarBackground = mainKeyboardViewAttr.getDrawable(
2502fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_spacebarBackground);
251d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean(
252c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
253d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable(
254c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
2552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
2562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
2572fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor(
2582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0);
2592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
2602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0);
261d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt(
262c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
2634daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
264d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
265c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
266d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId(
267c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
268d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
269c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
270160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
2710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
2720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewOffset, 0);
2730e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
2740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewHeight, 0);
2750e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewLingerTimeout = mainKeyboardViewAttr.getInt(
2760e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
277375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyPreviewLayoutId = mainKeyboardViewAttr.getResourceId(
278375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_keyPreviewLayout, 0);
2790e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (mKeyPreviewLayoutId == 0) {
2800e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            mShowKeyPreviewPopup = false;
2810e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
2822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyPreviewZoomInDuration = mainKeyboardViewAttr.getInt(
2832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyPreviewZoomInDuration, 0);
2842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyPreviewZoomOutDuration = mainKeyboardViewAttr.getInt(
2852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.styleable.MainKeyboardView_keyPreviewZoomOutDuration, 0);
286a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
287d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka                R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
288d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
289c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
290375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
291375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt(
292375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka                R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
293fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
294fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText = new GestureFloatingPreviewText(
295fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
296fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mGestureFloatingPreviewText);
297fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
298fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureTrailsPreview = new GestureTrailsPreview(
299fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
300fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mGestureTrailsPreview);
301fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka
302fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview = new SlidingKeyInputPreview(
303fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka                mPreviewPlacerView, mainKeyboardViewAttr);
304fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mPreviewPlacerView.addPreview(mSlidingKeyInputPreview);
305d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mainKeyboardViewAttr.recycle();
306160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
307a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardContainer = LayoutInflater.from(getContext())
308a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                .inflate(moreKeysKeyboardLayoutId, null);
30931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
31031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
31131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
31231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
31331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
31431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
315f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka
316f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
317741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka
3182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarHorizontalMargin = (int)getResources().getDimension(
3192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                R.dimen.config_language_on_spacebar_horizontal_margin);
32031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
32131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
32252876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    @Override
32352876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
32452876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa        super.setHardwareAcceleratedDrawingEnabled(enabled);
32552876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa        mPreviewPlacerView.setHardwareAcceleratedDrawingEnabled(enabled);
32652876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa    }
32752876bbefd774e8647910f73a2e4c17ac4e6bf5cKen Wakasa
328dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
32920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (resId == 0) {
33057372fd3da2e879f190e4ccd41a00813774c9019Tadashi G. Takaoka            // TODO: Stop returning null.
33120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return null;
33220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
33331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
33431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
335d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
33631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
33773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
33831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
33931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
34073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
3412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
3422fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final ObjectAnimator animatorToStart) {
3432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (animatorToCancel == null || animatorToStart == null) {
3442fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // TODO: Stop using null as a no-operation animator.
3452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
3462fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
3472fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        float startFraction = 0.0f;
3482fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (animatorToCancel.isStarted()) {
3492fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            animatorToCancel.cancel();
3502fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
3512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
3522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final long startTime = (long)(animatorToStart.getDuration() * startFraction);
3532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        animatorToStart.start();
3542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        animatorToStart.setCurrentPlayTime(startTime);
3552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
3572fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHander.Callbacks} method.
3582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
3592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void startWhileTypingFadeinAnimation() {
3602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        cancelAndStartAnimators(
3612fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
3622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3632fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
3642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
3652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void startWhileTypingFadeoutAnimation() {
3662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        cancelAndStartAnimators(
3672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
3682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
3692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
37015f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
37131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
37231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
373d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
374d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
37515f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
376dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
37731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
37831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
37931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
38031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
38115f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
38231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
38331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
38431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
38531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
38615f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
387dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
3885141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (mAltCodeKeyWhileTypingAnimAlpha == alpha) {
3895141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
3905141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3915141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        // Update the visual of alt-code-key-while-typing.
39231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
3935141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
3945141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        if (keyboard == null) {
3955141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            return;
3965141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
3975141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
3985141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka            invalidateKey(key);
3995141f04ef3b213fbe4816168d1c42f30449fc446Tadashi G. Takaoka        }
4005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
402dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
4035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
4045c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
4055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: We should reconsider which coordinate system should be used to represent keyboard
4082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // event.
4092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public int getKeyX(final int x) {
4102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return Constants.isValidCoordinate(x) ? mKeyDetector.getTouchX(x) : x;
4115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: We should reconsider which coordinate system should be used to represent keyboard
4142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // event.
4152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public int getKeyY(final int y) {
4162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return Constants.isValidCoordinate(y) ? mKeyDetector.getTouchY(y) : y;
4170efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4180efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
4215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
4225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
4235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
4245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
4255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
427dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
4282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // Remove any pending messages.
4292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mKeyTimerHandler.cancelAllKeyTimers();
4305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
4315a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
43208ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
4338335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
4342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        PointerTracker.setKeyboardActionListener(mKeyboardActionListener);
435a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
4364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
437240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
4382fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mSpacebarIcon = (mSpaceKey != null)
4394daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
4404112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
4412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio;
4429c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
44315c42454c0117d869f31ce278021f53ec74c4aa2Tadashi G. Takaoka            final int orientation = getContext().getResources().getConfiguration().orientation;
44415c42454c0117d869f31ce278021f53ec74c4aa2Tadashi G. Takaoka            ResearchLogger.mainKeyboardView_setKeyboard(keyboard, orientation);
44548a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
446f147794fd41491a3383e6aca6d49007f58124068alanv
447f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
448f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
449b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa        AccessibleKeyboardViewProxy.getInstance().setKeyboard();
4505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4520e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
4530e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Enables or disables the key feedback popup. This is a popup that shows a magnified
4540e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * version of the depressed key. By default the preview is enabled.
4550e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param previewEnabled whether or not to enable the key feedback preview
4560e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @param delay the delay after which the preview is dismissed
4570e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #isKeyPreviewPopupEnabled()
4580e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
4590e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
4600e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mShowKeyPreviewPopup = previewEnabled;
4610e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        mKeyPreviewLingerTimeout = delay;
4620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
4630e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
464375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    private void locatePreviewPlacerView() {
465375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (mPreviewPlacerView.getParent() != null) {
466375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
467375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
468375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int width = getWidth();
469375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final int height = getHeight();
470375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (width == 0 || height == 0) {
471375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
472375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
473375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
474375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        getLocationInWindow(mOriginCoords);
475375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final DisplayMetrics dm = getResources().getDisplayMetrics();
476375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (CoordinateUtils.y(mOriginCoords) < dm.heightPixels / 4) {
477375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            // In transient state.
478375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
479375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
480375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final View rootView = getRootView();
481375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (rootView == null) {
482375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find root view");
483375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            return;
484375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
485375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
486375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        // Note: It'd be very weird if we get null by android.R.id.content.
487375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (windowContentView == null) {
488375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView");
489375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        } else {
490375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            windowContentView.addView(mPreviewPlacerView);
491375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka            mPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, width, height);
492375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
493375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
494375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
4950e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    /**
4960e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * Returns the enabled state of the key feedback preview
4970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @return whether or not the key feedback preview is enabled
4980e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     * @see #setKeyPreviewPopupEnabled(boolean, int)
4990e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka     */
5000e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public boolean isKeyPreviewPopupEnabled() {
5010e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        return mShowKeyPreviewPopup;
5020e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
5030e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
5042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private TextView getKeyPreviewTextView(final Key key) {
5052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        TextView previewTextView = mShowingKeyPreviewTextViews.remove(key);
5062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (previewTextView != null) {
5072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return previewTextView;
5082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
5092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewTextView = mFreeKeyPreviewTextViews.poll();
5102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (previewTextView != null) {
5112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return previewTextView;
512375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
513375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final Context context = getContext();
514375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        if (mKeyPreviewLayoutId != 0) {
5152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView = (TextView)LayoutInflater.from(context)
5162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                    .inflate(mKeyPreviewLayoutId, null);
517375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        } else {
5182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView = new TextView(context);
519375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
5202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        locatePreviewPlacerView();
5212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mPreviewPlacerView.addView(
5222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                previewTextView, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
5232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return previewTextView;
524375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
525375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
5262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link DrawingHandler.Callbacks} method.
5272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
5282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissAllKeyPreviews() {
5292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) {
5302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            dismissKeyPreviewWithoutDelay(key);
531375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        }
532375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        PointerTracker.setReleasedKeyGraphicsToAllKeys();
533375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
534375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
5350e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    // Background state set
5360e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
5370e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_MIDDLE
5380e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            EMPTY_STATE_SET,
5390e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_has_morekeys }
5400e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        },
5410e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_LEFT
5420e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_left_edge },
5430e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_left_edge, R.attr.state_has_morekeys }
5440e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        },
5450e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        { // STATE_RIGHT
5460e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_right_edge },
5470e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            { R.attr.state_right_edge, R.attr.state_has_morekeys }
5480e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
5490e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    };
5500e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_MIDDLE = 0;
5510e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_LEFT = 1;
5520e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_RIGHT = 2;
5530e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_NORMAL = 0;
5540e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    private static final int STATE_HAS_MOREKEYS = 1;
5550e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
5562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: Take this method out of this class.
5570e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
5582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void showKeyPreview(final Key key) {
5590e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // If key is invalid or IME is already closed, we must not show key preview.
5600e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // Trying to show key preview while root window is closed causes
5610e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // WindowManager.BadTokenException.
5620e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (key == null) {
5630e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            return;
5640e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
5650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
5662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
5672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Keyboard keyboard = getKeyboard();
5682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (!mShowKeyPreviewPopup) {
5692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap;
5702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
5712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
5722fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
5732fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final TextView previewTextView = getKeyPreviewTextView(key);
5740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final KeyDrawParams drawParams = mKeyDrawParams;
5752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewTextView.setTextColor(drawParams.mPreviewTextColor);
5762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Drawable background = previewTextView.getBackground();
577914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka        final String label = key.getPreviewLabel();
5780e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // What we show as preview should match what we show on a key top in onDraw().
5790e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (label != null) {
5800e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            // TODO Should take care of temporaryShiftLabel here.
5812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setCompoundDrawables(null, null, null, null);
5822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
583914d054ea26d18a1ba6a60f8c5e749136477bbf2Tadashi G. Takaoka                    key.selectPreviewTextSize(drawParams));
5842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setTypeface(key.selectPreviewTypeface(drawParams));
5852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setText(label);
5860e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else {
5872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setCompoundDrawables(null, null, null,
5880e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                    key.getPreviewIcon(keyboard.mIconsSet));
5892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setText(null);
5900e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
5910e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
5922fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewTextView.measure(
5930e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
5940e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int keyDrawWidth = key.getDrawWidth();
5952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final int previewWidth = previewTextView.getMeasuredWidth();
5960e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int previewHeight = mKeyPreviewHeight;
5970e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The width and height of visible part of the key preview background. The content marker
5980e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // of the background 9-patch have to cover the visible part of the background.
5992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewParams.mPreviewVisibleWidth = previewWidth - previewTextView.getPaddingLeft()
6002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                - previewTextView.getPaddingRight();
6012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewParams.mPreviewVisibleHeight = previewHeight - previewTextView.getPaddingTop()
6022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                - previewTextView.getPaddingBottom();
6030e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The distance between the top edge of the parent key and the bottom of the visible part
6040e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // of the key preview background.
6052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewParams.mPreviewVisibleOffset =
6062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mKeyPreviewOffset - previewTextView.getPaddingBottom();
6070e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        getLocationInWindow(mOriginCoords);
6080e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The key preview is horizontally aligned with the center of the visible part of the
6090e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
6100e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // the left/right background is used if such background is specified.
6110e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        final int statePosition;
6120e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
6130e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                + CoordinateUtils.x(mOriginCoords);
6140e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (previewX < 0) {
6150e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewX = 0;
6160e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_LEFT;
6170e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else if (previewX > getWidth() - previewWidth) {
6180e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            previewX = getWidth() - previewWidth;
6190e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_RIGHT;
6200e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        } else {
6210e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            statePosition = STATE_MIDDLE;
6220e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
6230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // The key preview is placed vertically above the top edge of the parent key with an
6240e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        // arbitrary offset.
6257dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int previewY = key.getY() - previewHeight + mKeyPreviewOffset
6260e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka                + CoordinateUtils.y(mOriginCoords);
6270e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
6280e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        if (background != null) {
6297dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
6300e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka            background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
6310e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        }
6320e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        ViewLayoutUtils.placeViewAt(
6332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                previewTextView, previewX, previewY, previewWidth, previewHeight);
6342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
6352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (!isHardwareAccelerated()) {
6362fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setVisibility(VISIBLE);
6372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mShowingKeyPreviewTextViews.put(key, previewTextView);
6382fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
6392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
6402fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewTextView.setPivotX(previewWidth / 2.0f);
6412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewTextView.setPivotY(previewHeight);
6422fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
6432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Animator zoomIn = createZoomInAniation(key, previewTextView);
6442fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Animator zoomOut = createZoomOutAnimation(key, previewTextView);
6452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final KeyPreviewAnimations animation = new KeyPreviewAnimations(zoomIn, zoomOut);
6462fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        previewTextView.setTag(animation);
6472fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        animation.startZoomIn();
6480e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
6490e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
6502fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: Move this internal class out to a separate external class.
6512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private static class KeyPreviewAnimations extends AnimatorListenerAdapter {
6522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        private final Animator mZoomIn;
6532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        private final Animator mZoomOut;
6542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
6552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        public KeyPreviewAnimations(final Animator zoomIn, final Animator zoomOut) {
6562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mZoomIn = zoomIn;
6572fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mZoomOut = zoomOut;
6582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
6592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
6602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        public void startZoomIn() {
6612fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mZoomIn.start();
6622fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
6632fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
6642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        public void startZoomOut() {
6652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            if (mZoomIn.isRunning()) {
6662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mZoomIn.addListener(this);
6672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                return;
6682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            }
6692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mZoomOut.start();
6702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
6712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
6722fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        @Override
6732fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        public void onAnimationEnd(final Animator animation) {
6742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mZoomOut.start();
6752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
6762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
6772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
6782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: Take this method out of this class.
6792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private Animator createZoomInAniation(final Key key, final TextView previewTextView) {
6802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
6812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                previewTextView, SCALE_X, KEY_PREVIEW_START_ZOOM_IN_SCALE,
6822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                KEY_PREVIEW_END_ZOOM_IN_SCALE);
6832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
6842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                previewTextView, SCALE_Y, KEY_PREVIEW_START_ZOOM_IN_SCALE,
6852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                KEY_PREVIEW_END_ZOOM_IN_SCALE);
6862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final AnimatorSet zoomInAnimation = new AnimatorSet();
6872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomInAnimation.play(scaleXAnimation).with(scaleYAnimation);
6882fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // TODO: Implement preference option to control key preview animation duration.
6892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomInAnimation.setDuration(mKeyPreviewZoomInDuration);
6902fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomInAnimation.setInterpolator(DECELERATE_INTERPOLATOR);
6912fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomInAnimation.addListener(new AnimatorListenerAdapter() {
6922fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            @Override
6932fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            public void onAnimationStart(final Animator animation) {
6942fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                previewTextView.setVisibility(VISIBLE);
6952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mShowingKeyPreviewTextViews.put(key, previewTextView);
6962fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            }
6972fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        });
6982fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return zoomInAnimation;
6992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
7002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
7012fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: Take this method out of this class.
7022fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private Animator createZoomOutAnimation(final Key key, final TextView previewTextView) {
7032fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
7042fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                previewTextView, SCALE_X, KEY_PREVIEW_END_ZOOM_OUT_SCALE);
7052fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
7062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                previewTextView, SCALE_Y, KEY_PREVIEW_END_ZOOM_OUT_SCALE);
7072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final AnimatorSet zoomOutAnimation = new AnimatorSet();
7082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomOutAnimation.play(scaleXAnimation).with(scaleYAnimation);
7092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // TODO: Implement preference option to control key preview animation duration.
7102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomOutAnimation.setDuration(mKeyPreviewZoomOutDuration);
7112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomOutAnimation.setInterpolator(ACCELERATE_INTERPOLATOR);
7122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        zoomOutAnimation.addListener(new AnimatorListenerAdapter() {
7132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            @Override
7142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            public void onAnimationEnd(final Animator animation) {
7152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                dismissKeyPreviewWithoutDelay(key);
7162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            }
7172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        });
7182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return zoomOutAnimation;
7192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
7202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
7212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHandler.Callbacks} method.
7222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: Take this method out of this class.
7230e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
7242fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissKeyPreviewWithoutDelay(final Key key) {
7252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (key == null) {
7262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
7272fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
7282fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final TextView previewTextView = mShowingKeyPreviewTextViews.remove(key);
7292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (previewTextView != null) {
7302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final Object tag = previewTextView.getTag();
7312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            if (tag instanceof Animator) {
7322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                ((Animator)tag).cancel();
7332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            }
7342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setTag(null);
7352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            previewTextView.setVisibility(INVISIBLE);
7362fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mFreeKeyPreviewTextViews.add(previewTextView);
7372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
7382fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // To redraw key top letter.
7392fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        invalidateKey(key);
7402fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
7412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
7422fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // TODO: Take this method out of this class.
7432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
7442fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void dismissKeyPreview(final Key key) {
7452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final TextView previewTextView = mShowingKeyPreviewTextViews.get(key);
7462fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (previewTextView == null) {
7472fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
7482fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
7492fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (!isHardwareAccelerated()) {
7502fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // TODO: Implement preference option to control key preview method and duration.
7512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, key);
7522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
7532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
7542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final Object tag = previewTextView.getTag();
7552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (tag instanceof KeyPreviewAnimations) {
7562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag;
7572fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            animation.startZoomOut();
7582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
7590e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
7600e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
761ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
762ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        mSlidingKeyInputPreview.setPreviewEnabled(enabled);
763ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka    }
764ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka
7650e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
7660e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void showSlidingKeyInputPreview(final PointerTracker tracker) {
7670e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka        locatePreviewPlacerView();
768fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview.setPreviewPosition(tracker);
7690e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
7700e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
7710e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    @Override
7720e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    public void dismissSlidingKeyInputPreview() {
773fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mSlidingKeyInputPreview.dismissSlidingKeyInputPreview();
7740e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka    }
7750e4f0da449d0fc4f0c4c6b49b4c24961da36a5d1Tadashi G. Takaoka
776724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa    private void setGesturePreviewMode(final boolean isGestureTrailEnabled,
777724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureFloatingPreviewTextEnabled) {
778724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        mGestureFloatingPreviewText.setPreviewEnabled(isGestureFloatingPreviewTextEnabled);
779724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        mGestureTrailsPreview.setPreviewEnabled(isGestureTrailEnabled);
780375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
781375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
7822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link DrawingHandler.Callbacks} method.
7832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
784375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
785375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
786fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureFloatingPreviewText.setSuggetedWords(suggestedWords);
787375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
788375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
789375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    public void dismissGestureFloatingPreviewText() {
790375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
791375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout);
792375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
793375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
794fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka    @Override
795212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka    public void showGestureTrail(final PointerTracker tracker,
796212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            final boolean showsFloatingPreviewText) {
797375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        locatePreviewPlacerView();
798212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        if (showsFloatingPreviewText) {
799212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka            mGestureFloatingPreviewText.setPreviewPosition(tracker);
800212165b0b8308802a461a6a526d367ba67b5567aTadashi G. Takaoka        }
801fb523d7f5d40123a49a9128c455815db320c90fcTadashi G. Takaoka        mGestureTrailsPreview.setPreviewPosition(tracker);
802375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
803375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
8048335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
805dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
8068335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
8078335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
8088335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
809724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa    public void setGestureHandlingEnabledByUser(final boolean isGestureHandlingEnabledByUser,
810724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureTrailEnabled,
811724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa            final boolean isGestureFloatingPreviewTextEnabled) {
812724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        PointerTracker.setGestureHandlingEnabledByUser(isGestureHandlingEnabledByUser);
813724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa        setGesturePreviewMode(isGestureHandlingEnabledByUser && isGestureTrailEnabled,
814724db044957eb3544323c9814cbb4f20e81b8594Ken Wakasa                isGestureHandlingEnabledByUser && isGestureFloatingPreviewTextEnabled);
8150657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
8160657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
8175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
8184331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
8194331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
8209c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
8219c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been attached.  This is needed to properly show the splash screen, which requires that
8229c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // the window token of the KeyboardView be non-null.
8239c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
824c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
825c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        }
826c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
827c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
828c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
829c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
830c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
831375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mPreviewPlacerView.removeAllViews();
8329c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Notify the ResearchLogger (development only diagnostics) that the keyboard view has
8339c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // been detached.  This is needed to invalidate the reference of {@link MainKeyboardView}
8349c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // to null.
8359c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
836c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
8374331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
8384331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
8394331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
840a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) {
8417dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (key.getMoreKeys() == null) {
8425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
84320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
844a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
845a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (moreKeysKeyboard == null) {
846a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            moreKeysKeyboard = new MoreKeysKeyboard.Builder(
847a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka                    context, key, this, mKeyPreviewDrawParams).build();
848a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka            mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
84920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
8505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
851a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final View container = mMoreKeysKeyboardContainer;
8522affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
8532affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
8542affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
855b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
8562affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
8575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Implements {@link TimerHandler.Callbacks} method.
8605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
861d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka     * Called when a key is long pressed.
8625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
8635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
8642fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
8652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public void onLongPress(final PointerTracker tracker) {
866a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
86796efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
868a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
869a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final Key key = tracker.getKey();
870a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (key == null) {
87196efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
872a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        }
8739c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
874c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_onLongPress();
8759bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
876f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka        final KeyboardActionListener listener = mKeyboardActionListener;
8779552badf3c24d2098d227b0ddca0721b928a10b1Tadashi G. Takaoka        if (key.hasNoPanelAutoMoreKey()) {
8787dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            final int moreKeyCode = key.getMoreKeys()[0].mCode;
8793708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
880ab16237e69061bb0aa7f882e48e5d93459c22ef3Tadashi G. Takaoka            listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */);
881f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            listener.onCodeInput(moreKeyCode,
882f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
883f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            listener.onReleaseKey(moreKeyCode, false /* withSliding */);
88496efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
8853708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
8867dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
887a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
8883708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
889f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka            if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
8906dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
891f87e8f7ec1efb93398d909c67468d716b0248fe7Tadashi G. Takaoka                listener.onReleaseKey(code, false /* withSliding */);
89296efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka                return;
8936dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
8946dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
89596efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka        openMoreKeysPanel(key, tracker);
8966dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
8976dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
89896efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka    private void openMoreKeysPanel(final Key key, final PointerTracker tracker) {
899a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext());
9009d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
90196efb1252a0bf84ac72ef3d438ea3b3d5ac8ddd7Tadashi G. Takaoka            return;
9025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
90363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
904547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int[] lastCoords = CoordinateUtils.newInstance();
905547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        tracker.getLastCoordinates(lastCoords);
906a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !key.noKeyPreview();
9077ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
9087ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
9097ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
9107ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
911547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                ? CoordinateUtils.x(lastCoords)
9127dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                : key.getX() + key.getWidth() / 2;
9137ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
9147ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
9157ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
91629d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
91729d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
9187dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int pointY = key.getY() + mKeyPreviewDrawParams.mPreviewVisibleOffset;
919fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
92033482a9b9ccf605c63fab7c9b8273a240bbc2035Tadashi G. Takaoka        tracker.onShowMoreKeysPanel(moreKeysPanel);
9212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // TODO: Implement zoom in animation of more keys panel.
9222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        dismissKeyPreviewWithoutDelay(key);
9235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
9245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
9252fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public boolean isInDraggingFinger() {
926a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
92763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
9285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
9292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return PointerTracker.isAnyInDraggingFinger();
9305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
9315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
932d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
933d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
9341b6eea89037158a1e271730eacdacb8b6e550d3aTadashi G. Takaoka        locatePreviewPlacerView();
9357f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka        panel.showInParent(mPreviewPlacerView);
936d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        mMoreKeysPanel = panel;
937a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        dimEntireKeyboard(true /* dimmed */);
938d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
939d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
940d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    public boolean isShowingMoreKeysPanel() {
941a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent();
942d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
943d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
944d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
9450e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka    public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {
946d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
947d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
948d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
949d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    @Override
9500e08d70fe52737582b6c83dbf48aaf4d83063566Tadashi G. Takaoka    public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {
951d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        dimEntireKeyboard(false /* dimmed */);
952d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        if (isShowingMoreKeysPanel()) {
9537f8345d59a82a9b1d458b8e55ffd12c2a0d466a7Tadashi G. Takaoka            mMoreKeysPanel.removeFromParent();
954d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka            mMoreKeysPanel = null;
955d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka        }
956d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka    }
957d9449ccf3ce9e5eabb022aed6a45bba680bce115Tadashi G. Takaoka
9585433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void startDoubleTapShiftKeyTimer() {
9595433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        mKeyTimerHandler.startDoubleTapShiftKeyTimer();
9605433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
9615433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
9625433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public void cancelDoubleTapShiftKeyTimer() {
9635433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        mKeyTimerHandler.cancelDoubleTapShiftKeyTimer();
9645433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
9655433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
9665433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    public boolean isInDoubleTapShiftKeyTimeout() {
9675433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka        return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout();
9685433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka    }
9695433ce64c0f141bc4ee82912844f47f575654af5Tadashi G. Takaoka
9705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
971dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    public boolean dispatchTouchEvent(MotionEvent event) {
972dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
973dc2ee7772402633817702e95c2a5b17f6dec03ebalanv            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
974dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        }
975dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        return super.dispatchTouchEvent(event);
976dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    }
977dc2ee7772402633817702e95c2a5b17f6dec03ebalanv
978dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    @Override
979dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
98046286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
98146286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
98246286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
983b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        if (mNonDistinctMultitouchHelper != null) {
984b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) {
985b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka                // Key repeating timer will be canceled if 2 or more keys are in action.
9862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                mKeyTimerHandler.cancelKeyRepeatTimers();
987b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            }
988b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            // Non distinct multitouch screen support
9892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector);
990b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka            return true;
991b6cc3a85ab68cff2fae4c3858b48d9c5d7b45690Tadashi G. Takaoka        }
992310ea9ab9058e922a562309cb0dc5d5e092000f1Tadashi G. Takaoka        return processMotionEvent(me);
993c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
994c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
995dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
99630977a151e15e30f5385a349e92fb770b987435fTadashi G. Takaoka        if (LatinImeLogger.sUsabilityStudy) {
99730977a151e15e30f5385a349e92fb770b987435fTadashi G. Takaoka            UsabilityStudyLogUtils.writeMotionEvent(me);
998d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
999fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // Currently the same "move" event is being logged twice.
10009c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
100154dd1bed528331208f15df2693bcd01004dd471aTadashi G. Takaoka            ResearchLogger.mainKeyboardView_processMotionEvent(me);
100215d4793911fa305e0a58aced925961e948582979satok        }
10035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1004e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int index = me.getActionIndex();
1005e08c418ff8b374244677960903cee8dd52a4d831Tadashi G. Takaoka        final int id = me.getPointerId(index);
10062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final PointerTracker tracker = PointerTracker.getPointerTracker(id);
10072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // When a more keys panel is showing, we should ignore other fingers' single touch events
10082fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // other than the finger that is showing the more keys panel.
10092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel()
10102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                && PointerTracker.getActivePointerTrackerCount() == 1) {
10112fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return true;
10122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
10132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        tracker.processMotionEvent(me, mKeyDetector);
10145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
10155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
10165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
10175c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka    public void cancelAllOngoingEvents() {
1018375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
1019375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        mDrawingHandler.cancelAllMessages();
10205c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissAllKeyPreviews();
10215c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissGestureFloatingPreviewText();
10225c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        dismissSlidingKeyInputPreview();
10235c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.dismissAllMoreKeysPanels();
10245c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        PointerTracker.cancelAllPointerTrackers();
1025375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka    }
1026375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka
10275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
10285c095e59f679f726df1b6655fbbd73e310ac0decTadashi G. Takaoka        cancelAllOngoingEvents();
1029a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka        mMoreKeysKeyboardCache.clear();
10305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
10315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1032586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
1033c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
1034586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
1035586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
1036586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
1037586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
1038586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
1039c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
1040dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean dispatchHoverEvent(final MotionEvent event) {
10415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
10426a23b9e3d54031524445b1190cc1eba77916c5b3Tadashi G. Takaoka            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(
10436a23b9e3d54031524445b1190cc1eba77916c5b3Tadashi G. Takaoka                    event, mKeyDetector);
10445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
10455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1046586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
10475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
10485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
104922b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
1050dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
10517bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
105220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
105320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
105420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1055240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
105620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (shortcutKey == null) {
105720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
105820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
10597bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
10607bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
10615afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
10625afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
1063dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
1064dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
10654112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
1066fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
1067fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
1068d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
1069aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
10704c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
1071dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
10724daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
107331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
107431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
107531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
1076d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
1077dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
107831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
107931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
108031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
1081dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
10824c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
10834112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
10844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
10854112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1086dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
108720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) {
108820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
108920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
10904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
10914112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
109222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
109322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
1094a729377395967f7652d93992cbcf50cd2ff522d1Tadashi G. Takaoka    private void dimEntireKeyboard(final boolean dimmed) {
1095f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
1096f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        mNeedsToDimEntireKeyboard = dimmed;
1097f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (needsRedrawing) {
1098f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka            invalidateAllKeys();
1099f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
1100f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
1101f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
1102f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    @Override
1103f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    protected void onDraw(final Canvas canvas) {
1104f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        super.onDraw(canvas);
1105f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
1106f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        // Overlay a dark rectangle to dim.
1107f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        if (mNeedsToDimEntireKeyboard) {
110808ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint);
1109f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka        }
1110f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka    }
1111f3c4cd45a5df6f90b922ea44db259e53117bb25aTadashi G. Takaoka
11122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Draw key background.
11132fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
11142fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    protected void onDrawKeyBackground(final Key key, final Canvas canvas,
11152fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final Drawable background) {
11162fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (key.getCode() == Constants.CODE_SPACE) {
11172fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            super.onDrawKeyBackground(key, canvas, mSpacebarBackground);
11182fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
11192fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
11202fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        super.onDrawKeyBackground(key, canvas, background);
11212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
11222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
112322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
1124dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
1125dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
11266bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
112773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
112873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
11292fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        // Don't draw key top letter when key preview is showing.
11302fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED
11312fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                && mShowingKeyPreviewTextViews.containsKey(key)) {
11322fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // TODO: Fade out animation for the key top letter, and fade in animation for the key
11332fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            // background color when the user presses the key.
11342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            return;
11352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
11367dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int code = key.getCode();
11377dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        if (code == Constants.CODE_SPACE) {
1138bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
11394112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
1140fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
1141bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
11424112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
11437dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        } else if (code == Constants.CODE_LANGUAGE_SWITCH) {
114481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
1145fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
1146b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
1147b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
114822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
114922b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
11504112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1151741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
1152741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka        final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2;
1153bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
11542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final float textWidth = TypefaceUtils.getStringWidth(text, paint);
115520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (textWidth < width) {
115620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return true;
115720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1158bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1159741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka        final float scaleX = maxTextWidth / textWidth;
116020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
116120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
116220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
1163bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1164bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
11652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth;
1166bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
1167bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1168bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
1169741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka    private String layoutLanguageOnSpacebar(final Paint paint,
117008ae0d5ca03ed455827e82222df249d1cafb5d71Tadashi G. Takaoka            final InputMethodSubtype subtype, final int width) {
1171741831d32295acfb7675d7e0781a634d78868c63Satoshi Kataoka
1172bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
1173a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype);
117420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, fullText, paint)) {
117520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return fullText;
1176bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
1177bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
1178a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String middleText = SubtypeLocaleUtils.getMiddleDisplayName(subtype);
117920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, middleText, paint)) {
118020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return middleText;
11814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
11824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1183a410cb48eab0cd75aa27e20f60e47a29a59fb9ffTadashi G. Takaoka        final String shortText = SubtypeLocaleUtils.getShortDisplayName(subtype);
118420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, shortText, paint)) {
118520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return shortText;
11864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
11874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1188bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
11894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
11904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1191dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
11927dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int width = key.getWidth();
11937dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka        final int height = key.getHeight();
11944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1195f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
1196bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
1197bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
1198bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
11992fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            paint.setTextSize(mLanguageOnSpacebarTextSize);
1200bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
1201bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
12024112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
12034112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
12044112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
1205b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
12062fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            paint.setColor(mLanguageOnSpacebarTextShadowColor);
120731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
12084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
12092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            paint.setColor(mLanguageOnSpacebarTextColor);
121031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
12114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
12124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
12134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
12144112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
1215bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
12164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
12174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
12184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
12194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1220bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
12212fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        } else if (mSpacebarIcon != null) {
12222fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final int iconWidth = mSpacebarIcon.getIntrinsicWidth();
12232fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final int iconHeight = mSpacebarIcon.getIntrinsicHeight();
12244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
12254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
12262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            drawIcon(canvas, mSpacebarIcon, x, y, iconWidth, iconHeight);
12274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
12284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
1229c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa
1230e68d565d0f9704145adbf781105f1ac9a90f0a98Ken Wakasa    @Override
1231c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    public void deallocateMemory() {
1232e68d565d0f9704145adbf781105f1ac9a90f0a98Ken Wakasa        super.deallocateMemory();
1233c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa        mGestureTrailsPreview.deallocateMemory();
1234c8814e20b7b0ed5f7e11292480e89152618dd862Ken Wakasa    }
12355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
1236