LatinIME.java revision 243e370fcb1c2cb8608614206075ecdbe79a6372
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;
484e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalardimport android.view.KeyCharacterMap;
494e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalardimport android.view.KeyEvent;
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View;
518e09172df1bb176cc899940862c56bed9b9aec4esatokimport android.view.ViewGroup;
52c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaokaimport android.view.ViewGroup.LayoutParams;
53cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaokaimport android.view.ViewParent;
5413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokaimport android.view.Window;
5513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokaimport android.view.WindowManager;
56923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.CompletionInfo;
5796fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasaimport android.view.inputmethod.CorrectionInfo;
58923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.EditorInfo;
599cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
60c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka
615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.accessibility.AccessibilityUtils;
628d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanvimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
63c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.CompatUtils;
64c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
651fef530ec7626fa16777f52b48191e61db8f46d4satokimport com.android.inputmethod.compat.SuggestionSpanUtils;
66c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
67c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardActionListener;
686b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardId;
69c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardSwitcher;
70f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView;
71c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokaimport com.android.inputmethod.keyboard.LatinKeyboardView;
7216c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaokaimport com.android.inputmethod.latin.LocaleUtils.RunInLocale;
73c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
748c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.suggestions.SuggestionsView;
75923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
76466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.FileDescriptor;
77466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.PrintWriter;
784ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalardimport java.util.ArrayList;
79466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.util.Locale;
80466741d8a78965b8509bf527344f289e50873092Mike LeBeau
81923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
82923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Input method implementation for Qwerty'ish keyboard.
83923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
8413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaokapublic class LatinIME extends InputMethodService implements KeyboardActionListener,
85b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard        SuggestionsView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener {
868efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    private static final String TAG = LatinIME.class.getSimpleName();
87409220583333bdf06290dd9fd42f91b5c0d1b11asatok    private static final boolean TRACE = false;
889e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka    private static boolean DEBUG;
89a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
909e347d3d448e48229c46aad394ec9bd60cd5807bsatok    private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
91fd0bd57deb53c4cce32810a61133fa44b45dbb7bKen Wakasa
92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // How many continuous deletes at which to start deleting at a higher speed.
93923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int DELETE_ACCELERATE_AT = 20;
94923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // Key events coming any faster than this are long-presses.
95d67fe0e7583f1be18b35b33b7658e4427f1e3dedAmith Yamasani    private static final int QUICK_PRESS = 200;
96a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
9759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private static final int PENDING_IMS_CALLBACK_DURATION = 800;
98055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
99cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /**
100cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * The name of the scheme used by the Package Manager to warn of a new package installation,
101cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * replacement or removal.
102cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     */
103cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private static final String SCHEME_PACKAGE = "package";
104cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
105fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_NONE = 0;
106120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Double space: the state where the user pressed space twice quickly, which LatinIME
107120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // resolved as period-space. Undoing this converts the period to a space.
108fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_DOUBLE = 1;
109b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
110b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // have just been swapped. Undoing this swaps them back; the space is still considered weak.
111120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
112fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
113fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // spaces happen when the user presses space, accepting the current suggestion (whether
114fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // it's an auto-correction or not).
115fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_WEAK = 3;
116fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom space: a not-yet-inserted space that should get inserted on the next input,
117fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // character provided it's not a separator. If it's a separator, the phantom space is dropped.
118fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom spaces happen when a user chooses a word from the suggestion strip.
119fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_PHANTOM = 4;
120120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
121120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Current space state of the input method. This can be any of the above constants.
122120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private int mSpaceState;
123126698fdd256a2e3734634d3b923cabd800064baJean Chalard
124297e6d590bd957577c335aa8713a786145a70288Jean Chalard    private SettingsValues mCurrentSettings;
12580b66bb166f7f45adfcadcb84788477df9930828Jean Chalard    private InputAttributes mInputAttributes;
12617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
127d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka    private View mExtractArea;
128abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka    private View mKeyPreviewBackingView;
129913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private View mSuggestionsContainer;
130913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private SuggestionsView mSuggestionsView;
131816a8a0fd85ca0327436f8bd1cfa6928600ebc5dJean Chalard    /* package for tests */ Suggest mSuggest;
1321b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private CompletionInfo[] mApplicationSpecifiedCompletions;
133b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    private ApplicationInfo mTargetApplicationInfo;
134a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
135610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    private InputMethodManagerCompatWrapper mImm;
1362fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private Resources mResources;
1372fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private SharedPreferences mPrefs;
138109728193e45262099cbf88d8d6fcc4ed05240caJean Chalard    /* package for tests */ final KeyboardSwitcher mKeyboardSwitcher;
13989ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    private final SubtypeSwitcher mSubtypeSwitcher;
14081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    private boolean mShouldSwitchToLastSubtype = true;
141a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1426a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka    private boolean mIsMainDictionaryAvailable;
14367fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard    private UserBinaryDictionary mUserDictionary;
1449ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard    private UserHistoryDictionary mUserHistoryDictionary;
14588562bec54658840dcce352127bdc15705c20a89Jean Chalard    private boolean mIsUserDictionaryAvailable;
14636fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
1472692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
1489318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa    private WordComposer mWordComposer = new WordComposer();
1495475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private RichInputConnection mConnection = new RichInputConnection();
150409220583333bdf06290dd9fd42f91b5c0d1b11asatok
151979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // Keep track of the last selection range to decide if we need to show word alternatives
15277da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private static final int NOT_A_CURSOR_POSITION = -1;
15377da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionStart = NOT_A_CURSOR_POSITION;
15477da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionEnd = NOT_A_CURSOR_POSITION;
155979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1564733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
1574733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // "expect" it, it means the user actually moved the cursor.
1584733609947c0ec74e460bd714fffca0518ade93aJean Chalard    private boolean mExpectingUpdateSelection;
159923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mDeleteCount;
160923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private long mLastKeyTime;
161a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
162564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    private AudioAndHapticFeedbackManager mFeedbackManager;
16328f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
16438f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    // Member variables for remembering the current device orientation.
16538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    private int mDisplayOrientation;
16638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
167cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    // Object for reacting to adding/removing a dictionary pack.
168cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private BroadcastReceiver mDictionaryPackInstallReceiver =
169cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            new DictionaryPackInstallBroadcastReceiver(this);
170cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
171dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    // Keeps track of most recently inserted text (multi-character key) for reverting
172dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    private CharSequence mEnteredText;
173dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
17470852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard    private boolean mIsAutoCorrectionIndicatorOn;
175604d80c67185954d4691ac775be59c499eee3b1csatok
17613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    private AlertDialog mOptionsDialog;
17713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
1784f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public final UIHandler mHandler = new UIHandler(this);
179d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
1804f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
18145f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_UPDATE_SHIFT_STATE = 1;
1824c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_SPACE_TYPED = 4;
1834c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_SET_BIGRAM_PREDICTIONS = 5;
1844c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_PENDING_IMS_CALLBACK = 6;
1854c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        private static final int MSG_UPDATE_SUGGESTIONS = 7;
1864c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka
18710dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateSuggestions;
18810dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateShiftState;
18910dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private long mDoubleSpacesTurnIntoPeriodTimeout;
19038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
1914f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        public UIHandler(LatinIME outerInstance) {
1924f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
19310dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        }
194175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
19510dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        public void onCreate() {
19610dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka            final Resources res = getOuterInstance().getResources();
197175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateSuggestions =
198175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_suggestions);
199175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateShiftState =
200175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_shift_state);
201175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger(
202175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_double_spaces_turn_into_period_timeout);
2034f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
2044f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
205923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
206923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void handleMessage(Message msg) {
2074f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
2084f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
209923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (msg.what) {
210d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SUGGESTIONS:
2114f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateSuggestions();
212d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
213d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SHIFT_STATE:
214de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                switcher.updateShiftState();
215d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
216cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            case MSG_SET_BIGRAM_PREDICTIONS:
2174f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateBigramPredictions();
21889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                break;
219923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
220923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
221d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
222d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void postUpdateSuggestions() {
223d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
224175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions);
225d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
226d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
227d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateSuggestions() {
228d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
229d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
230d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
231d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public boolean hasPendingUpdateSuggestions() {
232d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            return hasMessages(MSG_UPDATE_SUGGESTIONS);
233d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
234d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
235beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void postUpdateShiftState() {
236d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
237175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState);
238d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
239d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
240d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateShiftState() {
241d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
242d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
243d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
244cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void postUpdateBigramPredictions() {
245cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
246175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions);
24789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
24889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
249cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void cancelUpdateBigramPredictions() {
250cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
25189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
25289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
253fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void startDoubleSpacesTimer() {
254fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
255175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout);
256fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
257fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
258fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void cancelDoubleSpacesTimer() {
259fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
260fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
261fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
262fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public boolean isAcceptingDoubleSpaces() {
263fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            return hasMessages(MSG_SPACE_TYPED);
264fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
26538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
26659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        // Working variables for the following methods.
26759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mIsOrientationChanging;
2685fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard        private boolean mPendingSuccessiveImsCallback;
26959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingStartInput;
27059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInputView;
27159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInput;
272e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private EditorInfo mAppliedEditorInfo;
27359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
27459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void startOrientationChanging() {
275dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            removeMessages(MSG_PENDING_IMS_CALLBACK);
276dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            resetPendingImsCallback();
27759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mIsOrientationChanging = true;
278055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            final LatinIME latinIme = getOuterInstance();
279f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            if (latinIme.isInputViewShown()) {
280f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka                latinIme.mKeyboardSwitcher.saveKeyboardState();
281f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            }
28259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
28359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
28459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private void resetPendingImsCallback() {
28559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInputView = false;
28659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInput = false;
28759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingStartInput = false;
28859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
28959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
290e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private void executePendingImsCallback(LatinIME latinIme, EditorInfo editorInfo,
29159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                boolean restarting) {
29259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInputView)
29359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
29459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInput)
29559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
29659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingStartInput)
297e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
29859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            resetPendingImsCallback();
29959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
30059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
301e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInput(EditorInfo editorInfo, boolean restarting) {
30259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
30359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the second onStartInput after orientation changed.
30459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingStartInput = true;
30559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
30659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                if (mIsOrientationChanging && restarting) {
30759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    // This is the first onStartInput after orientation changed.
30859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    mIsOrientationChanging = false;
3095fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = true;
31059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                }
31159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
312e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
313e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
314055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
315055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
316055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
317e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
3186b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)
3196b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka                    && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
320e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                // Typically this is the second onStartInputView after orientation changed.
321e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                resetPendingImsCallback();
322e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            } else {
3235fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                if (mPendingSuccessiveImsCallback) {
324e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    // This is the first onStartInputView after orientation changed.
3255fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = false;
326e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    resetPendingImsCallback();
327e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
328e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            PENDING_IMS_CALLBACK_DURATION);
329e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                }
330e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
331e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
332e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputViewInternal(editorInfo, restarting);
333e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = editorInfo;
334e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            }
33559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
33659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
33759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInputView(boolean finishingInput) {
33859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
33959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInputView after orientation changed.
34059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInputView = true;
34159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
34259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
34359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(finishingInput);
344e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = null;
34538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            }
34638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        }
347ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka
34859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInput() {
34959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
35059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInput after orientation changed.
35159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInput = true;
35259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
35359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
35459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                executePendingImsCallback(latinIme, null, false);
35559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
356ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka            }
357ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka        }
35838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    }
35938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
36089ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    public LatinIME() {
36189ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        super();
36289ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
36389ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
36489ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    }
36589ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard
3667e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
3677e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    public void onCreate() {
36827d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
36927d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        mPrefs = prefs;
37027d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        LatinImeLogger.init(this, prefs);
371c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
37207cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge            ResearchLogger.getInstance().init(this, prefs);
373c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        }
374bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        InputMethodManagerCompatWrapper.init(this);
375ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka        SubtypeSwitcher.init(this);
37627d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        KeyboardSwitcher.init(this, prefs);
3772ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka        AccessibilityUtils.init(this);
378363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
379923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onCreate();
380363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
381bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        mImm = InputMethodManagerCompatWrapper.getInstance();
38210dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        mHandler.onCreate();
3839e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka        DEBUG = LatinImeLogger.sDBG;
384363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
385363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        final Resources res = getResources();
386363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        mResources = res;
387fd7d814c81132bdd59146a39dd668532f9514cd1Jean Chalard
38828f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        loadSettings();
38928f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
390297e6d590bd957577c335aa8713a786145a70288Jean Chalard        ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes());
391f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka
3929502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
393979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        boolean tryGC = true;
3949502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
395979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            try {
3960ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok                initSuggest();
397979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = false;
398979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            } catch (OutOfMemoryError e) {
3999502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e);
400979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
401979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
402979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
403f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        mDisplayOrientation = res.getConfiguration().orientation;
404b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
405cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Register to receive ringer mode change and network state change.
406cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Also receive installation and removal of a dictionary pack.
407123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        final IntentFilter filter = new IntentFilter();
408123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
40921af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
410923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        registerReceiver(mReceiver, filter);
411cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
412cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final IntentFilter packageFilter = new IntentFilter();
413cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
414cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
415cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addDataScheme(SCHEME_PACKAGE);
416cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
417646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard
418646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        final IntentFilter newDictFilter = new IntentFilter();
419646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        newDictFilter.addAction(
420646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard                DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
421646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
422923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
42336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
42417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    // Has to be package-visible for unit tests
42517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    /* package */ void loadSettings() {
4264f96bb4520de3610ae94da96b98e507ca7b76362satok        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
4274f96bb4520de3610ae94da96b98e507ca7b76362satok        // is not guaranteed. It may even be called at the same time on a different thread.
42817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
42916c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka        final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
43016c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            @Override
43116c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            protected SettingsValues job(Resources res) {
4324b9e6ee6fb408e2793e9ead5de2d6b45270d1f0dJean Chalard                return new SettingsValues(mPrefs, mInputAttributes, LatinIME.this);
43316c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            }
43416c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka        };
435297e6d590bd957577c335aa8713a786145a70288Jean Chalard        mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
436297e6d590bd957577c335aa8713a786145a70288Jean Chalard        mFeedbackManager = new AudioAndHapticFeedbackManager(this, mCurrentSettings);
43714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
43817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    }
43917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
4400ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private void initSuggest() {
4416a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
4426a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final String localeStr = subtypeLocale.toString();
44336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
44467fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        final ContactsBinaryDictionary oldContactsDictionary;
44578ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        if (mSuggest != null) {
44678ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = mSuggest.getContactsDictionary();
44778ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            mSuggest.close();
44878ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        } else {
44978ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = null;
45078ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
4516a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSuggest = new Suggest(this, subtypeLocale);
452297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (mCurrentSettings.mAutoCorrectEnabled) {
453297e6d590bd957577c335aa8713a786145a70288Jean Chalard            mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
45478ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
455e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
4566a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
4576a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka
45867fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mUserDictionary = new UserBinaryDictionary(this, localeStr);
45967fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
46078ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserDictionary(mUserDictionary);
461e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
46278ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        resetContactsDictionary(oldContactsDictionary);
463e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
4644f96bb4520de3610ae94da96b98e507ca7b76362satok        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
4654f96bb4520de3610ae94da96b98e507ca7b76362satok        // is not guaranteed. It may even be called at the same time on a different thread.
4664f96bb4520de3610ae94da96b98e507ca7b76362satok        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
467a836808f8b6b4cf3d5ff2cf93f079763fca86abbSatoshi Kataoka        mUserHistoryDictionary = UserHistoryDictionary.getInstance(
468fd53b8cc2b78acd7e33f4dc39cfc2faaea92f0f8satok                this, localeStr, Suggest.DIC_USER_HISTORY, mPrefs);
46978ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
470923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
47136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
47214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    /**
47314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * Resets the contacts dictionary in mSuggest according to the user settings.
47414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
4752e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang     * This method takes an optional contacts dictionary to use when the locale hasn't changed
4762e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang     * since the contacts dictionary can be opened or closed as necessary depending on the settings.
47714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
47814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * @param oldContactsDictionary an optional dictionary to use, or null
47914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     */
48067fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard    private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) {
481297e6d590bd957577c335aa8713a786145a70288Jean Chalard        final boolean shouldSetDictionary = (null != mSuggest && mCurrentSettings.mUseContactsDict);
48214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
48367fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        final ContactsBinaryDictionary dictionaryToUse;
48414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (!shouldSetDictionary) {
48514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the dictionary is closed. If it is already closed, this is a no-op,
48614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // so it's safe to call it anyways.
48714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            if (null != oldContactsDictionary) oldContactsDictionary.close();
48814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = null;
48914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
4902e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang            final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
4912e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang            if (null != oldContactsDictionary) {
49267fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                if (!oldContactsDictionary.mLocale.equals(locale)) {
49367fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // If the locale has changed then recreate the contacts dictionary. This
49467fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // allows locale dependent rules for handling bigram name predictions.
49567fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    oldContactsDictionary.close();
49667fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    dictionaryToUse = new ContactsBinaryDictionary(
49767fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                        this, Suggest.DIC_CONTACTS, locale);
4982e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                } else {
49967fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // Make sure the old contacts dictionary is opened. If it is already open,
50067fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // this is a no-op, so it's safe to call it anyways.
50167fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    oldContactsDictionary.reopen(this);
5022e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                    dictionaryToUse = oldContactsDictionary;
5032e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                }
50418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            } else {
50567fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS, locale);
50618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            }
50714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
50814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
50914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (null != mSuggest) {
51014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            mSuggest.setContactsDictionary(dictionaryToUse);
51114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
512699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard    }
513699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard
514cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /* package private */ void resetSuggestMainDict() {
5156a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
5166a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSuggest.resetMainDict(this, subtypeLocale);
5176a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
518cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
519cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
520466741d8a78965b8509bf527344f289e50873092Mike LeBeau    @Override
521466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onDestroy() {
522e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        if (mSuggest != null) {
523e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest.close();
524e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest = null;
525979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
526923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        unregisterReceiver(mReceiver);
527cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        unregisterReceiver(mDictionaryPackInstallReceiver);
528979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
529979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.onDestroy();
530923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onDestroy();
531923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
532923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
533923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
534923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onConfigurationChanged(Configuration conf) {
535dc64b138b5e3fb3706c0818d0a308fe6e36985b0Tadashi G. Takaoka        mSubtypeSwitcher.onConfigurationChanged(conf);
536b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        // If orientation changed while predicting, commit the change
537f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        if (mDisplayOrientation != conf.orientation) {
538f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mDisplayOrientation = conf.orientation;
539f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mHandler.startOrientationChanging();
5405475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.beginBatchEdit(getCurrentInputConnection());
5415475b38328171a0841ae18074bd45380ec567e90Jean Chalard            commitTyped(LastComposedWord.NOT_A_SEPARATOR);
5425475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.finishComposingText();
5435475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.endBatchEdit();
5442fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            if (isShowingOptionDialog())
5452fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                mOptionsDialog.dismiss();
546b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
547923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onConfigurationChanged(conf);
548923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
549b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
550923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
551923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public View onCreateInputView() {
5526c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        return mKeyboardSwitcher.onCreateInputView();
5536c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    }
5546c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka
5556c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    @Override
5566c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    public void setInputView(View view) {
5576c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        super.setInputView(view);
558d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        mExtractArea = getWindow().getWindow().getDecorView()
559d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka                .findViewById(android.R.id.extractArea);
560abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
561913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
562913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
563913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
564913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setListener(this, view);
565f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        if (LatinImeLogger.sVISUALDEBUG) {
566f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka            mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
567f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        }
568923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
569923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
570923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
571c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public void setCandidatesView(View view) {
572c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        // To ensure that CandidatesView will never be set.
573c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return;
574923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
575923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
576a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker    @Override
577e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInput(EditorInfo editorInfo, boolean restarting) {
578e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInput(editorInfo, restarting);
57959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
58059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
58159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
582e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
583e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInputView(editorInfo, restarting);
58459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
58559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
58659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
58759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInputView(boolean finishingInput) {
58859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInputView(finishingInput);
58959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
59038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
59159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
59259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInput() {
59359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInput();
59459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
59559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
5969cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    @Override
5979cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
5984f96bb4520de3610ae94da96b98e507ca7b76362satok        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
5994f96bb4520de3610ae94da96b98e507ca7b76362satok        // is not guaranteed. It may even be called at the same time on a different thread.
6006a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSubtypeSwitcher.updateSubtype(subtype);
6019cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    }
6029cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka
603e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
604e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInput(editorInfo, restarting);
60559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
60659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
6071cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka    @SuppressWarnings("deprecation")
608e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
609e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInputView(editorInfo, restarting);
61045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
611c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = switcher.getKeyboardView();
6128e09172df1bb176cc899940862c56bed9b9aec4esatok
613ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        if (editorInfo == null) {
614ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.e(TAG, "Null EditorInfo in onStartInputView()");
615ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            if (LatinImeLogger.sDBG) {
616ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                throw new NullPointerException("Null EditorInfo in onStartInputView()");
617ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            }
618ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            return;
619ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        }
62089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (DEBUG) {
621ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.d(TAG, "onStartInputView: editorInfo:"
622ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    + String.format("inputType=0x%08x imeOptions=0x%08x",
623ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                            editorInfo.inputType, editorInfo.imeOptions));
624b6fb5eb391987f3e426649a892cdcbf781957f5asatok            Log.d(TAG, "All caps = "
625b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0)
626b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", sentence caps = "
627b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0)
628b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", word caps = "
629b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
630910b73127fa207dd26ec8124000262523b0aac0csatok        }
6319bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
63207cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge            ResearchLogger.getInstance().start();
63348a7681e064ae259b840f0e757da2d716043d893Kurt Partridge            ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
6349bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
6351cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
6364f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
6374f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
6381cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka            Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead");
6394f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
6401cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(getPackageName(), FORCE_ASCII, editorInfo)) {
6414f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
6424f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
6434f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
6444f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
6454f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka
6461b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard        mTargetApplicationInfo =
6471b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard                TargetApplicationGetter.getCachedApplicationInfo(editorInfo.packageName);
6481b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard        if (null == mTargetApplicationInfo) {
6491b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard            new TargetApplicationGetter(this /* context */, this /* listener */)
6501b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard                    .execute(editorInfo.packageName);
6511b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard        }
652b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
6537ef235f53f2291f22ddf8c56be9860a218b25bbbTadashi G. Takaoka        LatinImeLogger.onStartInputView(editorInfo);
654923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // In landscape mode, this method gets called without the input view being created.
655979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (inputView == null) {
656923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
657923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
658923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
659b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        // Forward this event to the accessibility utilities, if enabled.
660b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
661b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        if (accessUtils.isTouchExplorationEnabled()) {
662e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            accessUtils.onStartInputViewInternal(editorInfo, restarting);
663b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        }
664b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette
6658d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        mSubtypeSwitcher.updateParametersOnStartInputView();
6664ab730dbd34fad323063f2ffd31ce33de746668dsatok
667b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // The EditorInfo might have a flag that affects fullscreen mode.
668b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // Note: This call should be done by InputMethodService?
669b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        updateFullscreenMode();
6701fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard        mLastSelectionStart = editorInfo.initialSelStart;
6711fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard        mLastSelectionEnd = editorInfo.initialSelEnd;
67280b66bb166f7f45adfcadcb84788477df9930828Jean Chalard        mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
673ccc35f7fa74860a8d737a4e9ff01fc0168dd329dJean Chalard        mApplicationSpecifiedCompletions = null;
674c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
675c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.closing();
676c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mEnteredText = null;
6772692a8700737d8eed268039aa27b22a31669da08Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
678c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mDeleteCount = 0;
679120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
680c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
68117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
68217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
683297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (mSuggest != null && mCurrentSettings.mAutoCorrectEnabled) {
684297e6d590bd957577c335aa8713a786145a70288Jean Chalard            mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
685549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        }
68617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
687297e6d590bd957577c335aa8713a786145a70288Jean Chalard        switcher.loadKeyboard(editorInfo, mCurrentSettings);
688c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
689913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
690913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.clear();
691913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShownInternal(
692913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                isSuggestionsStripVisible(), /* needsInputViewShown */ false);
693c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        // Delay updating suggestions because keyboard input view may not be shown at this point.
694c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mHandler.postUpdateSuggestions();
695ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        mHandler.cancelDoubleSpacesTimer();
696c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
697297e6d590bd957577c335aa8713a786145a70288Jean Chalard        inputView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
698297e6d590bd957577c335aa8713a786145a70288Jean Chalard                mCurrentSettings.mKeyPreviewPopupDismissDelay);
699c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.setProximityCorrectionEnabled(true);
700c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
701c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
702c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    }
703c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
7047214617622fce8f3fea6620e782c16336260a2a3Jean Chalard    @Override
705b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    public void onTargetApplicationKnown(final ApplicationInfo info) {
706b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard        mTargetApplicationInfo = info;
707b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    }
708b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
709923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
710e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    public void onWindowHidden() {
711d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
712d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            ResearchLogger.latinIME_onWindowHidden(mLastSelectionStart, mLastSelectionEnd,
713d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    getCurrentInputConnection());
714d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge        }
715e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        super.onWindowHidden();
716f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
717e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        if (inputView != null) inputView.closing();
718e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
719e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
72059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputInternal() {
721923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onFinishInput();
722a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
723979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
72407cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
72507cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge            ResearchLogger.getInstance().stop();
72607cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge        }
727979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
728f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
729d024ea605cc6b5b0b9fa1bd838d5b0ebd3901a5dKen Wakasa        if (inputView != null) inputView.closing();
730466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
731466741d8a78965b8509bf527344f289e50873092Mike LeBeau
73259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputViewInternal(boolean finishingInput) {
7336495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa        super.onFinishInputView(finishingInput);
734055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardSwitcher.onFinishInputView();
735f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
7365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (inputView != null) inputView.cancelAllMessages();
737d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        // Remove pending messages related to update suggestions
738d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.cancelUpdateSuggestions();
7396495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    }
7406495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa
7416495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    @Override
742923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
743923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            int newSelStart, int newSelEnd,
744104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            int composingSpanStart, int composingSpanEnd) {
745923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
746104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                composingSpanStart, composingSpanEnd);
747466741d8a78965b8509bf527344f289e50873092Mike LeBeau        if (DEBUG) {
748466741d8a78965b8509bf527344f289e50873092Mike LeBeau            Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
749466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", ose=" + oldSelEnd
750025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lss=" + mLastSelectionStart
751025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lse=" + mLastSelectionEnd
752466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nss=" + newSelStart
753466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nse=" + newSelEnd
754104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", cs=" + composingSpanStart
755104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", ce=" + composingSpanEnd);
756466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
7579bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
758d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            final boolean expectingUpdateSelectionFromLogger =
759d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection();
7609bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd,
7619bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
762d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    composingSpanEnd, mExpectingUpdateSelection,
76302308bec632a5df23325c916bffec5def16b22b4Jean Chalard                    expectingUpdateSelectionFromLogger, mConnection);
764d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            if (expectingUpdateSelectionFromLogger) {
765d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                return;
766d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            }
7679bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
768466741d8a78965b8509bf527344f289e50873092Mike LeBeau
769104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // TODO: refactor the following code to be less contrived.
770104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "newSelStart != composingSpanEnd" || "newSelEnd != composingSpanEnd" means
771104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // that the cursor is not at the end of the composing span, or there is a selection.
772104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "mLastSelectionStart != newSelStart" means that the cursor is not in the same place
773104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // as last time we were called (if there is a selection, it means the start hasn't
774104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // changed, so it's the end that did).
775104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean selectionChanged = (newSelStart != composingSpanEnd
776104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                || newSelEnd != composingSpanEnd) && mLastSelectionStart != newSelStart;
777104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // if composingSpanStart and composingSpanEnd are -1, it means there is no composing
778104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // span in the view - we can use that to narrow down whether the cursor was moved
779104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // by us or not. If we are composing a word but there is no composing span, then
780104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // we know for sure the cursor moved while we were composing and we should reset
781104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // the state.
782104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
7834733609947c0ec74e460bd714fffca0518ade93aJean Chalard        if (!mExpectingUpdateSelection) {
784cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // TAKE CARE: there is a race condition when we enter this test even when the user
785cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // did not explicitly move the cursor. This happens when typing fast, where two keys
786cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // turn this flag on in succession and both onUpdateSelection() calls arrive after
787cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // the second one - the first call successfully avoids this test, but the second one
788104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            // enters. For the moment we rely on noComposingSpan to further reduce the impact.
78951fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
7909a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // TODO: the following is probably better done in resetEntireInputState().
7919a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // it should only happen when the cursor moved, and the very purpose of the
7929a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // test below is to narrow down whether this happened or not. Likewise with
7939a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // the call to postUpdateShiftState.
79451fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // We set this to NONE because after a cursor move, we don't want the space
79551fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // state-related special processing to kick in.
79651fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            mSpaceState = SPACE_STATE_NONE;
79751fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
7988a3d369840d6061692a19ceb1eb7267a50a9e056Jean Chalard            if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) {
7992649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard                resetEntireInputState();
8004c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard            }
801beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
802beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            mHandler.postUpdateShiftState();
8034733609947c0ec74e460bd714fffca0518ade93aJean Chalard        }
8044733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = false;
8056b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not
8066b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // here. It would probably be too expensive to call directly here but we may want to post a
8076b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // message to delay it. The point would be to unify behavior between backspace to the
8086b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // end of a word and manually put the pointer at the end of the word.
809466741d8a78965b8509bf527344f289e50873092Mike LeBeau
810979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // Make a note of the cursor position
811979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionStart = newSelStart;
812979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionEnd = newSelEnd;
8137a8dac55278cedd838be325f56b4c52d973c61f5satok    }
8147a8dac55278cedd838be325f56b4c52d973c61f5satok
815c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
816c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has clicked on the extracted text view,
817c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * when running in fullscreen mode.  The default implementation hides
818913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * the suggestions view when this happens, but only if the extracted text
819c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * editor has a vertical scroll bar because its text doesn't fit.
820c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
821913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
822c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
823c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
824c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedTextClicked() {
825fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
826c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
827c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedTextClicked();
828c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
829c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
830c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
831c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has performed a cursor movement in the
832c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * extracted text view, when it is running in fullscreen mode.  The default
833913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * implementation hides the suggestions view when a vertical movement
834c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * happens, but only if the extracted text editor has a vertical scroll bar
835c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * because its text doesn't fit.
836c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
837913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
838c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
839c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
840c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedCursorMovement(int dx, int dy) {
841fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
842c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
843c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedCursorMovement(dx, dy);
844c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
845c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
846923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
847923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void hideWindow() {
848979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
849c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mKeyboardSwitcher.onHideWindow();
850979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
851923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (TRACE) Debug.stopMethodTracing();
8526e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
8536e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog.dismiss();
8546e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog = null;
8556e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        }
856923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.hideWindow();
857923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
858923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
859923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
8601b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
861979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG) {
862a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa            Log.i(TAG, "Received completions:");
863bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            if (applicationSpecifiedCompletions != null) {
864bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
865bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                    Log.i(TAG, "  #" + i + ": " + applicationSpecifiedCompletions[i]);
866bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                }
867923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
868923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
8699bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
8709bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
8719bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
872dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn) {
8731b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
8741b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            if (applicationSpecifiedCompletions == null) {
875b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani                clearSuggestions();
876923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                return;
877923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
878a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
879660776e09b9a3b321074a94721d901a035ca1b9fKen Wakasa            final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
88088bf1ba5263f5a5c1df367ddc401db4109ef8677Jean Chalard                    SuggestedWords.getFromApplicationSpecifiedCompletions(
8811dfef0336d5968dbd00b73489778cee1fb233d56Jean Chalard                            applicationSpecifiedCompletions);
882bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard            final SuggestedWords suggestedWords = new SuggestedWords(
8837d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard                    applicationSuggestedWords,
8842e2519ee914d4bf9462950553840557a4c19faedJean Chalard                    false /* typedWordValid */,
885bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard                    false /* hasAutoCorrectionCandidate */,
886b5eeb724fc98bb7169683539027d9ba54ffb8b14Jean Chalard                    false /* allowsToBeAutoCorrected */,
88703a35170751a635332c00bf6c272a0127a255cf6Jean Chalard                    false /* isPunctuationSuggestions */,
8880142b997bf18f5d07e83b3fd403f0b3ea4736040satok                    false /* isObsoleteSuggestions */,
8890142b997bf18f5d07e83b3fd403f0b3ea4736040satok                    false /* isPrediction */);
890979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // When in fullscreen mode, show completions generated by the application
8914e112d07821d34c1dedd21b086817be9fce2fd47Jean Chalard            final boolean isAutoCorrection = false;
8927d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard            setSuggestions(suggestedWords, isAutoCorrection);
893dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard            setAutoCorrectionIndicator(isAutoCorrection);
894b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            // TODO: is this the right thing to do? What should we auto-correct to in
895b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            // this case? This says to keep whatever the user typed.
896b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
897c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            setSuggestionStripShown(true);
898923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
899923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
900923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
901c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
902913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // TODO: Modify this if we support suggestions with hard keyboard
903913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
904433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
905433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
906913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean shouldShowSuggestions = shown
907433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                    && (needsInputViewShown ? inputViewShown : true);
9084b1780fa9571409d65d9797d47949ffafaf0f083Tadashi G. Takaoka            if (isFullscreenMode()) {
909913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
910913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.GONE);
9117a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            } else {
912913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
913913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE);
9147a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            }
915923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
916923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
917a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
918c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShown(boolean shown) {
919c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
920543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    }
921543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa
922bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka    private int getAdjustedBackingViewHeight() {
923bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        final int currentHeight = mKeyPreviewBackingView.getHeight();
924bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        if (currentHeight > 0) {
925bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka            return currentHeight;
926c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
927c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
928c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
929bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        if (keyboardView == null) {
930bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka            return 0;
931bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        }
932c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int keyboardHeight = keyboardView.getHeight();
933c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int suggestionsHeight = mSuggestionsContainer.getHeight();
934c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int displayHeight = mResources.getDisplayMetrics().heightPixels;
935c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final Rect rect = new Rect();
936c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect);
937c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int notificationBarHeight = rect.top;
938c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight
939c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                - keyboardHeight;
940c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
941c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
942c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        params.height = mSuggestionsView.setMoreSuggestionsHeight(remainingHeight);
943c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.setLayoutParams(params);
944bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        return params.height;
945c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka    }
946c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
947543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    @Override
948923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onComputeInsets(InputMethodService.Insets outInsets) {
949923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onComputeInsets(outInsets);
950f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
951913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (inputView == null || mSuggestionsContainer == null)
95246ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka            return;
953b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final int adjustedBackingHeight = getAdjustedBackingViewHeight();
954b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE);
955b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final int backingHeight = backingGone ? 0 : adjustedBackingHeight;
956d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // In fullscreen mode, the height of the extract area managed by InputMethodService should
957d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // be considered.
958d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}.
959d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0;
96059010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0
96159010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                : mSuggestionsContainer.getHeight();
962d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
963abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        int touchY = extraHeight;
9649e347d3d448e48229c46aad394ec9bd60cd5807bsatok        // Need to set touchable region only if input view is being shown
965433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka        final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
966433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka        if (keyboardView != null && keyboardView.isShown()) {
967913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
96859010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                touchY -= suggestionsHeight;
9699e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
9707a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            final int touchWidth = inputView.getWidth();
971abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka            final int touchHeight = inputView.getHeight() + extraHeight
9727a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    // Extend touchable region below the keyboard.
9737a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
97413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
97513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight);
9769e347d3d448e48229c46aad394ec9bd60cd5807bsatok        }
97746ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.contentTopInsets = touchY;
97846ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.visibleTopInsets = touchY;
979923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
980a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
981923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
982979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public boolean onEvaluateFullscreenMode() {
9839751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        // Reread resource value here, because this method is called by framework anytime as needed.
9849751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        final boolean isFullscreenModeAllowed =
985297e6d590bd957577c335aa8713a786145a70288Jean Chalard                mCurrentSettings.isFullscreenModeAllowed(getResources());
9869751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        return super.onEvaluateFullscreenMode() && isFullscreenModeAllowed;
98759010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    }
98859010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka
98959010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    @Override
99059010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    public void updateFullscreenMode() {
99159010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        super.updateFullscreenMode();
992f80b6a06992ae08ca3601f4fbc6da550fd9ac730Tadashi G. Takaoka
99359010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        if (mKeyPreviewBackingView == null) return;
994549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        // In fullscreen mode, no need to have extra space to show the key preview.
99559010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        // If not, we should have extra space above the keyboard to show the key preview.
996549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
997979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
998979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
9992649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // This will reset the whole input state to the starting state. It will clear
10002649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // the composing word, reset the last composed word, tell the inputconnection
10012649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // and the composingStateManager about it.
10022649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    private void resetEntireInputState() {
10032649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
10042649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        updateSuggestions();
10055475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.finishComposingText();
10062649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    }
10072649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard
10082692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private void resetComposingState(final boolean alsoResetLastComposedWord) {
10092692a8700737d8eed268039aa27b22a31669da08Jean Chalard        mWordComposer.reset();
10102692a8700737d8eed268039aa27b22a31669da08Jean Chalard        if (alsoResetLastComposedWord)
10112692a8700737d8eed268039aa27b22a31669da08Jean Chalard            mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
10122692a8700737d8eed268039aa27b22a31669da08Jean Chalard    }
10132692a8700737d8eed268039aa27b22a31669da08Jean Chalard
10145475b38328171a0841ae18074bd45380ec567e90Jean Chalard    public void commitTyped(final int separatorCode) {
1015196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) return;
10163651220327c051d8017045aa5e8919461507b3f8Jean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
10173651220327c051d8017045aa5e8919461507b3f8Jean Chalard        if (typedWord.length() > 0) {
10185475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(typedWord, 1);
10195475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (ProductionFlag.IS_EXPERIMENTAL) {
10205475b38328171a0841ae18074bd45380ec567e90Jean Chalard                ResearchLogger.latinIME_commitText(typedWord);
1021923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1022c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            final CharSequence prevWord = addToUserHistoryDictionary(typedWord);
1023c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            mLastComposedWord = mWordComposer.commitWord(
1024c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
1025c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    separatorCode, prevWord);
1026923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
10278558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        updateSuggestions();
1028923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1029923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1030553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka    public int getCurrentAutoCapsState() {
1031297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (!mCurrentSettings.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
103203ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
103303ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final EditorInfo ei = getCurrentInputEditorInfo();
1034553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
103503ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
103603ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final int inputType = ei.inputType;
1037553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {
1038553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka            return TextUtils.CAP_MODE_CHARACTERS;
1039553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        }
104003ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
104103ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final boolean noNeedToCheckCapsMode = (inputType & (InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
104203ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka                | InputType.TYPE_TEXT_FLAG_CAP_WORDS)) == 0;
1043553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (noNeedToCheckCapsMode) return Constants.TextUtils.CAP_MODE_OFF;
104403ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
104516950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka        // Avoid making heavy round-trip IPC calls of {@link InputConnection#getCursorCapsMode}
104616950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka        // unless needed.
104716950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka        if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF;
104816950d65c323f99507d97cb7f0403dc653e2506cTadashi G. Takaoka
104903ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls.
105003ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // Note: getCursorCapsMode() returns the current capitalization mode that is any
105103ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none
105203ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        // of them.
10535475b38328171a0841ae18074bd45380ec567e90Jean Chalard        return mConnection.getCursorCapsMode(inputType);
10541c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
10551c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
1056bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard    private void swapSwapperAndSpace() {
10575475b38328171a0841ae18074bd45380ec567e90Jean Chalard        CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
1058863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
1059923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastTwo != null && lastTwo.length() == 2
1060863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard                && lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
10615475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.deleteSurroundingText(2, 0);
1062d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1063d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_deleteSurroundingText(2);
1064d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
10655475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(lastTwo.charAt(1) + " ", 1);
1066d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1067d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit();
1068d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
1069b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
10704ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa        }
10714ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa    }
10724ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1073bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard    private boolean maybeDoubleSpace() {
1074297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (mCurrentSettings.mCorrectionMode == Suggest.CORRECTION_NONE) return false;
10755475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
1076923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastThree != null && lastThree.length() == 3
1077344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                && canBeFollowedByPeriod(lastThree.charAt(0))
1078571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastThree.charAt(1) == Keyboard.CODE_SPACE
1079fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && lastThree.charAt(2) == Keyboard.CODE_SPACE
1080fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && mHandler.isAcceptingDoubleSpaces()) {
1081fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            mHandler.cancelDoubleSpacesTimer();
10825475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.deleteSurroundingText(2, 0);
10835475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(". ", 1);
1084d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1085d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_doubleSpaceAutoPeriod();
1086d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
1087b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1088120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            return true;
1089923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1090120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return false;
1091923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1092a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1093344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka    private static boolean canBeFollowedByPeriod(final int codePoint) {
1094344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: Check again whether there really ain't a better way to check this.
1095344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: This should probably be language-dependant...
1096344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        return Character.isLetterOrDigit(codePoint)
1097344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_SINGLE_QUOTE
1098344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_DOUBLE_QUOTE
1099344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
1100344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
1101344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
1102344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka                || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
1103344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka    }
1104344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka
1105c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
1106923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean addWordToDictionary(String word) {
110767fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mUserDictionary.addWordToUserDictionary(word, 128);
11086558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // Suggestion strip should be updated after the operation of adding word to the
11096558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // user dictionary
1110d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.postUpdateSuggestions();
1111923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
1112923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1113923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
11148fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean isAlphabet(int code) {
11158fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka        return Character.isLetter(code);
1116923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1117a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1118e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    private void onSettingsKeyPressed() {
1119cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return;
1120911b8f9d19c1c4903eeef29b43176cfeaa0e5d0cKen Wakasa        showSubtypeSelectorAndSettings();
11219a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
11229a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1123cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
1124cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
1125cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa
1126cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    @Override
1127cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public boolean onCustomRequest(int requestCode) {
1128cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return false;
1129cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        switch (requestCode) {
1130cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        case CODE_SHOW_INPUT_METHOD_PICKER:
113155d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka            if (ImfUtils.hasMultipleEnabledIMEsOrSubtypes(
113255d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka                    this, true /* include aux subtypes */)) {
113379efbed76f638be298493107fa2d0cd1b5eb529esatok                mImm.showInputMethodPicker();
1134cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa                return true;
11359a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok            }
1136cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa            return false;
11379a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
1138cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        return false;
11399a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
11409a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
11419a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    private boolean isShowingOptionDialog() {
11429a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        return mOptionsDialog != null && mOptionsDialog.isShowing();
11439a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
11449a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
114505bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka    private static int getActionId(Keyboard keyboard) {
114605bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        return keyboard != null ? keyboard.mId.imeActionId() : EditorInfo.IME_ACTION_NONE;
11477a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
11487a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
11498f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka    private void performEditorAction(int actionId) {
11505475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.performEditorAction(actionId);
11515475b38328171a0841ae18074bd45380ec567e90Jean Chalard        if (ProductionFlag.IS_EXPERIMENTAL) {
11525475b38328171a0841ae18074bd45380ec567e90Jean Chalard            ResearchLogger.latinIME_performEditorAction(actionId);
11537a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
11547a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
11557a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
115681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    private void handleLanguageSwitchKey() {
1157297e6d590bd957577c335aa8713a786145a70288Jean Chalard        final boolean includesOtherImes = mCurrentSettings.mIncludesOtherImesInLanguageSwitchList;
115881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        final IBinder token = getWindow().getWindow().getAttributes().token;
115981d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        if (mShouldSwitchToLastSubtype) {
11609cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka            final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype();
1161ae2388c7f799ab565f63d3ba83abaf3300475fd0Tadashi G. Takaoka            final boolean lastSubtypeBelongsToThisIme =
1162ae2388c7f799ab565f63d3ba83abaf3300475fd0Tadashi G. Takaoka                    ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(this, lastSubtype);
116381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            if ((includesOtherImes || lastSubtypeBelongsToThisIme)
116481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                    && mImm.switchToLastInputMethod(token)) {
116581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                mShouldSwitchToLastSubtype = false;
116681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            } else {
116781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                mImm.switchToNextInputMethod(token, !includesOtherImes);
116881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                mShouldSwitchToLastSubtype = true;
116981d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            }
117081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        } else {
117181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            mImm.switchToNextInputMethod(token, !includesOtherImes);
117281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        }
117381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    }
117481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka
11755475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void sendUpDownEnterOrBackspace(final int code) {
11764e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard        final long eventTime = SystemClock.uptimeMillis();
11775475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
11784e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
11794e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
11805475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
11814e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
11824e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
11834e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard    }
11844e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard
11857a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    private void sendKeyCodePoint(int code) {
11867a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // TODO: Remove this special handling of digit letters.
11877a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
11887a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (code >= '0' && code <= '9') {
11897a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            super.sendKeyChar((char)code);
11907a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            return;
11917a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
11927a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
11935475b38328171a0841ae18074bd45380ec567e90Jean Chalard        // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
11945475b38328171a0841ae18074bd45380ec567e90Jean Chalard        // we want to be able to compile against the Ice Cream Sandwich SDK.
11955475b38328171a0841ae18074bd45380ec567e90Jean Chalard        if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null
11965475b38328171a0841ae18074bd45380ec567e90Jean Chalard                && mTargetApplicationInfo.targetSdkVersion < 16) {
11975475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
11985475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // a hardware keyboard event on pressing enter or delete. This is bad for many
11995475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // reasons (there are race conditions with commits) but some applications are
12005475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // relying on this behavior so we continue to support it for older apps.
12015475b38328171a0841ae18074bd45380ec567e90Jean Chalard            sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER);
12025475b38328171a0841ae18074bd45380ec567e90Jean Chalard        } else {
12035475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final String text = new String(new int[] { code }, 0, 1);
12045475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(text, text.length());
12055475b38328171a0841ae18074bd45380ec567e90Jean Chalard        }
12065475b38328171a0841ae18074bd45380ec567e90Jean Chalard        if (ProductionFlag.IS_EXPERIMENTAL) {
12075475b38328171a0841ae18074bd45380ec567e90Jean Chalard            ResearchLogger.latinIME_sendKeyCodePoint(code);
12087a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12097a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12107a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12115f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    // Implementation of {@link KeyboardActionListener}.
12125a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
1213ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    public void onCodeInput(int primaryCode, int x, int y) {
1214175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final long when = SystemClock.uptimeMillis();
1215571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
1216923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mDeleteCount = 0;
1217923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1218923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mLastKeyTime = when;
1219e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard        mConnection.beginBatchEdit(getCurrentInputConnection());
1220fdd68f06579ab338f5d33115aa8300431c75b4faKurt Partridge
1221c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
122207cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge            ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
1223fdd68f06579ab338f5d33115aa8300431c75b4faKurt Partridge        }
1224fdd68f06579ab338f5d33115aa8300431c75b4faKurt Partridge
1225175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
1226120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // The space state depends only on the last character pressed and its own previous
1227120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // state. Here, we revert the space state to neutral if the key is actually modifying
1228120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // the input contents (any non-shift key), which is what we should do for
1229120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // all inputs that do not result in a special state. Each character handling is then
1230120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // free to override the state as they see fit.
1231120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final int spaceState = mSpaceState;
123270852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard        if (!mWordComposer.isComposingWord()) mIsAutoCorrectionIndicatorOn = false;
1233ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1234ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        // TODO: Consolidate the double space timer, mLastKeyTime, and the space state.
1235ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        if (primaryCode != Keyboard.CODE_SPACE) {
1236ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa            mHandler.cancelDoubleSpacesTimer();
1237ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        }
1238ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1239c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1240923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (primaryCode) {
1241571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_DELETE:
1242120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1243120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            handleBackspace(spaceState);
12444189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            mDeleteCount++;
12454733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
124681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            mShouldSwitchToLastSubtype = true;
1247140467b8b65eb9087a9b7f744dbb8a30481effe7Kurt Partridge            LatinImeLogger.logOnDelete(x, y);
12484189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1249571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_SHIFT:
1250e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
12512a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            // Shift and symbol key is handled in onPressKey() and onReleaseKey().
12524189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1253e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SETTINGS:
125493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            onSettingsKeyPressed();
12554189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1256d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka        case Keyboard.CODE_SHORTCUT:
125793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            mSubtypeSwitcher.switchToShortcutIME();
12584189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
12597a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        case Keyboard.CODE_ACTION_ENTER:
12608f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(getActionId(switcher.getKeyboard()));
12617a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            break;
126205bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        case Keyboard.CODE_ACTION_NEXT:
12638f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_NEXT);
126405bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka            break;
126505bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        case Keyboard.CODE_ACTION_PREVIOUS:
12668f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
12674189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
126881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        case Keyboard.CODE_LANGUAGE_SWITCH:
126981d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            handleLanguageSwitchKey();
127081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            break;
1271724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge        case Keyboard.CODE_RESEARCH:
1272724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1273724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge                ResearchLogger.getInstance().presentResearchDialog(this);
1274724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            }
1275724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            break;
12764189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        default:
12770b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka            if (primaryCode == Keyboard.CODE_TAB
12780b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka                    && mInputAttributes.mEditorAction == EditorInfo.IME_ACTION_NEXT) {
12790b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka                performEditorAction(EditorInfo.IME_ACTION_NEXT);
12800b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka                break;
12810b831ba2aaf7baf7be402f2245b225a04c28fadaTadashi G. Takaoka            }
1282120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1283297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (mCurrentSettings.isWordSeparator(primaryCode)) {
1284c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
12854189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            } else {
12868dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
12878dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
12888dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                    handleCharacter(primaryCode, x, y, spaceState);
12898dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                } else {
12908dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                    handleCharacter(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE,
12918dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                            spaceState);
12928dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                }
12934189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            }
12944733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
129581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            mShouldSwitchToLastSubtype = true;
12964733609947c0ec74e460bd714fffca0518ade93aJean Chalard            break;
1297923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1298eef6238f94b5046054d9ae9c06f775362893c0eeTadashi G. Takaoka        switcher.onCodeInput(primaryCode);
1299125de3dfdf548359de890247907f2e6f430008ecJean Chalard        // Reset after any single keystroke, except shift and symbol-shift
1300125de3dfdf548359de890247907f2e6f430008ecJean Chalard        if (!didAutoCorrect && primaryCode != Keyboard.CODE_SHIFT
1301125de3dfdf548359de890247907f2e6f430008ecJean Chalard                && primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL)
1302c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard            mLastComposedWord.deactivate();
1303dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = null;
1304e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard        mConnection.endBatchEdit();
1305923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1306a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
13075a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13088aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onTextInput(CharSequence text) {
13095475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.beginBatchEdit(getCurrentInputConnection());
13105475b38328171a0841ae18074bd45380ec567e90Jean Chalard        commitTyped(LastComposedWord.NOT_A_SEPARATOR);
13115475b38328171a0841ae18074bd45380ec567e90Jean Chalard        text = specificTldProcessingOnTextInput(text);
1312fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == mSpaceState) {
13137a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(Keyboard.CODE_SPACE);
1314fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
13155475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.commitText(text, 1);
1316d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
1317d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_commitText(text);
1318d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
13195475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.endBatchEdit();
1320b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
13218cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
1322120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
1323dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = text;
13242692a8700737d8eed268039aa27b22a31669da08Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
1325923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1326923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
13275475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
1328fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
1329fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                || !Character.isLetter(text.charAt(1))) {
1330fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            // Not a tld: do nothing.
1331fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1332fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
133312d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // We have a TLD (or something that looks like this): make sure we don't add
133412d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // a space even if currently in phantom mode.
133512d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        mSpaceState = SPACE_STATE_NONE;
13365475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
1337fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (lastOne != null && lastOne.length() == 1
1338fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                && lastOne.charAt(0) == Keyboard.CODE_PERIOD) {
1339fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text.subSequence(1, text.length());
1340fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        } else {
1341fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1342fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1343fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    }
1344fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
13455a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13468aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCancelInput() {
134783e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        // User released a finger outside any key
13485f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        mKeyboardSwitcher.onCancelInput();
134983e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka    }
135083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka
1351120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleBackspace(final int spaceState) {
13522245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard        // In many cases, we may have to put the keyboard in auto-shift state again.
1353beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        mHandler.postUpdateShiftState();
13542245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard
1355747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard        if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
13565c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // Cancel multi-character input: remove the text we just entered.
13575c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // This is triggered on backspace after a key that inputs multiple characters,
13585c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // like the smiley key or the .com key.
1359d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            final int length = mEnteredText.length();
13605475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.deleteSurroundingText(length, 0);
1361d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1362d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_deleteSurroundingText(length);
1363d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
13645c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
13655c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // In addition we know that spaceState is false, and that we should not be
13665c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // reverting any autocorrect at this point. So we can safely return.
13675c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            return;
13685c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        }
13695c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard
1370196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
13713651220327c051d8017045aa5e8919461507b3f8Jean Chalard            final int length = mWordComposer.size();
1372923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (length > 0) {
13739318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                mWordComposer.deleteLast();
13745475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1375196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                // If we have deleted the last remaining character of a word, then we are not
1376196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                // isComposingWord() any more.
1377196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                if (!mWordComposer.isComposingWord()) {
1378196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                    // Not composing word any more, so we can show bigrams.
1379cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                    mHandler.postUpdateBigramPredictions();
138089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                } else {
1381196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                    // Still composing a word, so we still have letters to deduce a suggestion from.
138289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    mHandler.postUpdateSuggestions();
138389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                }
1384923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
13855475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(1, 0);
1386d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1387d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ResearchLogger.latinIME_deleteSurroundingText(1);
1388d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
1389923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1390890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        } else {
13915935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard            if (mLastComposedWord.canRevertCommit()) {
1392d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                Utils.Stats.onAutoCorrectionCancellation();
13935475b38328171a0841ae18074bd45380ec567e90Jean Chalard                revertCommit();
1394120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                return;
1395120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1396d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            if (SPACE_STATE_DOUBLE == spaceState) {
1397243e370fcb1c2cb8608614206075ecdbe79a6372Jean Chalard                mHandler.cancelDoubleSpacesTimer();
13985475b38328171a0841ae18074bd45380ec567e90Jean Chalard                if (revertDoubleSpaceWhileInBatchEdit()) {
1399d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // No need to reset mSpaceState, it has already be done (that's why we
1400d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // receive it as a parameter)
1401d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1402d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
1403d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
14045475b38328171a0841ae18074bd45380ec567e90Jean Chalard                if (revertSwapPunctuation()) {
1405d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // Likewise
1406d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1407d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
14084733609947c0ec74e460bd714fffca0518ade93aJean Chalard            }
1409504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka
14104fef31510df542a3324426a6750950194d016086Jean Chalard            // No cancelling of commit/double space/swap: we have a regular backspace.
14114fef31510df542a3324426a6750950194d016086Jean Chalard            // We should backspace one char and restart suggestion if at the end of a word.
14124fef31510df542a3324426a6750950194d016086Jean Chalard            if (mLastSelectionStart != mLastSelectionEnd) {
14134fef31510df542a3324426a6750950194d016086Jean Chalard                // If there is a selection, remove it.
14144fef31510df542a3324426a6750950194d016086Jean Chalard                final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
14155475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
14165475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(lengthToDelete, 0);
1417d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1418d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete);
1419d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
14206558253160e2039c87f424bd814f402ecd31de3bKen Wakasa            } else {
14214fef31510df542a3324426a6750950194d016086Jean Chalard                // There is no selection, just delete one character.
14224fef31510df542a3324426a6750950194d016086Jean Chalard                if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
14234fef31510df542a3324426a6750950194d016086Jean Chalard                    // This should never happen.
14244fef31510df542a3324426a6750950194d016086Jean Chalard                    Log.e(TAG, "Backspace when we don't know the selection position");
14256558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                }
14264e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
14274e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                // we want to be able to compile against the Ice Cream Sandwich SDK.
14284e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                if (mTargetApplicationInfo != null
14294e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                        && mTargetApplicationInfo.targetSdkVersion < 16) {
14304e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
14314e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // a hardware keyboard event on pressing enter or delete. This is bad for many
14324e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // reasons (there are race conditions with commits) but some applications are
14334e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // relying on this behavior so we continue to support it for older apps.
14345475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL);
14354e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                } else {
14365475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.deleteSurroundingText(1, 0);
14374e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                }
1438d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                if (ProductionFlag.IS_EXPERIMENTAL) {
1439d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    ResearchLogger.latinIME_deleteSurroundingText(1);
1440d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                }
14414fef31510df542a3324426a6750950194d016086Jean Chalard                if (mDeleteCount > DELETE_ACCELERATE_AT) {
14425475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.deleteSurroundingText(1, 0);
1443d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    if (ProductionFlag.IS_EXPERIMENTAL) {
1444d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                        ResearchLogger.latinIME_deleteSurroundingText(1);
1445d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                    }
1446edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                }
1447923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
14484fef31510df542a3324426a6750950194d016086Jean Chalard            if (isSuggestionsRequested()) {
14495475b38328171a0841ae18074bd45380ec567e90Jean Chalard                restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
14504fef31510df542a3324426a6750950194d016086Jean Chalard            }
1451923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1452923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1453923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1454e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard    private boolean maybeStripSpace(final int code,
1455e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final int spaceState, final boolean isFromSuggestionStrip) {
1456e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1457bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard            mConnection.removeTrailingSpace();
1458e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1459e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else if ((SPACE_STATE_WEAK == spaceState
1460e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                || SPACE_STATE_SWAP_PUNCTUATION == spaceState)
1461e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                && isFromSuggestionStrip) {
1462297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (mCurrentSettings.isWeakSpaceSwapper(code)) {
1463e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return true;
1464e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            } else {
1465297e6d590bd957577c335aa8713a786145a70288Jean Chalard                if (mCurrentSettings.isWeakSpaceStripper(code)) {
1466bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                    mConnection.removeTrailingSpace();
1467e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                }
1468e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return false;
1469e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
1470e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else {
1471e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1472e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1473e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    }
1474e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1475ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    private void handleCharacter(final int primaryCode, final int x,
1476120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int y, final int spaceState) {
1477196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        boolean isComposingWord = mWordComposer.isComposingWord();
1478fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1479fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
1480297e6d590bd957577c335aa8713a786145a70288Jean Chalard                !mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) {
1481fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (isComposingWord) {
1482fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // Sanity check
1483fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                throw new RuntimeException("Should not be composing here");
1484fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            }
14857a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(Keyboard.CODE_SPACE);
1486fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1487fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1488d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // NOTE: isCursorTouchingWord() is a blocking IPC call, so it often takes several
1489d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
1490d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // thread here.
1491d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        if (!isComposingWord && (isAlphabet(primaryCode)
1492297e6d590bd957577c335aa8713a786145a70288Jean Chalard                || mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode))
1493297e6d590bd957577c335aa8713a786145a70288Jean Chalard                && isSuggestionsRequested() &&
1494297e6d590bd957577c335aa8713a786145a70288Jean Chalard                !mConnection.isCursorTouchingWord(mCurrentSettings)) {
1495736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Reset entirely the composing state anyway, then start composing a new word unless
1496736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // the character is a single quote. The idea here is, single quote is not a
1497736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // separator and it should be treated as a normal character, except in the first
1498736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // position where it should not start composing a word.
1499736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != primaryCode);
1500736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Here we don't need to reset the last composed word. It will be reset
1501736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // when we commit this one, if we ever do; if on the other hand we backspace
1502736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // it entirely and resume suggestions on the previous word, we'd like to still
1503736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // have touch coordinates for it.
1504736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            resetComposingState(false /* alsoResetLastComposedWord */);
1505736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            clearSuggestions();
1506923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
15077b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard        if (isComposingWord) {
1508ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            mWordComposer.add(
1509ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector());
15105475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // If it's the first letter, make note of auto-caps state
15115475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (mWordComposer.size() == 1) {
15125475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mWordComposer.setAutoCapitalized(
15135475b38328171a0841ae18074bd45380ec567e90Jean Chalard                        getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
1514923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
15155475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1516d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
1517923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1518e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard            final boolean swapWeakSpace = maybeStripSpace(primaryCode,
1519e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                    spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
1520863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
15217a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(primaryCode);
1522e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1523e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            if (swapWeakSpace) {
1524bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                swapSwapperAndSpace();
1525e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                mSpaceState = SPACE_STATE_WEAK;
1526e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
15275262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // Some characters are not word separators, yet they don't start a new
15285262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // composing span. For these, we haven't changed the suggestion strip, and
15295262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // if the "add to dictionary" hint is shown, we should do so now. Examples of
15305262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // such characters include single quote, dollar, and others; the exact list is
15315262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // the list of characters for which we enter handleCharacterWhileInBatchEdit
15325262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // that don't match the test if ((isAlphabet...)) at the top of this method.
15335262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) {
15345262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard                mHandler.postUpdateBigramPredictions();
15355262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            }
1536e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1537e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        Utils.Stats.onNonSeparator((char)primaryCode, x, y);
1538923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1539923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1540c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    // Returns true if we did an autocorrection, false otherwise.
1541c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    private boolean handleSeparator(final int primaryCode, final int x, final int y,
1542120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int spaceState) {
154355b10796522b871c1e04d6f2254fdff5dc7aced4Ken Wakasa        // Should dismiss the "Touch again to save" message when handling separator
1544913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
1545cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            mHandler.cancelUpdateBigramPredictions();
1546d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
15476558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        }
15486558253160e2039c87f424bd814f402ecd31de3bKen Wakasa
1549c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1550923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Handle separator
1551196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
1552923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // In certain languages where single quote is a separator, it's better
1553a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker            // not to auto correct, but accept the typed word. For instance,
1554923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // in Italian dov' should not be expanded to dove' because the elision
1555923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // requires the last vowel to be removed.
1556297e6d590bd957577c335aa8713a786145a70288Jean Chalard            final boolean shouldAutoCorrect = mCurrentSettings.mAutoCorrectEnabled
1557dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                    && !mInputAttributes.mInputTypeNoAutoCorrect;
155817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
15595475b38328171a0841ae18074bd45380ec567e90Jean Chalard                commitCurrentAutoCorrection(primaryCode);
1560c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = true;
1561923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
15625475b38328171a0841ae18074bd45380ec567e90Jean Chalard                commitTyped(primaryCode);
1563923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1564923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
15654ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1566e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard        final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
1567e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
1568863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
156900ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
1570297e6d590bd957577c335aa8713a786145a70288Jean Chalard                mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
157100ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard            sendKeyCodePoint(Keyboard.CODE_SPACE);
157200ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        }
15737a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        sendKeyCodePoint(primaryCode);
157489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
157589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (Keyboard.CODE_SPACE == primaryCode) {
1576120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            if (isSuggestionsRequested()) {
1577bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                if (maybeDoubleSpace()) {
1578120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_DOUBLE;
1579120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                } else if (!isShowingPunctuationList()) {
1580120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_WEAK;
1581120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                }
1582126698fdd256a2e3734634d3b923cabd800064baJean Chalard            }
1583120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1584120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mHandler.startDoubleSpacesTimer();
1585297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (!mConnection.isCursorTouchingWord(mCurrentSettings)) {
158689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                mHandler.cancelUpdateSuggestions();
1587cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                mHandler.postUpdateBigramPredictions();
158889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            }
158989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
1590fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (swapWeakSpace) {
1591bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                swapSwapperAndSpace();
15924721427c7de3600f6fe7dfff16508a6a974fb3e4Jean Chalard                mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
1593fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            } else if (SPACE_STATE_PHANTOM == spaceState) {
1594fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // If we are in phantom space state, and the user presses a separator, we want to
1595fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // stay in phantom space state so that the next keypress has a chance to add the
1596fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // space. For example, if I type "Good dat", pick "day" from the suggestion strip
1597fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // then insert a comma and go on to typing the next word, I want the space to be
1598fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // inserted automatically before the next word, the same way it is when I don't
1599fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // input the comma.
1600fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                mSpaceState = SPACE_STATE_PHANTOM;
1601120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1602120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
160389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Set punctuation right away. onUpdateSelection will fire but tests whether it is
160489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // already displayed or not, so it's okay.
160555b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            setPunctuationSuggestions();
1606923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1607120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1608406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)primaryCode, x, y);
1609120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1610c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        return didAutoCorrect;
1611923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1612466741d8a78965b8509bf527344f289e50873092Mike LeBeau
161377d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    private CharSequence getTextWithUnderline(final CharSequence text) {
16140a59ac2ba88ac1f99151d9336136bc6fe7d416c0Jean Chalard        return mIsAutoCorrectionIndicatorOn
161577d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
1616fda847a870829f1491cbd5325f9c985213081149Jean Chalard                : text;
161777d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    }
161877d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard
1619923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void handleClose() {
16205475b38328171a0841ae18074bd45380ec567e90Jean Chalard        commitTyped(LastComposedWord.NOT_A_SEPARATOR);
1621923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        requestHideSelf(0);
1622c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
16231679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        if (inputView != null)
16241679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka            inputView.closing();
1625923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1626923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
16277a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isSuggestionsRequested() {
16284b9e6ee6fb408e2793e9ead5de2d6b45270d1f0dJean Chalard        // TODO: move this method to mSettingsValues
1629dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        return mInputAttributes.mIsSettingsSuggestionStripOn
1630297e6d590bd957577c335aa8713a786145a70288Jean Chalard                && (mCurrentSettings.mCorrectionMode > 0 || isShowingSuggestionsStrip());
1631923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1632a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
16337a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingPunctuationList() {
1634cb8cb95d0afd340de9f1a0e15948f0068d2450bcKen Wakasa        if (mSuggestionsView == null) return false;
1635297e6d590bd957577c335aa8713a786145a70288Jean Chalard        return mCurrentSettings.mSuggestPuncList == mSuggestionsView.getSuggestions();
16367599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
16377599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
16387a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingSuggestionsStrip() {
1639297e6d590bd957577c335aa8713a786145a70288Jean Chalard        return mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation);
16407599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
16417599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1642913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    public boolean isSuggestionsStripVisible() {
1643913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView == null)
1644b5e00d5841b946de8970875231456228ae0eb6b1Ken Wakasa            return false;
1645d0c5f9395a1b94e8425982e353d090f972dc44f0Jean Chalard        if (mSuggestionsView.isShowingAddToDictionaryHint())
16469fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
16479fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        if (!isShowingSuggestionsStrip())
16489fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return false;
1649dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn)
16509fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
16519fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        return isSuggestionsRequested();
1652923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1653923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1654409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void switchToKeyboardView() {
1655060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (DEBUG) {
1656060efb6d82af1f896d90cb845c8ae07c726f85e1satok            Log.d(TAG, "Switch to keyboard view.");
1657060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
16589bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
16599bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_switchToKeyboardView();
16609bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
1661c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        View v = mKeyboardSwitcher.getKeyboardView();
1662060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (v != null) {
1663060efb6d82af1f896d90cb845c8ae07c726f85e1satok            // Confirms that the keyboard view doesn't have parent view.
1664060efb6d82af1f896d90cb845c8ae07c726f85e1satok            ViewParent p = v.getParent();
1665060efb6d82af1f896d90cb845c8ae07c726f85e1satok            if (p != null && p instanceof ViewGroup) {
1666060efb6d82af1f896d90cb845c8ae07c726f85e1satok                ((ViewGroup) p).removeView(v);
16675a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            }
1668060efb6d82af1f896d90cb845c8ae07c726f85e1satok            setInputView(v);
1669060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
1670913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1671060efb6d82af1f896d90cb845c8ae07c726f85e1satok        updateInputViewShown();
1672060efb6d82af1f896d90cb845c8ae07c726f85e1satok        mHandler.postUpdateSuggestions();
1673466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1674466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1675409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void clearSuggestions() {
1676dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setSuggestions(SuggestedWords.EMPTY, false);
16777204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
1678466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1679466741d8a78965b8509bf527344f289e50873092Mike LeBeau
16802be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard    private void setSuggestions(final SuggestedWords words, final boolean isAutoCorrection) {
1681913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null) {
1682913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setSuggestions(words);
1683dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard            mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection);
1684466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
1685d02783cb63293507e8544ea60d07559092ce83d4Jean Chalard    }
1686ec780e2868962bf17f0dfd35d36895f543bde40asatok
168738e535e59676ac4d7bf27026fe3e16fcd9eb292eJean Chalard    private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) {
1688ec780e2868962bf17f0dfd35d36895f543bde40asatok        // Put a blue underline to a word in TextView which will be auto-corrected.
16892be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard        if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
16902be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                && mWordComposer.isComposingWord()) {
16912be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
16922be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            final CharSequence textWithUnderline =
16932be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                    getTextWithUnderline(mWordComposer.getTypedWord());
16945475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.setComposingText(textWithUnderline, 1);
1695ec780e2868962bf17f0dfd35d36895f543bde40asatok        }
1696466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1697466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1698409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void updateSuggestions() {
1699923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if we have a suggestion engine attached.
1700911b8f9d19c1c4903eeef29b43176cfeaa0e5d0cKen Wakasa        if ((mSuggest == null || !isSuggestionsRequested())) {
1701edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            if (mWordComposer.isComposingWord()) {
1702edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                Log.w(TAG, "Called updateSuggestions but suggestions were not requested!");
1703edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
1704edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            }
1705923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1706923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1707466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1708cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateSuggestions();
1709cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateBigramPredictions();
1710cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard
1711196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) {
1712ca26f20fa4903de46e374babbfba8c8a1a5cac93satok            setPunctuationSuggestions();
1713923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1714923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1715979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
17169f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        // TODO: May need a better way of retrieving previous word
1717297e6d590bd957577c335aa8713a786145a70288Jean Chalard        final CharSequence prevWord = mConnection.getPreviousWord(mCurrentSettings.mWordSeparators);
1718dc5dfe37e06eb1d550d7aa5156cff226334d4e1eJean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
17192be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard        // getSuggestedWords handles gracefully a null value of prevWord
17205b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard        final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
17214b9e6ee6fb408e2793e9ead5de2d6b45270d1f0dJean Chalard                prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(),
1722297e6d590bd957577c335aa8713a786145a70288Jean Chalard                mCurrentSettings.mCorrectionMode);
1723923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1724a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // Basically, we update the suggestion strip only when suggestion count > 1.  However,
1725a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // there is an exception: We update the suggestion strip whenever typed word's length
1726f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // is 1 or typed word is found in dictionary, regardless of suggestion count.  Actually,
1727f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
1728f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // need to clear the previous state when the user starts typing a word (i.e. typed word's
1729f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // length == 1).
17305b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard        if (suggestedWords.size() > 1 || typedWord.length() == 1
17315b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard                || !suggestedWords.mAllowsToBeAutoCorrected
1732838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard                || mSuggestionsView.isShowingAddToDictionaryHint()) {
17335b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard            showSuggestions(suggestedWords, typedWord);
1734838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard        } else {
1735838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard            SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
1736297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (previousSuggestions == mCurrentSettings.mSuggestPuncList) {
1737838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard                previousSuggestions = SuggestedWords.EMPTY;
1738fe1a6d961cf039357f061482461e4d2e951ad346satok            }
17394ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalard            final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
174088bf1ba5263f5a5c1df367ddc401db4109ef8677Jean Chalard                    SuggestedWords.getTypedWordAndPreviousSuggestions(
17414ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalard                            typedWord, previousSuggestions);
17427d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard            final SuggestedWords obsoleteSuggestedWords =
1743bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard                    new SuggestedWords(typedWordAndPreviousSuggestions,
17442e2519ee914d4bf9462950553840557a4c19faedJean Chalard                            false /* typedWordValid */,
1745bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard                            false /* hasAutoCorrectionCandidate */,
1746b5eeb724fc98bb7169683539027d9ba54ffb8b14Jean Chalard                            false /* allowsToBeAutoCorrected */,
174703a35170751a635332c00bf6c272a0127a255cf6Jean Chalard                            false /* isPunctuationSuggestions */,
17480142b997bf18f5d07e83b3fd403f0b3ea4736040satok                            true /* isObsoleteSuggestions */,
17490142b997bf18f5d07e83b3fd403f0b3ea4736040satok                            false /* isPrediction */);
17507d55c891afdf7e74e505acac998a95a9ca7a9ec2Jean Chalard            showSuggestions(obsoleteSuggestedWords, typedWord);
17519fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        }
1752979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
17534a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
1754d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard    public void showSuggestions(final SuggestedWords suggestedWords, final CharSequence typedWord) {
1755d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard        final CharSequence autoCorrection;
17567e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        if (suggestedWords.size() > 0) {
175768823ae08e820f0951447ed12c1bd32a24333d2eJean Chalard            if (suggestedWords.hasAutoCorrectionWord()) {
1758d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard                autoCorrection = suggestedWords.getWord(1);
1759923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1760d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard                autoCorrection = typedWord;
1761923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1762923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1763d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard            autoCorrection = null;
1764923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1765d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard        mWordComposer.setAutoCorrection(autoCorrection);
17669b01890254c62a30b079bd9f79a30f9541faf11bJean Chalard        final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
1767dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setSuggestions(suggestedWords, isAutoCorrection);
1768dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setAutoCorrectionIndicator(isAutoCorrection);
1769913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1770923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1771923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
17725475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void commitCurrentAutoCorrection(final int separatorCodePoint) {
1773913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // Complete any pending suggestions query first
1774d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        if (mHandler.hasPendingUpdateSuggestions()) {
1775d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.cancelUpdateSuggestions();
1776923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            updateSuggestions();
1777923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1778117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
1779117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        if (autoCorrection != null) {
1780117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            final String typedWord = mWordComposer.getTypedWord();
178146798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            if (TextUtils.isEmpty(typedWord)) {
178246798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                throw new RuntimeException("We have an auto-correction but the typed word "
178346798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                        + "is empty? Impossible! I must commit suicide.");
178446798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            }
1785f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard            Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint);
178660adb8757496fecb8f376a80832c176b35e43d06Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
1787d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord,
178860adb8757496fecb8f376a80832c176b35e43d06Kurt Partridge                        autoCorrection.toString());
178960adb8757496fecb8f376a80832c176b35e43d06Kurt Partridge            }
17904733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
179166bb563535dbe3672f99f75bd71763a551444867Jean Chalard            commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
179266bb563535dbe3672f99f75bd71763a551444867Jean Chalard                    separatorCodePoint);
17935475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (!typedWord.equals(autoCorrection)) {
17941c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // This will make the correction flash for a short while as a visual clue
17951c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // to the user that auto-correction happened.
17965475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.commitCorrection(
17975475b38328171a0841ae18074bd45380ec567e90Jean Chalard                        new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
179896fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasa                        typedWord, autoCorrection));
17991c6cf26c3705e845418a29718c034598b52293ccJean Chalard            }
1800923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1801923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1802923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1803c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
18049bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge    public void pickSuggestionManually(final int index, final CharSequence suggestion,
18059bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge            int x, int y) {
18065475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.beginBatchEdit(getCurrentInputConnection());
18075475b38328171a0841ae18074bd45380ec567e90Jean Chalard        pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y);
18085475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.endBatchEdit();
1809d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard    }
18104f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
1811d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard    public void pickSuggestionManuallyWhileInBatchEdit(final int index,
18125475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence suggestion, final int x, final int y) {
1813d4cacb1e41263ea4d78e7328f9d7ee173b79c4eaJean Chalard        final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
1814551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
1815551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        if (suggestion.length() == 1 && isShowingPunctuationList()) {
1816551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Word separators are suggested before the user inputs something.
1817551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // So, LatinImeLogger logs "" as a user's input.
1818551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords);
1819551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
1820551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            if (ProductionFlag.IS_EXPERIMENTAL) {
1821551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard                ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y);
1822551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            }
1823551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            final int primaryCode = suggestion.charAt(0);
1824551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            onCodeInput(primaryCode,
1825551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard                    KeyboardActionListener.SUGGESTION_STRIP_COORDINATE,
1826551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard                    KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
1827551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            return;
1828551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        }
1829551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard
1830845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard        if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0) {
1831845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            int firstChar = Character.codePointAt(suggestion, 0);
1832297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if ((!mCurrentSettings.isWeakSpaceStripper(firstChar))
1833297e6d590bd957577c335aa8713a786145a70288Jean Chalard                    && (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) {
1834845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard                sendKeyCodePoint(Keyboard.CODE_SPACE);
1835845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            }
1836845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard        }
1837845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard
1838dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn
1839dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && mApplicationSpecifiedCompletions != null
18401b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
1841913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsView != null) {
1842913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsView.clear();
1843923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1844b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1845611a5bdf650f7bc54bf40176f84f1cd9c87aa9aaJean Chalard            resetComposingState(true /* alsoResetLastComposedWord */);
18465475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
18475475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitCompletion(completionInfo);
18485475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (ProductionFlag.IS_EXPERIMENTAL) {
18495475b38328171a0841ae18074bd45380ec567e90Jean Chalard                ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index,
18505475b38328171a0841ae18074bd45380ec567e90Jean Chalard                        completionInfo.getText(), x, y);
18519a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            }
1852923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1853923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
18546a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
1855af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // We need to log before we commit, because the word composer will store away the user
1856af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // typed word.
18579bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        final String replacedWord = mWordComposer.getTypedWord().toString();
18589bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        LatinImeLogger.logOnManualSuggestion(replacedWord,
18598cc8f26adfe0f06cebc697dac43a856326cf7afcTadashi G. Takaoka                suggestion.toString(), index, suggestedWords);
18609bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
18619bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge            ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y);
18629bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        }
18634733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = true;
186466bb563535dbe3672f99f75bd71763a551444867Jean Chalard        commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
186566bb563535dbe3672f99f75bd71763a551444867Jean Chalard                LastComposedWord.NOT_A_SEPARATOR);
186629a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        // Don't allow cancellation of manual pick
186729a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        mLastComposedWord.deactivate();
1868fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mSpaceState = SPACE_STATE_PHANTOM;
1869fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        // TODO: is this necessary?
1870fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mKeyboardSwitcher.updateShiftState();
1871979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1872c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // We should show the "Touch again to save" hint if the user pressed the first entry
1873c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // AND either:
18747f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is no dictionary (we know that because we tried to load it => null != mSuggest
1875c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        //   AND mSuggest.hasMainDictionary() is false)
18767f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is a dictionary and the word is not in it
18777f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // Please note that if mSuggest is null, it means that everything is off: suggestion
18787f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // and correction, so we shouldn't try to show the hint
18797f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // We used to look at mCorrectionMode here, but showing the hint should have nothing
18807f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // to do with the autocorrection setting.
1881bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
1882bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                // If there is no dictionary the hint should be shown.
1883c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa                && (!mSuggest.hasMainDictionary()
1884bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        // If "suggestion" is not in the dictionary, the hint should be shown.
1885bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        || !AutoCorrection.isValidWord(
1886bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                                mSuggest.getUnigramDictionaries(), suggestion, true));
1887b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
1888406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE,
1889406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard                WordComposer.NOT_A_COORDINATE);
1890777118a40a363ccab69a00016d3156066513cb78Jean Chalard        if (!showingAddToDictionaryHint) {
1891364da8c618303a7764595d2c15ee034a7671365dKen Wakasa            // If we're not showing the "Touch again to save", then show corrections again.
1892979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // In case the cursor position doesn't change, make sure we show the suggestions again.
189341ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            updateBigramPredictions();
189441ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // Updating the predictions right away may be slow and feel unresponsive on slower
189541ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // terminals. On the other hand if we just postUpdateBigramPredictions() it will
189641ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // take a noticeable delay to update them which may feel uneasy.
189795a6e58ebcd8f44b970a8238cf30e48b10fc4b61Jean Chalard        } else {
189888562bec54658840dcce352127bdc15705c20a89Jean Chalard            if (mIsUserDictionaryAvailable) {
1899644d33d60ea5a87501274488013d65f55238895eKen Wakasa                mSuggestionsView.showAddToDictionaryHint(
1900297e6d590bd957577c335aa8713a786145a70288Jean Chalard                        suggestion, mCurrentSettings.mHintToSaveText);
1901ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            } else {
1902ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka                mHandler.postUpdateSuggestions();
1903ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            }
190466a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani        }
1905923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1906a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1907979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
19088558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa     * Commits the chosen word to the text field and saves it for later retrieval.
1909979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
1910c54d558e2e70bdfb2c1078cae7b88440d421dc67satok    private void commitChosenWord(final CharSequence chosenWord, final int commitType,
191166bb563535dbe3672f99f75bd71763a551444867Jean Chalard            final int separatorCode) {
1912297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (mCurrentSettings.mEnableSuggestionSpanInsertion) {
19135475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
19145475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
19155475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    this, chosenWord, suggestedWords, mIsMainDictionaryAvailable),
19165475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    1);
19175475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (ProductionFlag.IS_EXPERIMENTAL) {
19185475b38328171a0841ae18074bd45380ec567e90Jean Chalard                ResearchLogger.latinIME_commitText(chosenWord);
19195475b38328171a0841ae18074bd45380ec567e90Jean Chalard            }
19205475b38328171a0841ae18074bd45380ec567e90Jean Chalard        } else {
19215475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(chosenWord, 1);
19225475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (ProductionFlag.IS_EXPERIMENTAL) {
19235475b38328171a0841ae18074bd45380ec567e90Jean Chalard                ResearchLogger.latinIME_commitText(chosenWord);
19241531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            }
1925923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1926c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        // Add the word to the user history dictionary
1927c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
19280fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // TODO: figure out here if this is an auto-correct or if the best word is actually
19290fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // what user typed. Note: currently this is done much later in
1930bdf89ce5feedb03e67b43f530b1eb9bd44203c63Jean Chalard        // LastComposedWord#didCommitTypedWord by string equality of the remembered
19310fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // strings.
1932c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord.toString(),
1933c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                separatorCode, prevWord);
1934923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1935923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
193641ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard    public void updateBigramPredictions() {
193789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (mSuggest == null || !isSuggestionsRequested())
193889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            return;
193989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
1940297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (!mCurrentSettings.mBigramPredictionEnabled) {
1941cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            setPunctuationSuggestions();
1942cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            return;
1943cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        }
1944cb3320179d39a7983874697a0aa428b127675c9dJean Chalard
19458e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard        final SuggestedWords suggestedWords;
1946297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (mCurrentSettings.mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
1947297e6d590bd957577c335aa8713a786145a70288Jean Chalard            final CharSequence prevWord = mConnection.getThisWord(mCurrentSettings.mWordSeparators);
19480cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard            if (!TextUtils.isEmpty(prevWord)) {
19495b0643f50d8c363296360f3ceaf32f7edc157141Jean Chalard                suggestedWords = mSuggest.getBigramPredictions(prevWord);
19500cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard            } else {
19518e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard                suggestedWords = null;
19520cf422fbb763e2672fb2f9e8e1e8af91d2e87cb3Jean Chalard            }
1953a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard        } else {
19548e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard            suggestedWords = null;
1955a333ff19ef330c93287cfa0f6568d0cdcd431b04Jean Chalard        }
195689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
19578e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard        if (null != suggestedWords && suggestedWords.size() > 0) {
195889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Explicitly supply an empty typed word (the no-second-arg version of
195989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // showSuggestions will retrieve the word near the cursor, we don't want that here)
19608e19b1183e4925b7c396de45a5e4e7d67a7b876aJean Chalard            showSuggestions(suggestedWords, "");
196189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
196289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            if (!isShowingPunctuationList()) setPunctuationSuggestions();
196389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
196489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    }
196589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
19667a8dac55278cedd838be325f56b4c52d973c61f5satok    public void setPunctuationSuggestions() {
1967297e6d590bd957577c335aa8713a786145a70288Jean Chalard        setSuggestions(mCurrentSettings.mSuggestPuncList, false);
19687204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
1969913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
19706a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani    }
19716a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
1972c54d558e2e70bdfb2c1078cae7b88440d421dc67satok    private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) {
1973c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (TextUtils.isEmpty(suggestion)) return null;
1974bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
19750c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
19760c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // adding words in situations where the user or application really didn't
19770c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // want corrections enabled or learned.
1978297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (!(mCurrentSettings.mCorrectionMode == Suggest.CORRECTION_FULL
1979297e6d590bd957577c335aa8713a786145a70288Jean Chalard                || mCurrentSettings.mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
1980c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            return null;
1981979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
1982bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
19839ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard        if (mUserHistoryDictionary != null) {
198402308bec632a5df23325c916bffec5def16b22b4Jean Chalard            final CharSequence prevWord
1985297e6d590bd957577c335aa8713a786145a70288Jean Chalard                    = mConnection.getPreviousWord(mCurrentSettings.mWordSeparators);
198671f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            final String secondWord;
198771f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
19886a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka                secondWord = suggestion.toString().toLowerCase(
19896a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka                        mSubtypeSwitcher.getCurrentSubtypeLocale());
199071f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            } else {
199171f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard                secondWord = suggestion.toString();
199271f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            }
1993c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka            // We demote unrecognized word and words with 0-frequency (assuming they would be
1994c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka            // profanity etc.) by specifying them as "invalid".
1995c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka            final int maxFreq = AutoCorrection.getMaxFrequency(
1996c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka                    mSuggest.getUnigramDictionaries(), suggestion);
19979ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard            mUserHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
1998c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka                    secondWord, maxFreq > 0);
1999c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            return prevWord;
200032e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani        }
2001c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        return null;
200232e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani    }
200332e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani
20046b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
20056b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
20066b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * word, else do nothing.
20076b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
20085475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
2009747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard        final CharSequence word = mConnection.getWordBeforeCursorIfAtEndOfWord(mCurrentSettings);
2010747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard        if (null != word) {
2011747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard            restartSuggestionsOnWordBeforeCursor(word);
2012fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        }
20136b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
20146b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
20155475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void restartSuggestionsOnWordBeforeCursor(final CharSequence word) {
20163708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
2017d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int length = word.length();
20185475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(length, 0);
2019d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2020d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(length);
2021d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
20225475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.setComposingText(word, 1);
20236b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mHandler.postUpdateSuggestions();
20246b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
20256b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
20265475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void revertCommit() {
2027c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        final CharSequence previousWord = mLastComposedWord.mPrevWord;
2028b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        final String originallyTypedWord = mLastComposedWord.mTypedWord;
2029cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final CharSequence committedWord = mLastComposedWord.mCommittedWord;
2030cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final int cancelLength = committedWord.length();
2031ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        final int separatorLength = LastComposedWord.getSeparatorLength(
2032193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                mLastComposedWord.mSeparatorCode);
2033193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        // TODO: should we check our saved separator against the actual contents of the text view?
2034d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int deleteLength = cancelLength + separatorLength;
2035890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2036b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            if (mWordComposer.isComposingWord()) {
20375935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit, but we are composing a word");
2038b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            }
2039890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            final String wordBeforeCursor =
20405475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.getTextBeforeCursor(deleteLength, 0)
2041193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                            .subSequence(0, cancelLength).toString();
2042cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard            if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
20435935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit check failed: we thought we were "
2044cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard                        + "reverting \"" + committedWord
2045890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
2046890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
20478558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        }
20485475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(deleteLength, 0);
2049d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2050d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(deleteLength);
2051d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
2052c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
2053c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            mUserHistoryDictionary.cancelAddingUserHistory(
2054c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    previousWord.toString(), committedWord.toString());
2055c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        }
2056193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) {
2057193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // This is the case when we cancel a manual pick.
2058193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // We should restart suggestion on the word right away.
205932f0af1fc48f67907f0e731e18359f29e2b1df14Jean Chalard            mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
20605475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.setComposingText(originallyTypedWord, 1);
2061193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        } else {
20625475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(originallyTypedWord, 1);
2063193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // Re-insert the separator
2064193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
2065193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
2066193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                    WordComposer.NOT_A_COORDINATE);
2067d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
2068d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge                ResearchLogger.latinIME_revertCommit(originallyTypedWord);
2069d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
2070193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // Don't restart suggestion yet. We'll restart if the user deletes the
2071193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // separator.
2072193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        }
2073b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
2074890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.cancelUpdateBigramPredictions();
2075890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.postUpdateSuggestions();
2076890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    }
2077890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard
20785475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private boolean revertDoubleSpaceWhileInBatchEdit() {
20794733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // Here we test whether we indeed have a period and a space before us. This should not
20804733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // be needed, but it's there just in case something went wrong.
20815475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence textBeforeCursor = mConnection.getTextBeforeCursor(2, 0);
20828ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        if (!". ".equals(textBeforeCursor)) {
208351fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // Theoretically we should not be coming here if there isn't ". " before the
208451fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // cursor, but the application may be changing the text while we are typing, so
208551fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // anything goes. We should not crash.
208651fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            Log.d(TAG, "Tried to revert double-space combo but we didn't find "
20878ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "\". \" just before the cursor.");
208851fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            return false;
20898ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
20905475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(2, 0);
2091d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2092d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(2);
2093d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
20945475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.commitText("  ", 1);
2095d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2096d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit();
2097d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
20984733609947c0ec74e460bd714fffca0518ade93aJean Chalard        return true;
20994733609947c0ec74e460bd714fffca0518ade93aJean Chalard    }
21004733609947c0ec74e460bd714fffca0518ade93aJean Chalard
21015475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private boolean revertSwapPunctuation() {
2102120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // Here we test whether we indeed have a space and something else before us. This should not
2103120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // be needed, but it's there just in case something went wrong.
21045475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence textBeforeCursor = mConnection.getTextBeforeCursor(2, 0);
2105120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
2106120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // enter surrogate pairs this code will have been removed.
21078be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard        if (TextUtils.isEmpty(textBeforeCursor)
21088be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard                || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
21098be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // We may only come here if the application is changing the text while we are typing.
21108be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // This is quite a broken case, but not logically impossible, so we shouldn't crash,
21118be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // but some debugging log may be in order.
21128be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
21138ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "find a space just before the cursor.");
21148be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            return false;
21158ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
21165475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(2, 0);
2117d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2118d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_deleteSurroundingText(2);
2119d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
21205475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
2121d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
2122d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            ResearchLogger.latinIME_revertSwapPunctuation();
2123d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        }
2124120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return true;
2125120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    }
2126120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
2127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean isWordSeparator(int code) {
2128297e6d590bd957577c335aa8713a786145a70288Jean Chalard        return mCurrentSettings.isWordSeparator(code);
2129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2130923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2131923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean preferCapitalization() {
21329318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa        return mWordComposer.isFirstCharCapitalized();
2133923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2134923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
213588fc9d44186120f9edc5cf7ec0e2af85260fed04satok    // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
2136c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    // according to new language or mode.
2137c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    public void onRefreshKeyboard() {
21381e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // When the device locale is changed in SetupWizard etc., this method may get called via
21391e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // onConfigurationChanged before SoftInputWindow is shown.
21401e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        if (mKeyboardSwitcher.getKeyboardView() != null) {
21411e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            // Reload keyboard because the current language has been changed.
2142297e6d590bd957577c335aa8713a786145a70288Jean Chalard            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
21431e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        }
21440ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        initSuggest();
214517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
2146b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // Since we just changed languages, we should re-evaluate suggestions with whatever word
2147b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // we are currently composing. If we are not composing anything, we may want to display
2148b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // predictions or punctuation signs (which is done by updateBigramPredictions anyway).
2149297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (mConnection.isCursorTouchingWord(mCurrentSettings)) {
2150b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard            mHandler.postUpdateSuggestions();
2151b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        } else {
2152b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard            mHandler.postUpdateBigramPredictions();
2153b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        }
215436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
215536fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
215678e3977e90a9946a057dfb628f99683e386015bdTadashi G. Takaoka    // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
215778e3977e90a9946a057dfb628f99683e386015bdTadashi G. Takaoka    // {@link KeyboardSwitcher}.
2158564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    public void hapticAndAudioFeedback(final int primaryCode) {
2159544c3c29527927239a6484efc30bc22f9cc4dad1Jean Chalard        mFeedbackManager.hapticAndAudioFeedback(primaryCode, mKeyboardSwitcher.getKeyboardView());
2160d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka    }
2161d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka
21625a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
21632a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    public void onPressKey(int primaryCode) {
2164a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        mKeyboardSwitcher.onPressKey(primaryCode);
2165923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2166923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
21675a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
21682a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    public void onReleaseKey(int primaryCode, boolean withSliding) {
21692a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
21708d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv
21718d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        // If accessibility is on, ensure the user receives keyboard state updates.
21728d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
21738d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            switch (primaryCode) {
21748d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            case Keyboard.CODE_SHIFT:
21758d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifyShiftState();
21768d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
21778d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
21788d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifySymbolsState();
21798d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
21808d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            }
21818d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        }
2182978c96aa995015658070346b60826a3a34fdaf84Jean Chalard
2183978c96aa995015658070346b60826a3a34fdaf84Jean Chalard        if (Keyboard.CODE_DELETE == primaryCode) {
2184978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // This is a stopgap solution to avoid leaving a high surrogate alone in a text view.
2185978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // In the future, we need to deprecate deteleSurroundingText() and have a surrogate
2186978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // pair-friendly way of deleting characters in InputConnection.
21875475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0);
21885475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
21895475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(1, 0);
2190978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            }
2191978c96aa995015658070346b60826a3a34fdaf84Jean Chalard        }
2192923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2193a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2194123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka    // receive ringer mode change and network state change.
2195923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
2196923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
2197923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void onReceive(Context context, Intent intent) {
2198123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            final String action = intent.getAction();
2199564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
2200123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                mSubtypeSwitcher.onNetworkStateChanged(intent);
220121af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard            } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
220221af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard                mFeedbackManager.onRingerModeChanged();
2203123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            }
2204923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2205923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    };
2206923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2207c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka    private void launchSettings() {
2208c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka        launchSettingsClass(SettingsActivity.class);
2209466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2210466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2211bf96661d33d0126adb60a48880ceba1ff055d4a4satok    public void launchDebugSettings() {
2212c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka        launchSettingsClass(DebugSettingsActivity.class);
2213bf96661d33d0126adb60a48880ceba1ff055d4a4satok    }
2214bf96661d33d0126adb60a48880ceba1ff055d4a4satok
2215c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka    private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
2216923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        handleClose();
2217923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        Intent intent = new Intent();
2218466741d8a78965b8509bf527344f289e50873092Mike LeBeau        intent.setClass(LatinIME.this, settingsClass);
2219923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2220923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        startActivity(intent);
2221923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2222923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
22232fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showSubtypeSelectorAndSettings() {
222485996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
222585996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
222685996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                // TODO: Should use new string "Select active input modes".
222785996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.language_selection_title),
222885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.english_ime_settings),
222985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
223055d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka        final Context context = this;
223185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
22322fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            @Override
22332fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            public void onClick(DialogInterface di, int position) {
22342fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                di.dismiss();
22352fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                switch (position) {
223685996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
22372cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok                    Intent intent = CompatUtils.getInputLanguageSelectionIntent(
223855d28fd1b2631a63542a647f693d8a8ed749bcf7Tadashi G. Takaoka                            ImfUtils.getInputMethodIdOfThisIme(context),
22392a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Intent.FLAG_ACTIVITY_NEW_TASK
22402fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
22412fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_CLEAR_TOP);
22422fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    startActivity(intent);
22432fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2244aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                case 1:
2245aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    launchSettings();
2246aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    break;
22472fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                }
22482fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            }
224985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2250bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2251bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2252bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2253724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge        showOptionDialog(builder.create());
22542fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    }
2255923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2256724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge    /* package */ void showOptionDialog(AlertDialog dialog) {
22576a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
225813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        if (windowToken == null) return;
225913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
226013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCancelable(true);
226113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCanceledOnTouchOutside(true);
226213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
226313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final Window window = dialog.getWindow();
226413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final WindowManager.LayoutParams lp = window.getAttributes();
226513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.token = windowToken;
226613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
226713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.setAttributes(lp);
226813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
226913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
227013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        mOptionsDialog = dialog;
227113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.show();
227213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    }
227313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
22747e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
22757e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2276923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.dump(fd, fout, args);
2277a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2278923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        final Printer p = new PrintWriterPrinter(fout);
2279923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("LatinIME state :");
22803708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
2281df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
2282df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        p.println("  Keyboard mode = " + keyboardMode);
2283dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        p.println("  mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn);
2284297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mCorrectionMode=" + mCurrentSettings.mCorrectionMode);
22854d0f03bd66742ee292da81a3e025e119f28b6940Jean Chalard        p.println("  isComposingWord=" + mWordComposer.isComposingWord());
2286297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mAutoCorrectEnabled=" + mCurrentSettings.mAutoCorrectEnabled);
2287297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mSoundOn=" + mCurrentSettings.mSoundOn);
2288297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mVibrateOn=" + mCurrentSettings.mVibrateOn);
2289297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn);
22906fa003ee234c6e2ca6cd9ec555221ac5c71a5405Jean Chalard        p.println("  mInputAttributes=" + mInputAttributes.toString());
2291923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2292923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
2293