18592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard/*
28592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard * Copyright (C) 2012 The Android Open Source Project
38592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard *
4ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
5ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka * you may not use this file except in compliance with the License.
6ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka * You may obtain a copy of the License at
78592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard *
8ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
98592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard *
108592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard * Unless required by applicable law or agreed to in writing, software
11ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
12ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka * See the License for the specific language governing permissions and
14ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka * limitations under the License.
158592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard */
168592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
178592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardpackage com.android.inputmethod.latin;
188592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
198592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.content.Context;
208592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.content.SharedPreferences;
219ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalardimport android.graphics.Point;
228592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.os.Looper;
238592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.preference.PreferenceManager;
248592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.test.ServiceTestCase;
258592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.text.InputType;
268592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.text.SpannableStringBuilder;
27f83a6821bbf3863415378ea39d3b705954318e41Jean Chalardimport android.text.style.CharacterStyle;
288592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.text.style.SuggestionSpan;
299f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagiimport android.util.Log;
308592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.view.LayoutInflater;
318592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.view.View;
326aaa105a851c1dc558a09a19fa716e9ad54270e4Tadashi G. Takaokaimport android.view.ViewGroup;
338592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.view.inputmethod.EditorInfo;
348592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.view.inputmethod.InputConnection;
352fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport android.view.inputmethod.InputMethodSubtype;
36d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataokaimport android.widget.EditText;
378592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport android.widget.FrameLayout;
388592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
396d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawaimport com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
40908f0ed1541d1f5a9cb6d89d06ec39c219d079bdJean Chalardimport com.android.inputmethod.event.Event;
418592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport com.android.inputmethod.keyboard.Key;
428592ed408e8e81138172eefe2df28f00807a1e1fJean Chalardimport com.android.inputmethod.keyboard.Keyboard;
4381a5a10476a8ada6bbcaeb58d7c23d05f4ec1ab0Jean Chalardimport com.android.inputmethod.latin.Dictionary.PhonyDictionary;
449310f42a36eabe99ed7dcd3b835d6cdaa3c6fdcaJean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
459342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport com.android.inputmethod.latin.common.Constants;
4636799b2aa2982ec17341cd2c5ed81e608bcee8c6Jean Chalardimport com.android.inputmethod.latin.common.InputPointers;
475b91b551e5ffaf2c2e691dfbd434f21c82293986Jean Chalardimport com.android.inputmethod.latin.common.LocaleUtils;
484beeb9253a06482299e0c67467531d30436a02fcJean Chalardimport com.android.inputmethod.latin.common.StringUtils;
49da973e75dcea8dac8c83d7dcc4a7a33a69bab2cdTadashi G. Takaokaimport com.android.inputmethod.latin.settings.DebugSettings;
50da973e75dcea8dac8c83d7dcc4a7a33a69bab2cdTadashi G. Takaokaimport com.android.inputmethod.latin.settings.Settings;
512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
528592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
53c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalardimport java.util.Locale;
549f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagiimport java.util.concurrent.TimeUnit;
559cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka
56c9aaba125365cf767356a4bea442153e95bbf6a1Jean Chalardpublic class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
579f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagi    private static final String TAG = InputTestsBase.class.getSimpleName();
588592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
592fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // Default value for auto-correction threshold. This is the string representation of the
602fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // index in the resources array of auto-correction threshold settings.
61116e58c437526ea2503564317e2731b8126acaa4Chieu Nguyen    private static final boolean DEFAULT_AUTO_CORRECTION = true;
628592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
632fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // The message that sets the underline is posted with a 500 ms delay
649ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    protected static final int DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS = 500;
65cc2751ba03fad6af5da0a7b5d421963e040d690fYuichiro Hanada    // The message that sets predictions is posted with a 200 ms delay
669ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS = 200;
679ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    // We wait for gesture computation for this delay
689ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    protected static final int DELAY_TO_WAIT_FOR_GESTURE_MILLIS = 200;
69ffd808b4462875a2c15d471dea020f263b833a60Dan Zivkovic    // If a dictionary takes longer to load, we could have serious problems.
70ffd808b4462875a2c15d471dea020f263b833a60Dan Zivkovic    private final int TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS = 5;
718592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
7281a5a10476a8ada6bbcaeb58d7c23d05f4ec1ab0Jean Chalard    // Type for a test phony dictionary
7381a5a10476a8ada6bbcaeb58d7c23d05f4ec1ab0Jean Chalard    private static final String TYPE_TEST = "test";
7481a5a10476a8ada6bbcaeb58d7c23d05f4ec1ab0Jean Chalard    private static final PhonyDictionary DICTIONARY_TEST = new PhonyDictionary(TYPE_TEST);
7581a5a10476a8ada6bbcaeb58d7c23d05f4ec1ab0Jean Chalard
768592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    protected LatinIME mLatinIME;
778592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    protected Keyboard mKeyboard;
78d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka    protected MyEditText mEditText;
792752287c425b9be87800136bd616ee63940caaacJean Chalard    protected View mInputView;
808592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    protected InputConnection mInputConnection;
81116e58c437526ea2503564317e2731b8126acaa4Chieu Nguyen    private boolean mPreviousAutoCorrectSetting;
825455179b54eb8230577f518db082796a3055685aDan Zivkovic    private boolean mPreviousBigramPredictionSettings;
838592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
848592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // A helper class to ease span tests
85f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard    public static class SpanGetter {
868592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        final SpannableStringBuilder mInputText;
87f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard        final CharacterStyle mSpan;
888592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        final int mStart;
898592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        final int mEnd;
908592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // The supplied CharSequence should be an instance of SpannableStringBuilder,
91f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard        // and it should contain exactly zero or one span. Otherwise, an exception
928592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // is thrown.
93f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard        public SpanGetter(final CharSequence inputText,
94f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard                final Class<? extends CharacterStyle> spanType) {
958592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            mInputText = (SpannableStringBuilder)inputText;
96f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard            final CharacterStyle[] spans =
97f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard                    mInputText.getSpans(0, mInputText.length(), spanType);
988592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            if (0 == spans.length) {
998592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                mSpan = null;
1008592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                mStart = -1;
1018592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                mEnd = -1;
1028592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            } else if (1 == spans.length) {
1038592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                mSpan = spans[0];
1048592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                mStart = mInputText.getSpanStart(mSpan);
1058592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                mEnd = mInputText.getSpanEnd(mSpan);
1068592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            } else {
107f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard                throw new RuntimeException("Expected one span, found " + spans.length);
1088592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            }
1098592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        }
1104262ef4e11ae2c5dc0a8db980d4996c04731bc2bDan Zivkovic        public SuggestionSpan getSpan() {
1114262ef4e11ae2c5dc0a8db980d4996c04731bc2bDan Zivkovic            return (SuggestionSpan) mSpan;
1124262ef4e11ae2c5dc0a8db980d4996c04731bc2bDan Zivkovic        }
1138592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        public boolean isAutoCorrectionIndicator() {
114f83a6821bbf3863415378ea39d3b705954318e41Jean Chalard            return (mSpan instanceof SuggestionSpan) &&
1154262ef4e11ae2c5dc0a8db980d4996c04731bc2bDan Zivkovic                    0 != (SuggestionSpan.FLAG_AUTO_CORRECTION & getSpan().getFlags());
1168592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        }
1175d96585a388a0af8bf0e37fc38ed3d2630733ab9Jean Chalard        public String[] getSuggestions() {
1184262ef4e11ae2c5dc0a8db980d4996c04731bc2bDan Zivkovic            return getSpan().getSuggestions();
1195d96585a388a0af8bf0e37fc38ed3d2630733ab9Jean Chalard        }
1205d96585a388a0af8bf0e37fc38ed3d2630733ab9Jean Chalard    }
1215d96585a388a0af8bf0e37fc38ed3d2630733ab9Jean Chalard
122d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka    // A helper class to increase control over the EditText
123d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka    public static class MyEditText extends EditText {
124c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalard        public Locale mCurrentLocale;
125d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        public MyEditText(final Context c) {
1265d96585a388a0af8bf0e37fc38ed3d2630733ab9Jean Chalard            super(c);
1275d96585a388a0af8bf0e37fc38ed3d2630733ab9Jean Chalard        }
128d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka
129c43417fb5d037d49c49176ed92f995b74b861d3eSatoshi Kataoka        @Override
130c43417fb5d037d49c49176ed92f995b74b861d3eSatoshi Kataoka        public void onAttachedToWindow() {
131c43417fb5d037d49c49176ed92f995b74b861d3eSatoshi Kataoka            // Make onAttachedToWindow "public"
132c43417fb5d037d49c49176ed92f995b74b861d3eSatoshi Kataoka            super.onAttachedToWindow();
133c43417fb5d037d49c49176ed92f995b74b861d3eSatoshi Kataoka        }
134c43417fb5d037d49c49176ed92f995b74b861d3eSatoshi Kataoka
135d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        // overriding hidden API in EditText
136c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalard        public Locale getTextServicesLocale() {
137d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka            // This method is necessary because EditText is asking this method for the language
138c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalard            // to check the spell in. If we don't override this, the spell checker will run in
139c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalard            // whatever language the keyboard is currently set on the test device, ignoring any
140c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalard            // settings we do inside the tests.
141c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalard            return mCurrentLocale;
142c61bed6860cf047e4ef7fd851ae82727ac230440Jean Chalard        }
143d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka
144d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        // overriding hidden API in EditText
145d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        public Locale getSpellCheckerLocale() {
146d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka            // This method is necessary because EditText is asking this method for the language
147d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka            // to check the spell in. If we don't override this, the spell checker will run in
148d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka            // whatever language the keyboard is currently set on the test device, ignoring any
149d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka            // settings we do inside the tests.
150d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka            return mCurrentLocale;
151d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        }
152d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka
1538592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
1548592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
1558592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    public InputTestsBase() {
156c9aaba125365cf767356a4bea442153e95bbf6a1Jean Chalard        super(LatinIMEForTests.class);
1578592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
1588592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
1599f7392ea9d9313b276bda59ec488cd5f994c1894Jean Chalard    protected boolean setBooleanPreference(final String key, final boolean value,
1609f7392ea9d9313b276bda59ec488cd5f994c1894Jean Chalard            final boolean defaultValue) {
1618592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME);
1629f7392ea9d9313b276bda59ec488cd5f994c1894Jean Chalard        final boolean previousSetting = prefs.getBoolean(key, defaultValue);
1638592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        final SharedPreferences.Editor editor = prefs.edit();
1649f7392ea9d9313b276bda59ec488cd5f994c1894Jean Chalard        editor.putBoolean(key, value);
1652fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        editor.apply();
1662fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        return previousSetting;
1672fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
1682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
169cab6fa3e1b9f22135825cfdee1fb9df992e63234Dan Zivkovic    protected boolean getBooleanPreference(final String key, final boolean defaultValue) {
170cab6fa3e1b9f22135825cfdee1fb9df992e63234Dan Zivkovic        return PreferenceManager.getDefaultSharedPreferences(mLatinIME)
171cab6fa3e1b9f22135825cfdee1fb9df992e63234Dan Zivkovic                .getBoolean(key, defaultValue);
172cab6fa3e1b9f22135825cfdee1fb9df992e63234Dan Zivkovic    }
173cab6fa3e1b9f22135825cfdee1fb9df992e63234Dan Zivkovic
1742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    protected String setStringPreference(final String key, final String value,
1752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa            final String defaultValue) {
1762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME);
1772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final String previousSetting = prefs.getString(key, defaultValue);
1782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        final SharedPreferences.Editor editor = prefs.edit();
1792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        editor.putString(key, value);
1802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        editor.apply();
1819f7392ea9d9313b276bda59ec488cd5f994c1894Jean Chalard        return previousSetting;
1829f7392ea9d9313b276bda59ec488cd5f994c1894Jean Chalard    }
1839f7392ea9d9313b276bda59ec488cd5f994c1894Jean Chalard
184d0d3d813d23efe40598970c51bb8f064b9e3771dJean Chalard    protected void setDebugMode(final boolean value) {
185d0d3d813d23efe40598970c51bb8f064b9e3771dJean Chalard        setBooleanPreference(DebugSettings.PREF_DEBUG_MODE, value, false);
186d0d3d813d23efe40598970c51bb8f064b9e3771dJean Chalard        setBooleanPreference(Settings.PREF_KEY_IS_INTERNAL, value, false);
1878592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
1888592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
1891404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard    protected EditorInfo enrichEditorInfo(final EditorInfo ei) {
1901404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard        // Some tests that inherit from us need to add some data in the EditorInfo (see
1911404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard        // AppWorkaroundsTests#enrichEditorInfo() for a concrete example of this). Since we
1921404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard        // control the EditorInfo, we supply a hook here for children to override.
1931404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard        return ei;
1941404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard    }
1951404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard
1968592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    @Override
19748a7681e064ae259b840f0e757da2d716043d893Kurt Partridge    protected void setUp() throws Exception {
19848a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        super.setUp();
199d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        mEditText = new MyEditText(getContext());
20084bfde04216b471407801bf57a31687c4992bca0Jean Chalard        final int inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
20184bfde04216b471407801bf57a31687c4992bca0Jean Chalard                | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
202d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        mEditText.setInputType(inputType);
203d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        mEditText.setEnabled(true);
2047e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        mLastCursorPos = 0;
205161080c0291d978ed43f11c7b8939fee5fb17e06Jean Chalard        if (null == Looper.myLooper()) {
206161080c0291d978ed43f11c7b8939fee5fb17e06Jean Chalard            Looper.prepare();
207161080c0291d978ed43f11c7b8939fee5fb17e06Jean Chalard        }
2088592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        setupService();
2098592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        mLatinIME = getService();
21028a59dd049bafa45de628e2cafda47bf46d9e22aJean Chalard        setDebugMode(true);
2115455179b54eb8230577f518db082796a3055685aDan Zivkovic        mPreviousBigramPredictionSettings = setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS,
2125455179b54eb8230577f518db082796a3055685aDan Zivkovic                true, true /* defaultValue */);
213116e58c437526ea2503564317e2731b8126acaa4Chieu Nguyen        mPreviousAutoCorrectSetting = setBooleanPreference(Settings.PREF_AUTO_CORRECTION,
214116e58c437526ea2503564317e2731b8126acaa4Chieu Nguyen                DEFAULT_AUTO_CORRECTION, DEFAULT_AUTO_CORRECTION);
2158592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        mLatinIME.onCreate();
2161404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard        EditorInfo ei = new EditorInfo();
217d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        final InputConnection ic = mEditText.onCreateInputConnection(ei);
2188592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        final LayoutInflater inflater =
2198592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
2208592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        final ViewGroup vg = new FrameLayout(getContext());
2212752287c425b9be87800136bd616ee63940caaacJean Chalard        mInputView = inflater.inflate(R.layout.input_view, vg);
2221404c1af850236da4fe4ed3825b70cb228a1da5bJean Chalard        ei = enrichEditorInfo(ei);
223b0561ae98063f83684706886490ba5670138fcccJean Chalard        mLatinIME.onCreateInputMethodInterface().startInput(ic, ei);
2242752287c425b9be87800136bd616ee63940caaacJean Chalard        mLatinIME.setInputView(mInputView);
2258592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        mLatinIME.onBindInput();
2268592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        mLatinIME.onCreateInputView();
2278592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        mLatinIME.onStartInputView(ei, false);
2288592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        mInputConnection = ic;
2298592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        changeLanguage("en_US");
2307b6e999cd174f63501ccabdade5489d61107b341Jean Chalard        // Run messages to avoid the messages enqueued by startInputView() and its friends
231bac89ecc508052161704ef02c26e4e1d4d5060faJean Chalard        // to run on a later call and ruin things. We need to wait first because some of them
232bac89ecc508052161704ef02c26e4e1d4d5060faJean Chalard        // can be posted with a delay (notably,  MSG_RESUME_SUGGESTIONS)
2339ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
2347b6e999cd174f63501ccabdade5489d61107b341Jean Chalard        runMessages();
2358592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
2368592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
2372fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    @Override
238da973e75dcea8dac8c83d7dcc4a7a33a69bab2cdTadashi G. Takaoka    protected void tearDown() throws Exception {
2394370ff0998d3240cfda7745d08edbdd11703b984Jean Chalard        mLatinIME.onFinishInputView(true);
2404370ff0998d3240cfda7745d08edbdd11703b984Jean Chalard        mLatinIME.onFinishInput();
2414370ff0998d3240cfda7745d08edbdd11703b984Jean Chalard        runMessages();
2422fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mLatinIME.mHandler.removeAllMessages();
2435455179b54eb8230577f518db082796a3055685aDan Zivkovic        setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mPreviousBigramPredictionSettings,
2445455179b54eb8230577f518db082796a3055685aDan Zivkovic                true /* defaultValue */);
245116e58c437526ea2503564317e2731b8126acaa4Chieu Nguyen        setBooleanPreference(Settings.PREF_AUTO_CORRECTION, mPreviousAutoCorrectSetting,
246116e58c437526ea2503564317e2731b8126acaa4Chieu Nguyen                DEFAULT_AUTO_CORRECTION);
24728a59dd049bafa45de628e2cafda47bf46d9e22aJean Chalard        setDebugMode(false);
2484370ff0998d3240cfda7745d08edbdd11703b984Jean Chalard        mLatinIME.recycle();
249da973e75dcea8dac8c83d7dcc4a7a33a69bab2cdTadashi G. Takaoka        super.tearDown();
2504370ff0998d3240cfda7745d08edbdd11703b984Jean Chalard        mLatinIME = null;
2512fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    }
2522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
2538592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // We need to run the messages added to the handler from LatinIME. The only way to do
2548592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // that is to call Looper#loop() on the right looper, so we're going to get the looper
2558592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // object and call #loop() here. The messages in the handler actually run on the UI
2568592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // thread of the keyboard by design of the handler, so we want to call it synchronously
2578592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // on the same thread that the tests are running on to mimic the actual environment as
2588592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // closely as possible.
2598592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // Now, Looper#loop() never exits in normal operation unless the Looper#quit() method
260d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard    // is called, which has a lot of bad side effects. We can however just throw an exception
261d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard    // in the runnable which will unwind the stack and allow us to exit.
2625f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka    final class InterruptRunMessagesException extends RuntimeException {
263d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard        // Empty class
264d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard    }
2658592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    protected void runMessages() {
2668592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        mLatinIME.mHandler.post(new Runnable() {
2678592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                @Override
2688592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                public void run() {
269d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard                    throw new InterruptRunMessagesException();
2708592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard                }
2718592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            });
2728592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        try {
273d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard            Looper.loop();
274d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard        } catch (InterruptRunMessagesException e) {
275d89ed476169f3d03aa8d080c40098f4724c093adJean Chalard            // Resume normal operation
2768592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        }
2778592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
2788592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
2798592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // type(int) and type(String): helper methods to send a code point resp. a string to LatinIME.
2802282e8520a2c1984989a14fb09896536f5033b26Jean Chalard    protected void typeInternal(final int codePoint, final boolean isKeyRepeat) {
2818592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // onPressKey and onReleaseKey are explicitly deactivated here, but they do happen in the
2828592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // code (although multitouch/slide input and other factors make the sequencing complicated).
2838592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // They are supposed to be entirely deconnected from the input logic from LatinIME point of
2848592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // view and only delegates to the parts of the code that care. So we don't include them here
2858592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // to keep these tests as pinpoint as possible and avoid bringing it too many dependencies,
2868592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        // but keep them in mind if something breaks. Commenting them out as is should work.
287e8264cab71af545a6124fc6137837b7459145d25Tadashi G. Takaoka        //mLatinIME.onPressKey(codePoint, 0 /* repeatCount */, true /* isSinglePointer */);
288e8264cab71af545a6124fc6137837b7459145d25Tadashi G. Takaoka        final Key key = mKeyboard.getKey(codePoint);
289908f0ed1541d1f5a9cb6d89d06ec39c219d079bdJean Chalard        final Event event;
2902282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        if (key == null) {
291908f0ed1541d1f5a9cb6d89d06ec39c219d079bdJean Chalard            event = Event.createSoftwareKeypressEvent(codePoint, Event.NOT_A_KEY_CODE,
292908f0ed1541d1f5a9cb6d89d06ec39c219d079bdJean Chalard                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, isKeyRepeat);
2932282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        } else {
2947dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            final int x = key.getX() + key.getWidth() / 2;
2957dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka            final int y = key.getY() + key.getHeight() / 2;
2965f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka            event = LatinIME.createSoftwareKeypressEvent(codePoint, x, y, isKeyRepeat);
2978592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        }
298908f0ed1541d1f5a9cb6d89d06ec39c219d079bdJean Chalard        mLatinIME.onEvent(event);
2992282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        // Also see the comment at the top of this function about onReleaseKey
300e8264cab71af545a6124fc6137837b7459145d25Tadashi G. Takaoka        //mLatinIME.onReleaseKey(codePoint, false /* withSliding */);
3018592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
3028592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
3032282e8520a2c1984989a14fb09896536f5033b26Jean Chalard    protected void type(final int codePoint) {
3042282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        typeInternal(codePoint, false /* isKeyRepeat */);
3052282e8520a2c1984989a14fb09896536f5033b26Jean Chalard    }
3062282e8520a2c1984989a14fb09896536f5033b26Jean Chalard
3072282e8520a2c1984989a14fb09896536f5033b26Jean Chalard    protected void repeatKey(final int codePoint) {
3082282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        typeInternal(codePoint, true /* isKeyRepeat */);
3092282e8520a2c1984989a14fb09896536f5033b26Jean Chalard    }
3102282e8520a2c1984989a14fb09896536f5033b26Jean Chalard
3118592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    protected void type(final String stringToType) {
3128592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        for (int i = 0; i < stringToType.length(); i = stringToType.offsetByCodePoints(i, 1)) {
3138592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            type(stringToType.codePointAt(i));
3148592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        }
3158592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
3168592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
3179ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    protected Point getXY(final int codePoint) {
3189ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        final Key key = mKeyboard.getKey(codePoint);
3199ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        if (key == null) {
3209ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            throw new RuntimeException("Code point not on the keyboard");
3219ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        }
3225f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka        return new Point(key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2);
3239ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    }
3249ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard
3259ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    protected void gesture(final String stringToGesture) {
3269ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        if (StringUtils.codePointCount(stringToGesture) < 2) {
3279ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            throw new RuntimeException("Can't gesture strings less than 2 chars long");
3289ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        }
3299ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard
3309ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        mLatinIME.onStartBatchInput();
3319ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        final int startCodePoint = stringToGesture.codePointAt(0);
3329ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        Point oldPoint = getXY(startCodePoint);
3339ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        int timestamp = 0; // In milliseconds since the start of the gesture
3349ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        final InputPointers pointers = new InputPointers(Constants.DEFAULT_GESTURE_POINTS_CAPACITY);
3359ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        pointers.addPointer(oldPoint.x, oldPoint.y, 0 /* pointerId */, timestamp);
3369ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard
3379ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        for (int i = Character.charCount(startCodePoint); i < stringToGesture.length();
3389ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard                i = stringToGesture.offsetByCodePoints(i, 1)) {
3399ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            final Point newPoint = getXY(stringToGesture.codePointAt(i));
3409ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            // Arbitrarily 0.5s between letters and 0.1 between events. Refine this later if needed.
3419ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            final int STEPS = 5;
3429ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            for (int j = 0; j < STEPS; ++j) {
3439ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard                timestamp += 100;
3449ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard                pointers.addPointer(oldPoint.x + ((newPoint.x - oldPoint.x) * j) / STEPS,
3459ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard                        oldPoint.y + ((newPoint.y - oldPoint.y) * j) / STEPS,
3469ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard                        0 /* pointerId */, timestamp);
3479ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            }
3489ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            oldPoint.x = newPoint.x;
3499ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            oldPoint.y = newPoint.y;
3509ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard            mLatinIME.onUpdateBatchInput(pointers);
3519ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        }
3529ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        mLatinIME.onEndBatchInput(pointers);
3539ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        sleep(DELAY_TO_WAIT_FOR_GESTURE_MILLIS);
3549ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard        runMessages();
3559ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard    }
3569ddbee8570af667b5b325ae4136853aaea85c5b9Jean Chalard
357d36328cecf3b0c5e7acde7bd5963ea670b8d634fKeisuke Kuroyanagi    protected void waitForDictionariesToBeLoaded() {
3589f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagi        try {
359d36328cecf3b0c5e7acde7bd5963ea670b8d634fKeisuke Kuroyanagi            mLatinIME.waitForLoadingDictionaries(
3609f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagi                    TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS, TimeUnit.SECONDS);
3619f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagi        } catch (InterruptedException e) {
3629f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagi            Log.e(TAG, "Interrupted during waiting for loading main dictionary.", e);
3638592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        }
3648592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
3658592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
3668592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    protected void changeLanguage(final String locale) {
36768730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard        changeLanguage(locale, null);
36868730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard    }
36968730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard
37068730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard    protected void changeLanguage(final String locale, final String combiningSpec) {
37168730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard        changeLanguageWithoutWait(locale, combiningSpec);
372d36328cecf3b0c5e7acde7bd5963ea670b8d634fKeisuke Kuroyanagi        waitForDictionariesToBeLoaded();
37372bc9ad37ed3d89ccea253542b867221a1d9a129Keisuke Kuroyanagi    }
37472bc9ad37ed3d89ccea253542b867221a1d9a129Keisuke Kuroyanagi
37568730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard    protected void changeLanguageWithoutWait(final String locale, final String combiningSpec) {
376d5781eef628c2cd4ac38029040746daa4679d637Satoshi Kataoka        mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale);
3776d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa        // TODO: this is forcing a QWERTY keyboard for all locales, which is wrong.
3786d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa        // It's still better than using whatever keyboard is the current one, but we
3796d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa        // should actually use the default keyboard for this locale.
3806d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa        // TODO: Use {@link InputMethodSubtype.InputMethodSubtypeBuilder} directly or indirectly so
3816d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa        // that {@link InputMethodSubtype#isAsciiCapable} can return the correct value.
3826d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa        final String EXTRA_VALUE_FOR_TEST =
3836d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
3846d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
3856d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
38668730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard                + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE
38768730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard                + null == combiningSpec ? "" : ("," + combiningSpec);
3886d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa        final InputMethodSubtype subtype = InputMethodSubtypeCompatUtils.newInputMethodSubtype(
3896d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                R.string.subtype_no_language_qwerty,
3906d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                R.drawable.ic_ime_switcher_dark,
3916d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                locale,
3926d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                Constants.Subtype.KEYBOARD_MODE,
3936d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                EXTRA_VALUE_FOR_TEST,
3946d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                false /* isAuxiliary */,
3956d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                false /* overridesImplicitlyEnabledSubtype */,
3966d43124215ca49feb35022c602d1dae7b5f23e4dYohei Yukawa                0 /* id */);
3977fb0ed58edd4cc2514f0b5dd5bd2083889ff325cTadashi G. Takaoka        RichInputMethodManager.forceSubtype(subtype);
39868730d1c8eac4f3fad94a6525b343e2a136f4ffeJean Chalard        mLatinIME.onCurrentInputMethodSubtypeChanged(subtype);
399a099638667f79e509a0c12d0d8457946b7df8de5Jean Chalard        runMessages();
4009fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka        mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
401d36328cecf3b0c5e7acde7bd5963ea670b8d634fKeisuke Kuroyanagi        mLatinIME.clearPersonalizedDictionariesForTest();
4028592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
4038592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
404c239a34262358e51aedb15f2e315bbe9bcd57c4bJean Chalard    protected void changeKeyboardLocaleAndDictLocale(final String keyboardLocale,
405c239a34262358e51aedb15f2e315bbe9bcd57c4bJean Chalard            final String dictLocale) {
406c239a34262358e51aedb15f2e315bbe9bcd57c4bJean Chalard        changeLanguage(keyboardLocale);
407c239a34262358e51aedb15f2e315bbe9bcd57c4bJean Chalard        if (!keyboardLocale.equals(dictLocale)) {
4089f1fe87580adc9ca041fc044f88341fa262c82a2Keisuke Kuroyanagi            mLatinIME.replaceDictionariesForTest(LocaleUtils.constructLocaleFromString(dictLocale));
409c239a34262358e51aedb15f2e315bbe9bcd57c4bJean Chalard        }
410d36328cecf3b0c5e7acde7bd5963ea670b8d634fKeisuke Kuroyanagi        waitForDictionariesToBeLoaded();
411c239a34262358e51aedb15f2e315bbe9bcd57c4bJean Chalard    }
412c239a34262358e51aedb15f2e315bbe9bcd57c4bJean Chalard
4133fb5a541a44bc9a82663895fd09c342299f45b56Jean Chalard    protected void pickSuggestionManually(final String suggestion) {
414ab5912959435c1901e268bc9766090e604f3523dMohammadinamul Sheik        mLatinIME.pickSuggestionManually(new SuggestedWordInfo(suggestion,
415ab5912959435c1901e268bc9766090e604f3523dMohammadinamul Sheik                "" /* prevWordsContext */, 1 /* score */,
41681a5a10476a8ada6bbcaeb58d7c23d05f4ec1ab0Jean Chalard                SuggestedWordInfo.KIND_CORRECTION, DICTIONARY_TEST,
41724aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
41824aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
4199bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge    }
4208592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard
4218592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    // Helper to avoid writing the try{}catch block each time
4228592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    protected static void sleep(final int milliseconds) {
4238592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        try {
4248592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard            Thread.sleep(milliseconds);
4258592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard        } catch (InterruptedException e) {}
4268592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard    }
4277e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard
4287e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    // Some helper methods to manage the mock cursor position
4297e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    // DO NOT CALL LatinIME#onUpdateSelection IF YOU WANT TO USE THOSE
4307e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    int mLastCursorPos = 0;
4317e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    /**
4327e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard     * Move the cached cursor position to the passed position and send onUpdateSelection to LatinIME
4337e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard     */
4347e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    protected int sendUpdateForCursorMoveTo(final int position) {
4357e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        mInputConnection.setSelection(position, position);
4367e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        mLatinIME.onUpdateSelection(mLastCursorPos, mLastCursorPos, position, position, -1, -1);
4377e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        mLastCursorPos = position;
4387e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        return position;
4397e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    }
4407e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard
4417e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    /**
4427e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard     * Move the cached cursor position by the passed amount and send onUpdateSelection to LatinIME
4437e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard     */
4447e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    protected int sendUpdateForCursorMoveBy(final int offset) {
4457e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        final int lastPos = mEditText.getText().length();
4467e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        final int requestedPosition = mLastCursorPos + offset;
4477e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        if (requestedPosition < 0) {
4487e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard            return sendUpdateForCursorMoveTo(0);
4497e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        } else if (requestedPosition > lastPos) {
4507e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard            return sendUpdateForCursorMoveTo(lastPos);
4517e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        } else {
4527e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard            return sendUpdateForCursorMoveTo(requestedPosition);
4537e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        }
4547e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    }
4557e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard
4567e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    /**
4577e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard     * Move the cached cursor position to the end of the line and send onUpdateSelection to LatinIME
4587e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard     */
4597e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    protected int sendUpdateForCursorMoveToEndOfLine() {
4607e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        final int lastPos = mEditText.getText().length();
4617e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard        return sendUpdateForCursorMoveTo(lastPos);
4627e930bfb3a6b4a36e6e6cde5cecc76640c148fb0Jean Chalard    }
4638592ed408e8e81138172eefe2df28f00807a1e1fJean Chalard}
464