LatinIME.java revision edbb65be3e5e391affbf47e134422ac1d1a4ae13
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
233c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridgeimport android.app.Activity;
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.app.AlertDialog;
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.BroadcastReceiver;
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
27923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.DialogInterface;
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Intent;
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.IntentFilter;
30923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.SharedPreferences;
31b224b60c94d85f30de93f66685adf06e662618c0Jean Chalardimport android.content.pm.ApplicationInfo;
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.res.Configuration;
3336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.content.res.Resources;
34c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaokaimport android.graphics.Rect;
35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.inputmethodservice.InputMethodService;
36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.media.AudioManager;
37123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaokaimport android.net.ConnectivityManager;
38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Debug;
39f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaokaimport android.os.Handler;
40f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaokaimport android.os.HandlerThread;
4181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaokaimport android.os.IBinder;
42923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Message;
43923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.SystemClock;
44923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.preference.PreferenceManager;
45e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.text.InputType;
46923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils;
47923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Log;
48923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.PrintWriterPrinter;
49923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Printer;
504e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalardimport android.view.KeyCharacterMap;
514e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalardimport android.view.KeyEvent;
52923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View;
53c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaokaimport android.view.ViewGroup.LayoutParams;
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;
6315f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.UsedForTesting;
64c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.CompatUtils;
65fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasaimport com.android.inputmethod.compat.InputMethodServiceCompatUtils;
661fef530ec7626fa16777f52b48191e61db8f46d4satokimport com.android.inputmethod.compat.SuggestionSpanUtils;
675c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyDetector;
68c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
69c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardActionListener;
706b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardId;
71c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardSwitcher;
72f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView;
73c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaokaimport com.android.inputmethod.keyboard.MainKeyboardView;
7416c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaokaimport com.android.inputmethod.latin.LocaleUtils.RunInLocale;
7587cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataokaimport com.android.inputmethod.latin.Utils.Stats;
76c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
774702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaokaimport com.android.inputmethod.latin.suggestions.SuggestionStripView;
786b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
79923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
80466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.FileDescriptor;
81466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.PrintWriter;
824ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalardimport java.util.ArrayList;
83466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.util.Locale;
84466741d8a78965b8509bf527344f289e50873092Mike LeBeau
85923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
86923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Input method implementation for Qwerty'ish keyboard.
87923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
88a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class LatinIME extends InputMethodService implements KeyboardActionListener,
89369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka        SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener,
90369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka        Suggest.SuggestInitializationListener {
918efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    private static final String TAG = LatinIME.class.getSimpleName();
92409220583333bdf06290dd9fd42f91b5c0d1b11asatok    private static final boolean TRACE = false;
939e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka    private static boolean DEBUG;
94a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
959e347d3d448e48229c46aad394ec9bd60cd5807bsatok    private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
96fd0bd57deb53c4cce32810a61133fa44b45dbb7bKen Wakasa
97923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // How many continuous deletes at which to start deleting at a higher speed.
98923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int DELETE_ACCELERATE_AT = 20;
99923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // Key events coming any faster than this are long-presses.
100d67fe0e7583f1be18b35b33b7658e4427f1e3dedAmith Yamasani    private static final int QUICK_PRESS = 200;
101a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
10259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private static final int PENDING_IMS_CALLBACK_DURATION = 800;
103055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
104cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /**
105cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * The name of the scheme used by the Package Manager to warn of a new package installation,
106cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * replacement or removal.
107cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     */
108cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private static final String SCHEME_PACKAGE = "package";
109cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
110fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_NONE = 0;
111120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Double space: the state where the user pressed space twice quickly, which LatinIME
112120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // resolved as period-space. Undoing this converts the period to a space.
113fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_DOUBLE = 1;
114b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
115b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // have just been swapped. Undoing this swaps them back; the space is still considered weak.
116120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
117fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
118fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // spaces happen when the user presses space, accepting the current suggestion (whether
119fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // it's an auto-correction or not).
120fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_WEAK = 3;
121fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom space: a not-yet-inserted space that should get inserted on the next input,
122fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // character provided it's not a separator. If it's a separator, the phantom space is dropped.
123fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom spaces happen when a user chooses a word from the suggestion strip.
124fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_PHANTOM = 4;
125120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
126120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Current space state of the input method. This can be any of the above constants.
127120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private int mSpaceState;
128126698fdd256a2e3734634d3b923cabd800064baJean Chalard
129297e6d590bd957577c335aa8713a786145a70288Jean Chalard    private SettingsValues mCurrentSettings;
13017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
131d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka    private View mExtractArea;
132abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka    private View mKeyPreviewBackingView;
133913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private View mSuggestionsContainer;
1344702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    private SuggestionStripView mSuggestionStripView;
13515f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting Suggest mSuggest;
1361b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private CompletionInfo[] mApplicationSpecifiedCompletions;
137b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    private ApplicationInfo mTargetApplicationInfo;
138a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1396fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka    private RichInputMethodManager mRichImm;
1402fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private Resources mResources;
1412fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private SharedPreferences mPrefs;
14215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
14389ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    private final SubtypeSwitcher mSubtypeSwitcher;
1441931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    private final SubtypeState mSubtypeState = new SubtypeState();
145a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1466a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka    private boolean mIsMainDictionaryAvailable;
14767fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard    private UserBinaryDictionary mUserDictionary;
1489ffb94fa1318f354692fab7abf4775fa14397a96Jean Chalard    private UserHistoryDictionary mUserHistoryDictionary;
14988562bec54658840dcce352127bdc15705c20a89Jean Chalard    private boolean mIsUserDictionaryAvailable;
15036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
1512692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
15287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka    private final WordComposer mWordComposer = new WordComposer();
153f254e3fec7744dc1eb2cc09ac157986c3b2b5408Jean Chalard    private RichInputConnection mConnection = new RichInputConnection(this);
154409220583333bdf06290dd9fd42f91b5c0d1b11asatok
155979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // Keep track of the last selection range to decide if we need to show word alternatives
15677da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private static final int NOT_A_CURSOR_POSITION = -1;
15777da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionStart = NOT_A_CURSOR_POSITION;
15877da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionEnd = NOT_A_CURSOR_POSITION;
159979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1604733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
1614733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // "expect" it, it means the user actually moved the cursor.
1624733609947c0ec74e460bd714fffca0518ade93aJean Chalard    private boolean mExpectingUpdateSelection;
163923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mDeleteCount;
164923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private long mLastKeyTime;
165a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
16638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    // Member variables for remembering the current device orientation.
16738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    private int mDisplayOrientation;
16838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
169cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    // Object for reacting to adding/removing a dictionary pack.
170710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa    // TODO: The experimental version is not supported by the Dictionary Pack Service yet.
171cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private BroadcastReceiver mDictionaryPackInstallReceiver =
172710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            ProductionFlag.IS_EXPERIMENTAL
173710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa                    ? null : new DictionaryPackInstallBroadcastReceiver(this);
174cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
175dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    // Keeps track of most recently inserted text (multi-character key) for reverting
176bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String mEnteredText;
177dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
17870852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard    private boolean mIsAutoCorrectionIndicatorOn;
179604d80c67185954d4691ac775be59c499eee3b1csatok
18013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    private AlertDialog mOptionsDialog;
18113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
182fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa    private final boolean mIsHardwareAcceleratedDrawingEnabled;
183fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa
1844f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public final UIHandler mHandler = new UIHandler(this);
185d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
186a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
18727e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_UPDATE_SHIFT_STATE = 0;
18827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_PENDING_IMS_CALLBACK = 1;
18927e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
190f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final int MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 3;
191f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
192f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
1934c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka
19410dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateSuggestions;
19510dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateShiftState;
1962b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        private long mDoubleSpacePeriodTimeout;
1972b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        private long mDoubleSpacePeriodTimerStart;
19838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
199f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public UIHandler(final LatinIME outerInstance) {
2004f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
20110dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        }
202175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
20310dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        public void onCreate() {
20410dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka            final Resources res = getOuterInstance().getResources();
205175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateSuggestions =
206175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_suggestions);
207175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateShiftState =
208175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_shift_state);
2092b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mDoubleSpacePeriodTimeout =
2102b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                    res.getInteger(R.integer.config_double_space_period_timeout);
2114f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
2124f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
213923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
214f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void handleMessage(final Message msg) {
2154f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
2164f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
217923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (msg.what) {
218dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            case MSG_UPDATE_SUGGESTION_STRIP:
219259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka                latinIme.updateSuggestionStrip();
220d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
221d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SHIFT_STATE:
222de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                switcher.updateShiftState();
223d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
224f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
225f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords)msg.obj,
226f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                        msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
227f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                break;
228923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
229923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
230d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
231a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        public void postUpdateSuggestionStrip() {
232dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions);
233d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
234d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
235d81e7d24d384a2bb1aeda65d9b423e4b1d23f185Jean Chalard        public void cancelUpdateSuggestionStrip() {
236dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
237d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
238d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
239d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public boolean hasPendingUpdateSuggestions() {
240dc1b84d96cf7fc4ee21cf7df8a12bc7913ffd64eJean Chalard            return hasMessages(MSG_UPDATE_SUGGESTION_STRIP);
241d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
242d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
243beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void postUpdateShiftState() {
244d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
245175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState);
246d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
247d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
248d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateShiftState() {
249d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
250d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
251d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
252f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
253f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                final boolean dismissGestureFloatingPreviewText) {
254f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
255f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final int arg1 = dismissGestureFloatingPreviewText
256f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : 0;
257f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, 0, suggestedWords)
258f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    .sendToTarget();
259f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
260f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
2612b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        public void startDoubleSpacePeriodTimer() {
2622b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mDoubleSpacePeriodTimerStart = SystemClock.uptimeMillis();
263fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
264fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
2652b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        public void cancelDoubleSpacePeriodTimer() {
2662b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mDoubleSpacePeriodTimerStart = 0;
267fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
268fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
2692b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        public boolean isAcceptingDoubleSpacePeriod() {
2702b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            return SystemClock.uptimeMillis() - mDoubleSpacePeriodTimerStart
2712b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                    < mDoubleSpacePeriodTimeout;
272fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
27338f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
27459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        // Working variables for the following methods.
27559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mIsOrientationChanging;
2765fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard        private boolean mPendingSuccessiveImsCallback;
27759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingStartInput;
27859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInputView;
27959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInput;
280e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private EditorInfo mAppliedEditorInfo;
28159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
28259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void startOrientationChanging() {
283dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            removeMessages(MSG_PENDING_IMS_CALLBACK);
284dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            resetPendingImsCallback();
28559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mIsOrientationChanging = true;
286055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            final LatinIME latinIme = getOuterInstance();
287f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            if (latinIme.isInputViewShown()) {
288f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka                latinIme.mKeyboardSwitcher.saveKeyboardState();
289f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            }
29059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
29159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
29259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private void resetPendingImsCallback() {
29359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInputView = false;
29459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInput = false;
29559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingStartInput = false;
29659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
29759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
298f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private void executePendingImsCallback(final LatinIME latinIme, final EditorInfo editorInfo,
29959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                boolean restarting) {
30059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInputView)
30159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
30259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInput)
30359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
30459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingStartInput)
305e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
30659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            resetPendingImsCallback();
30759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
30859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
309f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
31059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
31159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the second onStartInput after orientation changed.
31259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingStartInput = true;
31359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
31459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                if (mIsOrientationChanging && restarting) {
31559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    // This is the first onStartInput after orientation changed.
31659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    mIsOrientationChanging = false;
3175fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = true;
31859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                }
31959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
320e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
321e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
322055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
323055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
324055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
325f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
3266b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)
3276b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka                    && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
328e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                // Typically this is the second onStartInputView after orientation changed.
329e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                resetPendingImsCallback();
330e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            } else {
3315fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                if (mPendingSuccessiveImsCallback) {
332e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    // This is the first onStartInputView after orientation changed.
3335fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = false;
334e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    resetPendingImsCallback();
335e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
336e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            PENDING_IMS_CALLBACK_DURATION);
337e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                }
338e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
339e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
340e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputViewInternal(editorInfo, restarting);
341e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = editorInfo;
342e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            }
34359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
34459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
345f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onFinishInputView(final boolean finishingInput) {
34659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
34759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInputView after orientation changed.
34859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInputView = true;
34959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
35059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
35159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(finishingInput);
352e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = null;
35338f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            }
35438f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        }
355ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka
35659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInput() {
35759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
35859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInput after orientation changed.
35959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInput = true;
36059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
36159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
36259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                executePendingImsCallback(latinIme, null, false);
36359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
364ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka            }
365ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka        }
36638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    }
36738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
3681931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    static final class SubtypeState {
3691931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        private InputMethodSubtype mLastActiveSubtype;
3701931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        private boolean mCurrentSubtypeUsed;
3711931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka
3721931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        public void currentSubtypeUsed() {
3731931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            mCurrentSubtypeUsed = true;
3741931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        }
3751931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka
3761931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        public void switchSubtype(final IBinder token, final RichInputMethodManager richImm) {
3771931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            final InputMethodSubtype currentSubtype = richImm.getInputMethodManager()
3781931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                    .getCurrentInputMethodSubtype();
3791931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype;
3801931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            final boolean currentSubtypeUsed = mCurrentSubtypeUsed;
3811931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            if (currentSubtypeUsed) {
3821931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                mLastActiveSubtype = currentSubtype;
3831931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                mCurrentSubtypeUsed = false;
3841931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            }
3851931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            if (currentSubtypeUsed
3861931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                    && richImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastActiveSubtype)
3871931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                    && !currentSubtype.equals(lastActiveSubtype)) {
3881931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                richImm.setInputMethodAndSubtype(token, lastActiveSubtype);
3891931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka                return;
3901931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            }
3911931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka            richImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
3921931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        }
3931931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka    }
3941931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka
39589ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    public LatinIME() {
39689ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        super();
39789ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
39889ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
399fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa        mIsHardwareAcceleratedDrawingEnabled =
400fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa                InputMethodServiceCompatUtils.enableHardwareAcceleration(this);
401fb955693d4bb0c8d39092250a4208e385b8a31a7Ken Wakasa        Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled);
40289ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard    }
40389ffb212b469531db4a616afb9bb7ba6d2a56b50Jean Chalard
4047e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
4057e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    public void onCreate() {
40685e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
40785e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        mResources = getResources();
40885e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka
40985e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        LatinImeLogger.init(this, mPrefs);
410c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
41185e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka            ResearchLogger.getInstance().init(this, mPrefs);
412c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        }
41385e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        RichInputMethodManager.init(this, mPrefs);
41485e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        mRichImm = RichInputMethodManager.getInstance();
415ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka        SubtypeSwitcher.init(this);
41685e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        KeyboardSwitcher.init(this, mPrefs);
4172ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka        AccessibilityUtils.init(this);
418363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
419923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onCreate();
420363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
42110dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        mHandler.onCreate();
4229e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka        DEBUG = LatinImeLogger.sDBG;
423363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
42428f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        loadSettings();
4251f05cf6dab45c21d1ed334247929434d97c40584Tadashi G. Takaoka        initSuggest();
426979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
42785e397cd1060f3878d9a55373b7409641175179aTadashi G. Takaoka        mDisplayOrientation = mResources.getConfiguration().orientation;
428b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
429cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Register to receive ringer mode change and network state change.
430cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Also receive installation and removal of a dictionary pack.
431123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        final IntentFilter filter = new IntentFilter();
432123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
43321af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
434923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        registerReceiver(mReceiver, filter);
435cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
436710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        // TODO: The experimental version is not supported by the Dictionary Pack Service yet.
437710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        if (!ProductionFlag.IS_EXPERIMENTAL) {
438710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            final IntentFilter packageFilter = new IntentFilter();
439710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
440710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
441710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            packageFilter.addDataScheme(SCHEME_PACKAGE);
442710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
443710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa
444710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            final IntentFilter newDictFilter = new IntentFilter();
445710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            newDictFilter.addAction(
446710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa                    DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
447710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
448710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        }
449923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
45036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
45117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    // Has to be package-visible for unit tests
45215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting
4539fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    void loadSettings() {
4544f96bb4520de3610ae94da96b98e507ca7b76362satok        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
4554f96bb4520de3610ae94da96b98e507ca7b76362satok        // is not guaranteed. It may even be called at the same time on a different thread.
45617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
457b0561ae98063f83684706886490ba5670138fcccJean Chalard        final InputAttributes inputAttributes =
458b0561ae98063f83684706886490ba5670138fcccJean Chalard                new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode());
45916c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka        final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
46016c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            @Override
46116c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            protected SettingsValues job(Resources res) {
462b0561ae98063f83684706886490ba5670138fcccJean Chalard                return new SettingsValues(mPrefs, inputAttributes, LatinIME.this);
46316c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka            }
46416c6f355700ee5cdaa029f4a25b8b3d40718e6abTadashi G. Takaoka        };
465297e6d590bd957577c335aa8713a786145a70288Jean Chalard        mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
46614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
46717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    }
46817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
4698335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
470369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka    @Override
471f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onUpdateMainDictionaryAvailability(final boolean isMainDictionaryAvailable) {
472369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka        mIsMainDictionaryAvailable = isMainDictionaryAvailable;
4738335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
4748335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        if (mainKeyboardView != null) {
4758335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka            mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
4768335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        }
477369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka    }
478369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka
4790ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private void initSuggest() {
4806a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
4816a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final String localeStr = subtypeLocale.toString();
48236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
48367fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        final ContactsBinaryDictionary oldContactsDictionary;
48478ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        if (mSuggest != null) {
48578ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = mSuggest.getContactsDictionary();
48678ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            mSuggest.close();
48778ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        } else {
48878ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka            oldContactsDictionary = null;
48978ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
490369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka        mSuggest = new Suggest(this /* Context */, subtypeLocale,
491369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka                this /* SuggestInitializationListener */);
492ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        if (mCurrentSettings.mCorrectionEnabled) {
493297e6d590bd957577c335aa8713a786145a70288Jean Chalard            mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
49478ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        }
495e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
4966a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
4976080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
4986080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge            ResearchLogger.getInstance().initSuggest(mSuggest);
4996080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge        }
5006a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka
50167fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mUserDictionary = new UserBinaryDictionary(this, localeStr);
50267fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
50378ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserDictionary(mUserDictionary);
504e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
50578ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        resetContactsDictionary(oldContactsDictionary);
506e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
5074f96bb4520de3610ae94da96b98e507ca7b76362satok        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
5084f96bb4520de3610ae94da96b98e507ca7b76362satok        // is not guaranteed. It may even be called at the same time on a different thread.
5094f96bb4520de3610ae94da96b98e507ca7b76362satok        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
510b5afd3de0c20bce40a600357a15e8e8df0e62420Jean Chalard        mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, mPrefs);
51178ab80844b4f8e0369f4e86b2a02208197f9bd34Tadashi G. Takaoka        mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
512923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
51336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
51414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    /**
51514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * Resets the contacts dictionary in mSuggest according to the user settings.
51614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
5172e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang     * This method takes an optional contacts dictionary to use when the locale hasn't changed
5182e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang     * since the contacts dictionary can be opened or closed as necessary depending on the settings.
51914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
52014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * @param oldContactsDictionary an optional dictionary to use, or null
52114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     */
52267fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard    private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) {
523297e6d590bd957577c335aa8713a786145a70288Jean Chalard        final boolean shouldSetDictionary = (null != mSuggest && mCurrentSettings.mUseContactsDict);
52414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
52567fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        final ContactsBinaryDictionary dictionaryToUse;
52614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (!shouldSetDictionary) {
52714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the dictionary is closed. If it is already closed, this is a no-op,
52814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // so it's safe to call it anyways.
52914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            if (null != oldContactsDictionary) oldContactsDictionary.close();
53014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = null;
53114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
5322e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang            final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
5332e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang            if (null != oldContactsDictionary) {
53467fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                if (!oldContactsDictionary.mLocale.equals(locale)) {
53567fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // If the locale has changed then recreate the contacts dictionary. This
53667fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // allows locale dependent rules for handling bigram name predictions.
53767fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    oldContactsDictionary.close();
53805efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard                    dictionaryToUse = new ContactsBinaryDictionary(this, locale);
5392e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                } else {
54067fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // Make sure the old contacts dictionary is opened. If it is already open,
54167fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    // this is a no-op, so it's safe to call it anyways.
54267fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard                    oldContactsDictionary.reopen(this);
5432e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                    dictionaryToUse = oldContactsDictionary;
5442e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang                }
54518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            } else {
54605efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard                dictionaryToUse = new ContactsBinaryDictionary(this, locale);
54718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang            }
54814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
54914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
55014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (null != mSuggest) {
55114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            mSuggest.setContactsDictionary(dictionaryToUse);
55214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
553699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard    }
554699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard
555cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /* package private */ void resetSuggestMainDict() {
5566a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
55779eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka        mSuggest.resetMainDict(this, subtypeLocale, this /* SuggestInitializationListener */);
5586a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
559cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
560cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
561466741d8a78965b8509bf527344f289e50873092Mike LeBeau    @Override
562466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onDestroy() {
563e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        if (mSuggest != null) {
564e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest.close();
565e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest = null;
566979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
567923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        unregisterReceiver(mReceiver);
568710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        // TODO: The experimental version is not supported by the Dictionary Pack Service yet.
569710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        if (!ProductionFlag.IS_EXPERIMENTAL) {
570710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa            unregisterReceiver(mDictionaryPackInstallReceiver);
571710d06cea91a8e6bf04a27f0bcd88d76a5cc5acdKen Wakasa        }
572979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
573979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.onDestroy();
574923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onDestroy();
575923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
576923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
577923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
578f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onConfigurationChanged(final Configuration conf) {
5799fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka        // System locale has been changed. Needs to reload keyboard.
5806fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka        if (mSubtypeSwitcher.onConfigurationChanged(conf)) {
5819fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka            loadKeyboard();
5829fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka        }
583b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        // If orientation changed while predicting, commit the change
584f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        if (mDisplayOrientation != conf.orientation) {
585f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mDisplayOrientation = conf.orientation;
586f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mHandler.startOrientationChanging();
587f254e3fec7744dc1eb2cc09ac157986c3b2b5408Jean Chalard            mConnection.beginBatchEdit();
5885475b38328171a0841ae18074bd45380ec567e90Jean Chalard            commitTyped(LastComposedWord.NOT_A_SEPARATOR);
5895475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.finishComposingText();
5905475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.endBatchEdit();
5910a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka            if (isShowingOptionDialog()) {
5922fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                mOptionsDialog.dismiss();
5930a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka            }
594b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
595923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onConfigurationChanged(conf);
596923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
597b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
598923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
599923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public View onCreateInputView() {
600622d6a5b1b3d6a9140765b868abf1cdf412bc4f3Tadashi G. Takaoka        return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled);
6016c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    }
6026c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka
6036c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    @Override
604f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void setInputView(final View view) {
6056c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        super.setInputView(view);
606d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        mExtractArea = getWindow().getWindow().getDecorView()
607d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka                .findViewById(android.R.id.extractArea);
608abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
609913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
6104702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
6114702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        if (mSuggestionStripView != null)
6124702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripView.setListener(this, view);
613f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        if (LatinImeLogger.sVISUALDEBUG) {
614f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka            mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
615f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        }
616923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
617923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
618923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
619f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void setCandidatesView(final View view) {
620c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        // To ensure that CandidatesView will never be set.
621c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return;
622923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
623923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
624a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker    @Override
625f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
626e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInput(editorInfo, restarting);
62759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
62859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
62959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
630f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
631e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInputView(editorInfo, restarting);
63259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
63359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
63459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
635f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onFinishInputView(final boolean finishingInput) {
63659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInputView(finishingInput);
63759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
63838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
63959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
64059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInput() {
64159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInput();
64259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
64359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
6449cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    @Override
645f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) {
6464f96bb4520de3610ae94da96b98e507ca7b76362satok        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
6474f96bb4520de3610ae94da96b98e507ca7b76362satok        // is not guaranteed. It may even be called at the same time on a different thread.
6486a7019ff5db19b1e3f8d7afbba71af813cab9a37Tadashi G. Takaoka        mSubtypeSwitcher.updateSubtype(subtype);
6499fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka        loadKeyboard();
6509cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka    }
6519cc2c94c8b4bfd4e00e5d3478b9f6e520e791bc5Tadashi G. Takaoka
652f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
653e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInput(editorInfo, restarting);
65459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
65559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
6561cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka    @SuppressWarnings("deprecation")
657f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
658e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInputView(editorInfo, restarting);
65945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
66027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
6618e09172df1bb176cc899940862c56bed9b9aec4esatok
662ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        if (editorInfo == null) {
663ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.e(TAG, "Null EditorInfo in onStartInputView()");
664ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            if (LatinImeLogger.sDBG) {
665ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                throw new NullPointerException("Null EditorInfo in onStartInputView()");
666ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            }
667ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            return;
668ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        }
66989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (DEBUG) {
670ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok            Log.d(TAG, "onStartInputView: editorInfo:"
671ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                    + String.format("inputType=0x%08x imeOptions=0x%08x",
672ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok                            editorInfo.inputType, editorInfo.imeOptions));
673b6fb5eb391987f3e426649a892cdcbf781957f5asatok            Log.d(TAG, "All caps = "
674b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0)
675b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", sentence caps = "
676b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0)
677b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ", word caps = "
678b6fb5eb391987f3e426649a892cdcbf781957f5asatok                    + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
679910b73127fa207dd26ec8124000262523b0aac0csatok        }
6809bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
68148a7681e064ae259b840f0e757da2d716043d893Kurt Partridge            ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
6829bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
6831cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
6844f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
6854f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
6861cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka            Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead");
6874f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
6881cb08acaf3b4d58cbf4cb65f9fc3990b39e33f00Tadashi G. Takaoka        if (InputAttributes.inPrivateImeOptions(getPackageName(), FORCE_ASCII, editorInfo)) {
6894f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
6904f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
6914f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
6924f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
6934f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka
6941b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard        mTargetApplicationInfo =
6951b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard                TargetApplicationGetter.getCachedApplicationInfo(editorInfo.packageName);
6961b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard        if (null == mTargetApplicationInfo) {
6971b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard            new TargetApplicationGetter(this /* context */, this /* listener */)
6981b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard                    .execute(editorInfo.packageName);
6991b1243d61fd682d804e61de6a1eccbf0e8ba78b2Jean Chalard        }
700b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
7017ef235f53f2291f22ddf8c56be9860a218b25bbbTadashi G. Takaoka        LatinImeLogger.onStartInputView(editorInfo);
702923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // In landscape mode, this method gets called without the input view being created.
70327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView == null) {
704923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
705923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
706923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
707b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        // Forward this event to the accessibility utilities, if enabled.
708b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
709b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        if (accessUtils.isTouchExplorationEnabled()) {
71005384933097c1e9c35e8be5c03757d072e5ffa46alanv            accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
711b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        }
712b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette
713e234aed4288efd7b4336f3755a958c25a1540b98Jean Chalard        final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo);
714e234aed4288efd7b4336f3755a958c25a1540b98Jean Chalard        final boolean isDifferentTextField = !restarting || inputTypeChanged;
715e234aed4288efd7b4336f3755a958c25a1540b98Jean Chalard        if (isDifferentTextField) {
7169fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka            final boolean currentSubtypeEnabled = mSubtypeSwitcher
7179fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka                    .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
7189fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka            if (!currentSubtypeEnabled) {
7199fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka                // Current subtype is disabled. Needs to update subtype and keyboard.
7206fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                final InputMethodSubtype newSubtype = mRichImm.getCurrentInputMethodSubtype(
7216fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                        mSubtypeSwitcher.getNoLanguageSubtype());
7229fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka                mSubtypeSwitcher.updateSubtype(newSubtype);
7239fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka                loadKeyboard();
7249fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka            }
725aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        }
7264ab730dbd34fad323063f2ffd31ce33de746668dsatok
727b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // The EditorInfo might have a flag that affects fullscreen mode.
728b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // Note: This call should be done by InputMethodService?
729b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        updateFullscreenMode();
730ccc35f7fa74860a8d737a4e9ff01fc0168dd329dJean Chalard        mApplicationSpecifiedCompletions = null;
731c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
73261cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        // The app calling setText() has the effect of clearing the composing
73361cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        // span, so we should reset our state unconditionally, even if restarting is true.
73461cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        mEnteredText = null;
73561cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
73661cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        mDeleteCount = 0;
73761cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        mSpaceState = SPACE_STATE_NONE;
73861cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard
73961cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard        if (mSuggestionStripView != null) {
74061cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard            // This will set the punctuation suggestions if next word suggestion is off;
74161cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard            // otherwise it will clear the suggestion strip.
74261cc2a33911ff2a8115ce727c8cac6d73d989c42Jean Chalard            setPunctuationSuggestions();
743549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        }
74417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
74572c5d328882976a0b4ae8b01a872ff5ae4d10547Jean Chalard        mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart);
7469d1c73ffd88cd1bfef3de048b0b3a9a7dfbcfa70Jean Chalard
747e234aed4288efd7b4336f3755a958c25a1540b98Jean Chalard        if (isDifferentTextField) {
74827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
749aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard            loadSettings();
750c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
751aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard            if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) {
752aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard                mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
753aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard            }
754aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard
755aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard            switcher.loadKeyboard(editorInfo, mCurrentSettings);
7565d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa        } else if (restarting) {
7575d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa            // TODO: Come up with a more comprehensive way to reset the keyboard layout when
7585d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa            // a keyboard layout set doesn't get reloaded in this method.
7595d2556b93286f5f1d7d829b586b84a8b7ae55743Ken Wakasa            switcher.resetKeyboardStateToAlphabet();
760d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // In apps like Talk, we come here when the text is sent and the field gets emptied and
761d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // we need to re-evaluate the shift state, but not the whole layout which would be
762d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // disruptive.
763d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            // Space state must be updated before calling updateShiftState
764d0725a68421203ae42d78ceed57767a5204e858eTadashi G. Takaoka            switcher.updateShiftState();
765aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        }
766913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShownInternal(
767913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                isSuggestionsStripVisible(), /* needsInputViewShown */ false);
7688e36e5102e9ac294a0e64ca14920d38ba19e6eb3Jean Chalard
769aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        mLastSelectionStart = editorInfo.initialSelStart;
770aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard        mLastSelectionEnd = editorInfo.initialSelEnd;
771aa906c36aa31f51a38083e337f4307b49f133054Jean Chalard
7728e36e5102e9ac294a0e64ca14920d38ba19e6eb3Jean Chalard        mHandler.cancelUpdateSuggestionStrip();
7732b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        mHandler.cancelDoubleSpacePeriodTimer();
774c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
7752be2d776649c9c0b3914135794ab7a7e92e753f9Tadashi G. Takaoka        mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
77627e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
777297e6d590bd957577c335aa8713a786145a70288Jean Chalard                mCurrentSettings.mKeyPreviewPopupDismissDelay);
7788335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled);
7798335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled,
7808335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka                mCurrentSettings.mGestureFloatingPreviewTextEnabled);
781c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
782c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
783c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    }
784c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
785a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Callback for the TargetApplicationGetter
7867214617622fce8f3fea6620e782c16336260a2a3Jean Chalard    @Override
787b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    public void onTargetApplicationKnown(final ApplicationInfo info) {
788b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard        mTargetApplicationInfo = info;
789b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard    }
790b224b60c94d85f30de93f66685adf06e662618c0Jean Chalard
791923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
792e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    public void onWindowHidden() {
793d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
794d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            ResearchLogger.latinIME_onWindowHidden(mLastSelectionStart, mLastSelectionEnd,
795d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    getCurrentInputConnection());
796d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge        }
797e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        super.onWindowHidden();
798f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
79927e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
80027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
80127e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
802e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
803e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
80459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputInternal() {
805923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onFinishInput();
806a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
807979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
80807cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
8090df487678eca58bd4732cfd2b6fd03b3c712eb48Kurt Partridge            ResearchLogger.getInstance().latinIME_onFinishInputInternal();
81007cd1e1731a07ae014a78db59b518ff0dbce3e35Kurt Partridge        }
811979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
812f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
81327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
81427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
81527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
816466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
817466741d8a78965b8509bf527344f289e50873092Mike LeBeau
818f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void onFinishInputViewInternal(final boolean finishingInput) {
8196495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa        super.onFinishInputView(finishingInput);
820055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardSwitcher.onFinishInputView();
821f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
82227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
82327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.cancelAllMessages();
82427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
825d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        // Remove pending messages related to update suggestions
826d81e7d24d384a2bb1aeda65d9b423e4b1d23f185Jean Chalard        mHandler.cancelUpdateSuggestionStrip();
827edbb65be3e5e391affbf47e134422ac1d1a4ae13Tadashi G. Takaoka        resetComposingState(true /* alsoResetLastComposedWord */);
8286495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    }
8296495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa
8306495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    @Override
831f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onUpdateSelection(final int oldSelStart, final int oldSelEnd,
832f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final int newSelStart, final int newSelEnd,
833f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final int composingSpanStart, final int composingSpanEnd) {
834923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
835104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                composingSpanStart, composingSpanEnd);
836466741d8a78965b8509bf527344f289e50873092Mike LeBeau        if (DEBUG) {
837466741d8a78965b8509bf527344f289e50873092Mike LeBeau            Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
838466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", ose=" + oldSelEnd
839025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lss=" + mLastSelectionStart
840025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lse=" + mLastSelectionEnd
841466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nss=" + newSelStart
842466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nse=" + newSelEnd
843104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", cs=" + composingSpanStart
844104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", ce=" + composingSpanEnd);
845466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
8469bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
847d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            final boolean expectingUpdateSelectionFromLogger =
848d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection();
8499bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge            ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd,
8509bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
851d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                    composingSpanEnd, mExpectingUpdateSelection,
85202308bec632a5df23325c916bffec5def16b22b4Jean Chalard                    expectingUpdateSelectionFromLogger, mConnection);
853d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            if (expectingUpdateSelectionFromLogger) {
854e86054e5c2e4734d87d2fbf1eeba8c75fc888df0Jean Chalard                // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work
855d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge                return;
856d67a248de45a698d1009757c9f4e750c77bf35f1Kurt Partridge            }
8579bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
858466741d8a78965b8509bf527344f289e50873092Mike LeBeau
859104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // TODO: refactor the following code to be less contrived.
860104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "newSelStart != composingSpanEnd" || "newSelEnd != composingSpanEnd" means
861104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // that the cursor is not at the end of the composing span, or there is a selection.
862104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "mLastSelectionStart != newSelStart" means that the cursor is not in the same place
863104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // as last time we were called (if there is a selection, it means the start hasn't
864104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // changed, so it's the end that did).
865104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean selectionChanged = (newSelStart != composingSpanEnd
866104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                || newSelEnd != composingSpanEnd) && mLastSelectionStart != newSelStart;
867104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // if composingSpanStart and composingSpanEnd are -1, it means there is no composing
868104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // span in the view - we can use that to narrow down whether the cursor was moved
869104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // by us or not. If we are composing a word but there is no composing span, then
870104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // we know for sure the cursor moved while we were composing and we should reset
871104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // the state.
872104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
8735ed88457bf9ef3305d4a5aa4ac05b513433ad0ddJean Chalard        if (!mExpectingUpdateSelection
8745ed88457bf9ef3305d4a5aa4ac05b513433ad0ddJean Chalard                && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
875cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // TAKE CARE: there is a race condition when we enter this test even when the user
876cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // did not explicitly move the cursor. This happens when typing fast, where two keys
877cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // turn this flag on in succession and both onUpdateSelection() calls arrive after
878cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // the second one - the first call successfully avoids this test, but the second one
879104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            // enters. For the moment we rely on noComposingSpan to further reduce the impact.
88051fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
8819a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // TODO: the following is probably better done in resetEntireInputState().
8829a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // it should only happen when the cursor moved, and the very purpose of the
8839a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // test below is to narrow down whether this happened or not. Likewise with
884fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard            // the call to updateShiftState.
88551fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // We set this to NONE because after a cursor move, we don't want the space
88651fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // state-related special processing to kick in.
88751fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            mSpaceState = SPACE_STATE_NONE;
88851fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
8898a3d369840d6061692a19ceb1eb7267a50a9e056Jean Chalard            if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) {
8906a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // If we are composing a word and moving the cursor, we would want to set a
8916a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // suggestion span for recorrection to work correctly. Unfortunately, that
8926a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // would involve the keyboard committing some new text, which would move the
8936a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // cursor back to where it was. Latin IME could then fix the position of the cursor
8946a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // again, but the asynchronous nature of the calls results in this wreaking havoc
8956a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // with selection on double tap and the like.
8966a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // Another option would be to send suggestions each time we set the composing
8976a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // text, but that is probably too expensive to do, so we decided to leave things
8986a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                // as is.
89928d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                resetEntireInputState(newSelStart);
9004c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard            }
901beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
902fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard            mKeyboardSwitcher.updateShiftState();
9034733609947c0ec74e460bd714fffca0518ade93aJean Chalard        }
9044733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = false;
9056b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not
9066b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // here. It would probably be too expensive to call directly here but we may want to post a
9076b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // message to delay it. The point would be to unify behavior between backspace to the
9086b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // end of a word and manually put the pointer at the end of the word.
909466741d8a78965b8509bf527344f289e50873092Mike LeBeau
910979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // Make a note of the cursor position
911979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionStart = newSelStart;
912979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionEnd = newSelEnd;
9131931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        mSubtypeState.currentSubtypeUsed();
9147a8dac55278cedd838be325f56b4c52d973c61f5satok    }
9157a8dac55278cedd838be325f56b4c52d973c61f5satok
916c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
917c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has clicked on the extracted text view,
918c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * when running in fullscreen mode.  The default implementation hides
919913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * the suggestions view when this happens, but only if the extracted text
920c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * editor has a vertical scroll bar because its text doesn't fit.
921c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
922913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
923c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
924c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
925c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedTextClicked() {
926fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard        if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
927c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
928c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedTextClicked();
929c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
930c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
931c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
932c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has performed a cursor movement in the
933c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * extracted text view, when it is running in fullscreen mode.  The default
934913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * implementation hides the suggestions view when a vertical movement
935c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * happens, but only if the extracted text editor has a vertical scroll bar
936c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * because its text doesn't fit.
937c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
938913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
939c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
940c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
941f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onExtractedCursorMovement(final int dx, final int dy) {
942fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard        if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
943c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
944c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedCursorMovement(dx, dy);
945c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
946c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
947923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
948923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void hideWindow() {
949979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
950c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mKeyboardSwitcher.onHideWindow();
951979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
952923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (TRACE) Debug.stopMethodTracing();
9536e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
9546e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog.dismiss();
9556e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog = null;
9566e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        }
957923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.hideWindow();
958923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
959923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
960923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
961f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) {
962979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG) {
963a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa            Log.i(TAG, "Received completions:");
964bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            if (applicationSpecifiedCompletions != null) {
965bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
966bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                    Log.i(TAG, "  #" + i + ": " + applicationSpecifiedCompletions[i]);
967bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                }
968923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
969923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
970dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return;
971dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
972dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        if (applicationSpecifiedCompletions == null) {
973259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            clearSuggestionStrip();
97494e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
97594e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge                ResearchLogger.latinIME_onDisplayCompletions(null);
97694e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            }
977dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard            return;
978923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
979dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard
980dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
981dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                SuggestedWords.getFromApplicationSpecifiedCompletions(
982dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                        applicationSpecifiedCompletions);
983dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        final SuggestedWords suggestedWords = new SuggestedWords(
984dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                applicationSuggestedWords,
985dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* typedWordValid */,
986dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* hasAutoCorrectionCandidate */,
987dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* isPunctuationSuggestions */,
988dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* isObsoleteSuggestions */,
989dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard                false /* isPrediction */);
990dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        // When in fullscreen mode, show completions generated by the application
991dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        final boolean isAutoCorrection = false;
992259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka        setSuggestionStrip(suggestedWords, isAutoCorrection);
993dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        setAutoCorrectionIndicator(isAutoCorrection);
994dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        // TODO: is this the right thing to do? What should we auto-correct to in
995dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        // this case? This says to keep whatever the user typed.
996dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
997dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        setSuggestionStripShown(true);
99894e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
99994e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
100094e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge        }
1001923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1002923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1003f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void setSuggestionStripShownInternal(final boolean shown,
1004f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final boolean needsInputViewShown) {
1005913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // TODO: Modify this if we support suggestions with hard keyboard
1006913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
100727e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
100827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final boolean inputViewShown = (mainKeyboardView != null)
100927e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                    ? mainKeyboardView.isShown() : false;
1010913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean shouldShowSuggestions = shown
1011433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                    && (needsInputViewShown ? inputViewShown : true);
10124b1780fa9571409d65d9797d47949ffafaf0f083Tadashi G. Takaoka            if (isFullscreenMode()) {
1013913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
1014913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.GONE);
10157a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            } else {
1016913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
1017913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE);
10187a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            }
1019923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1020923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1021a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1022f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void setSuggestionStripShown(final boolean shown) {
1023c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
1024543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    }
1025543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa
1026bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka    private int getAdjustedBackingViewHeight() {
1027bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        final int currentHeight = mKeyPreviewBackingView.getHeight();
1028bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        if (currentHeight > 0) {
1029bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka            return currentHeight;
1030c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        }
1031c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
1032f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
103327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView == null) {
1034bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka            return 0;
1035bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        }
103627e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final int keyboardHeight = mainKeyboardView.getHeight();
1037c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int suggestionsHeight = mSuggestionsContainer.getHeight();
1038c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int displayHeight = mResources.getDisplayMetrics().heightPixels;
1039c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final Rect rect = new Rect();
1040c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect);
1041c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int notificationBarHeight = rect.top;
1042c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight
1043c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka                - keyboardHeight;
1044c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
1045c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
10464702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight);
1047c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka        mKeyPreviewBackingView.setLayoutParams(params);
1048bc5b89cdff7065b7eab0b9f230b37084d9a6446fTadashi G. Takaoka        return params.height;
1049c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka    }
1050c8b0e5797e20d3fa25d319a9709aabc9149f8ff9Tadashi G. Takaoka
1051543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    @Override
1052f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onComputeInsets(final InputMethodService.Insets outInsets) {
1053923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onComputeInsets(outInsets);
1054f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
105527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView == null || mSuggestionsContainer == null) {
105646ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka            return;
105727e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
1058b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final int adjustedBackingHeight = getAdjustedBackingViewHeight();
1059b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE);
1060b94f4cc71c5cbf84d07166efa42991ba96d93c73Tadashi G. Takaoka        final int backingHeight = backingGone ? 0 : adjustedBackingHeight;
1061d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // In fullscreen mode, the height of the extract area managed by InputMethodService should
1062d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // be considered.
1063d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}.
1064d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0;
106559010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0
106659010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                : mSuggestionsContainer.getHeight();
1067d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
1068fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        int visibleTopY = extraHeight;
10699e347d3d448e48229c46aad394ec9bd60cd5807bsatok        // Need to set touchable region only if input view is being shown
107027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView.isShown()) {
1071913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
1072fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang                visibleTopY -= suggestionsHeight;
10739e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
1074fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang            final int touchY = mainKeyboardView.isShowingMoreKeysPanel() ? 0 : visibleTopY;
107527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final int touchWidth = mainKeyboardView.getWidth();
107627e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            final int touchHeight = mainKeyboardView.getHeight() + extraHeight
10777a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    // Extend touchable region below the keyboard.
10787a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
107913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
108013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka            outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight);
10819e347d3d448e48229c46aad394ec9bd60cd5807bsatok        }
1082fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        outInsets.contentTopInsets = visibleTopY;
1083fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        outInsets.visibleTopInsets = visibleTopY;
1084923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1085a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1086923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1087979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public boolean onEvaluateFullscreenMode() {
10889751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        // Reread resource value here, because this method is called by framework anytime as needed.
10899751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        final boolean isFullscreenModeAllowed =
10902da886651874b2588f18f800417ba858ac93d88bJean Chalard                SettingsValues.isFullscreenModeAllowed(getResources());
109160dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka        if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) {
109260dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI
109360dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // implies NO_FULLSCREEN. However, the framework mistakenly does.  i.e. NO_EXTRACT_UI
109460dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // without NO_FULLSCREEN doesn't work as expected. Because of this we need this
109560dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            // hack for now.  Let's get rid of this once the framework gets fixed.
109660dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            final EditorInfo ei = getCurrentInputEditorInfo();
109760dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            return !(ei != null && ((ei.imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0));
109860dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka        } else {
109960dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka            return false;
110060dea36dafc4a567b948b5175bcca776b08fdfadSatoshi Kataoka        }
110159010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    }
110259010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka
110359010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    @Override
110459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    public void updateFullscreenMode() {
110559010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        super.updateFullscreenMode();
1106f80b6a06992ae08ca3601f4fbc6da550fd9ac730Tadashi G. Takaoka
110759010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        if (mKeyPreviewBackingView == null) return;
1108549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        // In fullscreen mode, no need to have extra space to show the key preview.
110959010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        // If not, we should have extra space above the keyboard to show the key preview.
1110549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
1111979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
1112979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
11132649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // This will reset the whole input state to the starting state. It will clear
11144a5cb5c36a6a385ec0036981a0e93b0253e884b0Jean Chalard    // the composing word, reset the last composed word, tell the inputconnection about it.
111528d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    private void resetEntireInputState(final int newCursorPosition) {
11162649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
1117ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard        if (mCurrentSettings.mBigramPredictionEnabled) {
1118ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard            clearSuggestionStrip();
1119ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard        } else {
1120ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard            setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
1121ac25fcac717b0d28dfb2cd5c8ecd4fde839c4eebJean Chalard        }
112228d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard        mConnection.resetCachesUponCursorMove(newCursorPosition);
11232649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    }
11242649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard
11252692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private void resetComposingState(final boolean alsoResetLastComposedWord) {
11262692a8700737d8eed268039aa27b22a31669da08Jean Chalard        mWordComposer.reset();
11272692a8700737d8eed268039aa27b22a31669da08Jean Chalard        if (alsoResetLastComposedWord)
11282692a8700737d8eed268039aa27b22a31669da08Jean Chalard            mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
11292692a8700737d8eed268039aa27b22a31669da08Jean Chalard    }
11302692a8700737d8eed268039aa27b22a31669da08Jean Chalard
1131a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard    private void commitTyped(final String separatorString) {
1132196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) return;
1133bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String typedWord = mWordComposer.getTypedWord();
11343651220327c051d8017045aa5e8919461507b3f8Jean Chalard        if (typedWord.length() > 0) {
11356a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard            commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
11366a58bb7ac95e804f87c4e88b5eb970d28210518eJean Chalard                    separatorString);
1137923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1138923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1139923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1140a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Called from the KeyboardSwitcher which needs to know auto caps state to display
1141a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // the right layout.
1142553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka    public int getCurrentAutoCapsState() {
1143297e6d590bd957577c335aa8713a786145a70288Jean Chalard        if (!mCurrentSettings.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
114403ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka
114503ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final EditorInfo ei = getCurrentInputEditorInfo();
1146553e2f19c1607080ff874cb642237f947809cdb3Tadashi G. Takaoka        if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
114703ca17c8415854c4c949b92b66543c920562ac3dTadashi G. Takaoka        final int inputType = ei.inputType;
114890a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Warning: this depends on mSpaceState, which may not be the most current value. If
114990a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // mSpaceState gets updated later, whoever called this may need to be told about it.
115090a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale(),
115190a91272447fd64bc54e06f08f45b11c45930767Jean Chalard                SPACE_STATE_PHANTOM == mSpaceState);
11521c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
11531c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
1154adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    // Factor in auto-caps and manual caps and compute the current caps mode.
1155adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    private int getActualCapsMode() {
11566686e63f2158eff07e39ff002ba211d0578f2a4fJean Chalard        final int keyboardShiftMode = mKeyboardSwitcher.getKeyboardShiftMode();
11576686e63f2158eff07e39ff002ba211d0578f2a4fJean Chalard        if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) return keyboardShiftMode;
1158adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        final int auto = getCurrentAutoCapsState();
1159adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
1160adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard            return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;
1161adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        }
1162adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED;
1163adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        return WordComposer.CAPS_MODE_OFF;
1164adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard    }
1165adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard
1166bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard    private void swapSwapperAndSpace() {
11675475b38328171a0841ae18074bd45380ec567e90Jean Chalard        CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
1168863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
1169923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastTwo != null && lastTwo.length() == 2
1170240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastTwo.charAt(0) == Constants.CODE_SPACE) {
11715475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.deleteSurroundingText(2, 0);
11725475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(lastTwo.charAt(1) + " ", 1);
1173d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
117494e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge                ResearchLogger.latinIME_swapSwapperAndSpace();
1175d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge            }
1176b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
11774ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa        }
11784ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa    }
11794ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
11802b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa    private boolean maybeDoubleSpacePeriod() {
11812f3a694e29ad5a63052a2f963327855fee099f55Jean Chalard        if (!mCurrentSettings.mCorrectionEnabled) return false;
1182139bd83f0c552e3fdb5f763b5f6fe25ad138802bKen Wakasa        if (!mCurrentSettings.mUseDoubleSpacePeriod) return false;
11832b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        if (!mHandler.isAcceptingDoubleSpacePeriod()) return false;
11845475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
1185923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastThree != null && lastThree.length() == 3
11862b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                && canBeFollowedByDoubleSpacePeriod(lastThree.charAt(0))
1187240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastThree.charAt(1) == Constants.CODE_SPACE
1188240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastThree.charAt(2) == Constants.CODE_SPACE) {
11892b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mHandler.cancelDoubleSpacePeriodTimer();
11905475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.deleteSurroundingText(2, 0);
11915475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(". ", 1);
1192b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1193120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            return true;
1194923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1195120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return false;
1196923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1197a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
11982b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa    private static boolean canBeFollowedByDoubleSpacePeriod(final int codePoint) {
1199344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: Check again whether there really ain't a better way to check this.
1200344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        // TODO: This should probably be language-dependant...
1201344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka        return Character.isLetterOrDigit(codePoint)
1202240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_SINGLE_QUOTE
1203240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_DOUBLE_QUOTE
1204240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_PARENTHESIS
1205240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET
1206240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
1207240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET;
1208344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka    }
1209344af156744c6866090fb70f151efd66668c1e20Tadashi G. Takaoka
12104702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
12114702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // pressed.
1212c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
1213f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public boolean addWordToUserDictionary(final String word) {
121467fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard        mUserDictionary.addWordToUserDictionary(word, 128);
1215923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
1216923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1217923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1218f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private static boolean isAlphabet(final int code) {
12198fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka        return Character.isLetter(code);
1220923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1221a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1222e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    private void onSettingsKeyPressed() {
1223cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return;
1224911b8f9d19c1c4903eeef29b43176cfeaa0e5d0cKen Wakasa        showSubtypeSelectorAndSettings();
12259a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12269a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1227cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
1228cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
1229cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa
1230cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    @Override
1231f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public boolean onCustomRequest(final int requestCode) {
1232cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return false;
1233cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        switch (requestCode) {
1234cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        case CODE_SHOW_INPUT_METHOD_PICKER:
12356fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
12366fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                mRichImm.getInputMethodManager().showInputMethodPicker();
1237cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa                return true;
12389a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok            }
1239cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa            return false;
12409a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
1241cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        return false;
12429a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12439a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
12449a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    private boolean isShowingOptionDialog() {
12459a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        return mOptionsDialog != null && mOptionsDialog.isShowing();
12469a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12479a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1248f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private static int getActionId(final Keyboard keyboard) {
124905bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        return keyboard != null ? keyboard.mId.imeActionId() : EditorInfo.IME_ACTION_NONE;
12507a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12517a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
1252f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void performEditorAction(final int actionId) {
12535475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.performEditorAction(actionId);
12547a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12557a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
1256aec5cc84760b47661ccc5a0c938368d3798be6b4Tadashi G. Takaoka    // TODO: Revise the language switch key behavior to make it much smarter and more reasonable.
125781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    private void handleLanguageSwitchKey() {
125881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        final IBinder token = getWindow().getWindow().getAttributes().token;
1259aec5cc84760b47661ccc5a0c938368d3798be6b4Tadashi G. Takaoka        if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) {
12606fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka            mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
1261aec5cc84760b47661ccc5a0c938368d3798be6b4Tadashi G. Takaoka            return;
1262aec5cc84760b47661ccc5a0c938368d3798be6b4Tadashi G. Takaoka        }
12631931c1cab1c2b077030210f2dd7a1839da7211bcTadashi G. Takaoka        mSubtypeState.switchSubtype(token, mRichImm);
126481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    }
126581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka
126628d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    private void sendDownUpKeyEventForBackwardCompatibility(final int code) {
12674e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard        final long eventTime = SystemClock.uptimeMillis();
12685475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
12694e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
12704e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
12715475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
12724e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
12734e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
12744e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard    }
12754e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard
1276f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void sendKeyCodePoint(final int code) {
12777a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // TODO: Remove this special handling of digit letters.
12787a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
12797a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (code >= '0' && code <= '9') {
128028d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard            sendDownUpKeyEventForBackwardCompatibility(code - '0' + KeyEvent.KEYCODE_0);
128194e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
128294e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge                ResearchLogger.latinIME_sendKeyCodePoint(code);
128394e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            }
12847a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            return;
12857a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12867a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12875475b38328171a0841ae18074bd45380ec567e90Jean Chalard        // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
12885475b38328171a0841ae18074bd45380ec567e90Jean Chalard        // we want to be able to compile against the Ice Cream Sandwich SDK.
1289240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_ENTER == code && mTargetApplicationInfo != null
12905475b38328171a0841ae18074bd45380ec567e90Jean Chalard                && mTargetApplicationInfo.targetSdkVersion < 16) {
12915475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
12925475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // a hardware keyboard event on pressing enter or delete. This is bad for many
12935475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // reasons (there are race conditions with commits) but some applications are
12945475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // relying on this behavior so we continue to support it for older apps.
129528d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard            sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_ENTER);
12965475b38328171a0841ae18074bd45380ec567e90Jean Chalard        } else {
12975475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final String text = new String(new int[] { code }, 0, 1);
12985475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitText(text, text.length());
12995475b38328171a0841ae18074bd45380ec567e90Jean Chalard        }
13007a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
13017a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
13025f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    // Implementation of {@link KeyboardActionListener}.
13035a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
1304f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onCodeInput(final int primaryCode, final int x, final int y) {
1305175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final long when = SystemClock.uptimeMillis();
1306240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (primaryCode != Constants.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
1307923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mDeleteCount = 0;
1308923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1309923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mLastKeyTime = when;
1310f254e3fec7744dc1eb2cc09ac157986c3b2b5408Jean Chalard        mConnection.beginBatchEdit();
1311175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
1312120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // The space state depends only on the last character pressed and its own previous
1313120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // state. Here, we revert the space state to neutral if the key is actually modifying
1314120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // the input contents (any non-shift key), which is what we should do for
1315120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // all inputs that do not result in a special state. Each character handling is then
1316120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // free to override the state as they see fit.
1317120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final int spaceState = mSpaceState;
131870852c91dc7209d0aaa875a2cb0f79739c7398e6Jean Chalard        if (!mWordComposer.isComposingWord()) mIsAutoCorrectionIndicatorOn = false;
1319ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
13202b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa        // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
1321240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (primaryCode != Constants.CODE_SPACE) {
13222b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mHandler.cancelDoubleSpacePeriodTimer();
1323ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        }
1324ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1325c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1326923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (primaryCode) {
1327240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_DELETE:
1328120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1329120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            handleBackspace(spaceState);
13304189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            mDeleteCount++;
13314733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
1332140467b8b65eb9087a9b7f744dbb8a30481effe7Kurt Partridge            LatinImeLogger.logOnDelete(x, y);
13334189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1334240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SHIFT:
1335240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SWITCH_ALPHA_SYMBOL:
13362a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            // Shift and symbol key is handled in onPressKey() and onReleaseKey().
13374189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1338240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SETTINGS:
133993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            onSettingsKeyPressed();
13404189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1341240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_SHORTCUT:
13429fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka            mSubtypeSwitcher.switchToShortcutIME(this);
13434189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1344240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_ACTION_ENTER:
13458f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(getActionId(switcher.getKeyboard()));
13467a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            break;
1347240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_ACTION_NEXT:
13488f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_NEXT);
134905bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka            break;
1350240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_ACTION_PREVIOUS:
13518f433a66b2e8b327d7854eb501cf0e93afed4117Tadashi G. Takaoka            performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
13524189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1353240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_LANGUAGE_SWITCH:
135481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            handleLanguageSwitchKey();
135581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            break;
1356240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        case Constants.CODE_RESEARCH:
1357724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
135858eb4d9f27595202927150766d198a0bff15efadKurt Partridge                ResearchLogger.getInstance().onResearchKeySelected(this);
1359724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            }
1360724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge            break;
13614189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        default:
1362120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1363297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (mCurrentSettings.isWordSeparator(primaryCode)) {
1364c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
13654189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            } else {
13664be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                if (SPACE_STATE_PHANTOM == spaceState) {
136787cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    if (ProductionFlag.IS_INTERNAL) {
136887cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                        if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) {
136987cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                            Stats.onAutoCorrection(
137087cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                                    "", mWordComposer.getTypedWord(), " ", mWordComposer);
137187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                        }
137287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    }
13734be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                    commitTyped(LastComposedWord.NOT_A_SEPARATOR);
13744be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                }
1375eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                final int keyX, keyY;
13768dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
13778dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
1378eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    keyX = x;
1379eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                    keyY = y;
13808dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                } else {
1381ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                    keyX = Constants.NOT_A_COORDINATE;
1382ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                    keyY = Constants.NOT_A_COORDINATE;
13838dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                }
1384eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                handleCharacter(primaryCode, keyX, keyY, spaceState);
13854189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            }
13864733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
13874733609947c0ec74e460bd714fffca0518ade93aJean Chalard            break;
1388923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1389eef6238f94b5046054d9ae9c06f775362893c0eeTadashi G. Takaoka        switcher.onCodeInput(primaryCode);
1390125de3dfdf548359de890247907f2e6f430008ecJean Chalard        // Reset after any single keystroke, except shift and symbol-shift
1391240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (!didAutoCorrect && primaryCode != Constants.CODE_SHIFT
1392240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && primaryCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
1393c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard            mLastComposedWord.deactivate();
1394240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_DELETE != primaryCode) {
1395a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            mEnteredText = null;
1396a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard        }
1397e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard        mConnection.endBatchEdit();
1398bf653996eab40e2c66cfd2eaeb48ed5175b78455Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
1399bf653996eab40e2c66cfd2eaeb48ed5175b78455Kurt Partridge            ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
1400bf653996eab40e2c66cfd2eaeb48ed5175b78455Kurt Partridge        }
1401923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1402a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1403a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Called from PointerTracker through the KeyboardActionListener interface
14045a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
1405bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void onTextInput(final String rawText) {
1406f254e3fec7744dc1eb2cc09ac157986c3b2b5408Jean Chalard        mConnection.beginBatchEdit();
14076c70b9200340537d05ff8932d7cd990269eb486dJean Chalard        if (mWordComposer.isComposingWord()) {
1408a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            commitCurrentAutoCorrection(rawText.toString());
1409a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard        } else {
1410a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            resetComposingState(true /* alsoResetLastComposedWord */);
14116c70b9200340537d05ff8932d7cd990269eb486dJean Chalard        }
14120a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka        mHandler.postUpdateSuggestionStrip();
1413bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String text = specificTldProcessingOnTextInput(rawText);
1414fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == mSpaceState) {
14150e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
1416fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
14175475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.commitText(text, 1);
14185475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.endBatchEdit();
141990a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Space state must be updated before calling updateShiftState
142090a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        mSpaceState = SPACE_STATE_NONE;
1421b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
1422240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT);
1423dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = text;
1424923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1425923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
14264be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    @Override
14274be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    public void onStartBatchInput() {
142858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        BatchInputUpdater.getInstance().onStartBatchInput();
14294be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.beginBatchEdit();
14304be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        if (mWordComposer.isComposingWord()) {
143187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            if (ProductionFlag.IS_INTERNAL) {
143287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                if (mWordComposer.isBatchMode()) {
143387cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Stats.onAutoCorrection("", mWordComposer.getTypedWord(), " ", mWordComposer);
143487cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                }
143587cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            }
1436b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard            if (mWordComposer.size() <= 1) {
1437b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // We auto-correct the previous (typed, not gestured) string iff it's one character
1438b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // long. The reason for this is, even in the middle of gesture typing, you'll still
1439b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // tap one-letter words and you want them auto-corrected (typically, "i" in English
1440b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // should become "I"). However for any longer word, we assume that the reason for
1441b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // tapping probably is that the word you intend to type is not in the dictionary,
1442b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // so we do not attempt to correct, on the assumption that if that was a dictionary
1443b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                // word, the user would probably have gestured instead.
1444b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                commitCurrentAutoCorrection(LastComposedWord.NOT_A_SEPARATOR);
1445b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard            } else {
1446b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard                commitTyped(LastComposedWord.NOT_A_SEPARATOR);
1447b4e74cfc87ccb0523da18cb9fe30a6d08d1a04e2Jean Chalard            }
14484be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka            mExpectingUpdateSelection = true;
1449e4783f95b2ad9f46ece34103cbe4d8eb5ea8afcaJean Chalard            // The following is necessary for the case where the user typed something but didn't
1450e4783f95b2ad9f46ece34103cbe4d8eb5ea8afcaJean Chalard            // manual pick it and didn't input any separator.
14514be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka            mSpaceState = SPACE_STATE_PHANTOM;
1452e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard        } else {
1453e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard            final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
1454f960eb186d63aa4f9fecd22f036fc8724d39d949Jean Chalard            // TODO: reverse this logic. We should have the means to determine whether a character
1455f960eb186d63aa4f9fecd22f036fc8724d39d949Jean Chalard            // should usually be followed by a space, and it should be more readable.
1456e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard            if (Constants.NOT_A_CODE != codePointBeforeCursor
1457f960eb186d63aa4f9fecd22f036fc8724d39d949Jean Chalard                    && !Character.isWhitespace(codePointBeforeCursor)
1458f960eb186d63aa4f9fecd22f036fc8724d39d949Jean Chalard                    && !mCurrentSettings.isPhantomSpacePromotingSymbol(codePointBeforeCursor)
1459f960eb186d63aa4f9fecd22f036fc8724d39d949Jean Chalard                    && !mCurrentSettings.isWeakSpaceStripper(codePointBeforeCursor)) {
1460e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard                mSpaceState = SPACE_STATE_PHANTOM;
1461e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard            }
14624be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        }
14634be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.endBatchEdit();
1464adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard        mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
1465eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang    }
1466eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang
1467f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private static final class BatchInputUpdater implements Handler.Callback {
1468f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private final Handler mHandler;
1469f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private LatinIME mLatinIme;
1470b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka        private boolean mInBatchInput; // synchronized using "this".
1471f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1472f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private BatchInputUpdater() {
1473f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final HandlerThread handlerThread = new HandlerThread(
1474f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    BatchInputUpdater.class.getSimpleName());
1475f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            handlerThread.start();
1476f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            mHandler = new Handler(handlerThread.getLooper(), this);
1477f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1478f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1479f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        // Initialization-on-demand holder
1480f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final class OnDemandInitializationHolder {
1481f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            public static final BatchInputUpdater sInstance = new BatchInputUpdater();
1482f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1483f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1484f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public static BatchInputUpdater getInstance() {
1485f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            return OnDemandInitializationHolder.sInstance;
1486f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1487f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1488f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        private static final int MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 1;
1489f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1490f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        @Override
1491f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public boolean handleMessage(final Message msg) {
1492f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            switch (msg.what) {
1493f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
149458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                updateBatchInput((InputPointers)msg.obj, mLatinIme);
1495f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                break;
1496f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            }
1497f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            return true;
1498f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1499f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
150058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the UI thread.
150158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        public synchronized void onStartBatchInput() {
1502b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka            mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
150358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mInBatchInput = true;
150458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
150558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
150658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the Handler thread.
150758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        private synchronized void updateBatchInput(final InputPointers batchPointers,
1508f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                final LatinIME latinIme) {
150958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            if (!mInBatchInput) {
1510b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka                // Batch input has ended or canceled while the message was being delivered.
151158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                return;
151258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            }
151358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(
151458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    batchPointers, latinIme);
151558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
151658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    suggestedWords, false /* dismissGestureFloatingPreviewText */);
151758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
151858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
151958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the UI thread.
152058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        public void onUpdateBatchInput(final InputPointers batchPointers, final LatinIME latinIme) {
1521f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            mLatinIme = latinIme;
1522f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            if (mHandler.hasMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP)) {
1523f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                return;
1524f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            }
1525f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            mHandler.obtainMessage(
1526f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, batchPointers)
1527f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                    .sendToTarget();
1528f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1529f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1530b5fc0e02d04550d39bfa98c2dde805f1c1d3f9a3Tadashi G. Takaoka        public synchronized void onCancelBatchInput(final LatinIME latinIme) {
1531b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            mInBatchInput = false;
1532b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
1533b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka                    SuggestedWords.EMPTY, true /* dismissGestureFloatingPreviewText */);
1534b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        }
1535b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
153658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        // Run in the UI thread.
153758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        public synchronized SuggestedWords onEndBatchInput(final InputPointers batchPointers,
153858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                final LatinIME latinIme) {
153958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            mInBatchInput = false;
154058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(
154158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    batchPointers, latinIme);
1542f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
154358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    suggestedWords, true /* dismissGestureFloatingPreviewText */);
154458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return suggestedWords;
1545f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1546f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1547f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
1548f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        // be synchronized.
154958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        private static SuggestedWords getSuggestedWordsGestureLocked(
1550f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                final InputPointers batchPointers, final LatinIME latinIme) {
1551f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            latinIme.mWordComposer.setBatchInputPointers(batchPointers);
1552f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            return latinIme.getSuggestedWords(Suggest.SESSION_GESTURE);
1553f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
1554f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    }
1555f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1556f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
1557f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            final boolean dismissGestureFloatingPreviewText) {
1558f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        showSuggestionStrip(suggestedWords, null);
15598e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
1560f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        if (dismissGestureFloatingPreviewText) {
1561f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka            mainKeyboardView.dismissGestureFloatingPreviewText();
15628e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka        } else {
15638e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            final String batchInputText = suggestedWords.isEmpty()
15648e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka                    ? null : suggestedWords.getWord(0);
15658e2b34cdb24adb1563cc296a4741be7391fa24e9Tadashi G. Takaoka            mainKeyboardView.showGestureFloatingPreviewText(batchInputText);
1566f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        }
15674be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    }
15684be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka
15694be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    @Override
1570f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onUpdateBatchInput(final InputPointers batchPointers) {
157158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        BatchInputUpdater.getInstance().onUpdateBatchInput(batchPointers, this);
1572f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    }
1573f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka
1574f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    @Override
1575f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onEndBatchInput(final InputPointers batchPointers) {
157658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
1577f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka                batchPointers, this);
1578c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka        final String batchInputText = suggestedWords.isEmpty()
1579c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka                ? null : suggestedWords.getWord(0);
1580f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        if (TextUtils.isEmpty(batchInputText)) {
158110102f02af1216cfca115d1742740f567b909e2cTadashi G. Takaoka            return;
158210102f02af1216cfca115d1742740f567b909e2cTadashi G. Takaoka        }
1583f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        mWordComposer.setBatchInputWord(batchInputText);
15844be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.beginBatchEdit();
15854be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        if (SPACE_STATE_PHANTOM == mSpaceState) {
15860e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
15874be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        }
1588f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        mConnection.setComposingText(batchInputText, 1);
15894be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mExpectingUpdateSelection = true;
15904be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mConnection.endBatchEdit();
159190a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Space state must be updated before calling updateShiftState
15924be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka        mSpaceState = SPACE_STATE_PHANTOM;
159390a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        mKeyboardSwitcher.updateShiftState();
15944be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka    }
15954be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka
1596bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String specificTldProcessingOnTextInput(final String text) {
1597240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD
1598fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                || !Character.isLetter(text.charAt(1))) {
1599fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            // Not a tld: do nothing.
1600fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1601fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
160212d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // We have a TLD (or something that looks like this): make sure we don't add
160312d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // a space even if currently in phantom mode.
160412d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        mSpaceState = SPACE_STATE_NONE;
1605e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard        // TODO: use getCodePointBeforeCursor instead to improve performance and simplify the code
16065475b38328171a0841ae18074bd45380ec567e90Jean Chalard        final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
1607fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (lastOne != null && lastOne.length() == 1
1608240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka                && lastOne.charAt(0) == Constants.CODE_PERIOD) {
1609bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            return text.substring(1);
1610fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        } else {
1611fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1612fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1613fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    }
1614fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1615a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Called from PointerTracker through the KeyboardActionListener interface
16165a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
16178aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCancelInput() {
161883e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        // User released a finger outside any key
16195f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        mKeyboardSwitcher.onCancelInput();
162083e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka    }
162183e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka
1622b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    @Override
1623b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public void onCancelBatchInput() {
1624b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        BatchInputUpdater.getInstance().onCancelBatchInput(this);
1625b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    }
1626b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
1627120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleBackspace(final int spaceState) {
1628fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        // In many cases, we may have to put the keyboard in auto-shift state again. However
1629fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        // we want to wait a few milliseconds before doing it to avoid the keyboard flashing
1630fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        // during key repeat.
1631beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        mHandler.postUpdateShiftState();
16322245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard
1633196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
16343651220327c051d8017045aa5e8919461507b3f8Jean Chalard            final int length = mWordComposer.size();
1635923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (length > 0) {
163601d47b809617aee03e240a6b4b41013269547bc1Jean Chalard                if (mWordComposer.isBatchMode()) {
16374be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                    mWordComposer.reset();
16384be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                } else {
16394be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                    mWordComposer.deleteLast();
16404be03befe3cf771a33448367f50c517dc01ced21Tadashi G. Takaoka                }
16415475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1642a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard                mHandler.postUpdateSuggestionStrip();
1643923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
16445475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(1, 0);
1645923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1646890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        } else {
16475935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard            if (mLastComposedWord.canRevertCommit()) {
164887cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                if (ProductionFlag.IS_INTERNAL) {
164987cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Stats.onAutoCorrectionCancellation();
165087cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                }
16515475b38328171a0841ae18074bd45380ec567e90Jean Chalard                revertCommit();
1652120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                return;
1653120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1654a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
1655a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // Cancel multi-character input: remove the text we just entered.
1656a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // This is triggered on backspace after a key that inputs multiple characters,
1657a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // like the smiley key or the .com key.
1658a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                final int length = mEnteredText.length();
1659a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                mConnection.deleteSurroundingText(length, 0);
1660a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                mEnteredText = null;
1661a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
1662a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // In addition we know that spaceState is false, and that we should not be
1663a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // reverting any autocorrect at this point. So we can safely return.
1664a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                return;
1665a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            }
1666d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            if (SPACE_STATE_DOUBLE == spaceState) {
16672b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                mHandler.cancelDoubleSpacePeriodTimer();
1668a32eb2721390d5964c83c787ad30fd3f61b936b0Jean Chalard                if (mConnection.revertDoubleSpace()) {
1669d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // No need to reset mSpaceState, it has already be done (that's why we
1670d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // receive it as a parameter)
1671d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1672d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
1673d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
16742010aad741bc1a7266913bcb8b8348d6e401c95bJean Chalard                if (mConnection.revertSwapPunctuation()) {
1675d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // Likewise
1676d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1677d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
16784733609947c0ec74e460bd714fffca0518ade93aJean Chalard            }
1679504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka
16804fef31510df542a3324426a6750950194d016086Jean Chalard            // No cancelling of commit/double space/swap: we have a regular backspace.
16814fef31510df542a3324426a6750950194d016086Jean Chalard            // We should backspace one char and restart suggestion if at the end of a word.
16824fef31510df542a3324426a6750950194d016086Jean Chalard            if (mLastSelectionStart != mLastSelectionEnd) {
16834fef31510df542a3324426a6750950194d016086Jean Chalard                // If there is a selection, remove it.
16844fef31510df542a3324426a6750950194d016086Jean Chalard                final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
16855475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
16865475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(lengthToDelete, 0);
16876558253160e2039c87f424bd814f402ecd31de3bKen Wakasa            } else {
16884fef31510df542a3324426a6750950194d016086Jean Chalard                // There is no selection, just delete one character.
16894fef31510df542a3324426a6750950194d016086Jean Chalard                if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
16904fef31510df542a3324426a6750950194d016086Jean Chalard                    // This should never happen.
16914fef31510df542a3324426a6750950194d016086Jean Chalard                    Log.e(TAG, "Backspace when we don't know the selection position");
16926558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                }
16934e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
16944e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                // we want to be able to compile against the Ice Cream Sandwich SDK.
16954e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                if (mTargetApplicationInfo != null
16964e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                        && mTargetApplicationInfo.targetSdkVersion < 16) {
16974e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
16984e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // a hardware keyboard event on pressing enter or delete. This is bad for many
16994e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // reasons (there are race conditions with commits) but some applications are
17004e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                    // relying on this behavior so we continue to support it for older apps.
170128d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                    sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_DEL);
17024e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                } else {
17035475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.deleteSurroundingText(1, 0);
17044e1a558ee8d7747b71dba5aac86a7c9003d6f57dJean Chalard                }
17054fef31510df542a3324426a6750950194d016086Jean Chalard                if (mDeleteCount > DELETE_ACCELERATE_AT) {
17065475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.deleteSurroundingText(1, 0);
1707edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                }
1708923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1709fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard            if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
17105475b38328171a0841ae18074bd45380ec567e90Jean Chalard                restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
17114fef31510df542a3324426a6750950194d016086Jean Chalard            }
1712923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1713923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1714923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1715e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard    private boolean maybeStripSpace(final int code,
1716e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final int spaceState, final boolean isFromSuggestionStrip) {
1717240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1718bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard            mConnection.removeTrailingSpace();
1719e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1720e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else if ((SPACE_STATE_WEAK == spaceState
1721e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                || SPACE_STATE_SWAP_PUNCTUATION == spaceState)
1722e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                && isFromSuggestionStrip) {
1723297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (mCurrentSettings.isWeakSpaceSwapper(code)) {
1724e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return true;
1725e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            } else {
1726297e6d590bd957577c335aa8713a786145a70288Jean Chalard                if (mCurrentSettings.isWeakSpaceStripper(code)) {
1727bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                    mConnection.removeTrailingSpace();
1728e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                }
1729e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return false;
1730e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
1731e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else {
1732e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1733e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1734e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    }
1735e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1736ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    private void handleCharacter(final int primaryCode, final int x,
1737120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int y, final int spaceState) {
1738196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        boolean isComposingWord = mWordComposer.isComposingWord();
1739fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1740fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
1741297e6d590bd957577c335aa8713a786145a70288Jean Chalard                !mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) {
1742fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (isComposingWord) {
1743fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // Sanity check
1744fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                throw new RuntimeException("Should not be composing here");
1745fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            }
17460e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
1747fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1748fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1749d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // NOTE: isCursorTouchingWord() is a blocking IPC call, so it often takes several
1750d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
1751d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        // thread here.
1752d5cd7e64aa30096241f63e47a9169699bdd98231Jean Chalard        if (!isComposingWord && (isAlphabet(primaryCode)
1753297e6d590bd957577c335aa8713a786145a70288Jean Chalard                || mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode))
1754fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard                && mCurrentSettings.isSuggestionsRequested(mDisplayOrientation) &&
1755297e6d590bd957577c335aa8713a786145a70288Jean Chalard                !mConnection.isCursorTouchingWord(mCurrentSettings)) {
1756736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Reset entirely the composing state anyway, then start composing a new word unless
1757736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // the character is a single quote. The idea here is, single quote is not a
1758736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // separator and it should be treated as a normal character, except in the first
1759736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // position where it should not start composing a word.
1760240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            isComposingWord = (Constants.CODE_SINGLE_QUOTE != primaryCode);
1761736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // Here we don't need to reset the last composed word. It will be reset
1762736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // when we commit this one, if we ever do; if on the other hand we backspace
1763736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // it entirely and resume suggestions on the previous word, we'd like to still
1764736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            // have touch coordinates for it.
1765736b109e1692374f5a2739885c8f931dbb8be1f2Jean Chalard            resetComposingState(false /* alsoResetLastComposedWord */);
1766923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
17677b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard        if (isComposingWord) {
17685c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka            final int keyX, keyY;
176924ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka            if (Constants.isValidCoordinate(x) && Constants.isValidCoordinate(y)) {
17705c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka                final KeyDetector keyDetector =
177127e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                        mKeyboardSwitcher.getMainKeyboardView().getKeyDetector();
17725c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka                keyX = keyDetector.getTouchX(x);
17735c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka                keyY = keyDetector.getTouchY(y);
177424ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka            } else {
177524ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka                keyX = x;
177624ab8055aaae75bcb80c686efc2046a301ebb6adTadashi G. Takaoka                keyY = y;
17775c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka            }
17785c641a9f59735f0eaa772bde027993275b1bdfd7Tadashi G. Takaoka            mWordComposer.add(primaryCode, keyX, keyY);
17795475b38328171a0841ae18074bd45380ec567e90Jean Chalard            // If it's the first letter, make note of auto-caps state
17805475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (mWordComposer.size() == 1) {
1781adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard                mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
1782923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
17835475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1784923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1785e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard            final boolean swapWeakSpace = maybeStripSpace(primaryCode,
1786ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                    spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x);
1787863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
17887a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(primaryCode);
1789e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1790e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            if (swapWeakSpace) {
1791bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                swapSwapperAndSpace();
1792e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                mSpaceState = SPACE_STATE_WEAK;
1793e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
17948f9c9377fc8944c9e96e6dcf661f0d673c23b83fJean Chalard            // In case the "add to dictionary" hint was still displayed.
17954702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint();
1796e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1797a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
179887cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        if (ProductionFlag.IS_INTERNAL) {
179987cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            Utils.Stats.onNonSeparator((char)primaryCode, x, y);
180087cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
1801923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1802923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1803c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    // Returns true if we did an autocorrection, false otherwise.
1804c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    private boolean handleSeparator(final int primaryCode, final int x, final int y,
1805120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int spaceState) {
1806c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1807923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Handle separator
1808196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
18098ee4b35c7010c6e8f792e49a88dc4e908f7230fcJean Chalard            if (mCurrentSettings.mCorrectionEnabled) {
1810a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                // TODO: maybe cache Strings in an <String> sparse array or something
1811a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                commitCurrentAutoCorrection(new String(new int[]{primaryCode}, 0, 1));
1812c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = true;
1813923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1814a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                commitTyped(new String(new int[]{primaryCode}, 0, 1));
1815923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1816923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
18174ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1818e091982868476845acbcc8eff2ae3cad6de8776cJean Chalard        final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
1819ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                Constants.SUGGESTION_STRIP_COORDINATE == x);
1820863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
182100ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
1822297e6d590bd957577c335aa8713a786145a70288Jean Chalard                mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
18230e84041bf740590230198fa845d8c45acd4cb586Jean Chalard            promotePhantomSpace();
182400ed3be95cee472685bcba1ea861ac75c61db690Jean Chalard        }
18257a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        sendKeyCodePoint(primaryCode);
182689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
1827240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_SPACE == primaryCode) {
1828fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard            if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
18292b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa                if (maybeDoubleSpacePeriod()) {
1830120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_DOUBLE;
1831120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                } else if (!isShowingPunctuationList()) {
1832120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_WEAK;
1833120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                }
1834126698fdd256a2e3734634d3b923cabd800064baJean Chalard            }
1835120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
18362b6a1fed767080aaa71acbd591d30b9f436b0499Ken Wakasa            mHandler.startDoubleSpacePeriodTimer();
1837297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (!mConnection.isCursorTouchingWord(mCurrentSettings)) {
1838a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard                mHandler.postUpdateSuggestionStrip();
183989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            }
184089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
1841fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (swapWeakSpace) {
1842bbbdab12be748cdc2158f0e04bbb5478052ecd89Jean Chalard                swapSwapperAndSpace();
18434721427c7de3600f6fe7dfff16508a6a974fb3e4Jean Chalard                mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
1844f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard            } else if (SPACE_STATE_PHANTOM == spaceState
18457e4ad33a655a3b7fdaf45163a93bc5ac0d0ea696Jean Chalard                    && !mCurrentSettings.isWeakSpaceStripper(primaryCode)
18467e4ad33a655a3b7fdaf45163a93bc5ac0d0ea696Jean Chalard                    && !mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
1847fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // If we are in phantom space state, and the user presses a separator, we want to
1848fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // stay in phantom space state so that the next keypress has a chance to add the
1849fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // space. For example, if I type "Good dat", pick "day" from the suggestion strip
1850fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // then insert a comma and go on to typing the next word, I want the space to be
1851fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // inserted automatically before the next word, the same way it is when I don't
1852fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // input the comma.
1853f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // The case is a little different if the separator is a space stripper. Such a
1854f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // separator does not normally need a space on the right (that's the difference
1855f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // between swappers and strippers), so we should not stay in phantom space state if
1856f68fe7a9c9262a3591eeba61f34a91855a34c930Jean Chalard                // the separator is a stripper. Hence the additional test above.
1857fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                mSpaceState = SPACE_STATE_PHANTOM;
1858120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1859120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
186089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Set punctuation right away. onUpdateSelection will fire but tests whether it is
186189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // already displayed or not, so it's okay.
186255b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            setPunctuationSuggestions();
1863923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
186487cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        if (ProductionFlag.IS_INTERNAL) {
186587cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            Utils.Stats.onSeparator((char)primaryCode, x, y);
186687cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
1867120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1868fff8613df04af17bf56db866075b220d1ef0fbe8Jean Chalard        mKeyboardSwitcher.updateShiftState();
1869c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        return didAutoCorrect;
1870923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1871466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1872bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private CharSequence getTextWithUnderline(final String text) {
18730a59ac2ba88ac1f99151d9336136bc6fe7d416c0Jean Chalard        return mIsAutoCorrectionIndicatorOn
187477d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
1875fda847a870829f1491cbd5325f9c985213081149Jean Chalard                : text;
187677d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    }
187777d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard
1878923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void handleClose() {
18795475b38328171a0841ae18074bd45380ec567e90Jean Chalard        commitTyped(LastComposedWord.NOT_A_SEPARATOR);
1880923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        requestHideSelf(0);
188127e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
188227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mainKeyboardView != null) {
188327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            mainKeyboardView.closing();
18840a524de3df86e3039acba40b69ccba67ec484a41Tadashi G. Takaoka        }
1885923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1886923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1887a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // TODO: make this private
1888a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Outside LatinIME, only used by the test suite.
188915f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting
18909fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    boolean isShowingPunctuationList() {
18914702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        if (mSuggestionStripView == null) return false;
18924702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions();
18937599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
18947599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1895a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    private boolean isSuggestionsStripVisible() {
18964702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        if (mSuggestionStripView == null)
1897b5e00d5841b946de8970875231456228ae0eb6b1Ken Wakasa            return false;
18984702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        if (mSuggestionStripView.isShowingAddToDictionaryHint())
18999fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
19003d44f489b321f7586af4af8f281550a45653f50aJean Chalard        if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation))
19019fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return false;
1902dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        if (mCurrentSettings.isApplicationSpecifiedCompletionsOn())
19039fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
1904fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard        return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation);
1905923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1906923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1907259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka    private void clearSuggestionStrip() {
1908259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka        setSuggestionStrip(SuggestedWords.EMPTY, false);
19097204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
1910466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1911466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1912259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka    private void setSuggestionStrip(final SuggestedWords words, final boolean isAutoCorrection) {
19134702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        if (mSuggestionStripView != null) {
19144702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripView.setSuggestions(words);
1915dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard            mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection);
1916466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
1917d02783cb63293507e8544ea60d07559092ce83d4Jean Chalard    }
1918ec780e2868962bf17f0dfd35d36895f543bde40asatok
191938e535e59676ac4d7bf27026fe3e16fcd9eb292eJean Chalard    private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) {
1920ec780e2868962bf17f0dfd35d36895f543bde40asatok        // Put a blue underline to a word in TextView which will be auto-corrected.
19212be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard        if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
19222be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                && mWordComposer.isComposingWord()) {
19232be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
19242be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard            final CharSequence textWithUnderline =
19252be7a37acfd498bfc83347597bfb8cb216a310ebJean Chalard                    getTextWithUnderline(mWordComposer.getTypedWord());
1926574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // TODO: when called from an updateSuggestionStrip() call that results from a posted
1927574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // message, this is called outside any batch edit. Potentially, this may result in some
1928574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // janky flickering of the screen, although the display speed makes it unlikely in
1929574b80aacee95df26e85e6b78876a73d7076a672Jean Chalard            // the practice.
19305475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.setComposingText(textWithUnderline, 1);
1931ec780e2868962bf17f0dfd35d36895f543bde40asatok        }
1932466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1933466741d8a78965b8509bf527344f289e50873092Mike LeBeau
19347575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka    private void updateSuggestionStrip() {
1935d81e7d24d384a2bb1aeda65d9b423e4b1d23f185Jean Chalard        mHandler.cancelUpdateSuggestionStrip();
1936305326e789c3a89517855cc5a023ed1aa3074dc0Jean Chalard
1937923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if we have a suggestion engine attached.
19387ed22f1f72dfa14b13ad6775617fd9e89f0ca224Jean Chalard        if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
1939edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            if (mWordComposer.isComposingWord()) {
19407ed22f1f72dfa14b13ad6775617fd9e89f0ca224Jean Chalard                Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not "
19417ed22f1f72dfa14b13ad6775617fd9e89f0ca224Jean Chalard                        + "requested!");
1942edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
1943edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            }
19447575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka            return;
1945923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1946466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1947e4498929b070cdd673c69450e316e2932e334fd5Jean Chalard        if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) {
1948e4498929b070cdd673c69450e316e2932e334fd5Jean Chalard            setPunctuationSuggestions();
19497575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka            return;
1950e4498929b070cdd673c69450e316e2932e334fd5Jean Chalard        }
1951e4498929b070cdd673c69450e316e2932e334fd5Jean Chalard
1952f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        final SuggestedWords suggestedWords = getSuggestedWords(Suggest.SESSION_TYPING);
19537575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka        final String typedWord = mWordComposer.getTypedWord();
19547575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka        showSuggestionStrip(suggestedWords, typedWord);
19557575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka    }
19567575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka
1957f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private SuggestedWords getSuggestedWords(final int sessionId) {
1958d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
1959c7318bbc0b47662799d2f7a01e5b53edb43ebf2aTadashi G. Takaoka        if (keyboard == null || mSuggest == null) {
1960d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka            return SuggestedWords.EMPTY;
1961d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka        }
19627575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka        final String typedWord = mWordComposer.getTypedWord();
196332f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // Get the word on which we should search the bigrams. If we are composing a word, it's
196432f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
196532f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // should just skip whitespace if any, so 1.
196632f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // TODO: this is slow (2-way IPC) - we should probably cache this instead.
1967bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String prevWord =
196832f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard                mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators,
196932f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard                mWordComposer.isComposingWord() ? 2 : 1);
19707575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka        final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
1971d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka                prevWord, keyboard.getProximityInfo(), mCurrentSettings.mCorrectionEnabled,
1972d445b56ce14152b30143302899790af255691148Tadashi G. Takaoka                sessionId);
19737575ac70546c6a19331102a7719337614f5e3a0fTadashi G. Takaoka        return maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
19747ed22f1f72dfa14b13ad6775617fd9e89f0ca224Jean Chalard    }
1975979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1976bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord,
197732f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard            final SuggestedWords suggestedWords) {
197832f5384fc955f1e9fa8613ca86081f1bf4ccc421Jean Chalard        // TODO: consolidate this into getSuggestedWords
197909fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // We update the suggestion strip only when we have some suggestions to show, i.e. when
198009fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // the suggestion count is > 1; else, we leave the old suggestions, with the typed word
198109fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // replaced with the new one. However, when the word is a dictionary word, or when the
198209fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // length of the typed word is 1 or 0 (after a deletion typically), we do want to remove the
198309fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // old suggestions. Also, if we are showing the "add to dictionary" hint, we need to
198409fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        // revert to suggestions - although it is unclear how we can come here if it's displayed.
198509fdd30e5261990ace636c52a8330d4c5dbb364bJean Chalard        if (suggestedWords.size() > 1 || typedWord.length() <= 1
1986f4267c052160d8865399a758ce9d60916ed783ecJean Chalard                || !suggestedWords.mTypedWordValid
19874702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                || mSuggestionStripView.isShowingAddToDictionaryHint()) {
1988ea80794dd43d44fe53884b4b8f4567af3d0e8331Jean Chalard            return suggestedWords;
1989838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard        } else {
19904702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            SuggestedWords previousSuggestions = mSuggestionStripView.getSuggestions();
1991297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if (previousSuggestions == mCurrentSettings.mSuggestPuncList) {
1992838629aea00ce90231df97f1dae3d8273ba80cbdJean Chalard                previousSuggestions = SuggestedWords.EMPTY;
1993fe1a6d961cf039357f061482461e4d2e951ad346satok            }
19944ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalard            final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
199588bf1ba5263f5a5c1df367ddc401db4109ef8677Jean Chalard                    SuggestedWords.getTypedWordAndPreviousSuggestions(
19964ee186920e642ae8ebe0b6c97dfdceb0ad2fdeefJean Chalard                            typedWord, previousSuggestions);
1997ea80794dd43d44fe53884b4b8f4567af3d0e8331Jean Chalard            return new SuggestedWords(typedWordAndPreviousSuggestions,
19982e2519ee914d4bf9462950553840557a4c19faedJean Chalard                            false /* typedWordValid */,
1999bdf6d1b18b3cebdde5f39d10066ead34be161bafJean Chalard                            false /* hasAutoCorrectionCandidate */,
200003a35170751a635332c00bf6c272a0127a255cf6Jean Chalard                            false /* isPunctuationSuggestions */,
20010142b997bf18f5d07e83b3fd403f0b3ea4736040satok                            true /* isObsoleteSuggestions */,
20020142b997bf18f5d07e83b3fd403f0b3ea4736040satok                            false /* isPrediction */);
20039fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        }
2004979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
20054a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
2006bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private void showSuggestionStrip(final SuggestedWords suggestedWords, final String typedWord) {
2007c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka        if (suggestedWords.isEmpty()) {
2008259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            clearSuggestionStrip();
20092c5cf744e554cbe0872f1b3e18cbd1383b0189f9Jean Chalard            return;
20102c5cf744e554cbe0872f1b3e18cbd1383b0189f9Jean Chalard        }
2011bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String autoCorrection;
2012c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka        if (suggestedWords.mWillAutoCorrect) {
2013c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka            autoCorrection = suggestedWords.getWord(1);
2014923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
2015c6ff7c42d9aeafe2b2d21a34be10f1e9a450153aTadashi G. Takaoka            autoCorrection = typedWord;
2016923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2017d992fa847da8f232d961ff097857757bfbff73a5Jean Chalard        mWordComposer.setAutoCorrection(autoCorrection);
20189b01890254c62a30b079bd9f79a30f9541faf11bJean Chalard        final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
2019259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka        setSuggestionStrip(suggestedWords, isAutoCorrection);
2020dd931c47be1a4fe4cf86c8ad018e479c2cbdf8ceJean Chalard        setAutoCorrectionIndicator(isAutoCorrection);
2021913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
2022923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2023923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2024a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard    private void commitCurrentAutoCorrection(final String separatorString) {
2025913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // Complete any pending suggestions query first
2026d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        if (mHandler.hasPendingUpdateSuggestions()) {
2027259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            updateSuggestionStrip();
2028923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2029bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
2030eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        final String typedWord = mWordComposer.getTypedWord();
2031bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String autoCorrection = (typedAutoCorrection != null)
2032eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                ? typedAutoCorrection : typedWord;
2033117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        if (autoCorrection != null) {
203446798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            if (TextUtils.isEmpty(typedWord)) {
203546798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                throw new RuntimeException("We have an auto-correction but the typed word "
203646798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                        + "is empty? Impossible! I must commit suicide.");
203746798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            }
203887cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            if (ProductionFlag.IS_INTERNAL) {
203987cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                Stats.onAutoCorrection(
204087cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                        typedWord, autoCorrection.toString(), separatorString, mWordComposer);
204187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            }
20424733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
204366bb563535dbe3672f99f75bd71763a551444867Jean Chalard            commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
2044a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                    separatorString);
20455475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (!typedWord.equals(autoCorrection)) {
20461c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // This will make the correction flash for a short while as a visual clue
204728d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // to the user that auto-correction happened. It has no other effect; in particular
204828d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // note that this won't affect the text inside the text field AT ALL: it only makes
204928d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // the segment of text starting at the supplied index and running for the length
205028d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // of the auto-correction flash. At this moment, the "typedWord" argument is
205128d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                // ignored by TextView.
20525475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.commitCorrection(
20535475b38328171a0841ae18074bd45380ec567e90Jean Chalard                        new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
205496fdc4dd8426a078500ce1d8a104bde7201bf9b5Ken Wakasa                        typedWord, autoCorrection));
20551c6cf26c3705e845418a29718c034598b52293ccJean Chalard            }
2056923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2057923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2058923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
20594702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
20604702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka    // interface
2061c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
2062bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public void pickSuggestionManually(final int index, final String suggestion) {
20634702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
2064551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
2065551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        if (suggestion.length() == 1 && isShowingPunctuationList()) {
2066551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Word separators are suggested before the user inputs something.
2067551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // So, LatinImeLogger logs "" as a user's input.
2068551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords);
2069551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
2070551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            final int primaryCode = suggestion.charAt(0);
2071551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            onCodeInput(primaryCode,
2072ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                    Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
207394e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            if (ProductionFlag.IS_EXPERIMENTAL) {
207494e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge                ResearchLogger.latinIME_punctuationSuggestion(index, suggestion);
207594e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            }
2076551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard            return;
2077551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard        }
2078551612dd9ec79c9f9a7e430801fdf430dfe625e9Jean Chalard
20794fde56b8182514bcae64d76488724d2a64a73f14Jean Chalard        mConnection.beginBatchEdit();
2080eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang        if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0
2081eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                // In the batch input mode, a manually picked suggested word should just replace
2082eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                // the current batch input text and there is no need for a phantom space.
2083eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang                && !mWordComposer.isBatchMode()) {
2084845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            int firstChar = Character.codePointAt(suggestion, 0);
2085297e6d590bd957577c335aa8713a786145a70288Jean Chalard            if ((!mCurrentSettings.isWeakSpaceStripper(firstChar))
2086297e6d590bd957577c335aa8713a786145a70288Jean Chalard                    && (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) {
20870e84041bf740590230198fa845d8c45acd4cb586Jean Chalard                promotePhantomSpace();
2088845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard            }
2089845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard        }
2090845b24d9d31072b98958c557366617ad1c34f1b7Jean Chalard
2091dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        if (mCurrentSettings.isApplicationSpecifiedCompletionsOn()
2092dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && mApplicationSpecifiedCompletions != null
20931b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
20944702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            if (mSuggestionStripView != null) {
20954702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                mSuggestionStripView.clear();
2096923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
2097b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
2098611a5bdf650f7bc54bf40176f84f1cd9c87aa9aaJean Chalard            resetComposingState(true /* alsoResetLastComposedWord */);
20995475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
21005475b38328171a0841ae18074bd45380ec567e90Jean Chalard            mConnection.commitCompletion(completionInfo);
21019d71748ba48dbc8793f3e1ecddf5fd31b8e59613Jean Chalard            mConnection.endBatchEdit();
2102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
2103923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
21046a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
2105af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // We need to log before we commit, because the word composer will store away the user
2106af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // typed word.
21079bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        final String replacedWord = mWordComposer.getTypedWord().toString();
21089bfb6202154e06d7156f2f374dd9359f1be4eb68Kurt Partridge        LatinImeLogger.logOnManualSuggestion(replacedWord,
21098cc8f26adfe0f06cebc697dac43a856326cf7afcTadashi G. Takaoka                suggestion.toString(), index, suggestedWords);
211035c11842a78e272ac4f61bfc58b96826c833f104Kurt Partridge        mExpectingUpdateSelection = true;
211135c11842a78e272ac4f61bfc58b96826c833f104Kurt Partridge        commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
211235c11842a78e272ac4f61bfc58b96826c833f104Kurt Partridge                LastComposedWord.NOT_A_SEPARATOR);
211394e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
211494e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge            ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion);
211594e7f4bef970431f509a806d1b92b19fc3b5ce7dKurt Partridge        }
21164fde56b8182514bcae64d76488724d2a64a73f14Jean Chalard        mConnection.endBatchEdit();
211729a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        // Don't allow cancellation of manual pick
211829a1fc0f6b18dd41e7810ee720041f7c7557eb4fJean Chalard        mLastComposedWord.deactivate();
211990a91272447fd64bc54e06f08f45b11c45930767Jean Chalard        // Space state must be updated before calling updateShiftState
2120fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mSpaceState = SPACE_STATE_PHANTOM;
2121fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mKeyboardSwitcher.updateShiftState();
2122979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2123c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // We should show the "Touch again to save" hint if the user pressed the first entry
2124491d451e382a8e7d42937a96e4e06ada483e57b5Jean Chalard        // AND it's in none of our current dictionaries (main, user or otherwise).
21257f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // Please note that if mSuggest is null, it means that everything is off: suggestion
21267f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // and correction, so we shouldn't try to show the hint
2127bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
2128491d451e382a8e7d42937a96e4e06ada483e57b5Jean Chalard                // If the suggestion is not in the dictionary, the hint should be shown.
2129491d451e382a8e7d42937a96e4e06ada483e57b5Jean Chalard                && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
2130b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
213187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        if (ProductionFlag.IS_INTERNAL) {
2132240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            Stats.onSeparator((char)Constants.CODE_SPACE,
213387cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
213487cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
2135b1dc8ad5f244337f91fcdac2a17078f5b9239cb7Jean Chalard        if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) {
21364702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka            mSuggestionStripView.showAddToDictionaryHint(
21374702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka                    suggestion, mCurrentSettings.mHintToSaveText);
2138b1dc8ad5f244337f91fcdac2a17078f5b9239cb7Jean Chalard        } else {
21398f9c9377fc8944c9e96e6dcf661f0d673c23b83fJean Chalard            // If we're not showing the "Touch again to save", then update the suggestion strip.
2140a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard            mHandler.postUpdateSuggestionStrip();
214166a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani        }
2142923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2143a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2144979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
21458558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa     * Commits the chosen word to the text field and saves it for later retrieval.
2146979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
2147bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private void commitChosenWord(final String chosenWord, final int commitType,
2148a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard            final String separatorString) {
21494702671ea4feb0c79a879e2e3013afdd6ed800b1Tadashi G. Takaoka        final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
2150e8bb8351d6f09f461851af619cabe5fcd2f66c0aJean Chalard        mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
2151e8bb8351d6f09f461851af619cabe5fcd2f66c0aJean Chalard                this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
2152c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        // Add the word to the user history dictionary
2153bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String prevWord = addToUserHistoryDictionary(chosenWord);
21540fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // TODO: figure out here if this is an auto-correct or if the best word is actually
21550fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // what user typed. Note: currently this is done much later in
2156bdf89ce5feedb03e67b43f530b1eb9bd44203c63Jean Chalard        // LastComposedWord#didCommitTypedWord by string equality of the remembered
21570fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // strings.
2158c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord.toString(),
2159a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                separatorString, prevWord);
2160923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2161923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2162a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    private void setPunctuationSuggestions() {
2163a19c5e63804e6cffac5771b9749aad6c441e5e21Jean Chalard        if (mCurrentSettings.mBigramPredictionEnabled) {
2164259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            clearSuggestionStrip();
2165d5b765b3bbceb80b2e145ac7f4d66acbadd1ee4fJean Chalard        } else {
2166259905ba901add520de56715200fb67fab818764Tadashi G. Takaoka            setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
2167d5b765b3bbceb80b2e145ac7f4d66acbadd1ee4fJean Chalard        }
21687204eab3da916d8873b7eeee4b4528a6b82de19cJean Chalard        setAutoCorrectionIndicator(false);
2169913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
21706a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani    }
21716a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
2172bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private String addToUserHistoryDictionary(final String suggestion) {
2173c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (TextUtils.isEmpty(suggestion)) return null;
2174e46d12927ee2717788248b05f00b751beaf2a02aKen Wakasa        if (mSuggest == null) return null;
2175bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
2176ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        // If correction is not enabled, we don't add words to the user history dictionary.
2177ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        // That's to avoid unintended additions in some sensitive fields, or fields that
2178ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        // expect to receive non-words.
2179ca6b7d52650917b92bf00e092ddad25d9f3f2537Jean Chalard        if (!mCurrentSettings.mCorrectionEnabled) return null;
2180bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
218167b9337b6cc478973d2c628fb5686583ce339bf0Satoshi Kataoka        final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
218267b9337b6cc478973d2c628fb5686583ce339bf0Satoshi Kataoka        if (userHistoryDictionary != null) {
218302308bec632a5df23325c916bffec5def16b22b4Jean Chalard            final CharSequence prevWord
2184d579f1aefc8d02254db297ffd6d8f9dbdcab0637Jean Chalard                    = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2);
218571f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            final String secondWord;
2186adbd9ae105e06287b59379d7f7127d95fd0663f4Jean Chalard            if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
2187bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka                secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
218871f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            } else {
2189bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka                secondWord = suggestion;
219071f9d30b18e05d7d97d607d1aac3f81c6724abd9Jean Chalard            }
219168c650fb2a3b26d4000e849f96f0664598c95470Satoshi Kataoka            // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
219268c650fb2a3b26d4000e849f96f0664598c95470Satoshi Kataoka            // We don't add words with 0-frequency (assuming they would be profanity etc.).
2193c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka            final int maxFreq = AutoCorrection.getMaxFrequency(
2194c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka                    mSuggest.getUnigramDictionaries(), suggestion);
219568c650fb2a3b26d4000e849f96f0664598c95470Satoshi Kataoka            if (maxFreq == 0) return null;
2196bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final String prevWordString = (null == prevWord) ? null : prevWord.toString();
2197bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            userHistoryDictionary.addToUserHistory(prevWordString, secondWord, maxFreq > 0);
2198bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            return prevWordString;
219932e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani        }
2200c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        return null;
220132e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani    }
220232e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani
22036b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
22046b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
22056b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * word, else do nothing.
22066b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
22075475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
2208747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard        final CharSequence word = mConnection.getWordBeforeCursorIfAtEndOfWord(mCurrentSettings);
2209747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard        if (null != word) {
2210747cf0435a7e978dfd43c30bd931b56146c3d852Jean Chalard            restartSuggestionsOnWordBeforeCursor(word);
2211fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        }
22126b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
22136b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
22145475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void restartSuggestionsOnWordBeforeCursor(final CharSequence word) {
22153708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
2216d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int length = word.length();
22175475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(length, 0);
22185475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.setComposingText(word, 1);
2219a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
22206b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
22216b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
22225475b38328171a0841ae18074bd45380ec567e90Jean Chalard    private void revertCommit() {
2223bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String previousWord = mLastComposedWord.mPrevWord;
2224b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        final String originallyTypedWord = mLastComposedWord.mTypedWord;
2225bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        final String committedWord = mLastComposedWord.mCommittedWord;
2226cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final int cancelLength = committedWord.length();
2227ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        final int separatorLength = LastComposedWord.getSeparatorLength(
2228a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard                mLastComposedWord.mSeparatorString);
2229193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        // TODO: should we check our saved separator against the actual contents of the text view?
2230d442984e96cc6299c905141e3e32e0a4f55394c8Kurt Partridge        final int deleteLength = cancelLength + separatorLength;
2231890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2232b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            if (mWordComposer.isComposingWord()) {
22335935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit, but we are composing a word");
2234b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            }
2235bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final CharSequence wordBeforeCursor =
22365475b38328171a0841ae18074bd45380ec567e90Jean Chalard                    mConnection.getTextBeforeCursor(deleteLength, 0)
2237bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka                            .subSequence(0, cancelLength);
2238cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard            if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
22395935950d4431dd7eef18ebc370f2abeb614465d4Jean Chalard                throw new RuntimeException("revertCommit check failed: we thought we were "
2240cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard                        + "reverting \"" + committedWord
2241890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
2242890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
22438558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        }
22445475b38328171a0841ae18074bd45380ec567e90Jean Chalard        mConnection.deleteSurroundingText(deleteLength, 0);
2245c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
2246c54d558e2e70bdfb2c1078cae7b88440d421dc67satok            mUserHistoryDictionary.cancelAddingUserHistory(
2247c54d558e2e70bdfb2c1078cae7b88440d421dc67satok                    previousWord.toString(), committedWord.toString());
2248c54d558e2e70bdfb2c1078cae7b88440d421dc67satok        }
2249a05a0f20776b4c33f41f043f1bff245331937580Jean Chalard        mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1);
225087cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        if (ProductionFlag.IS_INTERNAL) {
225187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka            Stats.onSeparator(mLastComposedWord.mSeparatorString,
225287cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka                    Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
225387cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka        }
22540e9e7e337defe97d4ede8c59d0e925f5401f9292Jean Chalard        if (ProductionFlag.IS_EXPERIMENTAL) {
22550e9e7e337defe97d4ede8c59d0e925f5401f9292Jean Chalard            ResearchLogger.latinIME_revertCommit(originallyTypedWord);
2256193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        }
22570e9e7e337defe97d4ede8c59d0e925f5401f9292Jean Chalard        // Don't restart suggestion yet. We'll restart if the user deletes the
22580e9e7e337defe97d4ede8c59d0e925f5401f9292Jean Chalard        // separator.
2259b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
2260746e014eb54f0d6278b948868dff4863bfe85ad8Jean Chalard        // We have a separator between the word and the cursor: we should show predictions.
2261a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
2262890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    }
2263890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard
22640e84041bf740590230198fa845d8c45acd4cb586Jean Chalard    // This essentially inserts a space, and that's it.
22650e84041bf740590230198fa845d8c45acd4cb586Jean Chalard    public void promotePhantomSpace() {
2266ec60d60078b4837b657dc207bdbde1748749199eJean Chalard        if (mCurrentSettings.shouldInsertSpacesAutomatically()) {
2267e36af707c8a8705ebd19fcc359c51181f7ebd171Jean Chalard            sendKeyCodePoint(Constants.CODE_SPACE);
2268ec60d60078b4837b657dc207bdbde1748749199eJean Chalard        }
22690e84041bf740590230198fa845d8c45acd4cb586Jean Chalard    }
22700e84041bf740590230198fa845d8c45acd4cb586Jean Chalard
2271a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Used by the RingCharBuffer
2272f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public boolean isWordSeparator(final int code) {
2273297e6d590bd957577c335aa8713a786145a70288Jean Chalard        return mCurrentSettings.isWordSeparator(code);
2274923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2275923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
22769fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    // TODO: Make this private
22779fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    // Outside LatinIME, only used by the {@link InputTestsBase} test suite.
227815f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @UsedForTesting
22799fc6af325ec91a1a22c0679d374c0c3d6d03d9acTadashi G. Takaoka    void loadKeyboard() {
22801e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // When the device locale is changed in SetupWizard etc., this method may get called via
22811e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // onConfigurationChanged before SoftInputWindow is shown.
22820657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka        initSuggest();
22830657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka        loadSettings();
228427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (mKeyboardSwitcher.getMainKeyboardView() != null) {
22851e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            // Reload keyboard because the current language has been changed.
2286297e6d590bd957577c335aa8713a786145a70288Jean Chalard            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
22871e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        }
2288b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // Since we just changed languages, we should re-evaluate suggestions with whatever word
2289b84ee82e66d608151fcf552c82e7765a74e47fb0Jean Chalard        // we are currently composing. If we are not composing anything, we may want to display
22908f9c9377fc8944c9e96e6dcf661f0d673c23b83fJean Chalard        // predictions or punctuation signs (which is done by the updateSuggestionStrip anyway).
2291a6757f400a7a1acc66b2c64f4aa84137dd4395cdJean Chalard        mHandler.postUpdateSuggestionStrip();
229236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
229336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
2294a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Callback called by PointerTracker through the KeyboardActionListener. This is called when a
2295a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // key is depressed; release matching call is onReleaseKey below.
22965a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
2297f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onPressKey(final int primaryCode) {
2298a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        mKeyboardSwitcher.onPressKey(primaryCode);
2299923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2300923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2301a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Callback by PointerTracker through the KeyboardActionListener. This is called when a key
2302a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // is released; press matching call is onPressKey above.
23035a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
2304f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void onReleaseKey(final int primaryCode, final boolean withSliding) {
23052a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
23068d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv
23078d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        // If accessibility is on, ensure the user receives keyboard state updates.
23088d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
23098d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            switch (primaryCode) {
2310240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
23118d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifyShiftState();
23128d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
2313240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SWITCH_ALPHA_SYMBOL:
23148d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifySymbolsState();
23158d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
23168d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            }
23178d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        }
2318978c96aa995015658070346b60826a3a34fdaf84Jean Chalard
2319240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (Constants.CODE_DELETE == primaryCode) {
2320978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // This is a stopgap solution to avoid leaving a high surrogate alone in a text view.
2321978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // In the future, we need to deprecate deteleSurroundingText() and have a surrogate
2322978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            // pair-friendly way of deleting characters in InputConnection.
2323e91d495c53a2606962159cfddada2b7a5e206c4cJean Chalard            // TODO: use getCodePointBeforeCursor instead to improve performance
23245475b38328171a0841ae18074bd45380ec567e90Jean Chalard            final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0);
23255475b38328171a0841ae18074bd45380ec567e90Jean Chalard            if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
23265475b38328171a0841ae18074bd45380ec567e90Jean Chalard                mConnection.deleteSurroundingText(1, 0);
2327978c96aa995015658070346b60826a3a34fdaf84Jean Chalard            }
2328978c96aa995015658070346b60826a3a34fdaf84Jean Chalard        }
2329923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2330a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2331123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka    // receive ringer mode change and network state change.
2332923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
2333923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
2334f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka        public void onReceive(final Context context, final Intent intent) {
2335123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            final String action = intent.getAction();
2336564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
2337123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                mSubtypeSwitcher.onNetworkStateChanged(intent);
233821af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard            } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
23398e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka                mKeyboardSwitcher.onRingerModeChanged();
2340123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            }
2341923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2342923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    };
2343923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2344c206d0462354b3bf1ad0cec61534da567829555dTadashi G. Takaoka    private void launchSettings() {
23453c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        handleClose();
23463c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        launchSubActivity(SettingsActivity.class);
2347466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2348466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2349a5bb353de92a6a6d010ba1695b9dd22b3a617b77Jean Chalard    // Called from debug code only
2350bf96661d33d0126adb60a48880ceba1ff055d4a4satok    public void launchDebugSettings() {
23513c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        handleClose();
23523c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        launchSubActivity(DebugSettingsActivity.class);
2353bf96661d33d0126adb60a48880ceba1ff055d4a4satok    }
2354bf96661d33d0126adb60a48880ceba1ff055d4a4satok
2355f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void launchKeyboardedDialogActivity(final Class<? extends Activity> activityClass) {
23563c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        // Put the text in the attached EditText into a safe, saved state before switching to a
23573c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        // new activity that will also use the soft keyboard.
23583c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        commitTyped(LastComposedWord.NOT_A_SEPARATOR);
23593c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        launchSubActivity(activityClass);
23603c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge    }
23613c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge
2362f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    private void launchSubActivity(final Class<? extends Activity> activityClass) {
2363923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        Intent intent = new Intent();
23643c233bf1a5003c478a27964758afe2ca581d3d8bKurt Partridge        intent.setClass(LatinIME.this, activityClass);
2365923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2366923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        startActivity(intent);
2367923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
23692fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showSubtypeSelectorAndSettings() {
237085996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
237185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
237285996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                // TODO: Should use new string "Select active input modes".
237385996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.language_selection_title),
237485996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.english_ime_settings),
237585996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
237685996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
23772fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            @Override
23782fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            public void onClick(DialogInterface di, int position) {
23792fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                di.dismiss();
23802fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                switch (position) {
238185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
23822cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok                    Intent intent = CompatUtils.getInputLanguageSelectionIntent(
23836fbbab3b8442ad010c53dc53a09535f7b0ef0323Tadashi G. Takaoka                            mRichImm.getInputMethodIdOfThisIme(),
23842a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Intent.FLAG_ACTIVITY_NEW_TASK
23852fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
23862fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_CLEAR_TOP);
23872fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    startActivity(intent);
23882fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2389aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                case 1:
2390aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    launchSettings();
2391aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    break;
23922fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                }
23932fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            }
239485996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2395bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2396bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2397bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2398724bc479f7d796d6ce5d5e200216bea855b818b2Kurt Partridge        showOptionDialog(builder.create());
23992fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    }
2400923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2401f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    public void showOptionDialog(final AlertDialog dialog) {
240227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
240327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        if (windowToken == null) {
240427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            return;
240527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        }
240613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
240713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCancelable(true);
240813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.setCanceledOnTouchOutside(true);
240913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
241013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final Window window = dialog.getWindow();
241113d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        final WindowManager.LayoutParams lp = window.getAttributes();
241213d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.token = windowToken;
241313d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
241413d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.setAttributes(lp);
241513d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
241613d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
241713d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        mOptionsDialog = dialog;
241813d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka        dialog.show();
241913d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka    }
242013d6ecc4c275b9e9c38c7713bb2c69d37f3467f3Tadashi G. Takaoka
242128d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    public void debugDumpStateAndCrashWithException(final String context) {
242228d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard        final StringBuilder s = new StringBuilder();
242328d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard        s.append("Target application : ").append(mTargetApplicationInfo.name)
242428d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                .append("\nPackage : ").append(mTargetApplicationInfo.packageName)
242528d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                .append("\nTarget app sdk version : ")
242628d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                .append(mTargetApplicationInfo.targetSdkVersion)
242728d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                .append("\nAttributes : ").append(mCurrentSettings.getInputAttributesDebugString())
242828d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard                .append("\nContext : ").append(context);
242928d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard        throw new RuntimeException(s.toString());
243028d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard    }
243128d765ed901bfd1e736056db1cd807c13ef88c35Jean Chalard
24327e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
2433f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka    protected void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) {
2434923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.dump(fd, fout, args);
2435a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2436923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        final Printer p = new PrintWriterPrinter(fout);
2437923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("LatinIME state :");
24383708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
2439df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
2440df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        p.println("  Keyboard mode = " + keyboardMode);
2441fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard        p.println("  mIsSuggestionsSuggestionsRequested = "
2442fe53e5c060dc4fd0acdfdffcadba94f9bb6062c3Jean Chalard                + mCurrentSettings.isSuggestionsRequested(mDisplayOrientation));
24432f3a694e29ad5a63052a2f963327855fee099f55Jean Chalard        p.println("  mCorrectionEnabled=" + mCurrentSettings.mCorrectionEnabled);
24444d0f03bd66742ee292da81a3e025e119f28b6940Jean Chalard        p.println("  isComposingWord=" + mWordComposer.isComposingWord());
2445297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mSoundOn=" + mCurrentSettings.mSoundOn);
2446297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mVibrateOn=" + mCurrentSettings.mVibrateOn);
2447297e6d590bd957577c335aa8713a786145a70288Jean Chalard        p.println("  mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn);
2448dca729fddd69f03d8eaca238c62478a7fd77db96Jean Chalard        p.println("  inputAttributes=" + mCurrentSettings.getInputAttributesDebugString());
2449923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2450923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
2451