1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
7a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker *
10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
191cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaokaimport static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII;
201cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaokaimport static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
211cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaokaimport static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
221cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka
233c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridgeimport android.app.Activity;
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.app.AlertDialog;
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.BroadcastReceiver;
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.DialogInterface;
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Intent;
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.IntentFilter;
30923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.SharedPreferences;
317058b02a9c798c21b169b778be2befc7739f4e9bJean Chalardimport android.content.pm.PackageInfo;
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.res.Configuration;
3336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.content.res.Resources;
34c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaokaimport android.graphics.Rect;
35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.inputmethodservice.InputMethodService;
36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.media.AudioManager;
37123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaokaimport android.net.ConnectivityManager;
38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Debug;
39f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaokaimport android.os.Handler;
40f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaokaimport android.os.HandlerThread;
4181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaokaimport android.os.IBinder;
42923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Message;
43923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.SystemClock;
44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.preference.PreferenceManager;
45e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.text.InputType;
466a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalardimport android.text.SpannableString;
47923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils;
486a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalardimport android.text.style.SuggestionSpan;
49923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Log;
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.PrintWriterPrinter;
51923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Printer;
524e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalardimport android.view.KeyCharacterMap;
534e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalardimport android.view.KeyEvent;
54923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View;
55c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaokaimport android.view.ViewGroup.LayoutParams;
5613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokaimport android.view.Window;
5713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokaimport android.view.WindowManager;
58923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.CompletionInfo;
5996fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasaimport android.view.inputmethod.CorrectionInfo;
60923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.EditorInfo;
619cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
62c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka
635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.accessibility.AccessibilityUtils;
648d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanvimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
6515f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.UsedForTesting;
667058b02a9c798c21b169b778be2befc7739f4e9bJean Chalardimport com.android.inputmethod.compat.AppWorkaroundsUtils;
67fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasaimport com.android.inputmethod.compat.InputMethodServiceCompatUtils;
681fef530ec7626fa16777f52b48191e61db8f46d4satokimport com.android.inputmethod.compat.SuggestionSpanUtils;
691061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalardimport com.android.inputmethod.dictionarypack.DictionaryPackConstants;
70cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalardimport com.android.inputmethod.event.EventInterpreter;
715c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyDetector;
72c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
73c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardActionListener;
746b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardId;
75c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardSwitcher;
76c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaokaimport com.android.inputmethod.keyboard.MainKeyboardView;
776a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalardimport com.android.inputmethod.latin.RichInputConnection.Range;
789310f42a36eabe99ed7dcd3b835d6cdaa3c6fdcaJean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
7987cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataokaimport com.android.inputmethod.latin.Utils.Stats;
80c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
814702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaokaimport com.android.inputmethod.latin.suggestions.SuggestionStripView;
826b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
83923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
84466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.FileDescriptor;
85466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.PrintWriter;
864ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalardimport java.util.ArrayList;
87466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.util.Locale;
88976a42b873215a669fe22709960aa1259292d75cJean Chalardimport java.util.TreeSet;
89466741d8a78965b8509bf527344f289e50873092Mike LeBeau
90923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
91923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Input method implementation for Qwerty'ish keyboard.
92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
93b1e37e4a478a577c59930767148e50f8f45af21fJean Chalardpublic class LatinIME extends InputMethodService implements KeyboardActionListener,
947058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        SuggestionStripView.Listener, TargetPackageInfoGetterTask.OnTargetPackageInfoKnownListener,
95369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka        Suggest.SuggestInitializationListener {
968efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    private static final String TAG = LatinIME.class.getSimpleName();
97409220583333bdf06290dd9fd42f91b5c0d1b11asatok    private static final boolean TRACE = false;
989e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka    private static boolean DEBUG;
99a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1009e347d3d448e48229c46aad394ec9bd60cd5807bsatok    private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
101fd0bd57deb53c4cce32810a61133fa44b45dbb7bKen Wakasa
102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // How many continuous deletes at which to start deleting at a higher speed.
103923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int DELETE_ACCELERATE_AT = 20;
104923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // Key events coming any faster than this are long-presses.
105d67fe0e7583f1be18b35b33b7658e4427f1e3dedAmith Yamasani    private static final int QUICK_PRESS = 200;
106a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
10759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private static final int PENDING_IMS_CALLBACK_DURATION = 800;
108055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
109cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /**
110cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * The name of the scheme used by the Package Manager to warn of a new package installation,
111cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * replacement or removal.
112cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     */
113cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private static final String SCHEME_PACKAGE = "package";
114cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
115fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_NONE = 0;
116120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Double space: the state where the user pressed space twice quickly, which LatinIME
117120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // resolved as period-space. Undoing this converts the period to a space.
118fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_DOUBLE = 1;
119b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
120b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // have just been swapped. Undoing this swaps them back; the space is still considered weak.
121120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
122fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
123fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // spaces happen when the user presses space, accepting the current suggestion (whether
124fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // it's an auto-correction or not).
125fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_WEAK = 3;
126fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom space: a not-yet-inserted space that should get inserted on the next input,
127fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // character provided it's not a separator. If it's a separator, the phantom space is dropped.
128fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom spaces happen when a user chooses a word from the suggestion strip.
129fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_PHANTOM = 4;
130120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
131120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Current space state of the input method. This can be any of the above constants.
132120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private int mSpaceState;
133126698fdd256a2e3734634d3b923cabd800064baJean Chalard
134ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka    private final Settings mSettings;
13517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
136d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka    private View mExtractArea;
137abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka    private View mKeyPreviewBackingView;
138913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private View mSuggestionsContainer;
1394702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    private SuggestionStripView mSuggestionStripView;
140abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard    // Never null
141abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard    private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
14215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting Suggest mSuggest;
1431b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private CompletionInfo[] mApplicationSpecifiedCompletions;
1447058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard    private AppWorkaroundsUtils mAppWorkAroundsUtils = new AppWorkaroundsUtils();
145a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1466fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private RichInputMethodManager mRichImm;
14715f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
14889ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    private final SubtypeSwitcher mSubtypeSwitcher;
1491931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    private final SubtypeState mSubtypeState = new SubtypeState();
150cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    // At start, create a default event interpreter that does nothing by passing it no decoder spec.
151cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    // The event interpreter should never be null.
152997cba7decce9694b3c2f9487deb9710ebb19595Jean Chalard    private EventInterpreter mEventInterpreter = new EventInterpreter(this);
153a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1546a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka    private boolean mIsMainDictionaryAvailable;
15567fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard    private UserBinaryDictionary mUserDictionary;
1569ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard    private UserHistoryDictionary mUserHistoryDictionary;
15788562bec54658840dcce352127bdc15705c20a89Jean Chalard    private boolean mIsUserDictionaryAvailable;
15836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
1592692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
16018d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard    private PositionalInfoForUserDictPendingAddition
16118d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            mPositionalInfoForUserDictPendingAddition = null;
16287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka    private final WordComposer mWordComposer = new WordComposer();
163a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka    private final RichInputConnection mConnection = new RichInputConnection(this);
164b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    private final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus();
165409220583333bdf06290dd9fd42f91b5c0d1b11asatok
166979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // Keep track of the last selection range to decide if we need to show word alternatives
16777da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private static final int NOT_A_CURSOR_POSITION = -1;
16877da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionStart = NOT_A_CURSOR_POSITION;
16977da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionEnd = NOT_A_CURSOR_POSITION;
170979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1714733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
1724733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // "expect" it, it means the user actually moved the cursor.
1734733609947c0ec74e460bd714fffca0518ade93aJean Chalard    private boolean mExpectingUpdateSelection;
174923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mDeleteCount;
175923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private long mLastKeyTime;
176976a42b873215a669fe22709960aa1259292d75cJean Chalard    private TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet();
177a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
17838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    // Member variables for remembering the current device orientation.
17938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    private int mDisplayOrientation;
18038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
181cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    // Object for reacting to adding/removing a dictionary pack.
1829c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa    // TODO: The development-only-diagnostic version is not supported by the Dictionary Pack
1839c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa    // Service yet.
184cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private BroadcastReceiver mDictionaryPackInstallReceiver =
1859c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS
186710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa                    ? null : new DictionaryPackInstallBroadcastReceiver(this);
187cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
188dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    // Keeps track of most recently inserted text (multi-character key) for reverting
189bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String mEnteredText;
190dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
191b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard    // TODO: This boolean is persistent state and causes large side effects at unexpected times.
192b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard    // Find a way to remove it for readability.
19370852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard    private boolean mIsAutoCorrectionIndicatorOn;
194604d80c67185954d4691ac775be59c499eee3b1csatok
19513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    private AlertDialog mOptionsDialog;
19613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
197fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa    private final boolean mIsHardwareAcceleratedDrawingEnabled;
198fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa
1994f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public final UIHandler mHandler = new UIHandler(this);
200d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
201a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
20227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_UPDATE_SHIFT_STATE = 0;
20327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_PENDING_IMS_CALLBACK = 1;
20427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
205f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final int MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 3;
2066a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        private static final int MSG_RESUME_SUGGESTIONS = 4;
207f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
208f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
2094c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka
21010dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateSuggestions;
21110dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateShiftState;
2122b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        private long mDoubleSpacePeriodTimeout;
2132b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        private long mDoubleSpacePeriodTimerStart;
21438f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
215f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public UIHandler(final LatinIME outerInstance) {
2164f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
21710dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        }
218175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
21910dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        public void onCreate() {
22010dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka            final Resources res = getOuterInstance().getResources();
221175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateSuggestions =
222175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_suggestions);
223175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateShiftState =
224175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_shift_state);
2252b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mDoubleSpacePeriodTimeout =
2262b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                    res.getInteger(R.integer.config_double_space_period_timeout);
2274f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
2284f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
229923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
230f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void handleMessage(final Message msg) {
2314f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
2324f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
233923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (msg.what) {
234dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            case MSG_UPDATE_SUGGESTION_STRIP:
235259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka                latinIme.updateSuggestionStrip();
236d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
237d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SHIFT_STATE:
238de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                switcher.updateShiftState();
239d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
240f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
241f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords)msg.obj,
242f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                        msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
243f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                break;
2446a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            case MSG_RESUME_SUGGESTIONS:
2456a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                latinIme.restartSuggestionsOnWordTouchedByCursor();
2466a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                break;
247923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
248923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
249d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
250a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        public void postUpdateSuggestionStrip() {
251dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions);
252d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
253d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
2546a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        public void postResumeSuggestions() {
2559f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard            removeMessages(MSG_RESUME_SUGGESTIONS);
2566a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions);
2576a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        }
2586a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
259d81e7d24d384a2bb1aeda65d9b423e4b1d23f185Jean Chalard        public void cancelUpdateSuggestionStrip() {
260dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
261d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
262d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
263d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public boolean hasPendingUpdateSuggestions() {
264dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            return hasMessages(MSG_UPDATE_SUGGESTION_STRIP);
265d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
266d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
267beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void postUpdateShiftState() {
268d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
269175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState);
270d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
271d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
272d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateShiftState() {
273d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
274d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
275d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
276f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
277f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                final boolean dismissGestureFloatingPreviewText) {
278f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
279f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final int arg1 = dismissGestureFloatingPreviewText
280f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : 0;
281f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, 0, suggestedWords)
282f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    .sendToTarget();
283f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
284f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
2852b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        public void startDoubleSpacePeriodTimer() {
2862b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mDoubleSpacePeriodTimerStart = SystemClock.uptimeMillis();
287fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
288fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
2892b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        public void cancelDoubleSpacePeriodTimer() {
2902b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mDoubleSpacePeriodTimerStart = 0;
291fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
292fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
2932b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        public boolean isAcceptingDoubleSpacePeriod() {
2942b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            return SystemClock.uptimeMillis() - mDoubleSpacePeriodTimerStart
2952b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                    < mDoubleSpacePeriodTimeout;
296fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
29738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
29859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        // Working variables for the following methods.
29959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mIsOrientationChanging;
3005fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard        private boolean mPendingSuccessiveImsCallback;
30159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingStartInput;
30259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInputView;
30359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInput;
304e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private EditorInfo mAppliedEditorInfo;
30559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
30659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void startOrientationChanging() {
307dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            removeMessages(MSG_PENDING_IMS_CALLBACK);
308dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            resetPendingImsCallback();
30959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mIsOrientationChanging = true;
310055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            final LatinIME latinIme = getOuterInstance();
311f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            if (latinIme.isInputViewShown()) {
312f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka                latinIme.mKeyboardSwitcher.saveKeyboardState();
313f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            }
31459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
31559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
31659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private void resetPendingImsCallback() {
31759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInputView = false;
31859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInput = false;
31959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingStartInput = false;
32059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
32159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
322f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private void executePendingImsCallback(final LatinIME latinIme, final EditorInfo editorInfo,
32359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                boolean restarting) {
32459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInputView)
32559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
32659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInput)
32759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
32859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingStartInput)
329e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
33059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            resetPendingImsCallback();
33159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
33259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
333f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
33459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
33559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the second onStartInput after orientation changed.
33659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingStartInput = true;
33759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
33859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                if (mIsOrientationChanging && restarting) {
33959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    // This is the first onStartInput after orientation changed.
34059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    mIsOrientationChanging = false;
3415fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = true;
34259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                }
34359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
344e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
345e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
346055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
347055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
348055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
349f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
3506b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)
3516b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka                    && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
352e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                // Typically this is the second onStartInputView after orientation changed.
353e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                resetPendingImsCallback();
354e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            } else {
3555fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                if (mPendingSuccessiveImsCallback) {
356e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    // This is the first onStartInputView after orientation changed.
3575fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = false;
358e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    resetPendingImsCallback();
359e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
360e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            PENDING_IMS_CALLBACK_DURATION);
361e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                }
362e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
363e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
364e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputViewInternal(editorInfo, restarting);
365e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = editorInfo;
366e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            }
36759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
36859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
369f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onFinishInputView(final boolean finishingInput) {
37059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
37159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInputView after orientation changed.
37259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInputView = true;
37359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
37459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
37559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(finishingInput);
376e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = null;
37738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            }
37838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        }
379ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka
38059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInput() {
38159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
38259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInput after orientation changed.
38359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInput = true;
38459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
38559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
38659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                executePendingImsCallback(latinIme, null, false);
38759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
388ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka            }
389ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka        }
39038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    }
39138f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
3921931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    static final class SubtypeState {
3931931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        private InputMethodSubtype mLastActiveSubtype;
3941931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        private boolean mCurrentSubtypeUsed;
3951931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka
3961931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        public void currentSubtypeUsed() {
3971931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            mCurrentSubtypeUsed = true;
3981931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        }
3991931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka
4001931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        public void switchSubtype(final IBinder token, final RichInputMethodManager richImm) {
4011931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            final InputMethodSubtype currentSubtype = richImm.getInputMethodManager()
4021931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                    .getCurrentInputMethodSubtype();
4031931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype;
4041931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            final boolean currentSubtypeUsed = mCurrentSubtypeUsed;
4051931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            if (currentSubtypeUsed) {
4061931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                mLastActiveSubtype = currentSubtype;
4071931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                mCurrentSubtypeUsed = false;
4081931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            }
4091931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            if (currentSubtypeUsed
4101931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                    && richImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastActiveSubtype)
4111931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                    && !currentSubtype.equals(lastActiveSubtype)) {
4121931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                richImm.setInputMethodAndSubtype(token, lastActiveSubtype);
4131931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                return;
4141931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            }
4151931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            richImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
4161931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        }
4171931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    }
4181931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka
41989ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    public LatinIME() {
42089ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        super();
421ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        mSettings = Settings.getInstance();
42289ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
42389ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
424fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa        mIsHardwareAcceleratedDrawingEnabled =
425fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa                InputMethodServiceCompatUtils.enableHardwareAcceleration(this);
426fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa        Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled);
42789ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    }
42889ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard
4297e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
4307e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    public void onCreate() {
431ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        Settings.init(this);
432f90fc105ab1159f43f536bcacdd1224c2c05bacbTadashi G. Takaoka        LatinImeLogger.init(this);
433f90fc105ab1159f43f536bcacdd1224c2c05bacbTadashi G. Takaoka        RichInputMethodManager.init(this);
43485e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        mRichImm = RichInputMethodManager.getInstance();
435ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka        SubtypeSwitcher.init(this);
436f90fc105ab1159f43f536bcacdd1224c2c05bacbTadashi G. Takaoka        KeyboardSwitcher.init(this);
437b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        AudioAndHapticFeedbackManager.init(this);
4382ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka        AccessibilityUtils.init(this);
439363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
440923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onCreate();
441363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
44210dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        mHandler.onCreate();
4439e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka        DEBUG = LatinImeLogger.sDBG;
444363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
445d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        // TODO: Resolve mutual dependencies of {@link #loadSettings()} and {@link #initSuggest()}.
44628f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        loadSettings();
4471f05cf6dab45c21d1ed334247929434d97c40584Tadashi G. Takaoka        initSuggest();
448979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
4499c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
450b094372bd68b2f1b1e62c48cc18503a60b6686b7Kurt Partridge            ResearchLogger.getInstance().init(this, mKeyboardSwitcher, mSuggest);
45163bc2795967ddf99d3ac57e61153f8b2faa649acKurt Partridge        }
452ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        mDisplayOrientation = getResources().getConfiguration().orientation;
453b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
454cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Register to receive ringer mode change and network state change.
455cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Also receive installation and removal of a dictionary pack.
456123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        final IntentFilter filter = new IntentFilter();
457123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
45821af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
459923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        registerReceiver(mReceiver, filter);
460cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
4619c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // TODO: The development-only-diagnostic version is not supported by the Dictionary Pack
4629c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Service yet.
4639c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (!ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
464710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            final IntentFilter packageFilter = new IntentFilter();
465710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
466710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
467710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            packageFilter.addDataScheme(SCHEME_PACKAGE);
468710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
469710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa
470710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            final IntentFilter newDictFilter = new IntentFilter();
4711061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard            newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
472710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
473710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        }
474923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
47536fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
47617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    // Has to be package-visible for unit tests
47715f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting
4789fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    void loadSettings() {
479ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
480b0561ae98063f83684706886490ba5670138fcccJean Chalard        final InputAttributes inputAttributes =
481b0561ae98063f83684706886490ba5670138fcccJean Chalard                new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode());
482ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        mSettings.loadSettings(locale, inputAttributes);
483d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        // May need to reset the contacts dictionary depending on the user settings.
48414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
48517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    }
48617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
4878335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
488369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka    @Override
489f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onUpdateMainDictionaryAvailability(final boolean isMainDictionaryAvailable) {
490369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka        mIsMainDictionaryAvailable = isMainDictionaryAvailable;
4918335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
4928335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        if (mainKeyboardView != null) {
4938335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka            mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
4948335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        }
495369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka    }
496369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka
4970ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private void initSuggest() {
4986a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
4996a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final String localeStr = subtypeLocale.toString();
50036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
50167fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        final ContactsBinaryDictionary oldContactsDictionary;
50278ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        if (mSuggest != null) {
50378ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = mSuggest.getContactsDictionary();
50478ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            mSuggest.close();
50578ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        } else {
50678ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = null;
50778ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
508369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka        mSuggest = new Suggest(this /* Context */, subtypeLocale,
509369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka                this /* SuggestInitializationListener */);
510ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSettings.getCurrent().mCorrectionEnabled) {
511ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka            mSuggest.setAutoCorrectionThreshold(mSettings.getCurrent().mAutoCorrectionThreshold);
51278ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
513e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
5146a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
5159c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5166080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge            ResearchLogger.getInstance().initSuggest(mSuggest);
5176080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge        }
5186a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka
51967fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mUserDictionary = new UserBinaryDictionary(this, localeStr);
52067fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
52178ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserDictionary(mUserDictionary);
522e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
52378ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        resetContactsDictionary(oldContactsDictionary);
524e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
525ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
526ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, prefs);
52778ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
528923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
52936fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
53014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    /**
53114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * Resets the contacts dictionary in mSuggest according to the user settings.
53214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
5332e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang     * This method takes an optional contacts dictionary to use when the locale hasn't changed
5342e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang     * since the contacts dictionary can be opened or closed as necessary depending on the settings.
53514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
53614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * @param oldContactsDictionary an optional dictionary to use, or null
53714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     */
53867fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard    private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) {
539ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        final boolean shouldSetDictionary =
540ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                (null != mSuggest && mSettings.getCurrent().mUseContactsDict);
54114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
54267fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        final ContactsBinaryDictionary dictionaryToUse;
54314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (!shouldSetDictionary) {
54414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the dictionary is closed. If it is already closed, this is a no-op,
54514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // so it's safe to call it anyways.
54614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            if (null != oldContactsDictionary) oldContactsDictionary.close();
54714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = null;
54814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
5492e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang            final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
5502e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang            if (null != oldContactsDictionary) {
55167fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                if (!oldContactsDictionary.mLocale.equals(locale)) {
55267fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // If the locale has changed then recreate the contacts dictionary. This
55367fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // allows locale dependent rules for handling bigram name predictions.
55467fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    oldContactsDictionary.close();
55505efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard                    dictionaryToUse = new ContactsBinaryDictionary(this, locale);
5562e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                } else {
55767fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // Make sure the old contacts dictionary is opened. If it is already open,
55867fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // this is a no-op, so it's safe to call it anyways.
55967fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    oldContactsDictionary.reopen(this);
5602e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                    dictionaryToUse = oldContactsDictionary;
5612e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                }
56218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            } else {
56305efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard                dictionaryToUse = new ContactsBinaryDictionary(this, locale);
56418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            }
56514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
56614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
56714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (null != mSuggest) {
56814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            mSuggest.setContactsDictionary(dictionaryToUse);
56914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
570699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard    }
571699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard
572cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /* package private */ void resetSuggestMainDict() {
5736a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
57479eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka        mSuggest.resetMainDict(this, subtypeLocale, this /* SuggestInitializationListener */);
5756a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
576cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
577cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
578466741d8a78965b8509bf527344f289e50873092Mike LeBeau    @Override
579466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onDestroy() {
580e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        if (mSuggest != null) {
581e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest.close();
582e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest = null;
583979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
584ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        mSettings.onDestroy();
585923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        unregisterReceiver(mReceiver);
586841c295f3c99214f45924434d9c0bb5b4760578dTadashi G. Takaoka        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
5877e2c1e69bf5782db8f358c9baeb518130aad0315Kurt Partridge            ResearchLogger.getInstance().onDestroy();
5887e2c1e69bf5782db8f358c9baeb518130aad0315Kurt Partridge        }
5899c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // TODO: The development-only-diagnostic version is not supported by the Dictionary Pack
5909c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        // Service yet.
5919c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (!ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
592710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            unregisterReceiver(mDictionaryPackInstallReceiver);
593710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        }
594979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
595979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.onDestroy();
596923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onDestroy();
597923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
598923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
599923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
600f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onConfigurationChanged(final Configuration conf) {
601b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        // If orientation changed while predicting, commit the change
602f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        if (mDisplayOrientation != conf.orientation) {
603f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mDisplayOrientation = conf.orientation;
604f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mHandler.startOrientationChanging();
605f254e3fec7744dc1eb2cc09ac157986c3b2b5408Jean Chalard            mConnection.beginBatchEdit();
6065475b38328171a0841ae18074bd45380ec567e90Jean Chalard            commitTyped(LastComposedWord.NOT_A_SEPARATOR);
6075475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.finishComposingText();
6085475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.endBatchEdit();
6090a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka            if (isShowingOptionDialog()) {
6102fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                mOptionsDialog.dismiss();
6110a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka            }
612b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
613923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onConfigurationChanged(conf);
614923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
615b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
616923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
617923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public View onCreateInputView() {
618622d6a5b1b3d6a9140765b868abf1cdf412bc4f3Tadashi G. Takaoka        return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled);
6196c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    }
6206c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka
6216c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    @Override
622f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void setInputView(final View view) {
6236c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        super.setInputView(view);
624d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        mExtractArea = getWindow().getWindow().getDecorView()
625d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka                .findViewById(android.R.id.extractArea);
626abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
627913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
6284702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
6294702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        if (mSuggestionStripView != null)
6304702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripView.setListener(this, view);
631f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        if (LatinImeLogger.sVISUALDEBUG) {
632f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka            mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
633f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        }
634923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
635923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
636923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
637f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void setCandidatesView(final View view) {
638c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        // To ensure that CandidatesView will never be set.
639c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return;
640923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
641923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
642a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker    @Override
643f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
644e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInput(editorInfo, restarting);
64559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
64659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
64759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
648f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
649e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInputView(editorInfo, restarting);
65059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
65159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
65259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
653f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onFinishInputView(final boolean finishingInput) {
65459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInputView(finishingInput);
65559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
65638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
65759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
65859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInput() {
65959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInput();
66059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
66159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
6629cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    @Override
663f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) {
6644f96bb4520de3610ae94da96b98e507ca7b76362satok        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
6654f96bb4520de3610ae94da96b98e507ca7b76362satok        // is not guaranteed. It may even be called at the same time on a different thread.
666f483225397ddbd130b415c26909fa47d24fac97aTadashi G. Takaoka        mSubtypeSwitcher.onSubtypeChanged(subtype);
6679fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka        loadKeyboard();
6689cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    }
6699cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka
670f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
671e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInput(editorInfo, restarting);
67259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
67359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
6741cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka    @SuppressWarnings("deprecation")
675f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
676e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInputView(editorInfo, restarting);
67745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
67827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
679ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        final SettingsValues currentSettings = mSettings.getCurrent();
6808e09172df1bb176cc899940862c56bed9b9aec4esatok
681ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        if (editorInfo == null) {
682ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.e(TAG, "Null EditorInfo in onStartInputView()");
683ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            if (LatinImeLogger.sDBG) {
684ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                throw new NullPointerException("Null EditorInfo in onStartInputView()");
685ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            }
686ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            return;
687ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        }
68889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (DEBUG) {
689ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.d(TAG, "onStartInputView: editorInfo:"
690ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    + String.format("inputType=0x%08x imeOptions=0x%08x",
691ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                            editorInfo.inputType, editorInfo.imeOptions));
692b6fb5eb391987f3e426649a892cdcbf781957f5asatok            Log.d(TAG, "All caps = "
693b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0)
694b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", sentence caps = "
695b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0)
696b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", word caps = "
697b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
698910b73127fa207dd26ec8124000262523b0aac0csatok        }
6999c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
700ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka            final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
701ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka            ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, prefs);
7029bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
7031cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
7044f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
7054f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
7061cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka            Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead");
7074f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
7081cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(getPackageName(), FORCE_ASCII, editorInfo)) {
7094f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
7104f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
7114f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
7124f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
7134f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka
7147058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        final PackageInfo packageInfo =
7157058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard                TargetPackageInfoGetterTask.getCachedPackageInfo(editorInfo.packageName);
7167058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        mAppWorkAroundsUtils.setPackageInfo(packageInfo);
7177058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        if (null == packageInfo) {
7187058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard            new TargetPackageInfoGetterTask(this /* context */, this /* listener */)
7191b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard                    .execute(editorInfo.packageName);
7201b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard        }
721b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
7227ef235f53f2291f22ddf8c56be9860a218b25bbbTadashi G. Takaoka        LatinImeLogger.onStartInputView(editorInfo);
723923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // In landscape mode, this method gets called without the input view being created.
72427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView == null) {
725923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
726923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
727923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
728b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        // Forward this event to the accessibility utilities, if enabled.
729b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
730b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        if (accessUtils.isTouchExplorationEnabled()) {
73105384933097c1e9c35e8be5c03757d072e5ffa46alanv            accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
732b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        }
733b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette
734ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        final boolean inputTypeChanged = !currentSettings.isSameInputType(editorInfo);
735e234aed4288efd7b4336f3755a958c25a1540b98Jean Chalard        final boolean isDifferentTextField = !restarting || inputTypeChanged;
736e234aed4288efd7b4336f3755a958c25a1540b98Jean Chalard        if (isDifferentTextField) {
737f483225397ddbd130b415c26909fa47d24fac97aTadashi G. Takaoka            mSubtypeSwitcher.updateParametersOnStartInputView();
738aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        }
7394ab730dbd34fad323063f2ffd31ce33de746668dsatok
740b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // The EditorInfo might have a flag that affects fullscreen mode.
741b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // Note: This call should be done by InputMethodService?
742b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        updateFullscreenMode();
743ccc35f7fa74860a8d737a4e9ff01fc0168dd329dJean Chalard        mApplicationSpecifiedCompletions = null;
744c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
74561cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        // The app calling setText() has the effect of clearing the composing
74661cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        // span, so we should reset our state unconditionally, even if restarting is true.
74761cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        mEnteredText = null;
74861cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
74961cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        mDeleteCount = 0;
75061cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        mSpaceState = SPACE_STATE_NONE;
751b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        mRecapitalizeStatus.deactivate();
752976a42b873215a669fe22709960aa1259292d75cJean Chalard        mCurrentlyPressedHardwareKeys.clear();
75361cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard
754d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        // Note: the following does a round-trip IPC on the main thread: be careful
755d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
756d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        if (null != mSuggest && null != currentLocale && !currentLocale.equals(mSuggest.mLocale)) {
757d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard            initSuggest();
758d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        }
75961cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        if (mSuggestionStripView != null) {
76061cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard            // This will set the punctuation suggestions if next word suggestion is off;
76161cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard            // otherwise it will clear the suggestion strip.
76261cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard            setPunctuationSuggestions();
763549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        }
764abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        mSuggestedWords = SuggestedWords.EMPTY;
76517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
7669f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart,
7679f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard                false /* shouldFinishComposition */);
7689d1c73ffd88cd1bfef3de048b0b3a9a7dfbcfa70Jean Chalard
769e234aed4288efd7b4336f3755a958c25a1540b98Jean Chalard        if (isDifferentTextField) {
77027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
771aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard            loadSettings();
772c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
773ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            if (mSuggest != null && currentSettings.mCorrectionEnabled) {
774ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka                mSuggest.setAutoCorrectionThreshold(currentSettings.mAutoCorrectionThreshold);
775aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard            }
776aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard
777ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka            switcher.loadKeyboard(editorInfo, currentSettings);
7785d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa        } else if (restarting) {
7795d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa            // TODO: Come up with a more comprehensive way to reset the keyboard layout when
7805d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa            // a keyboard layout set doesn't get reloaded in this method.
7815d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa            switcher.resetKeyboardStateToAlphabet();
782d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // In apps like Talk, we come here when the text is sent and the field gets emptied and
783d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // we need to re-evaluate the shift state, but not the whole layout which would be
784d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // disruptive.
785d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // Space state must be updated before calling updateShiftState
786d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            switcher.updateShiftState();
787aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        }
788913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShownInternal(
789913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                isSuggestionsStripVisible(), /* needsInputViewShown */ false);
7908e36e5102e9ac294a0e64ca14920d38ba19e6eb3Jean Chalard
791aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        mLastSelectionStart = editorInfo.initialSelStart;
792aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        mLastSelectionEnd = editorInfo.initialSelEnd;
793aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard
7948e36e5102e9ac294a0e64ca14920d38ba19e6eb3Jean Chalard        mHandler.cancelUpdateSuggestionStrip();
7952b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        mHandler.cancelDoubleSpacePeriodTimer();
796c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
7972be2d776649c9c0b3914135794ab7a7e92e753f9Tadashi G. Takaoka        mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
798ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        mainKeyboardView.setKeyPreviewPopupEnabled(currentSettings.mKeyPreviewPopupOn,
799ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka                currentSettings.mKeyPreviewPopupDismissDelay);
800ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        mainKeyboardView.setSlidingKeyInputPreviewEnabled(
801ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka                currentSettings.mSlidingKeyInputPreviewEnabled);
802ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        mainKeyboardView.setGestureHandlingEnabledByUser(
803ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka                currentSettings.mGestureInputEnabled);
804ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka        mainKeyboardView.setGesturePreviewMode(currentSettings.mGesturePreviewTrailEnabled,
805ff961ddf8c58df569c97684bfd83a01b2a9470aaTadashi G. Takaoka                currentSettings.mGestureFloatingPreviewTextEnabled);
806c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
80718d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // If we have a user dictionary addition in progress, we should check now if we should
80818d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // replace the previously committed string with the word that has actually been added
80918d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // to the user dictionary.
81018d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        if (null != mPositionalInfoForUserDictPendingAddition
81118d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard                && mPositionalInfoForUserDictPendingAddition.tryReplaceWithActualWord(
812d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard                        mConnection, editorInfo, mLastSelectionEnd, currentLocale)) {
81318d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            mPositionalInfoForUserDictPendingAddition = null;
81418d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        }
81518d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // If tryReplaceWithActualWord returns false, we don't know what word was
81618d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // added to the user dictionary yet, so we keep the data and defer processing. The word will
81718d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // be replaced when the user dictionary reports back with the actual word, which ends
81818d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // up calling #onWordAddedToUserDictionary() in this class.
81918d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard
820c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
821c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    }
822c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
8237058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard    // Callback for the TargetPackageInfoGetterTask
8247214617622fce8f3fea6620e782c16336260a2a3Jean Chalard    @Override
8257058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard    public void onTargetPackageInfoKnown(final PackageInfo info) {
8267058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        mAppWorkAroundsUtils.setPackageInfo(info);
827b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    }
828b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
829923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
830e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    public void onWindowHidden() {
831e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        super.onWindowHidden();
832f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
83327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
83427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
83527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
836e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
837e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
83859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputInternal() {
839923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onFinishInput();
840a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
841979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
842f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
84327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
84427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
84527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
846466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
847466741d8a78965b8509bf527344f289e50873092Mike LeBeau
848f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void onFinishInputViewInternal(final boolean finishingInput) {
8496495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa        super.onFinishInputView(finishingInput);
850055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardSwitcher.onFinishInputView();
851f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
85227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
85327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.cancelAllMessages();
85427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
855d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        // Remove pending messages related to update suggestions
856d81e7d24d384a2bb1aeda65d9b423e4b1d23f185Jean Chalard        mHandler.cancelUpdateSuggestionStrip();
857edbb65be3e5e391affbf47e134422ac1d1a4ae13Tadashi G. Takaoka        resetComposingState(true /* alsoResetLastComposedWord */);
85887bc3a4a62b28f9c64ced813a9af531a563b6cd9Kurt Partridge        // Notify ResearchLogger
8599c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
86087bc3a4a62b28f9c64ced813a9af531a563b6cd9Kurt Partridge            ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput, mLastSelectionStart,
86187bc3a4a62b28f9c64ced813a9af531a563b6cd9Kurt Partridge                    mLastSelectionEnd, getCurrentInputConnection());
862606058f777de394d38b364a655cff36ccaf0d5d2Kurt Partridge        }
8636495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    }
8646495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa
8656495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    @Override
866f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onUpdateSelection(final int oldSelStart, final int oldSelEnd,
867f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final int newSelStart, final int newSelEnd,
868f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final int composingSpanStart, final int composingSpanEnd) {
869923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
870104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                composingSpanStart, composingSpanEnd);
871466741d8a78965b8509bf527344f289e50873092Mike LeBeau        if (DEBUG) {
872466741d8a78965b8509bf527344f289e50873092Mike LeBeau            Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
873466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", ose=" + oldSelEnd
874025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lss=" + mLastSelectionStart
875025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lse=" + mLastSelectionEnd
876466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nss=" + newSelStart
877466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nse=" + newSelEnd
878104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", cs=" + composingSpanStart
879104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", ce=" + composingSpanEnd);
880466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
8819c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
882d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            final boolean expectingUpdateSelectionFromLogger =
883d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection();
8849bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd,
8859bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
886d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    composingSpanEnd, mExpectingUpdateSelection,
88702308bec632a5df23325c916bffec5def16b22b4Jean Chalard                    expectingUpdateSelectionFromLogger, mConnection);
888d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            if (expectingUpdateSelectionFromLogger) {
889e86054e5c2e4734d87d2fbf1eeba8c75fc888df0Jean Chalard                // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work
890d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                return;
891d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            }
8929bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
893466741d8a78965b8509bf527344f289e50873092Mike LeBeau
894104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // TODO: refactor the following code to be less contrived.
895104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "newSelStart != composingSpanEnd" || "newSelEnd != composingSpanEnd" means
896104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // that the cursor is not at the end of the composing span, or there is a selection.
897104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "mLastSelectionStart != newSelStart" means that the cursor is not in the same place
898104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // as last time we were called (if there is a selection, it means the start hasn't
899104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // changed, so it's the end that did).
900104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean selectionChanged = (newSelStart != composingSpanEnd
901104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                || newSelEnd != composingSpanEnd) && mLastSelectionStart != newSelStart;
902104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // if composingSpanStart and composingSpanEnd are -1, it means there is no composing
903104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // span in the view - we can use that to narrow down whether the cursor was moved
904104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // by us or not. If we are composing a word but there is no composing span, then
905104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // we know for sure the cursor moved while we were composing and we should reset
906104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // the state.
907104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
908b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // If the keyboard is not visible, we don't need to do all the housekeeping work, as it
909b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // will be reset when the keyboard shows up anyway.
910b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // TODO: revisit this when LatinIME supports hardware keyboards.
911b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown().
912b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // TODO: find a better way to simulate actual execution.
913b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        if (isInputViewShown() && !mExpectingUpdateSelection
9145ed88457bf9ef3305d4a5aa4ac05b513433ad0ddJean Chalard                && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
915cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // TAKE CARE: there is a race condition when we enter this test even when the user
916cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // did not explicitly move the cursor. This happens when typing fast, where two keys
917cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // turn this flag on in succession and both onUpdateSelection() calls arrive after
918cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // the second one - the first call successfully avoids this test, but the second one
919104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            // enters. For the moment we rely on noComposingSpan to further reduce the impact.
92051fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
9219a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // TODO: the following is probably better done in resetEntireInputState().
9229a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // it should only happen when the cursor moved, and the very purpose of the
9239a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // test below is to narrow down whether this happened or not. Likewise with
924fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard            // the call to updateShiftState.
92551fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // We set this to NONE because after a cursor move, we don't want the space
92651fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // state-related special processing to kick in.
92751fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            mSpaceState = SPACE_STATE_NONE;
92851fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
9298a3d369840d6061692a19ceb1eb7267a50a9e056Jean Chalard            if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) {
9306a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // If we are composing a word and moving the cursor, we would want to set a
9316a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // suggestion span for recorrection to work correctly. Unfortunately, that
9326a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // would involve the keyboard committing some new text, which would move the
9336a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // cursor back to where it was. Latin IME could then fix the position of the cursor
9346a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // again, but the asynchronous nature of the calls results in this wreaking havoc
9356a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // with selection on double tap and the like.
9366a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // Another option would be to send suggestions each time we set the composing
9376a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // text, but that is probably too expensive to do, so we decided to leave things
9386a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // as is.
93928d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                resetEntireInputState(newSelStart);
9404c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard            }
941beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
942aa3a96f3a1c5aeacefdfd6e6a33e58c9d9c49553Jean Chalard            // We moved the cursor. If we are touching a word, we need to resume suggestion,
943aa3a96f3a1c5aeacefdfd6e6a33e58c9d9c49553Jean Chalard            // unless suggestions are off.
944aa3a96f3a1c5aeacefdfd6e6a33e58c9d9c49553Jean Chalard            if (isSuggestionsStripVisible()) {
945aa3a96f3a1c5aeacefdfd6e6a33e58c9d9c49553Jean Chalard                mHandler.postResumeSuggestions();
946aa3a96f3a1c5aeacefdfd6e6a33e58c9d9c49553Jean Chalard            }
9471e5bbe4ce85db23528772d1b706149bb563c3ac7Jean Chalard            // Reset the last recapitalization.
948b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            mRecapitalizeStatus.deactivate();
949fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard            mKeyboardSwitcher.updateShiftState();
9504733609947c0ec74e460bd714fffca0518ade93aJean Chalard        }
9514733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = false;
952466741d8a78965b8509bf527344f289e50873092Mike LeBeau
953979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // Make a note of the cursor position
954979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionStart = newSelStart;
955979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionEnd = newSelEnd;
9561931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        mSubtypeState.currentSubtypeUsed();
9577a8dac55278cedd838be325f56b4c52d973c61f5satok    }
9587a8dac55278cedd838be325f56b4c52d973c61f5satok
959c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
960c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has clicked on the extracted text view,
961c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * when running in fullscreen mode.  The default implementation hides
962913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * the suggestions view when this happens, but only if the extracted text
963c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * editor has a vertical scroll bar because its text doesn't fit.
964c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
965913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
966c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
967c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
968c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedTextClicked() {
969ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) return;
970c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
971c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedTextClicked();
972c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
973c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
974c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
975c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has performed a cursor movement in the
976c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * extracted text view, when it is running in fullscreen mode.  The default
977913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * implementation hides the suggestions view when a vertical movement
978c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * happens, but only if the extracted text editor has a vertical scroll bar
979c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * because its text doesn't fit.
980c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
981913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
982c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
983c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
984f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onExtractedCursorMovement(final int dx, final int dy) {
985ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) return;
986c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
987c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedCursorMovement(dx, dy);
988c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
989c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
990923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
991923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void hideWindow() {
992979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
993c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mKeyboardSwitcher.onHideWindow();
994979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
995c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette        if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
996c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette            AccessibleKeyboardViewProxy.getInstance().onHideWindow();
997c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette        }
998c2ee72a214fef46bc02ce486220365bbefd78714Alan Viverette
999923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (TRACE) Debug.stopMethodTracing();
10006e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
10016e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog.dismiss();
10026e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog = null;
10036e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        }
1004923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.hideWindow();
1005923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1006923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1007923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1008f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) {
1009979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG) {
1010a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa            Log.i(TAG, "Received completions:");
1011bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            if (applicationSpecifiedCompletions != null) {
1012bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
1013bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                    Log.i(TAG, "  #" + i + ": " + applicationSpecifiedCompletions[i]);
1014bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                }
1015923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1016923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1017ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return;
1018dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        if (applicationSpecifiedCompletions == null) {
1019259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            clearSuggestionStrip();
10209c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
102194e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge                ResearchLogger.latinIME_onDisplayCompletions(null);
102294e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            }
1023dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard            return;
1024923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
10257be3ca2ccfe05f6659dbbac84de9ff1be4d92e3dJean Chalard        mApplicationSpecifiedCompletions =
10267be3ca2ccfe05f6659dbbac84de9ff1be4d92e3dJean Chalard                CompletionInfoUtils.removeNulls(applicationSpecifiedCompletions);
1027dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard
1028dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
1029dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                SuggestedWords.getFromApplicationSpecifiedCompletions(
1030dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                        applicationSpecifiedCompletions);
1031dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        final SuggestedWords suggestedWords = new SuggestedWords(
1032dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                applicationSuggestedWords,
1033dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* typedWordValid */,
1034dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* hasAutoCorrectionCandidate */,
1035dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* isPunctuationSuggestions */,
1036dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* isObsoleteSuggestions */,
1037dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* isPrediction */);
1038dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        // When in fullscreen mode, show completions generated by the application
1039dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        final boolean isAutoCorrection = false;
1040abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        setSuggestedWords(suggestedWords, isAutoCorrection);
1041dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        setAutoCorrectionIndicator(isAutoCorrection);
1042dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        setSuggestionStripShown(true);
10439c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
104494e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
104594e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge        }
1046923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1047923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1048f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void setSuggestionStripShownInternal(final boolean shown,
1049f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final boolean needsInputViewShown) {
1050913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // TODO: Modify this if we support suggestions with hard keyboard
1051913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
105227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
105327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final boolean inputViewShown = (mainKeyboardView != null)
105427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                    ? mainKeyboardView.isShown() : false;
1055913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean shouldShowSuggestions = shown
1056433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                    && (needsInputViewShown ? inputViewShown : true);
10574b1780fa9571409d65d9797d47949ffafaf0f083Tadashi G. Takaoka            if (isFullscreenMode()) {
1058913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
1059913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.GONE);
10607a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            } else {
1061913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
1062913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE);
10637a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            }
1064923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1065923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1066a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1067f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void setSuggestionStripShown(final boolean shown) {
1068c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
1069543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    }
1070543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa
1071bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka    private int getAdjustedBackingViewHeight() {
1072bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        final int currentHeight = mKeyPreviewBackingView.getHeight();
1073bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        if (currentHeight > 0) {
1074bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka            return currentHeight;
1075c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
1076c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
1077f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
107827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView == null) {
1079bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka            return 0;
1080bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        }
108127e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final int keyboardHeight = mainKeyboardView.getHeight();
1082c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int suggestionsHeight = mSuggestionsContainer.getHeight();
1083ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        final int displayHeight = getResources().getDisplayMetrics().heightPixels;
1084c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final Rect rect = new Rect();
1085c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect);
1086c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int notificationBarHeight = rect.top;
1087c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight
1088c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                - keyboardHeight;
1089c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
1090c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
10914702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight);
1092c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.setLayoutParams(params);
1093bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        return params.height;
1094c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka    }
1095c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
1096543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    @Override
1097f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onComputeInsets(final InputMethodService.Insets outInsets) {
1098923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onComputeInsets(outInsets);
1099f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
110027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView == null || mSuggestionsContainer == null) {
110146ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka            return;
110227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
1103b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final int adjustedBackingHeight = getAdjustedBackingViewHeight();
1104b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE);
1105b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final int backingHeight = backingGone ? 0 : adjustedBackingHeight;
1106d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // In fullscreen mode, the height of the extract area managed by InputMethodService should
1107d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // be considered.
1108d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}.
1109d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0;
111059010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0
111159010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                : mSuggestionsContainer.getHeight();
1112d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
1113fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        int visibleTopY = extraHeight;
11149e347d3d448e48229c46aad394ec9bd60cd5807bsatok        // Need to set touchable region only if input view is being shown
111527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView.isShown()) {
1116913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
1117fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang                visibleTopY -= suggestionsHeight;
11189e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
1119fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang            final int touchY = mainKeyboardView.isShowingMoreKeysPanel() ? 0 : visibleTopY;
112027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final int touchWidth = mainKeyboardView.getWidth();
112127e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final int touchHeight = mainKeyboardView.getHeight() + extraHeight
11227a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    // Extend touchable region below the keyboard.
11237a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
112413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
112513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight);
11269e347d3d448e48229c46aad394ec9bd60cd5807bsatok        }
1127fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        outInsets.contentTopInsets = visibleTopY;
1128fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        outInsets.visibleTopInsets = visibleTopY;
1129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1130a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1131923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1132979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public boolean onEvaluateFullscreenMode() {
11339751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        // Reread resource value here, because this method is called by framework anytime as needed.
11349751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        final boolean isFullscreenModeAllowed =
1135d3b0ecec22cda883150851dced32c1eda2910a66Tadashi G. Takaoka                Settings.readUseFullscreenMode(getResources());
113660dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka        if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) {
113760dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI
113860dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // implies NO_FULLSCREEN. However, the framework mistakenly does.  i.e. NO_EXTRACT_UI
113960dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // without NO_FULLSCREEN doesn't work as expected. Because of this we need this
114060dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // hack for now.  Let's get rid of this once the framework gets fixed.
114160dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            final EditorInfo ei = getCurrentInputEditorInfo();
114260dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            return !(ei != null && ((ei.imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0));
114360dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka        } else {
114460dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            return false;
114560dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka        }
114659010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    }
114759010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka
114859010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    @Override
114959010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    public void updateFullscreenMode() {
115059010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        super.updateFullscreenMode();
1151f80b6a06992ae08ca3601f4fbc6da550fd9ac730Tadashi G. Takaoka
115259010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        if (mKeyPreviewBackingView == null) return;
1153549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        // In fullscreen mode, no need to have extra space to show the key preview.
115459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        // If not, we should have extra space above the keyboard to show the key preview.
1155549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
1156979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
1157979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
11582649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // This will reset the whole input state to the starting state. It will clear
11594a5cb5c36a6a385ec0036981a0e93b0253e884b0Jean Chalard    // the composing word, reset the last composed word, tell the inputconnection about it.
116028d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    private void resetEntireInputState(final int newCursorPosition) {
11619f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        final boolean shouldFinishComposition = mWordComposer.isComposingWord();
11622649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
1163ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSettings.getCurrent().mBigramPredictionEnabled) {
1164ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard            clearSuggestionStrip();
1165ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard        } else {
1166abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard            setSuggestedWords(mSettings.getCurrent().mSuggestPuncList, false);
1167ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard        }
11689f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        mConnection.resetCachesUponCursorMove(newCursorPosition, shouldFinishComposition);
11692649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    }
11702649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard
11712692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private void resetComposingState(final boolean alsoResetLastComposedWord) {
11722692a8700737d8eed268039aa27b22a31669da08Jean Chalard        mWordComposer.reset();
11732692a8700737d8eed268039aa27b22a31669da08Jean Chalard        if (alsoResetLastComposedWord)
11742692a8700737d8eed268039aa27b22a31669da08Jean Chalard            mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
11752692a8700737d8eed268039aa27b22a31669da08Jean Chalard    }
11762692a8700737d8eed268039aa27b22a31669da08Jean Chalard
1177a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard    private void commitTyped(final String separatorString) {
1178196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) return;
1179bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String typedWord = mWordComposer.getTypedWord();
11803651220327c051d8017045aa5e8919461507b3f8Jean Chalard        if (typedWord.length() > 0) {
11819c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
11823b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                ResearchLogger.getInstance().onWordFinished(typedWord, mWordComposer.isBatchMode());
1183f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            }
1184cb25e1fe3740122fd52921c41711f4204ad257fdKurt Partridge            commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
1185cb25e1fe3740122fd52921c41711f4204ad257fdKurt Partridge                    separatorString);
1186923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1187923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1188923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1189a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Called from the KeyboardSwitcher which needs to know auto caps state to display
1190a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // the right layout.
1191553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka    public int getCurrentAutoCapsState() {
1192ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (!mSettings.getCurrent().mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
119303ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
119403ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final EditorInfo ei = getCurrentInputEditorInfo();
1195553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
119603ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final int inputType = ei.inputType;
119790a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Warning: this depends on mSpaceState, which may not be the most current value. If
119890a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // mSpaceState gets updated later, whoever called this may need to be told about it.
119990a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale(),
120090a91272447fd64bc54e06f08f45b11c45930767Jean Chalard                SPACE_STATE_PHANTOM == mSpaceState);
12011c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
12021c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
12038094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard    public int getCurrentRecapitalizeState() {
12048094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard        if (!mRecapitalizeStatus.isActive()
12058094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard                || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) {
12068094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard            // Not recapitalizing at the moment
12078094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard            return RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
12088094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard        }
12098094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard        return mRecapitalizeStatus.getCurrentMode();
12108094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard    }
12118094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard
1212adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // Factor in auto-caps and manual caps and compute the current caps mode.
1213adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    private int getActualCapsMode() {
12146686e63f2158eff07e39ff002ba211d0578f2a4fJean Chalard        final int keyboardShiftMode = mKeyboardSwitcher.getKeyboardShiftMode();
12156686e63f2158eff07e39ff002ba211d0578f2a4fJean Chalard        if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) return keyboardShiftMode;
1216adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        final int auto = getCurrentAutoCapsState();
1217adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
1218adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard            return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;
1219adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        }
1220adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED;
1221adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        return WordComposer.CAPS_MODE_OFF;
1222adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    }
1223adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard
1224bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard    private void swapSwapperAndSpace() {
12253b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge        final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
1226863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
1227923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastTwo != null && lastTwo.length() == 2
1228240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastTwo.charAt(0) == Constants.CODE_SPACE) {
12295475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.deleteSurroundingText(2, 0);
1230f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            final String text = lastTwo.charAt(1) + " ";
1231f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            mConnection.commitText(text, 1);
12329c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
12333b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                ResearchLogger.latinIME_swapSwapperAndSpace(lastTwo, text);
1234d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
1235b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
12364ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa        }
12374ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa    }
12384ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
12392b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa    private boolean maybeDoubleSpacePeriod() {
1240ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (!mSettings.getCurrent().mCorrectionEnabled) return false;
1241ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (!mSettings.getCurrent().mUseDoubleSpacePeriod) return false;
12422b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        if (!mHandler.isAcceptingDoubleSpacePeriod()) return false;
12435475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
1244923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastThree != null && lastThree.length() == 3
12452b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                && canBeFollowedByDoubleSpacePeriod(lastThree.charAt(0))
1246240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastThree.charAt(1) == Constants.CODE_SPACE
1247240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastThree.charAt(2) == Constants.CODE_SPACE) {
12482b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mHandler.cancelDoubleSpacePeriodTimer();
12495475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.deleteSurroundingText(2, 0);
1250f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            final String textToInsert = ". ";
1251f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            mConnection.commitText(textToInsert, 1);
12529c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
12533b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert,
12543b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                        false /* isBatchMode */);
1255f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            }
1256b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1257120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            return true;
1258923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1259120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return false;
1260923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1261a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
12622b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa    private static boolean canBeFollowedByDoubleSpacePeriod(final int codePoint) {
1263344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: Check again whether there really ain't a better way to check this.
1264344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: This should probably be language-dependant...
1265344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        return Character.isLetterOrDigit(codePoint)
1266240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_SINGLE_QUOTE
1267240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_DOUBLE_QUOTE
1268240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_PARENTHESIS
1269240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET
1270240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
1271240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET;
1272344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka    }
1273344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka
12744702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
12754702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // pressed.
1276c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
127718d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard    public void addWordToUserDictionary(final String word) {
127818d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        if (TextUtils.isEmpty(word)) {
127918d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            // Probably never supposed to happen, but just in case.
128018d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            mPositionalInfoForUserDictPendingAddition = null;
128118d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            return;
128218d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        }
128396845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard        final String wordToEdit;
1284ce6bcdd1a547c9874f05a08074cafdfea16196d6Tadashi G. Takaoka        if (CapsModeUtils.isAutoCapsMode(mLastComposedWord.mCapitalizedMode)) {
128596845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard            wordToEdit = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
128696845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard        } else {
128796845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard            wordToEdit = word;
128896845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard        }
128996845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard        mUserDictionary.addWordToUserDictionary(wordToEdit);
129018d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard    }
129118d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard
129218d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard    public void onWordAddedToUserDictionary(final String newSpelling) {
129318d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        // If word was added but not by us, bail out
129418d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        if (null == mPositionalInfoForUserDictPendingAddition) return;
129518d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        if (mWordComposer.isComposingWord()) {
129618d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            // We are late... give up and return
129718d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            mPositionalInfoForUserDictPendingAddition = null;
129818d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            return;
129918d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        }
130018d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        mPositionalInfoForUserDictPendingAddition.setActualWordBeingAdded(newSpelling);
130118d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        if (mPositionalInfoForUserDictPendingAddition.tryReplaceWithActualWord(
130296845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard                mConnection, getCurrentInputEditorInfo(), mLastSelectionEnd,
130396845ecff62ac5a1131ce3eb8e6a06d3298dd984Jean Chalard                mSubtypeSwitcher.getCurrentSubtypeLocale())) {
130418d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard            mPositionalInfoForUserDictPendingAddition = null;
130518d688c94bb8e1e26de2d12445cb3096c6126f75Jean Chalard        }
1306923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1307923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1308f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private static boolean isAlphabet(final int code) {
13098fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka        return Character.isLetter(code);
1310923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1311a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1312e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    private void onSettingsKeyPressed() {
1313cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return;
1314911b8f9d19c1c4903eeef29b43176cfeaa0e5d0cKen Wakasa        showSubtypeSelectorAndSettings();
13159a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
13169a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1317cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
1318cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
1319cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa
1320cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    @Override
1321f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public boolean onCustomRequest(final int requestCode) {
1322cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return false;
1323cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        switch (requestCode) {
1324cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        case CODE_SHOW_INPUT_METHOD_PICKER:
13256fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
13266fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                mRichImm.getInputMethodManager().showInputMethodPicker();
1327cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa                return true;
13289a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok            }
1329cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa            return false;
13309a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
1331cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        return false;
13329a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
13339a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
13349a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    private boolean isShowingOptionDialog() {
13359a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        return mOptionsDialog != null && mOptionsDialog.isShowing();
13369a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
13379a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1338f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void performEditorAction(final int actionId) {
13395475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.performEditorAction(actionId);
13407a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
13417a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
1342aec5cc84760b47661ccc5a0c938368d3798be6b4Tadashi G. Takaoka    // TODO: Revise the language switch key behavior to make it much smarter and more reasonable.
134381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    private void handleLanguageSwitchKey() {
134481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        final IBinder token = getWindow().getWindow().getAttributes().token;
1345ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSettings.getCurrent().mIncludesOtherImesInLanguageSwitchList) {
13466fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
1347aec5cc84760b47661ccc5a0c938368d3798be6b4Tadashi G. Takaoka            return;
1348aec5cc84760b47661ccc5a0c938368d3798be6b4Tadashi G. Takaoka        }
13491931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        mSubtypeState.switchSubtype(token, mRichImm);
135081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    }
135181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka
135228d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    private void sendDownUpKeyEventForBackwardCompatibility(final int code) {
13534e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard        final long eventTime = SystemClock.uptimeMillis();
13545475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
13554e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
13564e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
13575475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
13584e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
13594e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
13604e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard    }
13614e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard
1362f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void sendKeyCodePoint(final int code) {
13639c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
136499931201d0258ec93bd47de6f6f53ad59709bd85Kurt Partridge            ResearchLogger.latinIME_sendKeyCodePoint(code);
136599931201d0258ec93bd47de6f6f53ad59709bd85Kurt Partridge        }
13667a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // TODO: Remove this special handling of digit letters.
13677a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
13687a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (code >= '0' && code <= '9') {
136928d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard            sendDownUpKeyEventForBackwardCompatibility(code - '0' + KeyEvent.KEYCODE_0);
13707a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            return;
13717a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
13727a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
13737058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        if (Constants.CODE_ENTER == code && mAppWorkAroundsUtils.isBeforeJellyBean()) {
13745475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
13755475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // a hardware keyboard event on pressing enter or delete. This is bad for many
13765475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // reasons (there are race conditions with commits) but some applications are
13775475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // relying on this behavior so we continue to support it for older apps.
137828d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard            sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_ENTER);
13795475b38328171a0841ae18074bd45380ec567e90Jean Chalard        } else {
13805475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final String text = new String(new int[] { code }, 0, 1);
13815475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(text, text.length());
13825475b38328171a0841ae18074bd45380ec567e90Jean Chalard        }
13837a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
13847a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
13855f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    // Implementation of {@link KeyboardActionListener}.
13865a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
1387f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onCodeInput(final int primaryCode, final int x, final int y) {
13889c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
138999931201d0258ec93bd47de6f6f53ad59709bd85Kurt Partridge            ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
139099931201d0258ec93bd47de6f6f53ad59709bd85Kurt Partridge        }
1391175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final long when = SystemClock.uptimeMillis();
1392240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (primaryCode != Constants.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
1393923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mDeleteCount = 0;
1394923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1395923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mLastKeyTime = when;
1396f254e3fec7744dc1eb2cc09ac157986c3b2b5408Jean Chalard        mConnection.beginBatchEdit();
1397175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
1398120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // The space state depends only on the last character pressed and its own previous
1399120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // state. Here, we revert the space state to neutral if the key is actually modifying
1400120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // the input contents (any non-shift key), which is what we should do for
1401120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // all inputs that do not result in a special state. Each character handling is then
1402120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // free to override the state as they see fit.
1403120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final int spaceState = mSpaceState;
140470852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard        if (!mWordComposer.isComposingWord()) mIsAutoCorrectionIndicatorOn = false;
1405ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
14062b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
1407240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (primaryCode != Constants.CODE_SPACE) {
14082b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mHandler.cancelDoubleSpacePeriodTimer();
1409ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        }
1410ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1411c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1412923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (primaryCode) {
1413240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_DELETE:
1414120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1415120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            handleBackspace(spaceState);
14164189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            mDeleteCount++;
14174733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
1418140467b8b65eb9087a9b7f744dbb8a30481effe7Kurt Partridge            LatinImeLogger.logOnDelete(x, y);
14194189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1420240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SHIFT:
14212995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // Note: calling back to the keyboard on Shift key is handled in onPressKey()
14222995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // and onReleaseKey().
14231f523646303b935e3d5031e75a4c74f4ecd96b97Jean Chalard            final Keyboard currentKeyboard = switcher.getKeyboard();
14241f523646303b935e3d5031e75a4c74f4ecd96b97Jean Chalard            if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
14251f523646303b935e3d5031e75a4c74f4ecd96b97Jean Chalard                // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
14261f523646303b935e3d5031e75a4c74f4ecd96b97Jean Chalard                // alphabetic shift and shift while in symbol layout.
14271f523646303b935e3d5031e75a4c74f4ecd96b97Jean Chalard                handleRecapitalize();
14281f523646303b935e3d5031e75a4c74f4ecd96b97Jean Chalard            }
14292995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            break;
1430240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SWITCH_ALPHA_SYMBOL:
14312995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // Note: calling back to the keyboard on symbol key is handled in onPressKey()
14322995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // and onReleaseKey().
14334189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1434240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SETTINGS:
143593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            onSettingsKeyPressed();
14364189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1437240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SHORTCUT:
14389fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka            mSubtypeSwitcher.switchToShortcutIME(this);
14394189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1440240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_ACTION_NEXT:
14418f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_NEXT);
144205bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka            break;
1443240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_ACTION_PREVIOUS:
14448f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
14454189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1446240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_LANGUAGE_SWITCH:
144781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            handleLanguageSwitchKey();
144881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            break;
1449240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_RESEARCH:
14509c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
145158eb4d9f27595202927150766d198a0bff15efadKurt Partridge                ResearchLogger.getInstance().onResearchKeySelected(this);
1452724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            }
1453724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            break;
14544e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard        case Constants.CODE_ENTER:
14554e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard            final EditorInfo editorInfo = getCurrentInputEditorInfo();
14564e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard            final int imeOptionsActionId =
14574e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                    InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo);
14584e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard            if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) {
14594e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // Either we have an actionLabel and we should performEditorAction with actionId
14604e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // regardless of its value.
14614e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                performEditorAction(editorInfo.actionId);
14624e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard            } else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) {
14634e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // We didn't have an actionLabel, but we had another action to execute.
14644e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast,
14654e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it
14664e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // means there should be an action and the app didn't bother to set a specific
14674e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // code for it - presumably it only handles one. It does not have to be treated
14684e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // in any specific way: anything that is not IME_ACTION_NONE should be sent to
14694e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // performEditorAction.
14704e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                performEditorAction(imeOptionsActionId);
14714e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard            } else {
14724e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // No action label, and the action from imeOptions is NONE: this is a regular
14734e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                // enter key that should input a carriage return.
14744e4f88127b6a75302fac3a004e41fff1899a773fJean Chalard                didAutoCorrect = handleNonSpecialCharacter(Constants.CODE_ENTER, x, y, spaceState);
14754189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            }
1476dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            break;
1477dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        case Constants.CODE_SHIFT_ENTER:
1478dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            didAutoCorrect = handleNonSpecialCharacter(Constants.CODE_ENTER, x, y, spaceState);
1479dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            break;
1480dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        default:
1481dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            didAutoCorrect = handleNonSpecialCharacter(primaryCode, x, y, spaceState);
14824733609947c0ec74e460bd714fffca0518ade93aJean Chalard            break;
1483923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1484eef6238f94b5046054d9ae9c06f775362893c0eeTadashi G. Takaoka        switcher.onCodeInput(primaryCode);
1485125de3dfdf548359de890247907f2e6f430008ecJean Chalard        // Reset after any single keystroke, except shift and symbol-shift
1486240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (!didAutoCorrect && primaryCode != Constants.CODE_SHIFT
1487240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && primaryCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
1488c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard            mLastComposedWord.deactivate();
1489240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_DELETE != primaryCode) {
1490a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            mEnteredText = null;
1491a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard        }
1492e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard        mConnection.endBatchEdit();
1493923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1494a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1495dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard    private boolean handleNonSpecialCharacter(final int primaryCode, final int x, final int y,
1496dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            final int spaceState) {
1497dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        mSpaceState = SPACE_STATE_NONE;
1498dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        final boolean didAutoCorrect;
1499dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        if (mSettings.getCurrent().isWordSeparator(primaryCode)) {
1500dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
1501dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        } else {
1502dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            didAutoCorrect = false;
1503dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            if (SPACE_STATE_PHANTOM == spaceState) {
1504756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka                if (mSettings.isInternal()) {
1505dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                    if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) {
1506dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                        Stats.onAutoCorrection(
1507dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                                "", mWordComposer.getTypedWord(), " ", mWordComposer);
1508dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                    }
1509dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                }
15100e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
15110e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    // If we are in the middle of a recorrection, we need to commit the recorrection
15120e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    // first so that we can insert the character at the current cursor position.
15130e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    resetEntireInputState(mLastSelectionStart);
15140e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                } else {
15150e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    commitTyped(LastComposedWord.NOT_A_SEPARATOR);
15160e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                }
1517dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            }
1518dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            final int keyX, keyY;
1519dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
1520dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
1521dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                keyX = x;
1522dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                keyY = y;
1523dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            } else {
1524dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                keyX = Constants.NOT_A_COORDINATE;
1525dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard                keyY = Constants.NOT_A_COORDINATE;
1526dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            }
1527dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard            handleCharacter(primaryCode, keyX, keyY, spaceState);
1528dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        }
1529dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        mExpectingUpdateSelection = true;
1530dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard        return didAutoCorrect;
1531dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard    }
1532dcfceaff1b18b102d7fe875b48b84c24cbd822d9Jean Chalard
1533a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Called from PointerTracker through the KeyboardActionListener interface
15345a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
1535bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void onTextInput(final String rawText) {
1536f254e3fec7744dc1eb2cc09ac157986c3b2b5408Jean Chalard        mConnection.beginBatchEdit();
15376c70b9200340537d05ff8932d7cd990269eb486dJean Chalard        if (mWordComposer.isComposingWord()) {
15384aff3bf0eaf4aec38c1d8576fdfc85a9eaa1c544Kurt Partridge            commitCurrentAutoCorrection(rawText);
1539a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard        } else {
1540a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            resetComposingState(true /* alsoResetLastComposedWord */);
15416c70b9200340537d05ff8932d7cd990269eb486dJean Chalard        }
15420a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka        mHandler.postUpdateSuggestionStrip();
1543bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String text = specificTldProcessingOnTextInput(rawText);
1544fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == mSpaceState) {
15450e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
1546fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
15475475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.commitText(text, 1);
15489c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
15493b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge            ResearchLogger.latinIME_onTextInput(text, false /* isBatchMode */);
1550f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge        }
15515475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.endBatchEdit();
155290a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Space state must be updated before calling updateShiftState
155390a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        mSpaceState = SPACE_STATE_NONE;
1554b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
1555240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT);
1556dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = text;
1557923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1558923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
15594be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    @Override
15604be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    public void onStartBatchInput() {
1561f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        BatchInputUpdater.getInstance().onStartBatchInput(this);
1562a280e56014dd15321ecc96a65f18dbd0c0b210f6Jean Chalard        mHandler.cancelUpdateSuggestionStrip();
15634be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.beginBatchEdit();
15644be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        if (mWordComposer.isComposingWord()) {
1565756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka            if (mSettings.isInternal()) {
156687cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                if (mWordComposer.isBatchMode()) {
156787cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Stats.onAutoCorrection("", mWordComposer.getTypedWord(), " ", mWordComposer);
156887cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                }
156987cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            }
1570e3ec5fcc88513a4141577e5b2001b3896e111232Jean Chalard            final int wordComposerSize = mWordComposer.size();
1571e3ec5fcc88513a4141577e5b2001b3896e111232Jean Chalard            // Since isComposingWord() is true, the size is at least 1.
15720e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            final int lastChar = mWordComposer.getCodeBeforeCursor();
15730e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
15740e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // If we are in the middle of a recorrection, we need to commit the recorrection
15750e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // first so that we can insert the batch input at the current cursor position.
15760e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                resetEntireInputState(mLastSelectionStart);
15770e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            } else if (wordComposerSize <= 1) {
1578b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // We auto-correct the previous (typed, not gestured) string iff it's one character
1579b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // long. The reason for this is, even in the middle of gesture typing, you'll still
1580b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // tap one-letter words and you want them auto-corrected (typically, "i" in English
1581b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // should become "I"). However for any longer word, we assume that the reason for
1582b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // tapping probably is that the word you intend to type is not in the dictionary,
1583b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // so we do not attempt to correct, on the assumption that if that was a dictionary
1584b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // word, the user would probably have gestured instead.
1585b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                commitCurrentAutoCorrection(LastComposedWord.NOT_A_SEPARATOR);
1586b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard            } else {
1587b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                commitTyped(LastComposedWord.NOT_A_SEPARATOR);
1588b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard            }
15894be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka            mExpectingUpdateSelection = true;
1590e8b9b3547145c5f706eede949e6c1c1ddcc9f857Jean Chalard        }
1591e8b9b3547145c5f706eede949e6c1c1ddcc9f857Jean Chalard        final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
1592e8b9b3547145c5f706eede949e6c1c1ddcc9f857Jean Chalard        if (Character.isLetterOrDigit(codePointBeforeCursor)
1593e8b9b3547145c5f706eede949e6c1c1ddcc9f857Jean Chalard                || mSettings.getCurrent().isUsuallyFollowedBySpace(codePointBeforeCursor)) {
1594e8b9b3547145c5f706eede949e6c1c1ddcc9f857Jean Chalard            mSpaceState = SPACE_STATE_PHANTOM;
15954be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        }
15964be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.endBatchEdit();
1597adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
1598eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    }
1599eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
1600f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private static final class BatchInputUpdater implements Handler.Callback {
1601f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private final Handler mHandler;
1602f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private LatinIME mLatinIme;
1603ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka        private final Object mLock = new Object();
1604ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka        private boolean mInBatchInput; // synchronized using {@link #mLock}.
1605f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1606f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private BatchInputUpdater() {
1607f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final HandlerThread handlerThread = new HandlerThread(
1608f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    BatchInputUpdater.class.getSimpleName());
1609f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            handlerThread.start();
1610f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            mHandler = new Handler(handlerThread.getLooper(), this);
1611f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1612f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1613f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        // Initialization-on-demand holder
1614f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final class OnDemandInitializationHolder {
1615f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            public static final BatchInputUpdater sInstance = new BatchInputUpdater();
1616f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1617f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1618f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public static BatchInputUpdater getInstance() {
1619f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            return OnDemandInitializationHolder.sInstance;
1620f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1621f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1622f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final int MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 1;
1623f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1624f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        @Override
1625f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public boolean handleMessage(final Message msg) {
1626f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            switch (msg.what) {
1627f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
1628f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                updateBatchInput((InputPointers)msg.obj);
1629f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                break;
1630f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            }
1631f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            return true;
1632f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1633f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
163458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the UI thread.
1635ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka        public void onStartBatchInput(final LatinIME latinIme) {
1636ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka            synchronized (mLock) {
1637ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
1638ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mLatinIme = latinIme;
1639ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mInBatchInput = true;
1640ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka            }
164158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
164258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
164358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the Handler thread.
1644ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka        private void updateBatchInput(final InputPointers batchPointers) {
1645ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka            synchronized (mLock) {
1646ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                if (!mInBatchInput) {
1647ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                    // Batch input has ended or canceled while the message was being delivered.
1648ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                    return;
1649ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                }
1650ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers);
1651ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
1652ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                        suggestedWords, false /* dismissGestureFloatingPreviewText */);
165358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            }
165458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
165558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
165658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the UI thread.
1657f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        public void onUpdateBatchInput(final InputPointers batchPointers) {
1658f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            if (mHandler.hasMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP)) {
1659f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                return;
1660f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            }
1661f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            mHandler.obtainMessage(
1662f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, batchPointers)
1663f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    .sendToTarget();
1664f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1665f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1666ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka        public void onCancelBatchInput() {
1667ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka            synchronized (mLock) {
1668ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mInBatchInput = false;
1669ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
1670ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                        SuggestedWords.EMPTY, true /* dismissGestureFloatingPreviewText */);
1671ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka            }
1672b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        }
1673b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
167458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the UI thread.
1675ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka        public SuggestedWords onEndBatchInput(final InputPointers batchPointers) {
1676ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka            synchronized (mLock) {
1677ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mInBatchInput = false;
1678ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers);
1679ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
1680ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                        suggestedWords, true /* dismissGestureFloatingPreviewText */);
1681ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka                return suggestedWords;
1682ce811f50d2cfa1cb5586d60943cfc10a28dffcccTadashi G. Takaoka            }
1683f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1684f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1685f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
1686f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        // be synchronized.
1687f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        private SuggestedWords getSuggestedWordsGestureLocked(final InputPointers batchPointers) {
1688f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka            mLatinIme.mWordComposer.setBatchInputPointers(batchPointers);
1689f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka            final SuggestedWords suggestedWords =
1690ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard                    mLatinIme.getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_GESTURE);
1691f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka            final int suggestionCount = suggestedWords.size();
1692f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka            if (suggestionCount <= 1) {
1693f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                final String mostProbableSuggestion = (suggestionCount == 0) ? null
1694f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                        : suggestedWords.getWord(0);
1695f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                return mLatinIme.getOlderSuggestions(mostProbableSuggestion);
1696f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka            }
1697f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka            return suggestedWords;
1698f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1699f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    }
1700f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1701f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
1702f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final boolean dismissGestureFloatingPreviewText) {
1703f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        showSuggestionStrip(suggestedWords, null);
1704375982106bf073971fccd14b2c65ec8e089bb2aeTadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
170550d902ac6fd018131bd7044dd968bb99e0fdba7bTadashi G. Takaoka        mainKeyboardView.showGestureFloatingPreviewText(suggestedWords);
1706f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        if (dismissGestureFloatingPreviewText) {
1707f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            mainKeyboardView.dismissGestureFloatingPreviewText();
1708f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
17094be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    }
17104be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka
17114be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    @Override
1712f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onUpdateBatchInput(final InputPointers batchPointers) {
1713f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        BatchInputUpdater.getInstance().onUpdateBatchInput(batchPointers);
1714f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    }
1715f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1716f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    @Override
1717f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onEndBatchInput(final InputPointers batchPointers) {
171858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
1719f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                batchPointers);
1720c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka        final String batchInputText = suggestedWords.isEmpty()
1721c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka                ? null : suggestedWords.getWord(0);
1722f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        if (TextUtils.isEmpty(batchInputText)) {
172310102f02af1216cfca115d1742740f567b909e2cTadashi G. Takaoka            return;
172410102f02af1216cfca115d1742740f567b909e2cTadashi G. Takaoka        }
1725f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        mWordComposer.setBatchInputWord(batchInputText);
17264be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.beginBatchEdit();
17274be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        if (SPACE_STATE_PHANTOM == mSpaceState) {
17280e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
17294be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        }
1730f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        mConnection.setComposingText(batchInputText, 1);
17314be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mExpectingUpdateSelection = true;
17324be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.endBatchEdit();
17339c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
17340c16a5c6eef645fd536671994e0b4f05864ac338Kurt Partridge            ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords);
173548160f9d847c0246883bc81599613b1ebe2652a6Kurt Partridge        }
173690a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Space state must be updated before calling updateShiftState
17374be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mSpaceState = SPACE_STATE_PHANTOM;
173890a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        mKeyboardSwitcher.updateShiftState();
17394be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    }
17404be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka
1741bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String specificTldProcessingOnTextInput(final String text) {
1742240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD
1743fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                || !Character.isLetter(text.charAt(1))) {
1744fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            // Not a tld: do nothing.
1745fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1746fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
174712d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // We have a TLD (or something that looks like this): make sure we don't add
174812d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // a space even if currently in phantom mode.
174912d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        mSpaceState = SPACE_STATE_NONE;
1750e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard        // TODO: use getCodePointBeforeCursor instead to improve performance and simplify the code
17515475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
1752fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (lastOne != null && lastOne.length() == 1
1753240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastOne.charAt(0) == Constants.CODE_PERIOD) {
1754bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            return text.substring(1);
1755fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        } else {
1756fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1757fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1758fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    }
1759fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1760a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Called from PointerTracker through the KeyboardActionListener interface
17615a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
176241016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    public void onFinishSlidingInput() {
176341016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        // User finished sliding input.
176441016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        mKeyboardSwitcher.onFinishSlidingInput();
176541016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    }
176641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka
176741016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    // Called from PointerTracker through the KeyboardActionListener interface
176841016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    @Override
17698aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCancelInput() {
177083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        // User released a finger outside any key
177141016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        // Nothing to do so far.
177283e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka    }
177383e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka
1774b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
1775b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public void onCancelBatchInput() {
1776f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        BatchInputUpdater.getInstance().onCancelBatchInput();
1777b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1778b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
1779120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleBackspace(final int spaceState) {
1780fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        // In many cases, we may have to put the keyboard in auto-shift state again. However
1781fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        // we want to wait a few milliseconds before doing it to avoid the keyboard flashing
1782fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        // during key repeat.
1783beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        mHandler.postUpdateShiftState();
17842245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard
17850e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
17860e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // If we are in the middle of a recorrection, we need to commit the recorrection
17870e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // first so that we can remove the character at the current cursor position.
17886a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            resetEntireInputState(mLastSelectionStart);
17890e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // When we exit this if-clause, mWordComposer.isComposingWord() will return false.
17906a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        }
1791196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
17923651220327c051d8017045aa5e8919461507b3f8Jean Chalard            final int length = mWordComposer.size();
1793923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (length > 0) {
179401d47b809617aee03e240a6b4b41013269547bc1Jean Chalard                if (mWordComposer.isBatchMode()) {
17959c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                    if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
17963b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                        final String word = mWordComposer.getTypedWord();
1797ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                        ResearchLogger.latinIME_handleBackspace_batch(word, 1);
17983b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                        ResearchLogger.getInstance().uncommitCurrentLogUnit(
17993b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                                word, false /* dumpCurrentLogUnit */);
1800f1ce54891f54b4b75a7662fe08204e9d50e54e55Kurt Partridge                    }
1801d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard                    final String rejectedSuggestion = mWordComposer.getTypedWord();
18023b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                    mWordComposer.reset();
1803d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard                    mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
18044be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                } else {
18054be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                    mWordComposer.deleteLast();
18064be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                }
18075475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1808a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard                mHandler.postUpdateSuggestionStrip();
1809923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
18105475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(1, 0);
1811923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1812890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        } else {
18135935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard            if (mLastComposedWord.canRevertCommit()) {
1814756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka                if (mSettings.isInternal()) {
181587cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Stats.onAutoCorrectionCancellation();
181687cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                }
18175475b38328171a0841ae18074bd45380ec567e90Jean Chalard                revertCommit();
1818120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                return;
1819120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1820a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
1821a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // Cancel multi-character input: remove the text we just entered.
1822a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // This is triggered on backspace after a key that inputs multiple characters,
1823a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // like the smiley key or the .com key.
1824a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                final int length = mEnteredText.length();
1825a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                mConnection.deleteSurroundingText(length, 0);
1826a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                mEnteredText = null;
1827a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
1828a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // In addition we know that spaceState is false, and that we should not be
1829a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // reverting any autocorrect at this point. So we can safely return.
1830a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                return;
1831a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            }
1832d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            if (SPACE_STATE_DOUBLE == spaceState) {
18332b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                mHandler.cancelDoubleSpacePeriodTimer();
183499f31896454eff3789e00949dcce83f6914fe3f4Kurt Partridge                if (mConnection.revertDoubleSpacePeriod()) {
1835d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // No need to reset mSpaceState, it has already be done (that's why we
1836d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // receive it as a parameter)
1837d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1838d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
1839d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
18402010aad741bc1a7266913bcb8b8348d6e401c95bJean Chalard                if (mConnection.revertSwapPunctuation()) {
1841d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // Likewise
1842d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1843d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
18444733609947c0ec74e460bd714fffca0518ade93aJean Chalard            }
1845504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka
18464fef31510df542a3324426a6750950194d016086Jean Chalard            // No cancelling of commit/double space/swap: we have a regular backspace.
18474fef31510df542a3324426a6750950194d016086Jean Chalard            // We should backspace one char and restart suggestion if at the end of a word.
18484fef31510df542a3324426a6750950194d016086Jean Chalard            if (mLastSelectionStart != mLastSelectionEnd) {
18494fef31510df542a3324426a6750950194d016086Jean Chalard                // If there is a selection, remove it.
1850ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                final int numCharsDeleted = mLastSelectionEnd - mLastSelectionStart;
18515475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
1852516b63db2866f144a00ec7e1e6c29cb0865f5ff3Jean Chalard                // Reset mLastSelectionEnd to mLastSelectionStart. This is what is supposed to
1853516b63db2866f144a00ec7e1e6c29cb0865f5ff3Jean Chalard                // happen, and if it's wrong, the next call to onUpdateSelection will correct it,
1854516b63db2866f144a00ec7e1e6c29cb0865f5ff3Jean Chalard                // but we want to set it right away to avoid it being used with the wrong values
1855516b63db2866f144a00ec7e1e6c29cb0865f5ff3Jean Chalard                // later (typically, in a subsequent press on backspace).
1856516b63db2866f144a00ec7e1e6c29cb0865f5ff3Jean Chalard                mLastSelectionEnd = mLastSelectionStart;
1857ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                mConnection.deleteSurroundingText(numCharsDeleted, 0);
18589c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
1859ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                    ResearchLogger.latinIME_handleBackspace(numCharsDeleted);
1860ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                }
18616558253160e2039c87f424bd814f402ecd31de3bKen Wakasa            } else {
18624fef31510df542a3324426a6750950194d016086Jean Chalard                // There is no selection, just delete one character.
18634fef31510df542a3324426a6750950194d016086Jean Chalard                if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
18644fef31510df542a3324426a6750950194d016086Jean Chalard                    // This should never happen.
18654fef31510df542a3324426a6750950194d016086Jean Chalard                    Log.e(TAG, "Backspace when we don't know the selection position");
18666558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                }
18677058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard                if (mAppWorkAroundsUtils.isBeforeJellyBean()) {
18684e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
18694e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // a hardware keyboard event on pressing enter or delete. This is bad for many
18704e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // reasons (there are race conditions with commits) but some applications are
18714e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // relying on this behavior so we continue to support it for older apps.
187228d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                    sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_DEL);
18734e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                } else {
18745475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.deleteSurroundingText(1, 0);
18754e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                }
18769c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
1877ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                    ResearchLogger.latinIME_handleBackspace(1);
1878ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                }
18794fef31510df542a3324426a6750950194d016086Jean Chalard                if (mDeleteCount > DELETE_ACCELERATE_AT) {
18805475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.deleteSurroundingText(1, 0);
18819c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa                    if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
1882ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                        ResearchLogger.latinIME_handleBackspace(1);
1883ef3936bc5470360144c3efc4dab9ef24a57c1e34Kurt Partridge                    }
1884edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                }
1885923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1886ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka            if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) {
18875475b38328171a0841ae18074bd45380ec567e90Jean Chalard                restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
18884fef31510df542a3324426a6750950194d016086Jean Chalard            }
1889923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1890923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1891923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1892ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard    /*
1893ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard     * Strip a trailing space if necessary and returns whether it's a swap weak space situation.
1894ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard     */
1895e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard    private boolean maybeStripSpace(final int code,
1896e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final int spaceState, final boolean isFromSuggestionStrip) {
1897240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1898bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard            mConnection.removeTrailingSpace();
1899e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1900ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard        }
1901ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard        if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState)
1902e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                && isFromSuggestionStrip) {
1903ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard            if (mSettings.getCurrent().isUsuallyPrecededBySpace(code)) return false;
1904ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard            if (mSettings.getCurrent().isUsuallyFollowedBySpace(code)) return true;
1905ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard            mConnection.removeTrailingSpace();
1906e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1907ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard        return false;
1908e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    }
1909e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1910ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    private void handleCharacter(final int primaryCode, final int x,
1911120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int y, final int spaceState) {
1912196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        boolean isComposingWord = mWordComposer.isComposingWord();
1913fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1914e8b9b3547145c5f706eede949e6c1c1ddcc9f857Jean Chalard        // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead.
1915e8b9b3547145c5f706eede949e6c1c1ddcc9f857Jean Chalard        // See onStartBatchInput() to see how to do it.
1916fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
1917ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard                !mSettings.getCurrent().isWordConnector(primaryCode)) {
1918fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (isComposingWord) {
1919fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // Sanity check
1920fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                throw new RuntimeException("Should not be composing here");
1921fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            }
19220e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
1923fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1924fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
19250e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
19260e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // If we are in the middle of a recorrection, we need to commit the recorrection
19270e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // first so that we can insert the character at the current cursor position.
19286a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            resetEntireInputState(mLastSelectionStart);
19296a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            isComposingWord = false;
19306a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        }
1931d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // NOTE: isCursorTouchingWord() is a blocking IPC call, so it often takes several
1932d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
1933d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // thread here.
1934d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        if (!isComposingWord && (isAlphabet(primaryCode)
1935ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard                || mSettings.getCurrent().isWordConnector(primaryCode))
1936ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                && mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation) &&
1937ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                !mConnection.isCursorTouchingWord(mSettings.getCurrent())) {
1938736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Reset entirely the composing state anyway, then start composing a new word unless
1939736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // the character is a single quote. The idea here is, single quote is not a
1940736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // separator and it should be treated as a normal character, except in the first
1941736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // position where it should not start composing a word.
1942240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            isComposingWord = (Constants.CODE_SINGLE_QUOTE != primaryCode);
1943736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Here we don't need to reset the last composed word. It will be reset
1944736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // when we commit this one, if we ever do; if on the other hand we backspace
1945736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // it entirely and resume suggestions on the previous word, we'd like to still
1946736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // have touch coordinates for it.
1947736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            resetComposingState(false /* alsoResetLastComposedWord */);
1948923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
19497b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard        if (isComposingWord) {
19505c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka            final int keyX, keyY;
195124ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka            if (Constants.isValidCoordinate(x) && Constants.isValidCoordinate(y)) {
19525c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka                final KeyDetector keyDetector =
195327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                        mKeyboardSwitcher.getMainKeyboardView().getKeyDetector();
19545c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka                keyX = keyDetector.getTouchX(x);
19555c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka                keyY = keyDetector.getTouchY(y);
195624ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka            } else {
195724ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka                keyX = x;
195824ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka                keyY = y;
19595c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka            }
19605c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka            mWordComposer.add(primaryCode, keyX, keyY);
19615475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // If it's the first letter, make note of auto-caps state
19625475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (mWordComposer.size() == 1) {
1963adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard                mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
1964923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
19655475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1966923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1967e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard            final boolean swapWeakSpace = maybeStripSpace(primaryCode,
1968ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                    spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x);
1969863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
19707a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(primaryCode);
1971e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1972e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            if (swapWeakSpace) {
1973bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                swapSwapperAndSpace();
1974e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                mSpaceState = SPACE_STATE_WEAK;
1975e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
19768f9c9377fc8944c9e96e6dcf661f0d673c23b83fJean Chalard            // In case the "add to dictionary" hint was still displayed.
19774702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint();
1978e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1979a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
1980756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka        if (mSettings.isInternal()) {
198187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            Utils.Stats.onNonSeparator((char)primaryCode, x, y);
198287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
1983923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1984923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
19852995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private void handleRecapitalize() {
19862995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        if (mLastSelectionStart == mLastSelectionEnd) return; // No selection
19872995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        // If we have a recapitalize in progress, use it; otherwise, create a new one.
1988b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        if (!mRecapitalizeStatus.isActive()
19892995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) {
19903bdf341ea001cef70da803749e17484d19a9726aJean Chalard            final CharSequence selectedText =
19913bdf341ea001cef70da803749e17484d19a9726aJean Chalard                    mConnection.getSelectedText(0 /* flags, 0 for no styles */);
19923bdf341ea001cef70da803749e17484d19a9726aJean Chalard            if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection
1993b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            mRecapitalizeStatus.initialize(mLastSelectionStart, mLastSelectionEnd,
19943bdf341ea001cef70da803749e17484d19a9726aJean Chalard                    selectedText.toString(), mSettings.getCurrentLocale(),
19953bdf341ea001cef70da803749e17484d19a9726aJean Chalard                    mSettings.getWordSeparators());
19962995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // We trim leading and trailing whitespace.
19972995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mRecapitalizeStatus.trim();
19982995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // Trimming the object may have changed the length of the string, and we need to
19992995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // reposition the selection handles accordingly. As this result in an IPC call,
20002995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // only do it if it's actually necessary, in other words if the recapitalize status
20012995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // is not set at the same place as before.
20022995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            if (!mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) {
20032995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                mLastSelectionStart = mRecapitalizeStatus.getNewCursorStart();
20042995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                mLastSelectionEnd = mRecapitalizeStatus.getNewCursorEnd();
20052995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                mConnection.setSelection(mLastSelectionStart, mLastSelectionEnd);
20062995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            }
20072995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        }
20082995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mRecapitalizeStatus.rotate();
20092995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        final int numCharsDeleted = mLastSelectionEnd - mLastSelectionStart;
20102995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
20112995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mConnection.deleteSurroundingText(numCharsDeleted, 0);
20122995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mConnection.commitText(mRecapitalizeStatus.getRecapitalizedString(), 0);
20132995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mLastSelectionStart = mRecapitalizeStatus.getNewCursorStart();
20142995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mLastSelectionEnd = mRecapitalizeStatus.getNewCursorEnd();
20152995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mConnection.setSelection(mLastSelectionStart, mLastSelectionEnd);
20168094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard        // Match the keyboard to the new state.
20178094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard        mKeyboardSwitcher.updateShiftState();
20182995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
20192995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
2020c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    // Returns true if we did an autocorrection, false otherwise.
2021c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    private boolean handleSeparator(final int primaryCode, final int x, final int y,
2022120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int spaceState) {
20239c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
202480375649d36c3e3aeec3a266827bb2d66d95d0f7Kurt Partridge            ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
202590aaa2d0a59227e948aefc5fbc4c875dae006ccfKurt Partridge        }
2026c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
20270e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
20280e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // If we are in the middle of a recorrection, we need to commit the recorrection
20290e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // first so that we can insert the separator at the current cursor position.
20300e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            resetEntireInputState(mLastSelectionStart);
20310e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        }
2032196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
2033ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka            if (mSettings.getCurrent().mCorrectionEnabled) {
2034a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // TODO: maybe cache Strings in an <String> sparse array or something
2035a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                commitCurrentAutoCorrection(new String(new int[]{primaryCode}, 0, 1));
2036c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = true;
2037923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
2038a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                commitTyped(new String(new int[]{primaryCode}, 0, 1));
2039923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
2040923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
20414ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
2042e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard        final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
2043ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                Constants.SUGGESTION_STRIP_COORDINATE == x);
2044863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
204500ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
2046ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard                mSettings.getCurrent().isUsuallyPrecededBySpace(primaryCode)) {
20470e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
204800ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        }
20497a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        sendKeyCodePoint(primaryCode);
205089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
2051240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_SPACE == primaryCode) {
2052ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka            if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) {
20532b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                if (maybeDoubleSpacePeriod()) {
2054120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_DOUBLE;
2055120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                } else if (!isShowingPunctuationList()) {
2056120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_WEAK;
2057120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                }
2058126698fdd256a2e3734634d3b923cabd800064baJean Chalard            }
2059120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
20602b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mHandler.startDoubleSpacePeriodTimer();
2061ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard            mHandler.postUpdateSuggestionStrip();
206289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
2063fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (swapWeakSpace) {
2064bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                swapSwapperAndSpace();
20654721427c7de3600f6fe7dfff16508a6a974fb3e4Jean Chalard                mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
2066f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard            } else if (SPACE_STATE_PHANTOM == spaceState
2067ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard                    && mSettings.getCurrent().isUsuallyFollowedBySpace(primaryCode)) {
2068fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // If we are in phantom space state, and the user presses a separator, we want to
2069fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // stay in phantom space state so that the next keypress has a chance to add the
2070fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // space. For example, if I type "Good dat", pick "day" from the suggestion strip
2071fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // then insert a comma and go on to typing the next word, I want the space to be
2072fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // inserted automatically before the next word, the same way it is when I don't
2073fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // input the comma.
2074f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // The case is a little different if the separator is a space stripper. Such a
2075f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // separator does not normally need a space on the right (that's the difference
2076f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // between swappers and strippers), so we should not stay in phantom space state if
2077f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // the separator is a stripper. Hence the additional test above.
2078fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                mSpaceState = SPACE_STATE_PHANTOM;
2079120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
2080120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
208189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Set punctuation right away. onUpdateSelection will fire but tests whether it is
208289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // already displayed or not, so it's okay.
208355b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            setPunctuationSuggestions();
2084923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2085756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka        if (mSettings.isInternal()) {
208687cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            Utils.Stats.onSeparator((char)primaryCode, x, y);
208787cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
2088120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
2089fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        mKeyboardSwitcher.updateShiftState();
2090c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        return didAutoCorrect;
2091923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2092466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2093bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private CharSequence getTextWithUnderline(final String text) {
20940a59ac2ba88ac1f99151d9336136bc6fe7d416c0Jean Chalard        return mIsAutoCorrectionIndicatorOn
209577d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
2096fda847a870829f1491cbd5325f9c985213081149Jean Chalard                : text;
209777d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    }
209877d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard
2099923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void handleClose() {
2100f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge        // TODO: Verify that words are logged properly when IME is closed.
21015475b38328171a0841ae18074bd45380ec567e90Jean Chalard        commitTyped(LastComposedWord.NOT_A_SEPARATOR);
2102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        requestHideSelf(0);
210327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
210427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
210527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
21060a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka        }
2107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2108923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2109a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // TODO: make this private
2110a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Outside LatinIME, only used by the test suite.
211115f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting
21129fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    boolean isShowingPunctuationList() {
2113abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        if (mSuggestedWords == null) return false;
2114abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        return mSettings.getCurrent().mSuggestPuncList == mSuggestedWords;
21157599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
21167599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
2117a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    private boolean isSuggestionsStripVisible() {
21183efa36f6aab4d3fd970f87a72805e647213105ebKen Wakasa        if (mSuggestionStripView == null)
2119b5e00d5841b946de8970875231456228ae0eb6b1Ken Wakasa            return false;
21203efa36f6aab4d3fd970f87a72805e647213105ebKen Wakasa        if (mSuggestionStripView.isShowingAddToDictionaryHint())
21219fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
21223efa36f6aab4d3fd970f87a72805e647213105ebKen Wakasa        if (null == mSettings.getCurrent())
2123aa3a96f3a1c5aeacefdfd6e6a33e58c9d9c49553Jean Chalard            return false;
21243efa36f6aab4d3fd970f87a72805e647213105ebKen Wakasa        if (!mSettings.getCurrent().isSuggestionStripVisibleInOrientation(mDisplayOrientation))
21259fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return false;
21263efa36f6aab4d3fd970f87a72805e647213105ebKen Wakasa        if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn())
21279fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
2128ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        return mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation);
2129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2130923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2131259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka    private void clearSuggestionStrip() {
2132abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        setSuggestedWords(SuggestedWords.EMPTY, false);
21337204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
2134466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2135466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2136abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard    private void setSuggestedWords(final SuggestedWords words, final boolean isAutoCorrection) {
2137abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        mSuggestedWords = words;
21384702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        if (mSuggestionStripView != null) {
21394702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripView.setSuggestions(words);
2140dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard            mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection);
2141466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
2142d02783cb63293507e8544ea60d07559092ce83d4Jean Chalard    }
2143ec780e2868962bf17f0dfd35d36895f543bde40asatok
214438e535e59676ac4d7bf27026fe3e16fcd9eb292eJean Chalard    private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) {
2145ec780e2868962bf17f0dfd35d36895f543bde40asatok        // Put a blue underline to a word in TextView which will be auto-corrected.
21462be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard        if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
21472be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                && mWordComposer.isComposingWord()) {
21482be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
21492be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            final CharSequence textWithUnderline =
21502be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                    getTextWithUnderline(mWordComposer.getTypedWord());
2151574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // TODO: when called from an updateSuggestionStrip() call that results from a posted
2152574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // message, this is called outside any batch edit. Potentially, this may result in some
2153574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // janky flickering of the screen, although the display speed makes it unlikely in
2154574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // the practice.
21555475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.setComposingText(textWithUnderline, 1);
2156ec780e2868962bf17f0dfd35d36895f543bde40asatok        }
2157466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2158466741d8a78965b8509bf527344f289e50873092Mike LeBeau
21597575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka    private void updateSuggestionStrip() {
2160d81e7d24d384a2bb1aeda65d9b423e4b1d23f185Jean Chalard        mHandler.cancelUpdateSuggestionStrip();
2161305326e789c3a89517855cc5a023ed1aa3074dc0Jean Chalard
2162923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if we have a suggestion engine attached.
2163ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSuggest == null
2164ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                || !mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) {
2165edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            if (mWordComposer.isComposingWord()) {
21667ed22f1f72dfa14b13ad6775617fd9e89f0ca224Jean Chalard                Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not "
21677ed22f1f72dfa14b13ad6775617fd9e89f0ca224Jean Chalard                        + "requested!");
2168edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            }
21697575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka            return;
2170923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2171466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2172ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (!mWordComposer.isComposingWord() && !mSettings.getCurrent().mBigramPredictionEnabled) {
2173e4498929b070cdd673c69450e316e2932e334fd5Jean Chalard            setPunctuationSuggestions();
21747575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka            return;
2175e4498929b070cdd673c69450e316e2932e334fd5Jean Chalard        }
2176e4498929b070cdd673c69450e316e2932e334fd5Jean Chalard
2177ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard        final SuggestedWords suggestedWords =
2178ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard                getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_TYPING);
21797575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka        final String typedWord = mWordComposer.getTypedWord();
21807575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka        showSuggestionStrip(suggestedWords, typedWord);
21817575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka    }
21827575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka
2183f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private SuggestedWords getSuggestedWords(final int sessionId) {
2184d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
2185c7318bbc0b47662799d2f7a01e5b53edb43ebf2aTadashi G. Takaoka        if (keyboard == null || mSuggest == null) {
2186d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka            return SuggestedWords.EMPTY;
2187d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        }
218832f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // Get the word on which we should search the bigrams. If we are composing a word, it's
218932f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
219032f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // should just skip whitespace if any, so 1.
219132f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // TODO: this is slow (2-way IPC) - we should probably cache this instead.
2192bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String prevWord =
2193ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators,
219432f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard                mWordComposer.isComposingWord() ? 2 : 1);
2195ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard        return mSuggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
21962dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard                mSettings.getBlockPotentiallyOffensive(),
2197ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard                mSettings.getCurrent().mCorrectionEnabled, sessionId);
2198ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard    }
2199ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard
2200ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard    private SuggestedWords getSuggestedWordsOrOlderSuggestions(final int sessionId) {
2201ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard        return maybeRetrieveOlderSuggestions(mWordComposer.getTypedWord(),
2202ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard                getSuggestedWords(sessionId));
22037ed22f1f72dfa14b13ad6775617fd9e89f0ca224Jean Chalard    }
2204979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2205bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord,
220632f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard            final SuggestedWords suggestedWords) {
220732f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // TODO: consolidate this into getSuggestedWords
220809fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // We update the suggestion strip only when we have some suggestions to show, i.e. when
220909fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // the suggestion count is > 1; else, we leave the old suggestions, with the typed word
221009fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // replaced with the new one. However, when the word is a dictionary word, or when the
221109fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // length of the typed word is 1 or 0 (after a deletion typically), we do want to remove the
221209fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // old suggestions. Also, if we are showing the "add to dictionary" hint, we need to
221309fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // revert to suggestions - although it is unclear how we can come here if it's displayed.
221409fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        if (suggestedWords.size() > 1 || typedWord.length() <= 1
2215ae7d1530b48e31266dbe8c6c5f33481853092170Jean Chalard                || suggestedWords.mTypedWordValid || null == mSuggestionStripView
22164702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                || mSuggestionStripView.isShowingAddToDictionaryHint()) {
2217ea80794dd43d44fe53884b4b8f4567af3d0e8331Jean Chalard            return suggestedWords;
2218838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard        } else {
2219f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka            return getOlderSuggestions(typedWord);
22209fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        }
2221979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
22224a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
2223f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka    private SuggestedWords getOlderSuggestions(final String typedWord) {
2224abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        SuggestedWords previousSuggestedWords = mSuggestedWords;
2225abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        if (previousSuggestedWords == mSettings.getCurrent().mSuggestPuncList) {
2226abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard            previousSuggestedWords = SuggestedWords.EMPTY;
2227f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        }
2228f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        if (typedWord == null) {
2229abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard            return previousSuggestedWords;
2230f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        }
2231f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
2232abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard                SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord,
2233abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard                        previousSuggestedWords);
2234f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka        return new SuggestedWords(typedWordAndPreviousSuggestions,
2235f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                false /* typedWordValid */,
2236f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                false /* hasAutoCorrectionCandidate */,
2237f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                false /* isPunctuationSuggestions */,
2238f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                true /* isObsoleteSuggestions */,
2239f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka                false /* isPrediction */);
2240f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka    }
2241f21a491ad80c99063e461056ce3cb0eaca1a41e2Tadashi G. Takaoka
2242bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private void showSuggestionStrip(final SuggestedWords suggestedWords, final String typedWord) {
2243c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka        if (suggestedWords.isEmpty()) {
2244259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            clearSuggestionStrip();
22452c5cf744e554cbe0872f1b3e18cbd1383b0189f9Jean Chalard            return;
22462c5cf744e554cbe0872f1b3e18cbd1383b0189f9Jean Chalard        }
2247bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String autoCorrection;
2248c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka        if (suggestedWords.mWillAutoCorrect) {
2249c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka            autoCorrection = suggestedWords.getWord(1);
2250923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
2251c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka            autoCorrection = typedWord;
2252923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2253d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard        mWordComposer.setAutoCorrection(autoCorrection);
22549b01890254c62a30b079bd9f79a30f9541faf11bJean Chalard        final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
2255abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        setSuggestedWords(suggestedWords, isAutoCorrection);
2256dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setAutoCorrectionIndicator(isAutoCorrection);
2257913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
2258923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2259923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2260a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard    private void commitCurrentAutoCorrection(final String separatorString) {
2261913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // Complete any pending suggestions query first
2262d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        if (mHandler.hasPendingUpdateSuggestions()) {
2263259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            updateSuggestionStrip();
2264923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2265bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
2266eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final String typedWord = mWordComposer.getTypedWord();
2267bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String autoCorrection = (typedAutoCorrection != null)
2268eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                ? typedAutoCorrection : typedWord;
2269117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        if (autoCorrection != null) {
227046798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            if (TextUtils.isEmpty(typedWord)) {
227146798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                throw new RuntimeException("We have an auto-correction but the typed word "
227246798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                        + "is empty? Impossible! I must commit suicide.");
227346798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            }
2274756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka            if (mSettings.isInternal()) {
22754aff3bf0eaf4aec38c1d8576fdfc85a9eaa1c544Kurt Partridge                Stats.onAutoCorrection(typedWord, autoCorrection, separatorString, mWordComposer);
227687cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            }
22779c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
2278abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard                final SuggestedWords suggestedWords = mSuggestedWords;
22794aff3bf0eaf4aec38c1d8576fdfc85a9eaa1c544Kurt Partridge                ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
22800c16a5c6eef645fd536671994e0b4f05864ac338Kurt Partridge                        separatorString, mWordComposer.isBatchMode(), suggestedWords);
2281226377e65aabe3922c47a4e40f7d846d346c55d5Kurt Partridge            }
2282f77dd424b077a7f8ff547c09cb94d0dc7f0daed7Kurt Partridge            mExpectingUpdateSelection = true;
228366bb563535dbe3672f99f75bd71763a551444867Jean Chalard            commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
2284a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                    separatorString);
22855475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (!typedWord.equals(autoCorrection)) {
22861c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // This will make the correction flash for a short while as a visual clue
228728d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // to the user that auto-correction happened. It has no other effect; in particular
228828d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // note that this won't affect the text inside the text field AT ALL: it only makes
228928d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // the segment of text starting at the supplied index and running for the length
229028d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // of the auto-correction flash. At this moment, the "typedWord" argument is
229128d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // ignored by TextView.
22925475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.commitCorrection(
22935475b38328171a0841ae18074bd45380ec567e90Jean Chalard                        new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
229496fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasa                        typedWord, autoCorrection));
22951c6cf26c3705e845418a29718c034598b52293ccJean Chalard            }
2296923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2297923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2298923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
22994702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
23004702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // interface
2301c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
23029310f42a36eabe99ed7dcd3b835d6cdaa3c6fdcaJean Chalard    public void pickSuggestionManually(final int index, final SuggestedWordInfo suggestionInfo) {
2303abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        final SuggestedWords suggestedWords = mSuggestedWords;
23049310f42a36eabe99ed7dcd3b835d6cdaa3c6fdcaJean Chalard        final String suggestion = suggestionInfo.mWord;
2305551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
2306551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        if (suggestion.length() == 1 && isShowingPunctuationList()) {
2307551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Word separators are suggested before the user inputs something.
2308551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // So, LatinImeLogger logs "" as a user's input.
2309f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
2310551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
2311551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            final int primaryCode = suggestion.charAt(0);
2312551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            onCodeInput(primaryCode,
2313ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                    Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
23149c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
23153b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
23163338703a2fe8fa3ae549e1d884d9fb5a579a7f74Kurt Partridge                        false /* isBatchMode */, suggestedWords.mIsPrediction);
231794e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            }
2318551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            return;
2319551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        }
2320551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard
23214fde56b8182514bcae64d76488724d2a64a73f14Jean Chalard        mConnection.beginBatchEdit();
2322eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0
2323eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                // In the batch input mode, a manually picked suggested word should just replace
2324eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                // the current batch input text and there is no need for a phantom space.
2325eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                && !mWordComposer.isBatchMode()) {
2326ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard            final int firstChar = Character.codePointAt(suggestion, 0);
2327ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard            if (!mSettings.getCurrent().isWordSeparator(firstChar)
2328ae3b96b26ef86a9f342b4aeb2047abdaac5e57a5Jean Chalard                    || mSettings.getCurrent().isUsuallyPrecededBySpace(firstChar)) {
23290e84041bf740590230198fa845d8c45acd4cb586Jean Chalard                promotePhantomSpace();
2330845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            }
2331845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard        }
2332845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard
2333ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()
2334dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && mApplicationSpecifiedCompletions != null
23351b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
2336abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard            mSuggestedWords = SuggestedWords.EMPTY;
23374702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            if (mSuggestionStripView != null) {
23384702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                mSuggestionStripView.clear();
2339923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
2340b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
2341611a5bdf650f7bc54bf40176f84f1cd9c87aa9aaJean Chalard            resetComposingState(true /* alsoResetLastComposedWord */);
23425475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
23435475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitCompletion(completionInfo);
23449d71748ba48dbc8793f3e1ecddf5fd31b8e59613Jean Chalard            mConnection.endBatchEdit();
2345923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
2346923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
23476a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
2348af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // We need to log before we commit, because the word composer will store away the user
2349af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // typed word.
23504aff3bf0eaf4aec38c1d8576fdfc85a9eaa1c544Kurt Partridge        final String replacedWord = mWordComposer.getTypedWord();
2351f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge        LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
235235c11842a78e272ac4f61bfc58b96826c833f104Kurt Partridge        mExpectingUpdateSelection = true;
235335c11842a78e272ac4f61bfc58b96826c833f104Kurt Partridge        commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
235435c11842a78e272ac4f61bfc58b96826c833f104Kurt Partridge                LastComposedWord.NOT_A_SEPARATOR);
23559c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
23563b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge            ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
23573b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                    mWordComposer.isBatchMode());
235894e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge        }
23594fde56b8182514bcae64d76488724d2a64a73f14Jean Chalard        mConnection.endBatchEdit();
236029a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        // Don't allow cancellation of manual pick
236129a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        mLastComposedWord.deactivate();
236290a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Space state must be updated before calling updateShiftState
2363fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mSpaceState = SPACE_STATE_PHANTOM;
2364fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mKeyboardSwitcher.updateShiftState();
2365979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2366c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // We should show the "Touch again to save" hint if the user pressed the first entry
2367491d451e382a8e7d42937a96e4e06ada483e57b5Jean Chalard        // AND it's in none of our current dictionaries (main, user or otherwise).
23687f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // Please note that if mSuggest is null, it means that everything is off: suggestion
23697f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // and correction, so we shouldn't try to show the hint
23709310f42a36eabe99ed7dcd3b835d6cdaa3c6fdcaJean Chalard        final boolean showingAddToDictionaryHint =
23719310f42a36eabe99ed7dcd3b835d6cdaa3c6fdcaJean Chalard                SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind && mSuggest != null
2372491d451e382a8e7d42937a96e4e06ada483e57b5Jean Chalard                // If the suggestion is not in the dictionary, the hint should be shown.
2373491d451e382a8e7d42937a96e4e06ada483e57b5Jean Chalard                && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
2374b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
2375756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka        if (mSettings.isInternal()) {
2376240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            Stats.onSeparator((char)Constants.CODE_SPACE,
237787cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
237887cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
2379b1dc8ad5f244337f91fcdac2a17078f5b9239cb7Jean Chalard        if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) {
23804702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripView.showAddToDictionaryHint(
2381ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                    suggestion, mSettings.getCurrent().mHintToSaveText);
2382b1dc8ad5f244337f91fcdac2a17078f5b9239cb7Jean Chalard        } else {
23838f9c9377fc8944c9e96e6dcf661f0d673c23b83fJean Chalard            // If we're not showing the "Touch again to save", then update the suggestion strip.
2384a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard            mHandler.postUpdateSuggestionStrip();
238566a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani        }
2386923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2387a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2388979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
23898558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa     * Commits the chosen word to the text field and saves it for later retrieval.
2390979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
2391bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private void commitChosenWord(final String chosenWord, final int commitType,
2392a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            final String separatorString) {
2393abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        final SuggestedWords suggestedWords = mSuggestedWords;
2394e8bb8351d6f09f461851af619cabe5fcd2f66c0aJean Chalard        mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
2395e8bb8351d6f09f461851af619cabe5fcd2f66c0aJean Chalard                this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
2396c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        // Add the word to the user history dictionary
2397bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String prevWord = addToUserHistoryDictionary(chosenWord);
23980fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // TODO: figure out here if this is an auto-correct or if the best word is actually
23990fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // what user typed. Note: currently this is done much later in
2400bdf89ce5feedb03e67b43f530b1eb9bd44203c63Jean Chalard        // LastComposedWord#didCommitTypedWord by string equality of the remembered
24010fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // strings.
24024aff3bf0eaf4aec38c1d8576fdfc85a9eaa1c544Kurt Partridge        mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord, separatorString,
24034aff3bf0eaf4aec38c1d8576fdfc85a9eaa1c544Kurt Partridge                prevWord);
2404923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2405923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2406a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    private void setPunctuationSuggestions() {
2407ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (mSettings.getCurrent().mBigramPredictionEnabled) {
2408259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            clearSuggestionStrip();
2409d5b765b3bbceb80b2e145ac7f4d66acbadd1ee4fJean Chalard        } else {
2410abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard            setSuggestedWords(mSettings.getCurrent().mSuggestPuncList, false);
2411d5b765b3bbceb80b2e145ac7f4d66acbadd1ee4fJean Chalard        }
24127204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
2413913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
24146a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani    }
24156a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
2416bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String addToUserHistoryDictionary(final String suggestion) {
2417c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (TextUtils.isEmpty(suggestion)) return null;
2418e46d12927ee2717788248b05f00b751beaf2a02aKen Wakasa        if (mSuggest == null) return null;
2419bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
2420ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        // If correction is not enabled, we don't add words to the user history dictionary.
2421ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        // That's to avoid unintended additions in some sensitive fields, or fields that
2422ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        // expect to receive non-words.
2423ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        if (!mSettings.getCurrent().mCorrectionEnabled) return null;
2424bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
2425a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        final Suggest suggest = mSuggest;
242667b9337b6cc478973d2c628fb5686583ce339bf0Satoshi Kataoka        final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
2427a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        if (suggest == null || userHistoryDictionary == null) {
2428a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka            // Avoid concurrent issue
2429a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka            return null;
2430a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        }
2431a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        final String prevWord
2432a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka                = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2);
2433a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        final String secondWord;
2434a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
2435a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka            secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
2436a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        } else {
2437a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka            secondWord = suggestion;
2438a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        }
2439a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
2440a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        // We don't add words with 0-frequency (assuming they would be profanity etc.).
2441a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        final int maxFreq = AutoCorrection.getMaxFrequency(
2442a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka                suggest.getUnigramDictionaries(), suggestion);
2443a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        if (maxFreq == 0) return null;
2444a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        userHistoryDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0);
2445a17dccbfc624c991ca054a5436519e74e0a60b54Satoshi Kataoka        return prevWord;
244632e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani    }
244732e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani
24486b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
24496a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard     * Check if the cursor is touching a word. If so, restart suggestions on this word, else
24506a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard     * do nothing.
24516a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard     */
24526a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    private void restartSuggestionsOnWordTouchedByCursor() {
245389fe1f50881180730e451cbe6ec4d1f8dc67acf1Jean Chalard        // HACK: We may want to special-case some apps that exhibit bad behavior in case of
245489fe1f50881180730e451cbe6ec4d1f8dc67acf1Jean Chalard        // recorrection. This is a temporary, stopgap measure that will be removed later.
245589fe1f50881180730e451cbe6ec4d1f8dc67acf1Jean Chalard        // TODO: remove this.
245689fe1f50881180730e451cbe6ec4d1f8dc67acf1Jean Chalard        if (mAppWorkAroundsUtils.isBrokenByRecorrection()) return;
24576a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        // If the cursor is not touching a word, or if there is a selection, return right away.
24586a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        if (mLastSelectionStart != mLastSelectionEnd) return;
24599f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        // If we don't know the cursor location, return.
24609f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        if (mLastSelectionStart < 0) return;
24616a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return;
24626a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(),
24636a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                0 /* additionalPrecedingWordsCount */);
24642b42ad0eaa5398853fd7d87a2580226d784a55ebJean Chalard        if (null == range) return; // Happens if we don't have an input connection at all
24659f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        // If for some strange reason (editor bug or so) we measure the text before the cursor as
24669f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        // longer than what the entire text is supposed to be, the safe thing to do is bail out.
24679f9cc032773a528b4eb6e036db0c37ff45385cceJean Chalard        if (range.mCharsBefore > mLastSelectionStart) return;
24686a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
24690e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        final String typedWord = range.mWord.toString();
24706a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        if (range.mWord instanceof SpannableString) {
24716a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            final SpannableString spannableString = (SpannableString)range.mWord;
24726a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            int i = 0;
24736a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            for (Object object : spannableString.getSpans(0, spannableString.length(),
24746a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                    SuggestionSpan.class)) {
24756a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                SuggestionSpan span = (SuggestionSpan)object;
24766a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                for (String s : span.getSuggestions()) {
24776a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                    ++i;
24786a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                    if (!TextUtils.equals(s, typedWord)) {
24796a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                        suggestions.add(new SuggestedWordInfo(s,
24806a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                                SuggestionStripView.MAX_SUGGESTIONS - i,
24816a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                                SuggestedWordInfo.KIND_RESUMED, Dictionary.TYPE_RESUMED));
24826a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                    }
24836a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                }
24846a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            }
24856a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        }
24860e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard());
24876a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mWordComposer.setCursorPositionWithinWord(range.mCharsBefore);
24886a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        mConnection.setComposingRegion(mLastSelectionStart - range.mCharsBefore,
24896a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                mLastSelectionEnd + range.mCharsAfter);
24900e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        final SuggestedWords suggestedWords;
24916a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        if (suggestions.isEmpty()) {
24920e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // We come here if there weren't any suggestion spans on this word. We will try to
24930e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // compute suggestions for it instead.
24940e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            final SuggestedWords suggestedWordsIncludingTypedWord =
24950e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    getSuggestedWords(Suggest.SESSION_TYPING);
24960e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            if (suggestedWordsIncludingTypedWord.size() > 1) {
24970e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // We were able to compute new suggestions for this word.
24980e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // Remove the typed word, since we don't want to display it in this case.
24990e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to false.
25000e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                suggestedWords =
25010e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                        suggestedWordsIncludingTypedWord.getSuggestedWordsExcludingTypedWord();
25020e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            } else {
25030e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // No saved suggestions, and we were unable to compute any good one either.
25040e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // Rather than displaying an empty suggestion strip, we'll display the original
25050e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // word alone in the middle.
25060e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                // Since there is only one word, willAutoCorrect is false.
25070e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                suggestedWords = suggestedWordsIncludingTypedWord;
25080e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            }
25090e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        } else {
25100e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // We found suggestion spans in the word. We'll create the SuggestedWords out of
25110e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            // them, and make willAutoCorrect false.
25120e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard            suggestedWords = new SuggestedWords(suggestions,
25130e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    true /* typedWordValid */, false /* willAutoCorrect */,
25140e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
25150e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard                    false /* isPrediction */);
25166a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard        }
25170e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard
25180e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        // Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
25190e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        // We never want to auto-correct on a resumed suggestion. Please refer to the three
2520b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // places above where suggestedWords is affected. We also need to reset
2521b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching the text to adapt it.
2522b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        // TODO: remove mIsAutoCorrectionIndicator on (see comment on definition)
2523b1e37e4a478a577c59930767148e50f8f45af21fJean Chalard        mIsAutoCorrectionIndicatorOn = false;
25240e9ee4d3bf75459560670ca5c28ff4c4f7c346fbJean Chalard        showSuggestionStrip(suggestedWords, typedWord);
25256a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    }
25266a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard
25276a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    /**
25286b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
25296b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * word, else do nothing.
25306b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
25315475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
2532ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        final CharSequence word =
2533ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                mConnection.getWordBeforeCursorIfAtEndOfWord(mSettings.getCurrent());
2534747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard        if (null != word) {
25356a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            final String wordString = word.toString();
25366a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard            restartSuggestionsOnWordBeforeCursor(wordString);
25373b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge            // TODO: Handle the case where the user manually moves the cursor and then backs up over
25383b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge            // a separator.  In that case, the current log unit should not be uncommitted.
25399c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
25406a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard                ResearchLogger.getInstance().uncommitCurrentLogUnit(wordString,
25413b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge                        true /* dumpCurrentLogUnit */);
25423b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge            }
2543fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        }
25446b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
25456b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
25466a114fa700d3ca73c608e1291b74bbbdd5a1a7b7Jean Chalard    private void restartSuggestionsOnWordBeforeCursor(final String word) {
25473708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
2548d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int length = word.length();
25495475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(length, 0);
25505475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.setComposingText(word, 1);
2551a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
25526b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
25536b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
25545475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void revertCommit() {
2555bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String previousWord = mLastComposedWord.mPrevWord;
2556b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        final String originallyTypedWord = mLastComposedWord.mTypedWord;
2557bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String committedWord = mLastComposedWord.mCommittedWord;
2558cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final int cancelLength = committedWord.length();
2559ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        final int separatorLength = LastComposedWord.getSeparatorLength(
2560a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                mLastComposedWord.mSeparatorString);
2561193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        // TODO: should we check our saved separator against the actual contents of the text view?
2562d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int deleteLength = cancelLength + separatorLength;
2563890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2564b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            if (mWordComposer.isComposingWord()) {
25655935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit, but we are composing a word");
2566b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            }
2567bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final CharSequence wordBeforeCursor =
25685475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.getTextBeforeCursor(deleteLength, 0)
2569bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka                            .subSequence(0, cancelLength);
2570cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard            if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
25715935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit check failed: we thought we were "
2572cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard                        + "reverting \"" + committedWord
2573890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
2574890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
25758558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        }
25765475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(deleteLength, 0);
2577c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
2578f763dc5915d394378bdcdc90cc0b238e66926b8bKurt Partridge            mUserHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord);
2579c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        }
2580a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard        mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1);
2581756791e6bf80c1fd86f9a236f4ba8da770a7305fSatoshi Kataoka        if (mSettings.isInternal()) {
258287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            Stats.onSeparator(mLastComposedWord.mSeparatorString,
258387cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
258487cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
25859c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
25863b95eaf70b0a11b1c6e0c52361fe738b4a4b2aceKurt Partridge            ResearchLogger.latinIME_revertCommit(committedWord, originallyTypedWord,
258731156c816e57fc1824f32f26f0301bc21a402774Kurt Partridge                    mWordComposer.isBatchMode(), mLastComposedWord.mSeparatorString);
258875e69753b709c19d5a23baf88ec3ac2576ee9c24Kurt Partridge            ResearchLogger.getInstance().uncommitCurrentLogUnit(committedWord,
258975e69753b709c19d5a23baf88ec3ac2576ee9c24Kurt Partridge                    true /* dumpCurrentLogUnit */);
2590193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        }
25910e9e7e337defe97d4ede8c59d0e925f5401f9292Jean Chalard        // Don't restart suggestion yet. We'll restart if the user deletes the
25920e9e7e337defe97d4ede8c59d0e925f5401f9292Jean Chalard        // separator.
2593b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
2594746e014eb54f0d6278b948868dff4863bfe85ad8Jean Chalard        // We have a separator between the word and the cursor: we should show predictions.
2595a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
2596890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    }
2597890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard
25980e84041bf740590230198fa845d8c45acd4cb586Jean Chalard    // This essentially inserts a space, and that's it.
25990e84041bf740590230198fa845d8c45acd4cb586Jean Chalard    public void promotePhantomSpace() {
260073ec85b8ad3102ce1c7e6013be73afe83475e589Jean Chalard        if (mSettings.getCurrent().shouldInsertSpacesAutomatically()
260173ec85b8ad3102ce1c7e6013be73afe83475e589Jean Chalard                && !mConnection.textBeforeCursorLooksLikeURL()) {
2602e36af707c8a8705ebd19fcc359c51181f7ebd171Jean Chalard            sendKeyCodePoint(Constants.CODE_SPACE);
26039c3860ce461c3791891bf667edc77fe798c8d332Ken Wakasa            if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
260499931201d0258ec93bd47de6f6f53ad59709bd85Kurt Partridge                ResearchLogger.latinIME_promotePhantomSpace();
260599931201d0258ec93bd47de6f6f53ad59709bd85Kurt Partridge            }
2606ec60d60078b4837b657dc207bdbde1748749199eJean Chalard        }
26070e84041bf740590230198fa845d8c45acd4cb586Jean Chalard    }
26080e84041bf740590230198fa845d8c45acd4cb586Jean Chalard
2609a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Used by the RingCharBuffer
2610f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public boolean isWordSeparator(final int code) {
2611ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        return mSettings.getCurrent().isWordSeparator(code);
2612923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2613923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
26149fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    // TODO: Make this private
26159fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    // Outside LatinIME, only used by the {@link InputTestsBase} test suite.
261615f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting
26179fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    void loadKeyboard() {
2618d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        // TODO: Why are we calling {@link #loadSettings()} and {@link #initSuggest()} in a
2619d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard        // different order than in {@link #onStartInputView}?
26200657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka        initSuggest();
26210657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka        loadSettings();
262227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mKeyboardSwitcher.getMainKeyboardView() != null) {
26231e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            // Reload keyboard because the current language has been changed.
2624ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent());
26251e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        }
2626b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // Since we just changed languages, we should re-evaluate suggestions with whatever word
2627b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // we are currently composing. If we are not composing anything, we may want to display
26288f9c9377fc8944c9e96e6dcf661f0d673c23b83fJean Chalard        // predictions or punctuation signs (which is done by the updateSuggestionStrip anyway).
2629a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
263036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
263136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
2632a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Callback called by PointerTracker through the KeyboardActionListener. This is called when a
2633a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // key is depressed; release matching call is onReleaseKey below.
26345a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
263541016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka    public void onPressKey(final int primaryCode, final boolean isSinglePointer) {
263641016acacfa21354f59ed51db1f85ea3d99bf6a4Tadashi G. Takaoka        mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer);
2637923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2638923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2639a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Callback by PointerTracker through the KeyboardActionListener. This is called when a key
2640a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // is released; press matching call is onPressKey above.
26415a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
2642f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onReleaseKey(final int primaryCode, final boolean withSliding) {
26432a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
26448d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv
26458d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        // If accessibility is on, ensure the user receives keyboard state updates.
26468d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
26478d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            switch (primaryCode) {
2648240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
26498d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifyShiftState();
26508d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
2651240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SWITCH_ALPHA_SYMBOL:
26528d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifySymbolsState();
26538d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
26548d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            }
26558d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        }
2656978c96aa995015658070346b60826a3a34fdaf84Jean Chalard
2657240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_DELETE == primaryCode) {
2658978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // This is a stopgap solution to avoid leaving a high surrogate alone in a text view.
2659978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // In the future, we need to deprecate deteleSurroundingText() and have a surrogate
2660978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // pair-friendly way of deleting characters in InputConnection.
2661e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard            // TODO: use getCodePointBeforeCursor instead to improve performance
26625475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0);
26635475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
26645475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(1, 0);
2665978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            }
2666978c96aa995015658070346b60826a3a34fdaf84Jean Chalard        }
2667923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2668a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2669cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    // Hooks for hardware keyboard
2670cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    @Override
2671cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    public boolean onKeyDown(final int keyCode, final KeyEvent event) {
26722fc4d422e72cf88bd4b0e30bce2a9815b7856b8eJean Chalard        if (!ProductionFlag.IS_HARDWARE_KEYBOARD_SUPPORTED) return super.onKeyDown(keyCode, event);
2673cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard        // onHardwareKeyEvent, like onKeyDown returns true if it handled the event, false if
2674cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard        // it doesn't know what to do with it and leave it to the application. For example,
2675cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard        // hardware key events for adjusting the screen's brightness are passed as is.
2676976a42b873215a669fe22709960aa1259292d75cJean Chalard        if (mEventInterpreter.onHardwareKeyEvent(event)) {
2677976a42b873215a669fe22709960aa1259292d75cJean Chalard            final long keyIdentifier = event.getDeviceId() << 32 + event.getKeyCode();
2678976a42b873215a669fe22709960aa1259292d75cJean Chalard            mCurrentlyPressedHardwareKeys.add(keyIdentifier);
2679976a42b873215a669fe22709960aa1259292d75cJean Chalard            return true;
2680976a42b873215a669fe22709960aa1259292d75cJean Chalard        }
2681cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard        return super.onKeyDown(keyCode, event);
2682cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    }
2683cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard
2684cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    @Override
2685cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    public boolean onKeyUp(final int keyCode, final KeyEvent event) {
2686976a42b873215a669fe22709960aa1259292d75cJean Chalard        final long keyIdentifier = event.getDeviceId() << 32 + event.getKeyCode();
2687976a42b873215a669fe22709960aa1259292d75cJean Chalard        if (mCurrentlyPressedHardwareKeys.remove(keyIdentifier)) {
2688976a42b873215a669fe22709960aa1259292d75cJean Chalard            return true;
2689976a42b873215a669fe22709960aa1259292d75cJean Chalard        }
2690cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard        return super.onKeyUp(keyCode, event);
2691cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    }
2692cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard
2693cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    // onKeyDown and onKeyUp are the main events we are interested in. There are two more events
2694cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    // related to handling of hardware key events that we may want to implement in the future:
2695cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    // boolean onKeyLongPress(final int keyCode, final KeyEvent event);
2696cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard    // boolean onKeyMultiple(final int keyCode, final int count, final KeyEvent event);
2697cff28c5d5db3e0c3e97149519a3f705150aeb224Jean Chalard
2698123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka    // receive ringer mode change and network state change.
2699923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
2700923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
2701f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onReceive(final Context context, final Intent intent) {
2702123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            final String action = intent.getAction();
2703564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
2704123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                mSubtypeSwitcher.onNetworkStateChanged(intent);
270521af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard            } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
27068e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka                mKeyboardSwitcher.onRingerModeChanged();
2707123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            }
2708923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2709923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    };
2710923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2711c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka    private void launchSettings() {
27123c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        handleClose();
27133c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        launchSubActivity(SettingsActivity.class);
2714466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2715466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2716f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void launchKeyboardedDialogActivity(final Class<? extends Activity> activityClass) {
27173c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        // Put the text in the attached EditText into a safe, saved state before switching to a
27183c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        // new activity that will also use the soft keyboard.
27193c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        commitTyped(LastComposedWord.NOT_A_SEPARATOR);
27203c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        launchSubActivity(activityClass);
27213c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge    }
27223c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge
2723f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void launchSubActivity(final Class<? extends Activity> activityClass) {
2724923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        Intent intent = new Intent();
27253c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        intent.setClass(LatinIME.this, activityClass);
272640651c8cf7d4296927f9c49aaa57bcfc7b5372caTadashi G. Takaoka        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
272740651c8cf7d4296927f9c49aaa57bcfc7b5372caTadashi G. Takaoka                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
272840651c8cf7d4296927f9c49aaa57bcfc7b5372caTadashi G. Takaoka                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2729923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        startActivity(intent);
2730923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2731923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
27322fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showSubtypeSelectorAndSettings() {
273385996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
273485996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
273585996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                // TODO: Should use new string "Select active input modes".
273685996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.language_selection_title),
27373c8117c88fc738f841ac43f5a88602a99b901feeSatoshi Kataoka                getString(Utils.getAcitivityTitleResId(this, SettingsActivity.class)),
273885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
273985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
27402fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            @Override
27412fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            public void onClick(DialogInterface di, int position) {
27422fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                di.dismiss();
27432fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                switch (position) {
274485996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
2745298b4240f84344bbcd70b72dfb20806ba93ffd22Tadashi G. Takaoka                    final Intent intent = IntentUtils.getInputLanguageSelectionIntent(
27466fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                            mRichImm.getInputMethodIdOfThisIme(),
27472a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Intent.FLAG_ACTIVITY_NEW_TASK
27482fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
27492fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_CLEAR_TOP);
27502fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    startActivity(intent);
27512fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2752aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                case 1:
2753aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    launchSettings();
2754aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    break;
27552fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                }
27562fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            }
275785996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2758bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2759bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2760bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2761724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge        showOptionDialog(builder.create());
27622fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    }
2763923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2764f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void showOptionDialog(final AlertDialog dialog) {
276527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
276627e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (windowToken == null) {
276727e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            return;
276827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
276913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
277013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCancelable(true);
277113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCanceledOnTouchOutside(true);
277213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
277313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final Window window = dialog.getWindow();
277413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final WindowManager.LayoutParams lp = window.getAttributes();
277513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.token = windowToken;
277613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
277713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.setAttributes(lp);
277813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
277913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
278013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        mOptionsDialog = dialog;
278113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.show();
278213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    }
278313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
2784abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard    // TODO: can this be removed somehow without breaking the tests?
2785abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard    @UsedForTesting
2786abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard    /* package for test */ String getFirstSuggestedWord() {
2787abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard        return mSuggestedWords.size() > 0 ? mSuggestedWords.getWord(0) : null;
2788abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard    }
2789abaf5827e7a7f5f6c5d8d98e03e6b9528b0c9351Jean Chalard
279028d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    public void debugDumpStateAndCrashWithException(final String context) {
27917058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        final StringBuilder s = new StringBuilder(mAppWorkAroundsUtils.toString());
27927058b02a9c798c21b169b778be2befc7739f4e9bJean Chalard        s.append("\nAttributes : ").append(mSettings.getCurrent().mInputAttributes)
279328d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                .append("\nContext : ").append(context);
279428d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard        throw new RuntimeException(s.toString());
279528d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    }
279628d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard
27977e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
2798f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    protected void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) {
2799923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.dump(fd, fout, args);
2800a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2801923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        final Printer p = new PrintWriterPrinter(fout);
2802923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("LatinIME state :");
28033708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
2804df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
2805df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        p.println("  Keyboard mode = " + keyboardMode);
2806ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        final SettingsValues settingsValues = mSettings.getCurrent();
2807fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard        p.println("  mIsSuggestionsSuggestionsRequested = "
2808ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka                + settingsValues.isSuggestionsRequested(mDisplayOrientation));
2809ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        p.println("  mCorrectionEnabled=" + settingsValues.mCorrectionEnabled);
28104d0f03bd66742ee292da81a3e025e119f28b6940Jean Chalard        p.println("  isComposingWord=" + mWordComposer.isComposingWord());
2811ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        p.println("  mSoundOn=" + settingsValues.mSoundOn);
2812ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        p.println("  mVibrateOn=" + settingsValues.mVibrateOn);
2813ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        p.println("  mKeyPreviewPopupOn=" + settingsValues.mKeyPreviewPopupOn);
2814ff0735ada807239abeb4c6105b7d01d2bcea0aedTadashi G. Takaoka        p.println("  inputAttributes=" + settingsValues.mInputAttributes);
2815923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2816923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
2817