LatinIME.java revision b224b60c94d85f30de93f66685adf06e662618c0
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani *
4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of
6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at
7a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * 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
11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under
14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * 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
23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.app.AlertDialog;
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.BroadcastReceiver;
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.DialogInterface;
27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Intent;
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.IntentFilter;
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.SharedPreferences;
30b224b60c94d85f30de93f66685adf06e662618c0Jean Chalardimport android.content.pm.ApplicationInfo;
31923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.res.Configuration;
3236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.content.res.Resources;
33c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaokaimport android.graphics.Rect;
34923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.inputmethodservice.InputMethodService;
35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.media.AudioManager;
36123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaokaimport android.net.ConnectivityManager;
37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Debug;
3881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaokaimport android.os.IBinder;
39923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Message;
40923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.SystemClock;
41bf96661d33d0126adb60a48880ceba1ff055d4a4satokimport android.preference.PreferenceActivity;
42923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.preference.PreferenceManager;
43e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.text.InputType;
44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils;
45923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Log;
46923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.PrintWriterPrinter;
47923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Printer;
48923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View;
498e09172df1bb176cc899940862c56bed9b9aec4esatokimport android.view.ViewGroup;
50c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaokaimport android.view.ViewGroup.LayoutParams;
51cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaokaimport android.view.ViewParent;
5213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokaimport android.view.Window;
5313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokaimport android.view.WindowManager;
54923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.CompletionInfo;
5596fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasaimport android.view.inputmethod.CorrectionInfo;
56923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.EditorInfo;
57923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.InputConnection;
589cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
59c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka
605ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.accessibility.AccessibilityUtils;
618d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanvimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
62c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.CompatUtils;
63c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
641fef530ec7626fa16777f52b48191e61db8f46d4satokimport com.android.inputmethod.compat.SuggestionSpanUtils;
65c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
66c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardActionListener;
676b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardId;
68c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardSwitcher;
69f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView;
70c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokaimport com.android.inputmethod.keyboard.LatinKeyboardView;
7116c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaokaimport com.android.inputmethod.latin.LocaleUtils.RunInLocale;
72c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
738c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.suggestions.SuggestionsView;
74923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
75466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.FileDescriptor;
76466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.PrintWriter;
774ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalardimport java.util.ArrayList;
78466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.util.Locale;
79466741d8a78965b8509bf527344f289e50873092Mike LeBeau
80923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
81923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Input method implementation for Qwerty'ish keyboard.
82923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
8313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokapublic class LatinIME extends InputMethodService implements KeyboardActionListener,
84b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard        SuggestionsView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener {
858efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    private static final String TAG = LatinIME.class.getSimpleName();
86409220583333bdf06290dd9fd42f91b5c0d1b11asatok    private static final boolean TRACE = false;
879e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka    private static boolean DEBUG;
88a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
899e347d3d448e48229c46aad394ec9bd60cd5807bsatok    private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
90fd0bd57deb53c4cce32810a61133fa44b45dbb7bKen Wakasa
91923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // How many continuous deletes at which to start deleting at a higher speed.
92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int DELETE_ACCELERATE_AT = 20;
93923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // Key events coming any faster than this are long-presses.
94d67fe0e7583f1be18b35b33b7658e4427f1e3dedAmith Yamasani    private static final int QUICK_PRESS = 200;
95a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
9659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private static final int PENDING_IMS_CALLBACK_DURATION = 800;
97f4cf5b9952ae331806bf656a6f977a5ece47fe80satok    // TODO: remove this
98f4cf5b9952ae331806bf656a6f977a5ece47fe80satok    private static final boolean WORKAROUND_USE_LAST_BACKING_HEIGHT_WHEN_NOT_READY = true;
99f4cf5b9952ae331806bf656a6f977a5ece47fe80satok    private static int sLastBackingHeight = 0;
100055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
101cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /**
102cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * The name of the scheme used by the Package Manager to warn of a new package installation,
103cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * replacement or removal.
104cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     */
105cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private static final String SCHEME_PACKAGE = "package";
106cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
10718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang    /** Whether to use the binary version of the contacts dictionary */
10818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang    public static final boolean USE_BINARY_CONTACTS_DICTIONARY = true;
10918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang
110f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang    /** Whether to use the binary version of the user dictionary */
111f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang    public static final boolean USE_BINARY_USER_DICTIONARY = true;
112f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang
1130fe3611bee5095e7bd0fff2d0fdf8d5a13379132Jean Chalard    // TODO: migrate this to SettingsValues
1147599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private int mSuggestionVisibility;
1157599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
1167599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_show_value;
1177599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
1187599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_show_only_portrait_value;
1197599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_HIDE_VALUE
1207599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_hide_value;
1217599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1227599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
1237599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_SHOW_VALUE,
1247599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE,
1257599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_HIDE_VALUE
1267599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    };
1277599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
128fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_NONE = 0;
129120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Double space: the state where the user pressed space twice quickly, which LatinIME
130120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // resolved as period-space. Undoing this converts the period to a space.
131fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_DOUBLE = 1;
132b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
133b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // have just been swapped. Undoing this swaps them back; the space is still considered weak.
134120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
135fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
136fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // spaces happen when the user presses space, accepting the current suggestion (whether
137fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // it's an auto-correction or not).
138fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_WEAK = 3;
139fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom space: a not-yet-inserted space that should get inserted on the next input,
140fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // character provided it's not a separator. If it's a separator, the phantom space is dropped.
141fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom spaces happen when a user chooses a word from the suggestion strip.
142fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_PHANTOM = 4;
143120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
144120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Current space state of the input method. This can be any of the above constants.
145120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private int mSpaceState;
146126698fdd256a2e3734634d3b923cabd800064baJean Chalard
1477a16a061e622539e54d7d649dcb8d4965aea575aJean Chalard    private SettingsValues mSettingsValues;
14880b66bb166f7f45adfcadcb84788477df9930828Jean Chalard    private InputAttributes mInputAttributes;
14917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
150d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka    private View mExtractArea;
151abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka    private View mKeyPreviewBackingView;
152913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private View mSuggestionsContainer;
153913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private SuggestionsView mSuggestionsView;
154816a8a0fd85ca0327436f8bd1cfa6928600ebc5dJean Chalard    /* package for tests */ Suggest mSuggest;
1551b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private CompletionInfo[] mApplicationSpecifiedCompletions;
156b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    private ApplicationInfo mTargetApplicationInfo;
157a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
158610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    private InputMethodManagerCompatWrapper mImm;
1592fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private Resources mResources;
1602fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private SharedPreferences mPrefs;
161109728193e45262099cbf88d8d6fcc4ed05240caJean Chalard    /* package for tests */ final KeyboardSwitcher mKeyboardSwitcher;
16289ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    private final SubtypeSwitcher mSubtypeSwitcher;
16381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    private boolean mShouldSwitchToLastSubtype = true;
164a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1656a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka    private boolean mIsMainDictionaryAvailable;
166f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang    // TODO: revert this back to the concrete class after transition.
167f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang    private Dictionary mUserDictionary;
1689ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard    private UserHistoryDictionary mUserHistoryDictionary;
16988562bec54658840dcce352127bdc15705c20a89Jean Chalard    private boolean mIsUserDictionaryAvailable;
17036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
1712692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
1729318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa    private WordComposer mWordComposer = new WordComposer();
173409220583333bdf06290dd9fd42f91b5c0d1b11asatok
17479efbed76f638be298493107fa2d0cd1b5eb529esatok    private int mCorrectionMode;
17577da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard
176979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // Keep track of the last selection range to decide if we need to show word alternatives
17777da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private static final int NOT_A_CURSOR_POSITION = -1;
17877da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionStart = NOT_A_CURSOR_POSITION;
17977da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionEnd = NOT_A_CURSOR_POSITION;
180979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1814733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
1824733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // "expect" it, it means the user actually moved the cursor.
1834733609947c0ec74e460bd714fffca0518ade93aJean Chalard    private boolean mExpectingUpdateSelection;
184923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mDeleteCount;
185923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private long mLastKeyTime;
186a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
187564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    private AudioAndHapticFeedbackManager mFeedbackManager;
18828f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
18938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    // Member variables for remembering the current device orientation.
19038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    private int mDisplayOrientation;
19138f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
192cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    // Object for reacting to adding/removing a dictionary pack.
193cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private BroadcastReceiver mDictionaryPackInstallReceiver =
194cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            new DictionaryPackInstallBroadcastReceiver(this);
195cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
196dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    // Keeps track of most recently inserted text (multi-character key) for reverting
197dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    private CharSequence mEnteredText;
198dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
19970852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard    private boolean mIsAutoCorrectionIndicatorOn;
200604d80c67185954d4691ac775be59c499eee3b1csatok
20113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    private AlertDialog mOptionsDialog;
20213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
2034f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public final UIHandler mHandler = new UIHandler(this);
204d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
2054f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
20645f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_UPDATE_SHIFT_STATE = 1;
2074c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_SPACE_TYPED = 4;
2084c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_SET_BIGRAM_PREDICTIONS = 5;
2094c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_PENDING_IMS_CALLBACK = 6;
2104c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_UPDATE_SUGGESTIONS = 7;
2114c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka
21210dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateSuggestions;
21310dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateShiftState;
21410dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private long mDoubleSpacesTurnIntoPeriodTimeout;
21538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
2164f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        public UIHandler(LatinIME outerInstance) {
2174f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
21810dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        }
219175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
22010dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        public void onCreate() {
22110dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka            final Resources res = getOuterInstance().getResources();
222175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateSuggestions =
223175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_suggestions);
224175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateShiftState =
225175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_shift_state);
226175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger(
227175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_double_spaces_turn_into_period_timeout);
2284f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
2294f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
230923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
231923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void handleMessage(Message msg) {
2324f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
2334f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
234923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (msg.what) {
235d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SUGGESTIONS:
2364f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateSuggestions();
237d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
238d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SHIFT_STATE:
239de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                switcher.updateShiftState();
240d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
241cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            case MSG_SET_BIGRAM_PREDICTIONS:
2424f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateBigramPredictions();
24389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                break;
244923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
245923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
246d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
247d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void postUpdateSuggestions() {
248d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
249175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions);
250d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
251d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
252d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateSuggestions() {
253d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
254d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
255d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
256d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public boolean hasPendingUpdateSuggestions() {
257d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            return hasMessages(MSG_UPDATE_SUGGESTIONS);
258d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
259d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
260beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void postUpdateShiftState() {
261d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
262175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState);
263d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
264d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
265d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateShiftState() {
266d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
267d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
268d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
269cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void postUpdateBigramPredictions() {
270cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
271175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions);
27289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
27389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
274cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void cancelUpdateBigramPredictions() {
275cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
27689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
27789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
278fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void startDoubleSpacesTimer() {
279fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
280175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout);
281fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
282fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
283fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void cancelDoubleSpacesTimer() {
284fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
285fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
286fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
287fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public boolean isAcceptingDoubleSpaces() {
288fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            return hasMessages(MSG_SPACE_TYPED);
289fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
29038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
29159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        // Working variables for the following methods.
29259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mIsOrientationChanging;
2935fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard        private boolean mPendingSuccessiveImsCallback;
29459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingStartInput;
29559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInputView;
29659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInput;
297e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private EditorInfo mAppliedEditorInfo;
29859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
29959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void startOrientationChanging() {
300dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            removeMessages(MSG_PENDING_IMS_CALLBACK);
301dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            resetPendingImsCallback();
30259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mIsOrientationChanging = true;
303055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            final LatinIME latinIme = getOuterInstance();
304f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            if (latinIme.isInputViewShown()) {
305f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka                latinIme.mKeyboardSwitcher.saveKeyboardState();
306f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            }
30759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
30859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
30959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private void resetPendingImsCallback() {
31059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInputView = false;
31159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInput = false;
31259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingStartInput = false;
31359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
31459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
315e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private void executePendingImsCallback(LatinIME latinIme, EditorInfo editorInfo,
31659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                boolean restarting) {
31759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInputView)
31859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
31959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInput)
32059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
32159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingStartInput)
322e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
32359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            resetPendingImsCallback();
32459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
32559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
326e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInput(EditorInfo editorInfo, boolean restarting) {
32759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
32859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the second onStartInput after orientation changed.
32959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingStartInput = true;
33059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
33159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                if (mIsOrientationChanging && restarting) {
33259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    // This is the first onStartInput after orientation changed.
33359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    mIsOrientationChanging = false;
3345fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = true;
33559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                }
33659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
337e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
338e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
339055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
340055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
341055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
342e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
3436b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)
3446b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka                    && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
345e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                // Typically this is the second onStartInputView after orientation changed.
346e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                resetPendingImsCallback();
347e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            } else {
3485fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                if (mPendingSuccessiveImsCallback) {
349e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    // This is the first onStartInputView after orientation changed.
3505fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = false;
351e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    resetPendingImsCallback();
352e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
353e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            PENDING_IMS_CALLBACK_DURATION);
354e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                }
355e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
356e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
357e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputViewInternal(editorInfo, restarting);
358e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = editorInfo;
359e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            }
36059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
36159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
36259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInputView(boolean finishingInput) {
36359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
36459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInputView after orientation changed.
36559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInputView = true;
36659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
36759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
36859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(finishingInput);
369e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = null;
37038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            }
37138f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        }
372ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka
37359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInput() {
37459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
37559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInput after orientation changed.
37659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInput = true;
37759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
37859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
37959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                executePendingImsCallback(latinIme, null, false);
38059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
381ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka            }
382ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka        }
38338f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    }
38438f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
38589ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    public LatinIME() {
38689ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        super();
38789ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
38889ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
38989ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    }
39089ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard
3917e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
3927e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    public void onCreate() {
39327d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
39427d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        mPrefs = prefs;
39527d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        LatinImeLogger.init(this, prefs);
396c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
397c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa            ResearchLogger.init(this, prefs);
398c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        }
399bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        InputMethodManagerCompatWrapper.init(this);
400ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka        SubtypeSwitcher.init(this);
40127d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        KeyboardSwitcher.init(this, prefs);
4022ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka        AccessibilityUtils.init(this);
403363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
404923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onCreate();
405363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
406bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        mImm = InputMethodManagerCompatWrapper.getInstance();
40710dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        mHandler.onCreate();
4089e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka        DEBUG = LatinImeLogger.sDBG;
409363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
410363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        final Resources res = getResources();
411363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        mResources = res;
412fd7d814c81132bdd59146a39dd668532f9514cd1Jean Chalard
41328f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        loadSettings();
41428f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
415f5a0bd2c28fd9ec1de8fe49196e7f7ae38566f90Tadashi G. Takaoka        ImfUtils.setAdditionalInputMethodSubtypes(this, mSettingsValues.getAdditionalSubtypes());
416f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka
417dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        // TODO: remove the following when it's not needed by updateCorrectionMode() any more
418644c8b7c96627199c13297082e4566adae159bf3Jean Chalard        mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
4193fe263fac6375836c48ce71da29f098b66eb4f11Jean Chalard        updateCorrectionMode();
4203fe263fac6375836c48ce71da29f098b66eb4f11Jean Chalard
4219502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
422979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        boolean tryGC = true;
4239502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
424979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            try {
4250ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok                initSuggest();
426979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = false;
427979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            } catch (OutOfMemoryError e) {
4289502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e);
429979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
430979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
431979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
432f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        mDisplayOrientation = res.getConfiguration().orientation;
433b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
434cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Register to receive ringer mode change and network state change.
435cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Also receive installation and removal of a dictionary pack.
436123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        final IntentFilter filter = new IntentFilter();
437123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
43821af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
439923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        registerReceiver(mReceiver, filter);
440cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
441cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final IntentFilter packageFilter = new IntentFilter();
442cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
443cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
444cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addDataScheme(SCHEME_PACKAGE);
445cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
446646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard
447646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        final IntentFilter newDictFilter = new IntentFilter();
448646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        newDictFilter.addAction(
449646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard                DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
450646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
451923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
45236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
45317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    // Has to be package-visible for unit tests
45417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    /* package */ void loadSettings() {
45517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
45616c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka        final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
45716c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            @Override
45816c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            protected SettingsValues job(Resources res) {
45916c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka                return new SettingsValues(mPrefs, LatinIME.this);
46016c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            }
46116c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka        };
4626a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSettingsValues = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
46321af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard        mFeedbackManager = new AudioAndHapticFeedbackManager(this, mSettingsValues);
46414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
46517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    }
46617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
4670ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private void initSuggest() {
4686a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
4696a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final String localeStr = subtypeLocale.toString();
47036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
47118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang        final Dictionary oldContactsDictionary;
47278ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        if (mSuggest != null) {
47378ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = mSuggest.getContactsDictionary();
47478ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            mSuggest.close();
47578ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        } else {
47678ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = null;
47778ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
4786a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSuggest = new Suggest(this, subtypeLocale);
47978ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        if (mSettingsValues.mAutoCorrectEnabled) {
48078ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
48178ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
482e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
4836a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
4846a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka
485f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang        if (USE_BINARY_USER_DICTIONARY) {
486f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang            mUserDictionary = new UserBinaryDictionary(this, localeStr);
487f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang            mIsUserDictionaryAvailable = ((UserBinaryDictionary)mUserDictionary).isEnabled();
488f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang        } else {
489f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang            mUserDictionary = new UserDictionary(this, localeStr);
490f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang            mIsUserDictionaryAvailable = ((UserDictionary)mUserDictionary).isEnabled();
491f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang        }
49278ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserDictionary(mUserDictionary);
493e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
49478ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        resetContactsDictionary(oldContactsDictionary);
495e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
49678ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mUserHistoryDictionary = new UserHistoryDictionary(
49778ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka                this, localeStr, Suggest.DIC_USER_HISTORY);
49878ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
499923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
50036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
50114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    /**
50214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * Resets the contacts dictionary in mSuggest according to the user settings.
50314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
50414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * This method takes an optional contacts dictionary to use. Since the contacts dictionary
50514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * does not depend on the locale, it can be reused across different instances of Suggest.
50614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * The dictionary will also be opened or closed as necessary depending on the settings.
50714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
50814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * @param oldContactsDictionary an optional dictionary to use, or null
50914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     */
51018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang    private void resetContactsDictionary(final Dictionary oldContactsDictionary) {
51114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict);
51214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
51318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang        final Dictionary dictionaryToUse;
51414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (!shouldSetDictionary) {
51514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the dictionary is closed. If it is already closed, this is a no-op,
51614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // so it's safe to call it anyways.
51714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            if (null != oldContactsDictionary) oldContactsDictionary.close();
51814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = null;
51914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else if (null != oldContactsDictionary) {
52014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the old contacts dictionary is opened. If it is already open, this is a
52114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // no-op, so it's safe to call it anyways.
52218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            if (USE_BINARY_CONTACTS_DICTIONARY) {
52318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang                ((ContactsBinaryDictionary)oldContactsDictionary).reopen(this);
52418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            } else {
52518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang                ((ContactsDictionary)oldContactsDictionary).reopen(this);
52618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            }
52714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = oldContactsDictionary;
52814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
52918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            if (USE_BINARY_CONTACTS_DICTIONARY) {
53018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang                dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS,
5316a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka                        mSubtypeSwitcher.getCurrentSubtypeLocale());
53218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            } else {
53318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang                dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
53418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            }
53514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
53614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
53714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (null != mSuggest) {
53814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            mSuggest.setContactsDictionary(dictionaryToUse);
53914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
540699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard    }
541699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard
542cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /* package private */ void resetSuggestMainDict() {
5436a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
5446a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSuggest.resetMainDict(this, subtypeLocale);
5456a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
546cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
547cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
548466741d8a78965b8509bf527344f289e50873092Mike LeBeau    @Override
549466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onDestroy() {
550e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        if (mSuggest != null) {
551e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest.close();
552e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest = null;
553979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
554923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        unregisterReceiver(mReceiver);
555cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        unregisterReceiver(mDictionaryPackInstallReceiver);
556979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
557979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.onDestroy();
558923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onDestroy();
559923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
560923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
561923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
562923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onConfigurationChanged(Configuration conf) {
563dc64b138b5e3fb3706c0818d0a308fe6e36985b0Tadashi G. Takaoka        mSubtypeSwitcher.onConfigurationChanged(conf);
564b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        // If orientation changed while predicting, commit the change
565f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        if (mDisplayOrientation != conf.orientation) {
566f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mDisplayOrientation = conf.orientation;
567f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mHandler.startOrientationChanging();
5689351550dc6af7859e5280e16144c9386a37b976dKen Wakasa            final InputConnection ic = getCurrentInputConnection();
56966bb563535dbe3672f99f75bd71763a551444867Jean Chalard            commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
570466741d8a78965b8509bf527344f289e50873092Mike LeBeau            if (ic != null) ic.finishComposingText(); // For voice input
5712fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            if (isShowingOptionDialog())
5722fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                mOptionsDialog.dismiss();
573b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
574923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onConfigurationChanged(conf);
575923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
576b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
577923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
578923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public View onCreateInputView() {
5796c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        return mKeyboardSwitcher.onCreateInputView();
5806c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    }
5816c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka
5826c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    @Override
5836c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    public void setInputView(View view) {
5846c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        super.setInputView(view);
585d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        mExtractArea = getWindow().getWindow().getDecorView()
586d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka                .findViewById(android.R.id.extractArea);
587abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
588913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
589913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
590913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
591913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setListener(this, view);
592f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        if (LatinImeLogger.sVISUALDEBUG) {
593f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka            mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
594f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        }
595923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
596923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
597923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
598c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public void setCandidatesView(View view) {
599c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        // To ensure that CandidatesView will never be set.
600c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return;
601923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
602923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
603a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker    @Override
604e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInput(EditorInfo editorInfo, boolean restarting) {
605e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInput(editorInfo, restarting);
60659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
60759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
60859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
609e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
610e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInputView(editorInfo, restarting);
61159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
61259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
61359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
61459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInputView(boolean finishingInput) {
61559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInputView(finishingInput);
61659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
61738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
61859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
61959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInput() {
62059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInput();
62159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
62259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
6239cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    @Override
6249cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
6256a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSubtypeSwitcher.updateSubtype(subtype);
6269cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    }
6279cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka
628e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
629e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInput(editorInfo, restarting);
63059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
63159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
6321cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka    @SuppressWarnings("deprecation")
633e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
634e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInputView(editorInfo, restarting);
63545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
636c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = switcher.getKeyboardView();
6378e09172df1bb176cc899940862c56bed9b9aec4esatok
638ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        if (editorInfo == null) {
639ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.e(TAG, "Null EditorInfo in onStartInputView()");
640ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            if (LatinImeLogger.sDBG) {
641ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                throw new NullPointerException("Null EditorInfo in onStartInputView()");
642ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            }
643ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            return;
644ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        }
64589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (DEBUG) {
646ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.d(TAG, "onStartInputView: editorInfo:"
647ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    + String.format("inputType=0x%08x imeOptions=0x%08x",
648ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                            editorInfo.inputType, editorInfo.imeOptions));
649b6fb5eb391987f3e426649a892cdcbf781957f5asatok            Log.d(TAG, "All caps = "
650b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0)
651b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", sentence caps = "
652b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0)
653b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", word caps = "
654b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
655910b73127fa207dd26ec8124000262523b0aac0csatok        }
6569bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
65748a7681e064ae259b840f0e757da2d716043d893Kurt Partridge            ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
6589bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
6591cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
6604f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
6614f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
6621cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka            Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead");
6634f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
6641cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(getPackageName(), FORCE_ASCII, editorInfo)) {
6654f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
6664f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
6674f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
6684f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
6694f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka
670b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard        mTargetApplicationInfo = null;
671b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard        new TargetApplicationGetter(this /* context */, this /* listener */)
672b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard                .execute(editorInfo.packageName);
673b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
6747ef235f53f2291f22ddf8c56be9860a218b25bbbTadashi G. Takaoka        LatinImeLogger.onStartInputView(editorInfo);
675923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // In landscape mode, this method gets called without the input view being created.
676979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (inputView == null) {
677923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
678923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
679923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
680b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        // Forward this event to the accessibility utilities, if enabled.
681b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
682b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        if (accessUtils.isTouchExplorationEnabled()) {
683e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            accessUtils.onStartInputViewInternal(editorInfo, restarting);
684b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        }
685b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette
6868d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        mSubtypeSwitcher.updateParametersOnStartInputView();
6874ab730dbd34fad323063f2ffd31ce33de746668dsatok
688b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // The EditorInfo might have a flag that affects fullscreen mode.
689b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // Note: This call should be done by InputMethodService?
690b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        updateFullscreenMode();
6911fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard        mLastSelectionStart = editorInfo.initialSelStart;
6921fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard        mLastSelectionEnd = editorInfo.initialSelEnd;
69380b66bb166f7f45adfcadcb84788477df9930828Jean Chalard        mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
694ccc35f7fa74860a8d737a4e9ff01fc0168dd329dJean Chalard        mApplicationSpecifiedCompletions = null;
695c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
696c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.closing();
697c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mEnteredText = null;
6982692a8700737d8eed268039aa27b22a31669da08Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
699c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mDeleteCount = 0;
700120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
701c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
70217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
70317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        updateCorrectionMode();
7042ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka        updateSuggestionVisibility(mResources);
70517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
70617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) {
70717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
708549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        }
70917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
710cb389ef0d6e6eec737c249e1729c2a2cdc30f341Tadashi G. Takaoka        switcher.loadKeyboard(editorInfo, mSettingsValues);
711c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
712913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
713913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.clear();
714913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShownInternal(
715913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                isSuggestionsStripVisible(), /* needsInputViewShown */ false);
716c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        // Delay updating suggestions because keyboard input view may not be shown at this point.
717c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mHandler.postUpdateSuggestions();
718ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        mHandler.cancelDoubleSpacesTimer();
719c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
720240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard        inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
721240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard                mSettingsValues.mKeyPreviewPopupDismissDelay);
722c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.setProximityCorrectionEnabled(true);
723c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
724c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
725c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    }
726c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
727b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    public void onTargetApplicationKnown(final ApplicationInfo info) {
728b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard        mTargetApplicationInfo = info;
729b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    }
730b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
731923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
732e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    public void onWindowHidden() {
733e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        super.onWindowHidden();
734f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
735e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        if (inputView != null) inputView.closing();
736e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
737e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
73859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputInternal() {
739923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onFinishInput();
740a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
741979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
742979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
743f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
744d024ea605cc6b5b0b9fa1bd838d5b0ebd3901a5dKen Wakasa        if (inputView != null) inputView.closing();
7459ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard        if (mUserHistoryDictionary != null) mUserHistoryDictionary.flushPendingWrites();
746466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
747466741d8a78965b8509bf527344f289e50873092Mike LeBeau
74859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputViewInternal(boolean finishingInput) {
7496495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa        super.onFinishInputView(finishingInput);
750055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardSwitcher.onFinishInputView();
751f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
7525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (inputView != null) inputView.cancelAllMessages();
753d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        // Remove pending messages related to update suggestions
754d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.cancelUpdateSuggestions();
7556495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    }
7566495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa
7576495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    @Override
758923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
759923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            int newSelStart, int newSelEnd,
760104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            int composingSpanStart, int composingSpanEnd) {
761923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
762104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                composingSpanStart, composingSpanEnd);
763466741d8a78965b8509bf527344f289e50873092Mike LeBeau
764466741d8a78965b8509bf527344f289e50873092Mike LeBeau        if (DEBUG) {
765466741d8a78965b8509bf527344f289e50873092Mike LeBeau            Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
766466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", ose=" + oldSelEnd
767025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lss=" + mLastSelectionStart
768025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lse=" + mLastSelectionEnd
769466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nss=" + newSelStart
770466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nse=" + newSelEnd
771104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", cs=" + composingSpanStart
772104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", ce=" + composingSpanEnd);
773466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
7749bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
7759bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd,
7769bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
7779bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    composingSpanEnd);
7789bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
779466741d8a78965b8509bf527344f289e50873092Mike LeBeau
780104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // TODO: refactor the following code to be less contrived.
781104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "newSelStart != composingSpanEnd" || "newSelEnd != composingSpanEnd" means
782104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // that the cursor is not at the end of the composing span, or there is a selection.
783104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "mLastSelectionStart != newSelStart" means that the cursor is not in the same place
784104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // as last time we were called (if there is a selection, it means the start hasn't
785104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // changed, so it's the end that did).
786104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean selectionChanged = (newSelStart != composingSpanEnd
787104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                || newSelEnd != composingSpanEnd) && mLastSelectionStart != newSelStart;
788104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // if composingSpanStart and composingSpanEnd are -1, it means there is no composing
789104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // span in the view - we can use that to narrow down whether the cursor was moved
790104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // by us or not. If we are composing a word but there is no composing span, then
791104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // we know for sure the cursor moved while we were composing and we should reset
792104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // the state.
793104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
7944733609947c0ec74e460bd714fffca0518ade93aJean Chalard        if (!mExpectingUpdateSelection) {
795cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // TAKE CARE: there is a race condition when we enter this test even when the user
796cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // did not explicitly move the cursor. This happens when typing fast, where two keys
797cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // turn this flag on in succession and both onUpdateSelection() calls arrive after
798cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // the second one - the first call successfully avoids this test, but the second one
799104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            // enters. For the moment we rely on noComposingSpan to further reduce the impact.
80051fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
8019a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // TODO: the following is probably better done in resetEntireInputState().
8029a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // it should only happen when the cursor moved, and the very purpose of the
8039a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // test below is to narrow down whether this happened or not. Likewise with
8049a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // the call to postUpdateShiftState.
80551fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // We set this to NONE because after a cursor move, we don't want the space
80651fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // state-related special processing to kick in.
80751fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            mSpaceState = SPACE_STATE_NONE;
80851fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
8098a3d369840d6061692a19ceb1eb7267a50a9e056Jean Chalard            if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) {
8102649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard                resetEntireInputState();
8114c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard            }
812beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
813beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            mHandler.postUpdateShiftState();
8144733609947c0ec74e460bd714fffca0518ade93aJean Chalard        }
8154733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = false;
8166b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not
8176b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // here. It would probably be too expensive to call directly here but we may want to post a
8186b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // message to delay it. The point would be to unify behavior between backspace to the
8196b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // end of a word and manually put the pointer at the end of the word.
820466741d8a78965b8509bf527344f289e50873092Mike LeBeau
821979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // Make a note of the cursor position
822979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionStart = newSelStart;
823979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionEnd = newSelEnd;
8247a8dac55278cedd838be325f56b4c52d973c61f5satok    }
8257a8dac55278cedd838be325f56b4c52d973c61f5satok
826c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
827c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has clicked on the extracted text view,
828c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * when running in fullscreen mode.  The default implementation hides
829913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * the suggestions view when this happens, but only if the extracted text
830c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * editor has a vertical scroll bar because its text doesn't fit.
831c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
832913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
833c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
834c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
835c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedTextClicked() {
836fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
837c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
838c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedTextClicked();
839c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
840c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
841c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
842c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has performed a cursor movement in the
843c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * extracted text view, when it is running in fullscreen mode.  The default
844913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * implementation hides the suggestions view when a vertical movement
845c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * happens, but only if the extracted text editor has a vertical scroll bar
846c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * because its text doesn't fit.
847c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
848913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
849c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
850c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
851c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedCursorMovement(int dx, int dy) {
852fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
853c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
854c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedCursorMovement(dx, dy);
855c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
856c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
857923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
858923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void hideWindow() {
859979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
860c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mKeyboardSwitcher.onHideWindow();
861979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
862923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (TRACE) Debug.stopMethodTracing();
8636e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
8646e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog.dismiss();
8656e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog = null;
8666e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        }
867923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.hideWindow();
868923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
869923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
870923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
8711b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
872979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG) {
873a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa            Log.i(TAG, "Received completions:");
874bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            if (applicationSpecifiedCompletions != null) {
875bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
876bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                    Log.i(TAG, "  #" + i + ": " + applicationSpecifiedCompletions[i]);
877bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                }
878923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
879923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
8809bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
8819bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
8829bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
883dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn) {
8841b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
8851b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            if (applicationSpecifiedCompletions == null) {
886b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani                clearSuggestions();
887923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                return;
888923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
889a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
890660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa            final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
89188bf1ba5263f5a5c1df367ddc401db4109ef8677Jean Chalard                    SuggestedWords.getFromApplicationSpecifiedCompletions(
8921dfef0336d5968dbd00b73489778cee1fb233d56Jean Chalard                            applicationSpecifiedCompletions);
893bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard            final SuggestedWords suggestedWords = new SuggestedWords(
8947d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard                    applicationSuggestedWords,
8952e2519ee914d4bf9462950553840557a4c19faedJean Chalard                    false /* typedWordValid */,
896bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard                    false /* hasAutoCorrectionCandidate */,
897b5eeb724fc98bb7169683539027d9ba54ffb8b14Jean Chalard                    false /* allowsToBeAutoCorrected */,
89803a35170751a635332c00bf6c272a0127a255cf6Jean Chalard                    false /* isPunctuationSuggestions */,
8990142b997bf18f5d07e83b3fd403f0b3ea4736040satok                    false /* isObsoleteSuggestions */,
9000142b997bf18f5d07e83b3fd403f0b3ea4736040satok                    false /* isPrediction */);
901979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // When in fullscreen mode, show completions generated by the application
9024e112d07821d34c1dedd21b086817be9fce2fd47Jean Chalard            final boolean isAutoCorrection = false;
9037d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard            setSuggestions(suggestedWords, isAutoCorrection);
904dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard            setAutoCorrectionIndicator(isAutoCorrection);
905b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            // TODO: is this the right thing to do? What should we auto-correct to in
906b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            // this case? This says to keep whatever the user typed.
907b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
908c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            setSuggestionStripShown(true);
909923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
910923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
911923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
912c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
913913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // TODO: Modify this if we support suggestions with hard keyboard
914913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
915433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
916433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
917913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean shouldShowSuggestions = shown
918433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                    && (needsInputViewShown ? inputViewShown : true);
9194b1780fa9571409d65d9797d47949ffafaf0f083Tadashi G. Takaoka            if (isFullscreenMode()) {
920913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
921913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.GONE);
9227a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            } else {
923913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
924913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE);
9257a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            }
926923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
927923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
928a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
929c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShown(boolean shown) {
930c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
931543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    }
932543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa
933c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka    private void adjustInputViewHeight() {
934c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        if (mKeyPreviewBackingView.getHeight() > 0) {
935c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka            return;
936c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
937c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
938c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
939c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        if (keyboardView == null) return;
940c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int keyboardHeight = keyboardView.getHeight();
941c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int suggestionsHeight = mSuggestionsContainer.getHeight();
942c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int displayHeight = mResources.getDisplayMetrics().heightPixels;
943c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final Rect rect = new Rect();
944c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect);
945c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int notificationBarHeight = rect.top;
946c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight
947c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                - keyboardHeight;
948c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
949c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
950c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        params.height = mSuggestionsView.setMoreSuggestionsHeight(remainingHeight);
951c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.setLayoutParams(params);
952c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka    }
953c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
954543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    @Override
955923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onComputeInsets(InputMethodService.Insets outInsets) {
956923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onComputeInsets(outInsets);
957f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
958913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (inputView == null || mSuggestionsContainer == null)
95946ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka            return;
960c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        adjustInputViewHeight();
961d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // In fullscreen mode, the height of the extract area managed by InputMethodService should
962d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // be considered.
963d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}.
964d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0;
965f4cf5b9952ae331806bf656a6f977a5ece47fe80satok        final boolean backingGone = mKeyPreviewBackingView.getVisibility() == View.GONE;
966f4cf5b9952ae331806bf656a6f977a5ece47fe80satok        int backingHeight = backingGone ? 0 : mKeyPreviewBackingView.getHeight();
967f4cf5b9952ae331806bf656a6f977a5ece47fe80satok        if (WORKAROUND_USE_LAST_BACKING_HEIGHT_WHEN_NOT_READY && !backingGone) {
968f4cf5b9952ae331806bf656a6f977a5ece47fe80satok            if (backingHeight <= 0) {
969f4cf5b9952ae331806bf656a6f977a5ece47fe80satok                backingHeight = sLastBackingHeight;
970f4cf5b9952ae331806bf656a6f977a5ece47fe80satok            } else {
971f4cf5b9952ae331806bf656a6f977a5ece47fe80satok                sLastBackingHeight = backingHeight;
972f4cf5b9952ae331806bf656a6f977a5ece47fe80satok            }
973f4cf5b9952ae331806bf656a6f977a5ece47fe80satok        }
97459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0
97559010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                : mSuggestionsContainer.getHeight();
976d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
977abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        int touchY = extraHeight;
9789e347d3d448e48229c46aad394ec9bd60cd5807bsatok        // Need to set touchable region only if input view is being shown
979433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka        final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
980433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka        if (keyboardView != null && keyboardView.isShown()) {
981913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
98259010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                touchY -= suggestionsHeight;
9839e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
9847a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            final int touchWidth = inputView.getWidth();
985abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka            final int touchHeight = inputView.getHeight() + extraHeight
9867a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    // Extend touchable region below the keyboard.
9877a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
98813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
98913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight);
9909e347d3d448e48229c46aad394ec9bd60cd5807bsatok        }
99146ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.contentTopInsets = touchY;
99246ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.visibleTopInsets = touchY;
9939ec06c17d4b0c2d551d89152320d0cae4c061c77satok        if (WORKAROUND_USE_LAST_BACKING_HEIGHT_WHEN_NOT_READY) {
994f4cf5b9952ae331806bf656a6f977a5ece47fe80satok            if (LatinImeLogger.sDBG) {
995f4cf5b9952ae331806bf656a6f977a5ece47fe80satok                Log.i(TAG, "--- insets: " + touchY + "," + backingHeight + "," + suggestionsHeight);
996f4cf5b9952ae331806bf656a6f977a5ece47fe80satok            }
997f4cf5b9952ae331806bf656a6f977a5ece47fe80satok        }
998923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
999a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1000923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1001979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public boolean onEvaluateFullscreenMode() {
10029751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        // Reread resource value here, because this method is called by framework anytime as needed.
10039751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        final boolean isFullscreenModeAllowed =
10049751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka                mSettingsValues.isFullscreenModeAllowed(getResources());
10059751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        return super.onEvaluateFullscreenMode() && isFullscreenModeAllowed;
100659010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    }
100759010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka
100859010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    @Override
100959010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    public void updateFullscreenMode() {
101059010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        super.updateFullscreenMode();
1011f80b6a06992ae08ca3601f4fbc6da550fd9ac730Tadashi G. Takaoka
101259010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        if (mKeyPreviewBackingView == null) return;
1013549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        // In fullscreen mode, no need to have extra space to show the key preview.
101459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        // If not, we should have extra space above the keyboard to show the key preview.
1015549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
1016979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
1017979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
10182649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // This will reset the whole input state to the starting state. It will clear
10192649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // the composing word, reset the last composed word, tell the inputconnection
10202649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // and the composingStateManager about it.
10212649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    private void resetEntireInputState() {
10222649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
10232649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        updateSuggestions();
10242649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        final InputConnection ic = getCurrentInputConnection();
10252649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        if (ic != null) {
10262649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard            ic.finishComposingText();
10272649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        }
10282649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    }
10292649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard
10302692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private void resetComposingState(final boolean alsoResetLastComposedWord) {
10312692a8700737d8eed268039aa27b22a31669da08Jean Chalard        mWordComposer.reset();
10322692a8700737d8eed268039aa27b22a31669da08Jean Chalard        if (alsoResetLastComposedWord)
10332692a8700737d8eed268039aa27b22a31669da08Jean Chalard            mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
10342692a8700737d8eed268039aa27b22a31669da08Jean Chalard    }
10352692a8700737d8eed268039aa27b22a31669da08Jean Chalard
103666bb563535dbe3672f99f75bd71763a551444867Jean Chalard    public void commitTyped(final InputConnection ic, final int separatorCode) {
1037196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) return;
10383651220327c051d8017045aa5e8919461507b3f8Jean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
10393651220327c051d8017045aa5e8919461507b3f8Jean Chalard        if (typedWord.length() > 0) {
10409351550dc6af7859e5280e16144c9386a37b976dKen Wakasa            if (ic != null) {
10413651220327c051d8017045aa5e8919461507b3f8Jean Chalard                ic.commitText(typedWord, 1);
1042d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1043d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ResearchLogger.latinIME_commitText(typedWord);
1044d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
1045923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1046c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            final CharSequence prevWord = addToUserHistoryDictionary(typedWord);
1047c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            mLastComposedWord = mWordComposer.commitWord(
1048c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
1049c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    separatorCode, prevWord);
1050923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
10518558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        updateSuggestions();
1052923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1053923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1054553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka    public int getCurrentAutoCapsState() {
1055553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (!mSettingsValues.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
105603ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
105703ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final EditorInfo ei = getCurrentInputEditorInfo();
1058553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
105903ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
106003ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final int inputType = ei.inputType;
1061553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {
1062553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka            return TextUtils.CAP_MODE_CHARACTERS;
1063553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        }
106403ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
106503ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final boolean noNeedToCheckCapsMode = (inputType & (InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
106603ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka                | InputType.TYPE_TEXT_FLAG_CAP_WORDS)) == 0;
1067553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (noNeedToCheckCapsMode) return Constants.TextUtils.CAP_MODE_OFF;
106803ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
106916950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka        // Avoid making heavy round-trip IPC calls of {@link InputConnection#getCursorCapsMode}
107016950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka        // unless needed.
107116950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka        if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF;
107216950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka
10739351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
1074553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (ic == null) return Constants.TextUtils.CAP_MODE_OFF;
107503ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls.
107603ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // Note: getCursorCapsMode() returns the current capitalization mode that is any
107703ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none
107803ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // of them.
1079553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        return ic.getCursorCapsMode(inputType);
10801c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
10811c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
1082b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    // "ic" may be null
1083b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) {
1084b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        if (null == ic) return;
1085923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
1086863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
1087923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastTwo != null && lastTwo.length() == 2
1088863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard                && lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
1089923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.deleteSurroundingText(2, 0);
1090d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1091d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_deleteSurroundingText(2);
1092d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
1093923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.commitText(lastTwo.charAt(1) + " ", 1);
1094d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1095d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit();
1096d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
1097b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
10984ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa        }
10994ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa    }
11004ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1101120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
1102120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (mCorrectionMode == Suggest.CORRECTION_NONE) return false;
1103120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (ic == null) return false;
11042b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard        final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
1105923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastThree != null && lastThree.length() == 3
1106344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                && canBeFollowedByPeriod(lastThree.charAt(0))
1107571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastThree.charAt(1) == Keyboard.CODE_SPACE
1108fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && lastThree.charAt(2) == Keyboard.CODE_SPACE
1109fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && mHandler.isAcceptingDoubleSpaces()) {
1110fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            mHandler.cancelDoubleSpacesTimer();
1111923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.deleteSurroundingText(2, 0);
1112923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.commitText(". ", 1);
1113d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1114d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_doubleSpaceAutoPeriod();
1115d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
1116b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1117120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            return true;
1118923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1119120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return false;
1120923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1121a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1122344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka    private static boolean canBeFollowedByPeriod(final int codePoint) {
1123344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: Check again whether there really ain't a better way to check this.
1124344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: This should probably be language-dependant...
1125344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        return Character.isLetterOrDigit(codePoint)
1126344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_SINGLE_QUOTE
1127344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_DOUBLE_QUOTE
1128344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
1129344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
1130344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
1131344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
1132344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka    }
1133344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka
1134b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    // "ic" may be null
11358fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) {
11369a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (ic == null) return;
1137b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
11389a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (lastOne != null && lastOne.length() == 1
1139571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
11409a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            ic.deleteSurroundingText(1, 0);
1141d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1142d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_deleteSurroundingText(1);
1143d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
11449a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        }
11459a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa    }
11469a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa
1147c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
1148923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean addWordToDictionary(String word) {
1149f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang        if (USE_BINARY_USER_DICTIONARY) {
1150f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang            ((UserBinaryDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
1151f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang        } else {
1152f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang            ((UserDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
1153f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang        }
11546558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // Suggestion strip should be updated after the operation of adding word to the
11556558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // user dictionary
1156d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.postUpdateSuggestions();
1157923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
1158923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1159923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
11608fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean isAlphabet(int code) {
11618fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka        return Character.isLetter(code);
1162923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1163a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1164e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    private void onSettingsKeyPressed() {
1165cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return;
1166911b8f9d19c1c4903eeef29b43176cfeaa0e5d0cKen Wakasa        showSubtypeSelectorAndSettings();
11679a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
11689a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1169cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
1170cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
1171cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa
1172cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    @Override
1173cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public boolean onCustomRequest(int requestCode) {
1174cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return false;
1175cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        switch (requestCode) {
1176cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        case CODE_SHOW_INPUT_METHOD_PICKER:
117755d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka            if (ImfUtils.hasMultipleEnabledIMEsOrSubtypes(
117855d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka                    this, true /* include aux subtypes */)) {
117979efbed76f638be298493107fa2d0cd1b5eb529esatok                mImm.showInputMethodPicker();
1180cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa                return true;
11819a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok            }
1182cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa            return false;
11839a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
1184cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        return false;
11859a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
11869a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
11879a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    private boolean isShowingOptionDialog() {
11889a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        return mOptionsDialog != null && mOptionsDialog.isShowing();
11899a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
11909a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
119105bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka    private static int getActionId(Keyboard keyboard) {
119205bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        return keyboard != null ? keyboard.mId.imeActionId() : EditorInfo.IME_ACTION_NONE;
11937a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
11947a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
11958f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka    private void performEditorAction(int actionId) {
11967a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
11977a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (ic != null) {
11987a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            ic.performEditorAction(actionId);
1199d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1200d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_performEditorAction(actionId);
1201d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
12027a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12037a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12047a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
120581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    private void handleLanguageSwitchKey() {
1206c1ca8815f59bb1bce25e521571a1d87c71bf3fc3Ken Wakasa        final boolean includesOtherImes = mSettingsValues.mIncludesOtherImesInLanguageSwitchList;
120781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        final IBinder token = getWindow().getWindow().getAttributes().token;
120881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        if (mShouldSwitchToLastSubtype) {
12099cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka            final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype();
1210ae2388c7f799ab565f63d3ba83abaf3300475fd0Tadashi G. Takaoka            final boolean lastSubtypeBelongsToThisIme =
1211ae2388c7f799ab565f63d3ba83abaf3300475fd0Tadashi G. Takaoka                    ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(this, lastSubtype);
121281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            if ((includesOtherImes || lastSubtypeBelongsToThisIme)
121381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                    && mImm.switchToLastInputMethod(token)) {
121481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                mShouldSwitchToLastSubtype = false;
121581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            } else {
121681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                mImm.switchToNextInputMethod(token, !includesOtherImes);
121781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                mShouldSwitchToLastSubtype = true;
121881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            }
121981d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        } else {
122081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            mImm.switchToNextInputMethod(token, !includesOtherImes);
122181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        }
122281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    }
122381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka
12247a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    private void sendKeyCodePoint(int code) {
12257a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // TODO: Remove this special handling of digit letters.
12267a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
12277a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (code >= '0' && code <= '9') {
12287a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            super.sendKeyChar((char)code);
12297a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            return;
12307a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12317a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12327a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
12337a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (ic != null) {
12347a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            final String text = new String(new int[] { code }, 0, 1);
12357a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            ic.commitText(text, text.length());
1236d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1237d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_sendKeyCodePoint(code);
1238d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
12397a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12407a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12417a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12425f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    // Implementation of {@link KeyboardActionListener}.
12435a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
1244ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    public void onCodeInput(int primaryCode, int x, int y) {
1245175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final long when = SystemClock.uptimeMillis();
1246571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
1247923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mDeleteCount = 0;
1248923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1249923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mLastKeyTime = when;
1250fdd68f06579ab338f5d33115aa8300431c75b4faKurt Partridge
1251c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
1252c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa            if (ResearchLogger.sIsLogging) {
1253c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa                ResearchLogger.getInstance().logKeyEvent(primaryCode, x, y);
1254c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa            }
1255fdd68f06579ab338f5d33115aa8300431c75b4faKurt Partridge        }
1256fdd68f06579ab338f5d33115aa8300431c75b4faKurt Partridge
1257175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
1258120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // The space state depends only on the last character pressed and its own previous
1259120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // state. Here, we revert the space state to neutral if the key is actually modifying
1260120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // the input contents (any non-shift key), which is what we should do for
1261120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // all inputs that do not result in a special state. Each character handling is then
1262120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // free to override the state as they see fit.
1263120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final int spaceState = mSpaceState;
126470852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard        if (!mWordComposer.isComposingWord()) mIsAutoCorrectionIndicatorOn = false;
1265ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1266ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        // TODO: Consolidate the double space timer, mLastKeyTime, and the space state.
1267ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        if (primaryCode != Keyboard.CODE_SPACE) {
1268ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa            mHandler.cancelDoubleSpacesTimer();
1269ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        }
1270ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1271c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1272923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (primaryCode) {
1273571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_DELETE:
1274120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1275120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            handleBackspace(spaceState);
12764189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            mDeleteCount++;
12774733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
127881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            mShouldSwitchToLastSubtype = true;
1279140467b8b65eb9087a9b7f744dbb8a30481effe7Kurt Partridge            LatinImeLogger.logOnDelete(x, y);
12804189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1281571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_SHIFT:
1282e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
12832a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            // Shift and symbol key is handled in onPressKey() and onReleaseKey().
12844189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1285e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SETTINGS:
128693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            onSettingsKeyPressed();
12874189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1288d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka        case Keyboard.CODE_SHORTCUT:
128993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            mSubtypeSwitcher.switchToShortcutIME();
12904189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
12917a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        case Keyboard.CODE_ACTION_ENTER:
12928f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(getActionId(switcher.getKeyboard()));
12937a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            break;
129405bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        case Keyboard.CODE_ACTION_NEXT:
12958f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_NEXT);
129605bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka            break;
129705bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        case Keyboard.CODE_ACTION_PREVIOUS:
12988f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
12994189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
130081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        case Keyboard.CODE_LANGUAGE_SWITCH:
130181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            handleLanguageSwitchKey();
130281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            break;
13034189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        default:
13040b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka            if (primaryCode == Keyboard.CODE_TAB
13050b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka                    && mInputAttributes.mEditorAction == EditorInfo.IME_ACTION_NEXT) {
13060b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka                performEditorAction(EditorInfo.IME_ACTION_NEXT);
13070b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka                break;
13080b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka            }
1309120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
131017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (mSettingsValues.isWordSeparator(primaryCode)) {
1311c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
13124189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            } else {
13138dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
13148dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
13158dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                    handleCharacter(primaryCode, x, y, spaceState);
13168dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                } else {
13178dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                    handleCharacter(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE,
13188dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                            spaceState);
13198dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                }
13204189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            }
13214733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
132281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            mShouldSwitchToLastSubtype = true;
13234733609947c0ec74e460bd714fffca0518ade93aJean Chalard            break;
1324923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1325eef6238f94b5046054d9ae9c06f775362893c0eeTadashi G. Takaoka        switcher.onCodeInput(primaryCode);
1326125de3dfdf548359de890247907f2e6f430008ecJean Chalard        // Reset after any single keystroke, except shift and symbol-shift
1327125de3dfdf548359de890247907f2e6f430008ecJean Chalard        if (!didAutoCorrect && primaryCode != Keyboard.CODE_SHIFT
1328125de3dfdf548359de890247907f2e6f430008ecJean Chalard                && primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL)
1329c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard            mLastComposedWord.deactivate();
1330dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = null;
1331923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1332a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
13335a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13348aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onTextInput(CharSequence text) {
13359351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
1336923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic == null) return;
1337923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.beginBatchEdit();
133866bb563535dbe3672f99f75bd71763a551444867Jean Chalard        commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
1339fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        text = specificTldProcessingOnTextInput(ic, text);
1340fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == mSpaceState) {
13417a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(Keyboard.CODE_SPACE);
1342fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1343923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.commitText(text, 1);
1344d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
1345d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_commitText(text);
1346d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
1347923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.endBatchEdit();
1348b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
13498cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
1350120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
1351dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = text;
13522692a8700737d8eed268039aa27b22a31669da08Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
1353923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1354923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1355fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // ic may not be null
1356fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private CharSequence specificTldProcessingOnTextInput(final InputConnection ic,
1357fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            final CharSequence text) {
1358fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
1359fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                || !Character.isLetter(text.charAt(1))) {
1360fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            // Not a tld: do nothing.
1361fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1362fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
136312d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // We have a TLD (or something that looks like this): make sure we don't add
136412d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // a space even if currently in phantom mode.
136512d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        mSpaceState = SPACE_STATE_NONE;
1366fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
1367fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (lastOne != null && lastOne.length() == 1
1368fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                && lastOne.charAt(0) == Keyboard.CODE_PERIOD) {
1369fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text.subSequence(1, text.length());
1370fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        } else {
1371fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1372fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1373fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    }
1374fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
13755a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13768aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCancelInput() {
137783e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        // User released a finger outside any key
13785f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        mKeyboardSwitcher.onCancelInput();
137983e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka    }
138083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka
1381120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleBackspace(final int spaceState) {
1382504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
1383504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka        if (ic == null) return;
1384979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        ic.beginBatchEdit();
1385a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard        handleBackspaceWhileInBatchEdit(spaceState, ic);
1386a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard        ic.endBatchEdit();
1387a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    }
1388979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1389a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    // "ic" may not be null.
1390a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
13912245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard        // In many cases, we may have to put the keyboard in auto-shift state again.
1392beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        mHandler.postUpdateShiftState();
13932245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard
13945c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
13955c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // Cancel multi-character input: remove the text we just entered.
13965c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // This is triggered on backspace after a key that inputs multiple characters,
13975c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // like the smiley key or the .com key.
1398d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            final int length = mEnteredText.length();
1399d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ic.deleteSurroundingText(length, 0);
1400d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1401d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_deleteSurroundingText(length);
1402d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
14035c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
14045c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // In addition we know that spaceState is false, and that we should not be
14055c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // reverting any autocorrect at this point. So we can safely return.
14065c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            return;
14075c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        }
14085c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard
1409196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
14103651220327c051d8017045aa5e8919461507b3f8Jean Chalard            final int length = mWordComposer.size();
1411923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (length > 0) {
14129318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                mWordComposer.deleteLast();
141377d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1414196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                // If we have deleted the last remaining character of a word, then we are not
1415196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                // isComposingWord() any more.
1416196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                if (!mWordComposer.isComposingWord()) {
1417196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                    // Not composing word any more, so we can show bigrams.
1418cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                    mHandler.postUpdateBigramPredictions();
141989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                } else {
1420196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                    // Still composing a word, so we still have letters to deduce a suggestion from.
142189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    mHandler.postUpdateSuggestions();
142289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                }
1423923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1424923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                ic.deleteSurroundingText(1, 0);
1425d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1426d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ResearchLogger.latinIME_deleteSurroundingText(1);
1427d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
1428923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1429890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        } else {
14305935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard            if (mLastComposedWord.canRevertCommit()) {
1431d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                Utils.Stats.onAutoCorrectionCancellation();
14325935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                revertCommit(ic);
1433120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                return;
1434120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1435d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            if (SPACE_STATE_DOUBLE == spaceState) {
14362124bc5bf5af31cf3d2789b70ebd2f24c815f5f4Jean Chalard                if (revertDoubleSpaceWhileInBatchEdit(ic)) {
1437d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // No need to reset mSpaceState, it has already be done (that's why we
1438d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // receive it as a parameter)
1439d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1440d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
1441d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1442d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                if (revertSwapPunctuation(ic)) {
1443d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // Likewise
1444d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1445d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
14464733609947c0ec74e460bd714fffca0518ade93aJean Chalard            }
1447504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka
14484fef31510df542a3324426a6750950194d016086Jean Chalard            // No cancelling of commit/double space/swap: we have a regular backspace.
14494fef31510df542a3324426a6750950194d016086Jean Chalard            // We should backspace one char and restart suggestion if at the end of a word.
14504fef31510df542a3324426a6750950194d016086Jean Chalard            if (mLastSelectionStart != mLastSelectionEnd) {
14514fef31510df542a3324426a6750950194d016086Jean Chalard                // If there is a selection, remove it.
14524fef31510df542a3324426a6750950194d016086Jean Chalard                final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
14534fef31510df542a3324426a6750950194d016086Jean Chalard                ic.setSelection(mLastSelectionEnd, mLastSelectionEnd);
14544fef31510df542a3324426a6750950194d016086Jean Chalard                ic.deleteSurroundingText(lengthToDelete, 0);
1455d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1456d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete);
1457d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
14586558253160e2039c87f424bd814f402ecd31de3bKen Wakasa            } else {
14594fef31510df542a3324426a6750950194d016086Jean Chalard                // There is no selection, just delete one character.
14604fef31510df542a3324426a6750950194d016086Jean Chalard                if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
14614fef31510df542a3324426a6750950194d016086Jean Chalard                    // This should never happen.
14624fef31510df542a3324426a6750950194d016086Jean Chalard                    Log.e(TAG, "Backspace when we don't know the selection position");
14636558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                }
14644fef31510df542a3324426a6750950194d016086Jean Chalard                ic.deleteSurroundingText(1, 0);
1465d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1466d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ResearchLogger.latinIME_deleteSurroundingText(1);
1467d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
14684fef31510df542a3324426a6750950194d016086Jean Chalard                if (mDeleteCount > DELETE_ACCELERATE_AT) {
14694fef31510df542a3324426a6750950194d016086Jean Chalard                    ic.deleteSurroundingText(1, 0);
1470d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    if (ProductionFlag.IS_EXPERIMENTAL) {
1471d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                        ResearchLogger.latinIME_deleteSurroundingText(1);
1472d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    }
1473edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                }
1474923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
14754fef31510df542a3324426a6750950194d016086Jean Chalard            if (isSuggestionsRequested()) {
14764fef31510df542a3324426a6750950194d016086Jean Chalard                restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
14774fef31510df542a3324426a6750950194d016086Jean Chalard            }
1478923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1479923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1480923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1481e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    // ic may be null
1482e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code,
1483e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final int spaceState, final boolean isFromSuggestionStrip) {
1484e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1485e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            removeTrailingSpaceWhileInBatchEdit(ic);
1486e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1487e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else if ((SPACE_STATE_WEAK == spaceState
1488e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                || SPACE_STATE_SWAP_PUNCTUATION == spaceState)
1489e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                && isFromSuggestionStrip) {
1490b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard            if (mSettingsValues.isWeakSpaceSwapper(code)) {
1491e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return true;
1492e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            } else {
1493b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard                if (mSettingsValues.isWeakSpaceStripper(code)) {
1494e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                    removeTrailingSpaceWhileInBatchEdit(ic);
1495e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                }
1496e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return false;
1497e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
1498e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else {
1499e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1500e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1501e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    }
1502e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1503ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    private void handleCharacter(final int primaryCode, final int x,
1504120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int y, final int spaceState) {
1505b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        final InputConnection ic = getCurrentInputConnection();
1506dafa7a8e15447544842975047f831883e67700c5Jean Chalard        if (null != ic) ic.beginBatchEdit();
1507dafa7a8e15447544842975047f831883e67700c5Jean Chalard        // TODO: if ic is null, does it make any sense to call this?
1508ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState, ic);
1509dafa7a8e15447544842975047f831883e67700c5Jean Chalard        if (null != ic) ic.endBatchEdit();
1510dafa7a8e15447544842975047f831883e67700c5Jean Chalard    }
1511dafa7a8e15447544842975047f831883e67700c5Jean Chalard
1512dafa7a8e15447544842975047f831883e67700c5Jean Chalard    // "ic" may be null without this crashing, but the behavior will be really strange
1513ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    private void handleCharacterWhileInBatchEdit(final int primaryCode,
1514dafa7a8e15447544842975047f831883e67700c5Jean Chalard            final int x, final int y, final int spaceState, final InputConnection ic) {
1515196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        boolean isComposingWord = mWordComposer.isComposingWord();
1516fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1517fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
1518fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                !mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) {
1519fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (isComposingWord) {
1520fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // Sanity check
1521fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                throw new RuntimeException("Should not be composing here");
1522fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            }
15237a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(Keyboard.CODE_SPACE);
1524fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1525fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1526d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // NOTE: isCursorTouchingWord() is a blocking IPC call, so it often takes several
1527d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
1528d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // thread here.
1529d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        if (!isComposingWord && (isAlphabet(primaryCode)
1530e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                || mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode))
1531d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard                && isSuggestionsRequested() && !isCursorTouchingWord()) {
1532736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Reset entirely the composing state anyway, then start composing a new word unless
1533736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // the character is a single quote. The idea here is, single quote is not a
1534736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // separator and it should be treated as a normal character, except in the first
1535736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // position where it should not start composing a word.
1536736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != primaryCode);
1537736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Here we don't need to reset the last composed word. It will be reset
1538736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // when we commit this one, if we ever do; if on the other hand we backspace
1539736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // it entirely and resume suggestions on the previous word, we'd like to still
1540736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // have touch coordinates for it.
1541736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            resetComposingState(false /* alsoResetLastComposedWord */);
1542736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            clearSuggestions();
1543923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
15447b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard        if (isComposingWord) {
1545ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            mWordComposer.add(
1546ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector());
1547923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (ic != null) {
15481c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani                // If it's the first letter, make note of auto-caps state
15499318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                if (mWordComposer.size() == 1) {
1550553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka                    mWordComposer.setAutoCapitalized(
1551553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka                            getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
15521c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani                }
155377d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1554923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1555d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
1556923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1557e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode,
1558e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                    spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
1559863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
15607a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(primaryCode);
1561e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1562e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            if (swapWeakSpace) {
1563e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                swapSwapperAndSpaceWhileInBatchEdit(ic);
1564e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                mSpaceState = SPACE_STATE_WEAK;
1565e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
15665262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // Some characters are not word separators, yet they don't start a new
15675262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // composing span. For these, we haven't changed the suggestion strip, and
15685262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // if the "add to dictionary" hint is shown, we should do so now. Examples of
15695262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // such characters include single quote, dollar, and others; the exact list is
15705262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // the list of characters for which we enter handleCharacterWhileInBatchEdit
15715262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // that don't match the test if ((isAlphabet...)) at the top of this method.
15725262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) {
15735262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard                mHandler.postUpdateBigramPredictions();
15745262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            }
1575e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1576e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        Utils.Stats.onNonSeparator((char)primaryCode, x, y);
1577923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1578923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1579c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    // Returns true if we did an autocorrection, false otherwise.
1580c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    private boolean handleSeparator(final int primaryCode, final int x, final int y,
1581120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int spaceState) {
158255b10796522b871c1e04d6f2254fdff5dc7aced4Ken Wakasa        // Should dismiss the "Touch again to save" message when handling separator
1583913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
1584cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            mHandler.cancelUpdateBigramPredictions();
1585d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
15866558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        }
15876558253160e2039c87f424bd814f402ecd31de3bKen Wakasa
1588c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1589923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Handle separator
15901b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
1591923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
1592923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.beginBatchEdit();
1593923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1594196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
1595923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // In certain languages where single quote is a separator, it's better
1596a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker            // not to auto correct, but accept the typed word. For instance,
1597923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // in Italian dov' should not be expanded to dove' because the elision
1598923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // requires the last vowel to be removed.
1599f50aa193377492e9c4afb3cc6e7f3448ab5a97a4Jean Chalard            final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
1600dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                    && !mInputAttributes.mInputTypeNoAutoCorrect;
160117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
1602f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard                commitCurrentAutoCorrection(primaryCode, ic);
1603c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = true;
1604923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
160566bb563535dbe3672f99f75bd71763a551444867Jean Chalard                commitTyped(ic, primaryCode);
1606923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1607923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
16084ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1609e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState,
1610e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
1611863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
161200ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
161300ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard                mSettingsValues.isPhantomSpacePromotingSymbol(primaryCode)) {
161400ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard            sendKeyCodePoint(Keyboard.CODE_SPACE);
161500ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        }
16167a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        sendKeyCodePoint(primaryCode);
161789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
161889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (Keyboard.CODE_SPACE == primaryCode) {
1619120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            if (isSuggestionsRequested()) {
1620120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                if (maybeDoubleSpaceWhileInBatchEdit(ic)) {
1621120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_DOUBLE;
1622120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                } else if (!isShowingPunctuationList()) {
1623120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_WEAK;
1624120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                }
1625126698fdd256a2e3734634d3b923cabd800064baJean Chalard            }
1626120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1627120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mHandler.startDoubleSpacesTimer();
162889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            if (!isCursorTouchingWord()) {
162989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                mHandler.cancelUpdateSuggestions();
1630cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                mHandler.postUpdateBigramPredictions();
163189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            }
163289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
1633fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (swapWeakSpace) {
1634120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                swapSwapperAndSpaceWhileInBatchEdit(ic);
16354721427c7de3600f6fe7dfff16508a6a974fb3e4Jean Chalard                mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
1636fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            } else if (SPACE_STATE_PHANTOM == spaceState) {
1637fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // If we are in phantom space state, and the user presses a separator, we want to
1638fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // stay in phantom space state so that the next keypress has a chance to add the
1639fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // space. For example, if I type "Good dat", pick "day" from the suggestion strip
1640fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // then insert a comma and go on to typing the next word, I want the space to be
1641fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // inserted automatically before the next word, the same way it is when I don't
1642fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // input the comma.
1643fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                mSpaceState = SPACE_STATE_PHANTOM;
1644120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1645120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
164689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Set punctuation right away. onUpdateSelection will fire but tests whether it is
164789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // already displayed or not, so it's okay.
164855b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            setPunctuationSuggestions();
1649923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1650120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1651406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)primaryCode, x, y);
1652120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1653923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
1654923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.endBatchEdit();
1655923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1656c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        return didAutoCorrect;
1657923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1658466741d8a78965b8509bf527344f289e50873092Mike LeBeau
165977d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    private CharSequence getTextWithUnderline(final CharSequence text) {
16600a59ac2ba88ac1f99151d9336136bc6fe7d416c0Jean Chalard        return mIsAutoCorrectionIndicatorOn
166177d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
1662fda847a870829f1491cbd5325f9c985213081149Jean Chalard                : text;
166377d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    }
166477d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard
1665923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void handleClose() {
166666bb563535dbe3672f99f75bd71763a551444867Jean Chalard        commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR);
1667923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        requestHideSelf(0);
1668c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
16691679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        if (inputView != null)
16701679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka            inputView.closing();
1671923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1672923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
16737a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isSuggestionsRequested() {
1674dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        return mInputAttributes.mIsSettingsSuggestionStripOn
1675c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka                && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
1676923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1677a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
16787a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingPunctuationList() {
1679cb8cb95d0afd340de9f1a0e15948f0068d2450bcKen Wakasa        if (mSuggestionsView == null) return false;
1680913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions();
16817599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
16827599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
16837a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingSuggestionsStrip() {
16847599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
16857599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
168638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka                        && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
16877599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
16887599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1689913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    public boolean isSuggestionsStripVisible() {
1690913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView == null)
1691b5e00d5841b946de8970875231456228ae0eb6b1Ken Wakasa            return false;
1692d0c5f9395a1b94e8425982e353d090f972dc44f0Jean Chalard        if (mSuggestionsView.isShowingAddToDictionaryHint())
16939fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
16949fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        if (!isShowingSuggestionsStrip())
16959fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return false;
1696dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn)
16979fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
16989fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        return isSuggestionsRequested();
1699923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1700923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1701409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void switchToKeyboardView() {
1702060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (DEBUG) {
1703060efb6d82af1f896d90cb845c8ae07c726f85e1satok            Log.d(TAG, "Switch to keyboard view.");
1704060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
17059bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
17069bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_switchToKeyboardView();
17079bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
1708c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        View v = mKeyboardSwitcher.getKeyboardView();
1709060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (v != null) {
1710060efb6d82af1f896d90cb845c8ae07c726f85e1satok            // Confirms that the keyboard view doesn't have parent view.
1711060efb6d82af1f896d90cb845c8ae07c726f85e1satok            ViewParent p = v.getParent();
1712060efb6d82af1f896d90cb845c8ae07c726f85e1satok            if (p != null && p instanceof ViewGroup) {
1713060efb6d82af1f896d90cb845c8ae07c726f85e1satok                ((ViewGroup) p).removeView(v);
17145a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            }
1715060efb6d82af1f896d90cb845c8ae07c726f85e1satok            setInputView(v);
1716060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
1717913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1718060efb6d82af1f896d90cb845c8ae07c726f85e1satok        updateInputViewShown();
1719060efb6d82af1f896d90cb845c8ae07c726f85e1satok        mHandler.postUpdateSuggestions();
1720466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1721466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1722409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void clearSuggestions() {
1723dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setSuggestions(SuggestedWords.EMPTY, false);
17247204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
1725466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1726466741d8a78965b8509bf527344f289e50873092Mike LeBeau
17272be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard    private void setSuggestions(final SuggestedWords words, final boolean isAutoCorrection) {
1728913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null) {
1729913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setSuggestions(words);
1730dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard            mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection);
1731466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
1732d02783cb63293507e8544ea60d07559092ce83d4Jean Chalard    }
1733ec780e2868962bf17f0dfd35d36895f543bde40asatok
173438e535e59676ac4d7bf27026fe3e16fcd9eb292eJean Chalard    private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) {
1735ec780e2868962bf17f0dfd35d36895f543bde40asatok        // Put a blue underline to a word in TextView which will be auto-corrected.
1736ec780e2868962bf17f0dfd35d36895f543bde40asatok        final InputConnection ic = getCurrentInputConnection();
17372be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard        if (ic == null) return;
17382be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard        if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
17392be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                && mWordComposer.isComposingWord()) {
17402be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
17412be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            final CharSequence textWithUnderline =
17422be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                    getTextWithUnderline(mWordComposer.getTypedWord());
17432be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            ic.setComposingText(textWithUnderline, 1);
1744ec780e2868962bf17f0dfd35d36895f543bde40asatok        }
1745466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1746466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1747409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void updateSuggestions() {
1748923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if we have a suggestion engine attached.
1749911b8f9d19c1c4903eeef29b43176cfeaa0e5d0cKen Wakasa        if ((mSuggest == null || !isSuggestionsRequested())) {
1750edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            if (mWordComposer.isComposingWord()) {
1751edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                Log.w(TAG, "Called updateSuggestions but suggestions were not requested!");
1752edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
1753edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            }
1754923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1755923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1756466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1757cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateSuggestions();
1758cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateBigramPredictions();
1759cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard
1760196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) {
1761ca26f20fa4903de46e374babbfba8c8a1a5cac93satok            setPunctuationSuggestions();
1762923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1763923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1764979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
17659f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        // TODO: May need a better way of retrieving previous word
176640f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        final InputConnection ic = getCurrentInputConnection();
176740f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        final CharSequence prevWord;
176840f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        if (null == ic) {
176940f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            prevWord = null;
177040f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        } else {
177140f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
177240f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        }
1773dc5dfe37e06eb1d550d7aa5156cff226334d4e1eJean Chalard
1774dc5dfe37e06eb1d550d7aa5156cff226334d4e1eJean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
17752be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard        // getSuggestedWords handles gracefully a null value of prevWord
17765b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard        final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
17773708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka                prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode);
1778923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1779a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // Basically, we update the suggestion strip only when suggestion count > 1.  However,
1780a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // there is an exception: We update the suggestion strip whenever typed word's length
1781f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // is 1 or typed word is found in dictionary, regardless of suggestion count.  Actually,
1782f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
1783f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // need to clear the previous state when the user starts typing a word (i.e. typed word's
1784f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // length == 1).
17855b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard        if (suggestedWords.size() > 1 || typedWord.length() == 1
17865b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard                || !suggestedWords.mAllowsToBeAutoCorrected
1787838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard                || mSuggestionsView.isShowingAddToDictionaryHint()) {
17885b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard            showSuggestions(suggestedWords, typedWord);
1789838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard        } else {
1790838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard            SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
1791838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard            if (previousSuggestions == mSettingsValues.mSuggestPuncList) {
1792838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard                previousSuggestions = SuggestedWords.EMPTY;
1793fe1a6d961cf039357f061482461e4d2e951ad346satok            }
17944ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalard            final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
179588bf1ba5263f5a5c1df367ddc401db4109ef8677Jean Chalard                    SuggestedWords.getTypedWordAndPreviousSuggestions(
17964ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalard                            typedWord, previousSuggestions);
17977d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard            final SuggestedWords obsoleteSuggestedWords =
1798bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard                    new SuggestedWords(typedWordAndPreviousSuggestions,
17992e2519ee914d4bf9462950553840557a4c19faedJean Chalard                            false /* typedWordValid */,
1800bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard                            false /* hasAutoCorrectionCandidate */,
1801b5eeb724fc98bb7169683539027d9ba54ffb8b14Jean Chalard                            false /* allowsToBeAutoCorrected */,
180203a35170751a635332c00bf6c272a0127a255cf6Jean Chalard                            false /* isPunctuationSuggestions */,
18030142b997bf18f5d07e83b3fd403f0b3ea4736040satok                            true /* isObsoleteSuggestions */,
18040142b997bf18f5d07e83b3fd403f0b3ea4736040satok                            false /* isPrediction */);
18057d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard            showSuggestions(obsoleteSuggestedWords, typedWord);
18069fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        }
1807979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
18084a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
1809d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard    public void showSuggestions(final SuggestedWords suggestedWords, final CharSequence typedWord) {
1810d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard        final CharSequence autoCorrection;
18117e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        if (suggestedWords.size() > 0) {
181268823ae08e820f0951447ed12c1bd32a24333d2eJean Chalard            if (suggestedWords.hasAutoCorrectionWord()) {
1813d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard                autoCorrection = suggestedWords.getWord(1);
1814923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1815d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard                autoCorrection = typedWord;
1816923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1817923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1818d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard            autoCorrection = null;
1819923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1820d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard        mWordComposer.setAutoCorrection(autoCorrection);
18219b01890254c62a30b079bd9f79a30f9541faf11bJean Chalard        final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
1822dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setSuggestions(suggestedWords, isAutoCorrection);
1823dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setAutoCorrectionIndicator(isAutoCorrection);
1824913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1825923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1826923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1827f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard    private void commitCurrentAutoCorrection(final int separatorCodePoint,
1828f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard            final InputConnection ic) {
1829913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // Complete any pending suggestions query first
1830d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        if (mHandler.hasPendingUpdateSuggestions()) {
1831d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.cancelUpdateSuggestions();
1832923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            updateSuggestions();
1833923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1834117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
1835117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        if (autoCorrection != null) {
1836117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            final String typedWord = mWordComposer.getTypedWord();
183746798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            if (TextUtils.isEmpty(typedWord)) {
183846798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                throw new RuntimeException("We have an auto-correction but the typed word "
183946798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                        + "is empty? Impossible! I must commit suicide.");
184046798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            }
1841f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard            Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint);
184260adb8757496fecb8f376a80832c176b35e43d06Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1843d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord,
184460adb8757496fecb8f376a80832c176b35e43d06Kurt Partridge                        autoCorrection.toString());
184560adb8757496fecb8f376a80832c176b35e43d06Kurt Partridge            }
18464733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
184766bb563535dbe3672f99f75bd71763a551444867Jean Chalard            commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
184866bb563535dbe3672f99f75bd71763a551444867Jean Chalard                    separatorCodePoint);
1849f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard            if (!typedWord.equals(autoCorrection) && null != ic) {
18501c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // This will make the correction flash for a short while as a visual clue
18511c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // to the user that auto-correction happened.
185296fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasa                ic.commitCorrection(new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
185396fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasa                        typedWord, autoCorrection));
18541c6cf26c3705e845418a29718c034598b52293ccJean Chalard            }
1855923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1856923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1857923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1858c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
18599bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge    public void pickSuggestionManually(final int index, final CharSequence suggestion,
18609bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge            int x, int y) {
186196fb3094aaacf4bae65db54414a7aac285695245Jean Chalard        final InputConnection ic = getCurrentInputConnection();
1862d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard        if (null != ic) ic.beginBatchEdit();
1863d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard        pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y, ic);
1864d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard        if (null != ic) ic.endBatchEdit();
1865d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard    }
18664f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
1867d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard    public void pickSuggestionManuallyWhileInBatchEdit(final int index,
1868d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard        final CharSequence suggestion, final int x, final int y, final InputConnection ic) {
1869d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard        final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
1870551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
1871551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        if (suggestion.length() == 1 && isShowingPunctuationList()) {
1872551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Word separators are suggested before the user inputs something.
1873551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // So, LatinImeLogger logs "" as a user's input.
1874551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords);
1875551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
1876551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            if (ProductionFlag.IS_EXPERIMENTAL) {
1877551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard                ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y);
1878551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            }
1879551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            final int primaryCode = suggestion.charAt(0);
1880551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            onCodeInput(primaryCode,
1881551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard                    KeyboardActionListener.SUGGESTION_STRIP_COORDINATE,
1882551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard                    KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
1883551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            return;
1884551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        }
1885551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard
1886845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard        if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0) {
1887845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            int firstChar = Character.codePointAt(suggestion, 0);
1888845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            if ((!mSettingsValues.isWeakSpaceStripper(firstChar))
1889845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard                    && (!mSettingsValues.isWeakSpaceSwapper(firstChar))) {
1890845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard                sendKeyCodePoint(Keyboard.CODE_SPACE);
1891845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            }
1892845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard        }
1893845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard
1894dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn
1895dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && mApplicationSpecifiedCompletions != null
18961b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
1897913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsView != null) {
1898913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsView.clear();
1899923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1900b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1901611a5bdf650f7bc54bf40176f84f1cd9c87aa9aaJean Chalard            resetComposingState(true /* alsoResetLastComposedWord */);
19029a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            if (ic != null) {
1903e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
1904e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                ic.commitCompletion(completionInfo);
19059bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
19069bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge                    ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index,
19079bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge                            completionInfo.getText(), x, y);
19089bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge                }
19099a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            }
1910923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1911923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
19126a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
1913af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // We need to log before we commit, because the word composer will store away the user
1914af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // typed word.
19159bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        final String replacedWord = mWordComposer.getTypedWord().toString();
19169bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        LatinImeLogger.logOnManualSuggestion(replacedWord,
19178cc8f26adfe0f06cebc697dac43a856326cf7afcTadashi G. Takaoka                suggestion.toString(), index, suggestedWords);
19189bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
19199bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge            ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y);
19209bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        }
19214733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = true;
192266bb563535dbe3672f99f75bd71763a551444867Jean Chalard        commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
192366bb563535dbe3672f99f75bd71763a551444867Jean Chalard                LastComposedWord.NOT_A_SEPARATOR);
192429a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        // Don't allow cancellation of manual pick
192529a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        mLastComposedWord.deactivate();
1926fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mSpaceState = SPACE_STATE_PHANTOM;
1927fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        // TODO: is this necessary?
1928fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mKeyboardSwitcher.updateShiftState();
1929979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1930c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // We should show the "Touch again to save" hint if the user pressed the first entry
1931c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // AND either:
19327f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is no dictionary (we know that because we tried to load it => null != mSuggest
1933c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        //   AND mSuggest.hasMainDictionary() is false)
19347f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is a dictionary and the word is not in it
19357f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // Please note that if mSuggest is null, it means that everything is off: suggestion
19367f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // and correction, so we shouldn't try to show the hint
19377f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // We used to look at mCorrectionMode here, but showing the hint should have nothing
19387f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // to do with the autocorrection setting.
1939bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
1940bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                // If there is no dictionary the hint should be shown.
1941c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa                && (!mSuggest.hasMainDictionary()
1942bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        // If "suggestion" is not in the dictionary, the hint should be shown.
1943bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        || !AutoCorrection.isValidWord(
1944bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                                mSuggest.getUnigramDictionaries(), suggestion, true));
1945b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
1946406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE,
1947406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard                WordComposer.NOT_A_COORDINATE);
1948777118a40a363ccab69a00016d3156066513cb78Jean Chalard        if (!showingAddToDictionaryHint) {
1949364da8c618303a7764595d2c15ee034a7671365dKen Wakasa            // If we're not showing the "Touch again to save", then show corrections again.
1950979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // In case the cursor position doesn't change, make sure we show the suggestions again.
195141ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            updateBigramPredictions();
195241ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // Updating the predictions right away may be slow and feel unresponsive on slower
195341ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // terminals. On the other hand if we just postUpdateBigramPredictions() it will
195441ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // take a noticeable delay to update them which may feel uneasy.
195595a6e58ebcd8f44b970a8238cf30e48b10fc4b61Jean Chalard        } else {
195688562bec54658840dcce352127bdc15705c20a89Jean Chalard            if (mIsUserDictionaryAvailable) {
1957644d33d60ea5a87501274488013d65f55238895eKen Wakasa                mSuggestionsView.showAddToDictionaryHint(
1958644d33d60ea5a87501274488013d65f55238895eKen Wakasa                        suggestion, mSettingsValues.mHintToSaveText);
1959ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            } else {
1960ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka                mHandler.postUpdateSuggestions();
1961ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            }
196266a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani        }
1963923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1964a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1965979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
19668558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa     * Commits the chosen word to the text field and saves it for later retrieval.
1967979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
1968c54d558e2e70bdfb2c1078cae7b88440d421dc67satok    private void commitChosenWord(final CharSequence chosenWord, final int commitType,
196966bb563535dbe3672f99f75bd71763a551444867Jean Chalard            final int separatorCode) {
19709351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
1971923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
19721531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            if (mSettingsValues.mEnableSuggestionSpanInsertion) {
19731531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
19741531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
1975c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                        this, chosenWord, suggestedWords, mIsMainDictionaryAvailable),
1976cf6b2099be5deda896ba129a835a06867be2293fsatok                        1);
1977d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1978c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    ResearchLogger.latinIME_commitText(chosenWord);
1979d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
19801531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            } else {
1981c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                ic.commitText(chosenWord, 1);
1982d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1983c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    ResearchLogger.latinIME_commitText(chosenWord);
1984d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
19851531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            }
1986923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1987c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        // Add the word to the user history dictionary
1988c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
19890fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // TODO: figure out here if this is an auto-correct or if the best word is actually
19900fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // what user typed. Note: currently this is done much later in
1991bdf89ce5feedb03e67b43f530b1eb9bd44203c63Jean Chalard        // LastComposedWord#didCommitTypedWord by string equality of the remembered
19920fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // strings.
1993c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord.toString(),
1994c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                separatorCode, prevWord);
1995923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1996923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
199741ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard    public void updateBigramPredictions() {
199889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (mSuggest == null || !isSuggestionsRequested())
199989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            return;
200089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
200117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (!mSettingsValues.mBigramPredictionEnabled) {
2002cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            setPunctuationSuggestions();
2003cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            return;
2004cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        }
2005cb3320179d39a7983874697a0aa428b127675c9dJean Chalard
20068e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard        final SuggestedWords suggestedWords;
2007a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard        if (mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
2008a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard            final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
2009a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard                    mSettingsValues.mWordSeparators);
20100cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard            if (!TextUtils.isEmpty(prevWord)) {
20115b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard                suggestedWords = mSuggest.getBigramPredictions(prevWord);
20120cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard            } else {
20138e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard                suggestedWords = null;
20140cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard            }
2015a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard        } else {
20168e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard            suggestedWords = null;
2017a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard        }
201889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
20198e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard        if (null != suggestedWords && suggestedWords.size() > 0) {
202089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Explicitly supply an empty typed word (the no-second-arg version of
202189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // showSuggestions will retrieve the word near the cursor, we don't want that here)
20228e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard            showSuggestions(suggestedWords, "");
202389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
202489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            if (!isShowingPunctuationList()) setPunctuationSuggestions();
202589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
202689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    }
202789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
20287a8dac55278cedd838be325f56b4c52d973c61f5satok    public void setPunctuationSuggestions() {
2029dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setSuggestions(mSettingsValues.mSuggestPuncList, false);
20307204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
2031913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
20326a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani    }
20336a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
2034c54d558e2e70bdfb2c1078cae7b88440d421dc67satok    private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) {
2035c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (TextUtils.isEmpty(suggestion)) return null;
2036bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
20370c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
20380c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // adding words in situations where the user or application really didn't
20390c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // want corrections enabled or learned.
2040979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (!(mCorrectionMode == Suggest.CORRECTION_FULL
2041979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
2042c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            return null;
2043979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
2044bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
20459ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard        if (mUserHistoryDictionary != null) {
204640f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            final InputConnection ic = getCurrentInputConnection();
20479ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard            final CharSequence prevWord;
204840f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            if (null != ic) {
20499ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard                prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
20509ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard            } else {
20519ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard                prevWord = null;
2052979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
205371f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            final String secondWord;
205471f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
20556a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka                secondWord = suggestion.toString().toLowerCase(
20566a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka                        mSubtypeSwitcher.getCurrentSubtypeLocale());
205771f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            } else {
205871f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard                secondWord = suggestion.toString();
205971f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            }
20609ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard            mUserHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
206171f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard                    secondWord);
2062c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            return prevWord;
206332e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani        }
2064c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        return null;
206532e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani    }
206632e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani
20677a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isCursorTouchingWord() {
20689351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
2069923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic == null) return false;
20708667bbeab3321e05f518770f7360a3fef52b4407Jean Chalard        CharSequence before = ic.getTextBeforeCursor(1, 0);
20718667bbeab3321e05f518770f7360a3fef52b4407Jean Chalard        CharSequence after = ic.getTextAfterCursor(1, 0);
20726ec1209a33fe2dc151b86d3f662e22e564e2f4f8Jean Chalard        if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0))
20736ec1209a33fe2dc151b86d3f662e22e564e2f4f8Jean Chalard                && !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
2074923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
2075923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
20766ec1209a33fe2dc151b86d3f662e22e564e2f4f8Jean Chalard        if (!TextUtils.isEmpty(after) && !mSettingsValues.isWordSeparator(after.charAt(0))
20776ec1209a33fe2dc151b86d3f662e22e564e2f4f8Jean Chalard                && !mSettingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) {
2078923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
2079923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2080923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return false;
2081923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2082a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2083120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
20848ff0564f6a7c78e2fda517ea49b2ac2fa96d0f8eJean Chalard    private static boolean sameAsTextBeforeCursor(final InputConnection ic,
20858ff0564f6a7c78e2fda517ea49b2ac2fa96d0f8eJean Chalard            final CharSequence text) {
20868ff0564f6a7c78e2fda517ea49b2ac2fa96d0f8eJean Chalard        final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
2087dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        return TextUtils.equals(text, beforeText);
2088dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    }
2089dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
2090120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
20916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
20926b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
20936b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * word, else do nothing.
20946b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
20956b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(
20966b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final InputConnection ic) {
20976b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if the cursor is not at the end of a word (cursor must be preceded by
20986b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // non-whitespace, non-separator, non-start-of-text)
20996b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
21006b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0);
21016b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (TextUtils.isEmpty(textBeforeCursor)
21026b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return;
21036b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21046b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
21056b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // separator or end of line/text)
21066b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Example: "test|"<EOL> "te|st" get rejected here
21076b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0);
21086b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (!TextUtils.isEmpty(textAfterCursor)
21096b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return;
21106b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21116b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
2112fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // Example: " -|" gets rejected here but "e-|" and "e|" are okay
2113fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators);
2114fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // We don't suggest on leading single quotes, so we have to remove them from the word if
2115fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // it starts with single quotes.
2116fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) {
2117fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard            word = word.subSequence(1, word.length());
2118fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        }
21196b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (TextUtils.isEmpty(word)) return;
2120fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        final char firstChar = word.charAt(0); // we just tested that word is not empty
2121fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        if (word.length() == 1 && !Character.isLetter(firstChar)) return;
2122fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard
2123fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // We only suggest on words that start with a letter or a symbol that is excluded from
2124fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // word separators (see #handleCharacterWhileInBatchEdit).
2125fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        if (!(isAlphabet(firstChar)
2126fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard                || mSettingsValues.isSymbolExcludedFromWordSeparators(firstChar))) {
2127fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard            return;
2128fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        }
21296b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21306b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Okay, we are at the end of a word. Restart suggestions.
21316b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        restartSuggestionsOnWordBeforeCursor(ic, word);
21326b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
21336b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21346b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    // "ic" must not be null
21356b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
21366b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final CharSequence word) {
21373708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
2138d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int length = word.length();
2139d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        ic.deleteSurroundingText(length, 0);
2140d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2141d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(length);
2142d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
21436b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        ic.setComposingText(word, 1);
21446b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mHandler.postUpdateSuggestions();
21456b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
21466b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21476b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    // "ic" must not be null
21485935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard    private void revertCommit(final InputConnection ic) {
2149c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        final CharSequence previousWord = mLastComposedWord.mPrevWord;
2150b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        final String originallyTypedWord = mLastComposedWord.mTypedWord;
2151cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final CharSequence committedWord = mLastComposedWord.mCommittedWord;
2152cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final int cancelLength = committedWord.length();
2153ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        final int separatorLength = LastComposedWord.getSeparatorLength(
2154193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                mLastComposedWord.mSeparatorCode);
2155193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        // TODO: should we check our saved separator against the actual contents of the text view?
2156d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int deleteLength = cancelLength + separatorLength;
2157890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2158b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            if (mWordComposer.isComposingWord()) {
21595935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit, but we are composing a word");
2160b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            }
2161890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            final String wordBeforeCursor =
2162d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ic.getTextBeforeCursor(deleteLength, 0)
2163193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                            .subSequence(0, cancelLength).toString();
2164cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard            if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
21655935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit check failed: we thought we were "
2166cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard                        + "reverting \"" + committedWord
2167890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
2168890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
21698558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        }
2170d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        ic.deleteSurroundingText(deleteLength, 0);
2171d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2172d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(deleteLength);
2173d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
2174c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
2175c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            mUserHistoryDictionary.cancelAddingUserHistory(
2176c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    previousWord.toString(), committedWord.toString());
2177c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        }
2178193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) {
2179193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // This is the case when we cancel a manual pick.
2180193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // We should restart suggestion on the word right away.
218132f0af1fc48f67907f0e731e18359f29e2b1df14Jean Chalard            mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
218232f0af1fc48f67907f0e731e18359f29e2b1df14Jean Chalard            ic.setComposingText(originallyTypedWord, 1);
2183193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        } else {
2184193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            ic.commitText(originallyTypedWord, 1);
2185193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // Re-insert the separator
2186193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
2187193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
2188193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                    WordComposer.NOT_A_COORDINATE);
2189d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
2190d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_revertCommit(originallyTypedWord);
2191d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
2192193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // Don't restart suggestion yet. We'll restart if the user deletes the
2193193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // separator.
2194193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        }
2195b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
2196890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.cancelUpdateBigramPredictions();
2197890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.postUpdateSuggestions();
2198890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    }
2199890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard
2200890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    // "ic" must not be null
22012124bc5bf5af31cf3d2789b70ebd2f24c815f5f4Jean Chalard    private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
22024733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mHandler.cancelDoubleSpacesTimer();
22034733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // Here we test whether we indeed have a period and a space before us. This should not
22044733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // be needed, but it's there just in case something went wrong.
22054733609947c0ec74e460bd714fffca0518ade93aJean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
22068ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        if (!". ".equals(textBeforeCursor)) {
220751fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // Theoretically we should not be coming here if there isn't ". " before the
220851fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // cursor, but the application may be changing the text while we are typing, so
220951fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // anything goes. We should not crash.
221051fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            Log.d(TAG, "Tried to revert double-space combo but we didn't find "
22118ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "\". \" just before the cursor.");
221251fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            return false;
22138ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
22144733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.deleteSurroundingText(2, 0);
2215d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2216d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(2);
2217d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
22184733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.commitText("  ", 1);
2219d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2220d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit();
2221d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
22224733609947c0ec74e460bd714fffca0518ade93aJean Chalard        return true;
22234733609947c0ec74e460bd714fffca0518ade93aJean Chalard    }
22244733609947c0ec74e460bd714fffca0518ade93aJean Chalard
22258fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean revertSwapPunctuation(final InputConnection ic) {
2226120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // Here we test whether we indeed have a space and something else before us. This should not
2227120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // be needed, but it's there just in case something went wrong.
2228120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
2229120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
2230120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // enter surrogate pairs this code will have been removed.
22318be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard        if (TextUtils.isEmpty(textBeforeCursor)
22328be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard                || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
22338be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // We may only come here if the application is changing the text while we are typing.
22348be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // This is quite a broken case, but not logically impossible, so we shouldn't crash,
22358be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // but some debugging log may be in order.
22368be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
22378ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "find a space just before the cursor.");
22388be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            return false;
22398ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
2240120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.beginBatchEdit();
2241120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.deleteSurroundingText(2, 0);
2242d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2243d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(2);
2244d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
2245120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
2246d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2247d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_revertSwapPunctuation();
2248d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
2249120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.endBatchEdit();
2250120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return true;
2251120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    }
2252120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
2253923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean isWordSeparator(int code) {
225417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        return mSettingsValues.isWordSeparator(code);
2255923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2256923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2257923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean preferCapitalization() {
22589318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa        return mWordComposer.isFirstCharCapitalized();
2259923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2260923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
226188fc9d44186120f9edc5cf7ec0e2af85260fed04satok    // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
2262c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    // according to new language or mode.
2263c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    public void onRefreshKeyboard() {
22641e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // When the device locale is changed in SetupWizard etc., this method may get called via
22651e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // onConfigurationChanged before SoftInputWindow is shown.
22661e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        if (mKeyboardSwitcher.getKeyboardView() != null) {
22671e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            // Reload keyboard because the current language has been changed.
22681e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
22691e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        }
22700ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        initSuggest();
22713fe263fac6375836c48ce71da29f098b66eb4f11Jean Chalard        updateCorrectionMode();
227217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
2273b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // Since we just changed languages, we should re-evaluate suggestions with whatever word
2274b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // we are currently composing. If we are not composing anything, we may want to display
2275b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // predictions or punctuation signs (which is done by updateBigramPredictions anyway).
2276b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        if (isCursorTouchingWord()) {
2277b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard            mHandler.postUpdateSuggestions();
2278b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        } else {
2279b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard            mHandler.postUpdateBigramPredictions();
2280b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        }
228136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
228236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
228378e3977e90a9946a057dfb628f99683e386015bdTadashi G. Takaoka    // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
228478e3977e90a9946a057dfb628f99683e386015bdTadashi G. Takaoka    // {@link KeyboardSwitcher}.
2285564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    public void hapticAndAudioFeedback(final int primaryCode) {
2286544c3c29527927239a6484efc30bc22f9cc4dad1Jean Chalard        mFeedbackManager.hapticAndAudioFeedback(primaryCode, mKeyboardSwitcher.getKeyboardView());
2287d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka    }
2288d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka
22895a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
22902a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    public void onPressKey(int primaryCode) {
2291a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        mKeyboardSwitcher.onPressKey(primaryCode);
2292923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2293923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
22945a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
22952a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    public void onReleaseKey(int primaryCode, boolean withSliding) {
22962a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
22978d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv
22988d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        // If accessibility is on, ensure the user receives keyboard state updates.
22998d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
23008d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            switch (primaryCode) {
23018d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            case Keyboard.CODE_SHIFT:
23028d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifyShiftState();
23038d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
23048d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
23058d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifySymbolsState();
23068d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
23078d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            }
23088d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        }
2309978c96aa995015658070346b60826a3a34fdaf84Jean Chalard
2310978c96aa995015658070346b60826a3a34fdaf84Jean Chalard        if (Keyboard.CODE_DELETE == primaryCode) {
2311978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // This is a stopgap solution to avoid leaving a high surrogate alone in a text view.
2312978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // In the future, we need to deprecate deteleSurroundingText() and have a surrogate
2313978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // pair-friendly way of deleting characters in InputConnection.
2314978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            final InputConnection ic = getCurrentInputConnection();
2315978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            if (null != ic) {
2316978c96aa995015658070346b60826a3a34fdaf84Jean Chalard                final CharSequence lastChar = ic.getTextBeforeCursor(1, 0);
23176c0349012eb0edad56b6b89defebb922bbddbb34Jean Chalard                if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
2318978c96aa995015658070346b60826a3a34fdaf84Jean Chalard                    ic.deleteSurroundingText(1, 0);
2319978c96aa995015658070346b60826a3a34fdaf84Jean Chalard                }
2320978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            }
2321978c96aa995015658070346b60826a3a34fdaf84Jean Chalard        }
2322923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2323a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2324123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka    // receive ringer mode change and network state change.
2325923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
2326923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
2327923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void onReceive(Context context, Intent intent) {
2328123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            final String action = intent.getAction();
2329564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
2330123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                mSubtypeSwitcher.onNetworkStateChanged(intent);
233121af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard            } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
233221af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard                mFeedbackManager.onRingerModeChanged();
2333123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            }
2334923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2335923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    };
2336923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2337e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    private void updateCorrectionMode() {
23389f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        // TODO: cleanup messy flags
2339f50aa193377492e9c4afb3cc6e7f3448ab5a97a4Jean Chalard        final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
2340dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && !mInputAttributes.mInputTypeNoAutoCorrect;
2341283b0c2b642030539ba3e41f3f54b6aed6bb9e2dJean Chalard        mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE;
2342283b0c2b642030539ba3e41f3f54b6aed6bb9e2dJean Chalard        mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect)
2343979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
2344e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    }
2345e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani
23462ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka    private void updateSuggestionVisibility(final Resources res) {
23470fe3611bee5095e7bd0fff2d0fdf8d5a13379132Jean Chalard        final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting;
23487599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
2349458249e703bded3a1cbd25a2ab2249f9366a8188Ken Wakasa            if (suggestionVisiblityStr.equals(res.getString(visibility))) {
23507599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                mSuggestionVisibility = visibility;
23517599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                break;
23527599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            }
23537599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        }
23547599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
23557599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
2356c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka    private void launchSettings() {
2357c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka        launchSettingsClass(SettingsActivity.class);
2358466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2359466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2360bf96661d33d0126adb60a48880ceba1ff055d4a4satok    public void launchDebugSettings() {
2361c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka        launchSettingsClass(DebugSettingsActivity.class);
2362bf96661d33d0126adb60a48880ceba1ff055d4a4satok    }
2363bf96661d33d0126adb60a48880ceba1ff055d4a4satok
2364c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka    private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
2365923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        handleClose();
2366923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        Intent intent = new Intent();
2367466741d8a78965b8509bf527344f289e50873092Mike LeBeau        intent.setClass(LatinIME.this, settingsClass);
2368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        startActivity(intent);
2370923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2371923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
23722fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showSubtypeSelectorAndSettings() {
237385996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
237485996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
237585996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                // TODO: Should use new string "Select active input modes".
237685996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.language_selection_title),
237785996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.english_ime_settings),
237885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
237955d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka        final Context context = this;
238085996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
23812fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            @Override
23822fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            public void onClick(DialogInterface di, int position) {
23832fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                di.dismiss();
23842fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                switch (position) {
238585996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
23862cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok                    Intent intent = CompatUtils.getInputLanguageSelectionIntent(
238755d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka                            ImfUtils.getInputMethodIdOfThisIme(context),
23882a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Intent.FLAG_ACTIVITY_NEW_TASK
23892fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
23902fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_CLEAR_TOP);
23912fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    startActivity(intent);
23922fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2393aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                case 1:
2394aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    launchSettings();
2395aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    break;
23962fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                }
23972fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            }
239885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2399bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2400bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2401bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2402bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        showOptionDialogInternal(builder.create());
24032fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    }
2404923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
240513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    private void showOptionDialogInternal(AlertDialog dialog) {
24066a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
240713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        if (windowToken == null) return;
240813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
240913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCancelable(true);
241013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCanceledOnTouchOutside(true);
241113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
241213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final Window window = dialog.getWindow();
241313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final WindowManager.LayoutParams lp = window.getAttributes();
241413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.token = windowToken;
241513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
241613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.setAttributes(lp);
241713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
241813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
241913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        mOptionsDialog = dialog;
242013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.show();
242113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    }
242213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
24237e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
24247e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2425923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.dump(fd, fout, args);
2426a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2427923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        final Printer p = new PrintWriterPrinter(fout);
2428923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("LatinIME state :");
24293708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
2430df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
2431df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        p.println("  Keyboard mode = " + keyboardMode);
2432dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        p.println("  mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn);
2433923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("  mCorrectionMode=" + mCorrectionMode);
24344d0f03bd66742ee292da81a3e025e119f28b6940Jean Chalard        p.println("  isComposingWord=" + mWordComposer.isComposingWord());
243517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
243617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mSoundOn=" + mSettingsValues.mSoundOn);
243717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mVibrateOn=" + mSettingsValues.mVibrateOn);
2438240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard        p.println("  mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
24396fa003ee234c6e2ca6cd9ec555221ac5c71a5405Jean Chalard        p.println("  mInputAttributes=" + mInputAttributes.toString());
2440923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2441923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
2442