LatinIME.java revision 32f0af1fc48f67907f0e731e18359f29e2b1df14
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
19923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.app.AlertDialog;
20923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.BroadcastReceiver;
21923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context;
22923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.DialogInterface;
23923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Intent;
24923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.IntentFilter;
25923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.SharedPreferences;
26923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.res.Configuration;
2736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.content.res.Resources;
28923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.inputmethodservice.InputMethodService;
29923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.media.AudioManager;
30123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaokaimport android.net.ConnectivityManager;
31923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Debug;
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.Message;
33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.os.SystemClock;
34bf96661d33d0126adb60a48880ceba1ff055d4a4satokimport android.preference.PreferenceActivity;
35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.preference.PreferenceManager;
36e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.text.InputType;
37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils;
38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Log;
39923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.PrintWriterPrinter;
40923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.util.Printer;
4166a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasaniimport android.view.HapticFeedbackConstants;
42923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.KeyEvent;
43923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.View;
448e09172df1bb176cc899940862c56bed9b9aec4esatokimport android.view.ViewGroup;
458e09172df1bb176cc899940862c56bed9b9aec4esatokimport android.view.ViewParent;
46923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.CompletionInfo;
47923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.EditorInfo;
48466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport android.view.inputmethod.ExtractedText;
49923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.view.inputmethod.InputConnection;
50c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka
515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.accessibility.AccessibilityUtils;
528d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanvimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
53c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.CompatUtils;
54c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.EditorInfoCompatUtils;
55c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputConnectionCompatUtils;
56c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
57c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
58c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputTypeCompatUtils;
591fef530ec7626fa16777f52b48191e61db8f46d4satokimport com.android.inputmethod.compat.SuggestionSpanUtils;
6028f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasaimport com.android.inputmethod.compat.VibratorCompatWrapper;
61c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.deprecated.LanguageSwitcherProxy;
62c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.deprecated.VoiceProxy;
63c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
64c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardActionListener;
656b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardId;
66c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardSwitcher;
67f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView;
68c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokaimport com.android.inputmethod.keyboard.LatinKeyboardView;
698c3d5b6961a9b9d40c4bf21ad495f852971c24f4Tadashi G. Takaokaimport com.android.inputmethod.latin.suggestions.SuggestionsView;
70923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
71466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.FileDescriptor;
72466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.io.PrintWriter;
73466741d8a78965b8509bf527344f289e50873092Mike LeBeauimport java.util.Locale;
74466741d8a78965b8509bf527344f289e50873092Mike LeBeau
75923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
76923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Input method implementation for Qwerty'ish keyboard.
77923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
78c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaokapublic class LatinIME extends InputMethodServiceCompatWrapper implements KeyboardActionListener,
79913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        SuggestionsView.Listener {
808efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    private static final String TAG = LatinIME.class.getSimpleName();
81409220583333bdf06290dd9fd42f91b5c0d1b11asatok    private static final boolean TRACE = false;
829e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka    private static boolean DEBUG;
83a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
848efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    /**
858efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     * The private IME option used to indicate that no microphone should be
868efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     * shown for a given text field. For instance, this is specified by the
878efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     * search dialog when the dialog is already showing a voice search button.
888efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     *
898efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     * @deprecated Use {@link LatinIME#IME_OPTION_NO_MICROPHONE} with package name prefixed.
908efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     */
9110dd34de0ffcde0104f7d2dae3a3c9fd66abffccsatok    @SuppressWarnings("dep-ann")
928efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    public static final String IME_OPTION_NO_MICROPHONE_COMPAT = "nm";
938efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka
948efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka    /**
958efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     * The private IME option used to indicate that no microphone should be
968efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     * shown for a given text field. For instance, this is specified by the
978efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     * search dialog when the dialog is already showing a voice search button.
988efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka     */
994199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    public static final String IME_OPTION_NO_MICROPHONE = "noMicrophoneKey";
1004199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka
1014199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    /**
1024199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka     * The private IME option used to indicate that no settings key should be
1034199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka     * shown for a given text field.
1044199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka     */
1054199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    public static final String IME_OPTION_NO_SETTINGS_KEY = "noSettingsKey";
1068efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka
107af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka    /**
108af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     * The private IME option used to indicate that the given text field needs
109af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     * ASCII code points input.
1101ef6fc7e1519cedec9e84a64968bfba4212d0436Tadashi G. Takaoka     *
1111ef6fc7e1519cedec9e84a64968bfba4212d0436Tadashi G. Takaoka     * @deprecated Use {@link EditorInfo#IME_FLAG_FORCE_ASCII}.
112af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     */
1131ef6fc7e1519cedec9e84a64968bfba4212d0436Tadashi G. Takaoka    @SuppressWarnings("dep-ann")
114af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka    public static final String IME_OPTION_FORCE_ASCII = "forceAscii";
115af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka
116af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka    /**
117af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     * The subtype extra value used to indicate that the subtype keyboard layout is capable for
118af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     * typing ASCII characters.
119af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     */
120af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka    public static final String SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable";
121af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka
122d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka    /**
123294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima     * The subtype extra value used to indicate that the subtype keyboard layout supports touch
124294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima     * position correction.
125294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima     */
126294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima    public static final String SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION =
127294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            "SupportTouchPositionCorrection";
128294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima    /**
129d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka     * The subtype extra value used to indicate that the subtype keyboard layout should be loaded
130d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka     * from the specified locale.
131d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka     */
132d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka    public static final String SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE = "KeyboardLocale";
133d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka
1349e347d3d448e48229c46aad394ec9bd60cd5807bsatok    private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
135fd0bd57deb53c4cce32810a61133fa44b45dbb7bKen Wakasa
136923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // How many continuous deletes at which to start deleting at a higher speed.
137923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int DELETE_ACCELERATE_AT = 20;
138923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // Key events coming any faster than this are long-presses.
139d67fe0e7583f1be18b35b33b7658e4427f1e3dedAmith Yamasani    private static final int QUICK_PRESS = 200;
140a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
14159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private static final int PENDING_IMS_CALLBACK_DURATION = 800;
142055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
143cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /**
144cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * The name of the scheme used by the Package Manager to warn of a new package installation,
145cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * replacement or removal.
146cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     */
147cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private static final String SCHEME_PACKAGE = "package";
148cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
1490fe3611bee5095e7bd0fff2d0fdf8d5a13379132Jean Chalard    // TODO: migrate this to SettingsValues
1507599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private int mSuggestionVisibility;
1517599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
1527599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_show_value;
1537599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
1547599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_show_only_portrait_value;
1557599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_HIDE_VALUE
1567599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_hide_value;
1577599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1587599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
1597599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_SHOW_VALUE,
1607599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE,
1617599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_HIDE_VALUE
1627599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    };
1637599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
164fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_NONE = 0;
165120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Double space: the state where the user pressed space twice quickly, which LatinIME
166120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // resolved as period-space. Undoing this converts the period to a space.
167fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_DOUBLE = 1;
168b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
169b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard    // have just been swapped. Undoing this swaps them back; the space is still considered weak.
170120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
171fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
172fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // spaces happen when the user presses space, accepting the current suggestion (whether
173fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // it's an auto-correction or not).
174fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_WEAK = 3;
175fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom space: a not-yet-inserted space that should get inserted on the next input,
176fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // character provided it's not a separator. If it's a separator, the phantom space is dropped.
177fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // Phantom spaces happen when a user chooses a word from the suggestion strip.
178fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private static final int SPACE_STATE_PHANTOM = 4;
179120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
180120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Current space state of the input method. This can be any of the above constants.
181120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private int mSpaceState;
182126698fdd256a2e3734634d3b923cabd800064baJean Chalard
1837a16a061e622539e54d7d649dcb8d4965aea575aJean Chalard    private SettingsValues mSettingsValues;
18480b66bb166f7f45adfcadcb84788477df9930828Jean Chalard    private InputAttributes mInputAttributes;
18517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
186d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka    private View mExtractArea;
187abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka    private View mKeyPreviewBackingView;
188913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private View mSuggestionsContainer;
189913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private SuggestionsView mSuggestionsView;
190816a8a0fd85ca0327436f8bd1cfa6928600ebc5dJean Chalard    /* package for tests */ Suggest mSuggest;
1911b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private CompletionInfo[] mApplicationSpecifiedCompletions;
192a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
193610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    private InputMethodManagerCompatWrapper mImm;
1942fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private Resources mResources;
1952fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private SharedPreferences mPrefs;
19671c353aa875f5237b1dce4e18bd4fe86ce28b58eTadashi G. Takaoka    private KeyboardSwitcher mKeyboardSwitcher;
1970ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private SubtypeSwitcher mSubtypeSwitcher;
198b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok    private VoiceProxy mVoiceProxy;
199a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
200923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private UserDictionary mUserDictionary;
201979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private UserBigramDictionary mUserBigramDictionary;
202f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard    private UserUnigramDictionary mUserUnigramDictionary;
20388562bec54658840dcce352127bdc15705c20a89Jean Chalard    private boolean mIsUserDictionaryAvailable;
20436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
2052692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
2069318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa    private WordComposer mWordComposer = new WordComposer();
207409220583333bdf06290dd9fd42f91b5c0d1b11asatok
20879efbed76f638be298493107fa2d0cd1b5eb529esatok    private int mCorrectionMode;
20977da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard
210979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // Keep track of the last selection range to decide if we need to show word alternatives
21177da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private static final int NOT_A_CURSOR_POSITION = -1;
21277da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionStart = NOT_A_CURSOR_POSITION;
21377da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard    private int mLastSelectionEnd = NOT_A_CURSOR_POSITION;
214979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2154733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
2164733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // "expect" it, it means the user actually moved the cursor.
2174733609947c0ec74e460bd714fffca0518ade93aJean Chalard    private boolean mExpectingUpdateSelection;
218923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mDeleteCount;
219923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private long mLastKeyTime;
220a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
221923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private AudioManager mAudioManager;
22217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    private boolean mSilentModeOn; // System-wide current configuration
223923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
22428f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa    private VibratorCompatWrapper mVibrator;
22528f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
226b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok    // TODO: Move this flag to VoiceProxy
22781c52293f84ce475ac6b1661f4a4b92703405247Amith Yamasani    private boolean mConfigurationChanging;
228466741d8a78965b8509bf527344f289e50873092Mike LeBeau
22938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    // Member variables for remembering the current device orientation.
23038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    private int mDisplayOrientation;
23138f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
232cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    // Object for reacting to adding/removing a dictionary pack.
233cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private BroadcastReceiver mDictionaryPackInstallReceiver =
234cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            new DictionaryPackInstallBroadcastReceiver(this);
235cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
236dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    // Keeps track of most recently inserted text (multi-character key) for reverting
237dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    private CharSequence mEnteredText;
238dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
239604d80c67185954d4691ac775be59c499eee3b1csatok    private final ComposingStateManager mComposingStateManager =
240fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok            ComposingStateManager.getInstance();
241604d80c67185954d4691ac775be59c499eee3b1csatok
2424f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public final UIHandler mHandler = new UIHandler(this);
243d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
2444f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
245d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        private static final int MSG_UPDATE_SUGGESTIONS = 0;
24645f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_UPDATE_SHIFT_STATE = 1;
24745f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_VOICE_RESULTS = 2;
24845f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_FADEOUT_LANGUAGE_ON_SPACEBAR = 3;
24945f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 4;
25045f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_SPACE_TYPED = 5;
25193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        private static final int MSG_SET_BIGRAM_PREDICTIONS = 6;
25293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        private static final int MSG_PENDING_IMS_CALLBACK = 7;
253175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
25410dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayBeforeFadeoutLanguageOnSpacebar;
25510dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateSuggestions;
25610dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateShiftState;
25710dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDurationOfFadeoutLanguageOnSpacebar;
25810dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private float mFinalFadeoutFactorOfLanguageOnSpacebar;
25910dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private long mDoubleSpacesTurnIntoPeriodTimeout;
26038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
2614f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        public UIHandler(LatinIME outerInstance) {
2624f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
26310dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        }
264175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
26510dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        public void onCreate() {
26610dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka            final Resources res = getOuterInstance().getResources();
267175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger(
268175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_delay_before_fadeout_language_on_spacebar);
269175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateSuggestions =
270175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_suggestions);
271175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateShiftState =
272175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_shift_state);
273175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDurationOfFadeoutLanguageOnSpacebar = res.getInteger(
274175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_duration_of_fadeout_language_on_spacebar);
275175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger(
276175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f;
277175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger(
278175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_double_spaces_turn_into_period_timeout);
2794f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
2804f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
281923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
282923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void handleMessage(Message msg) {
2834f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
2844f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
285c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka            final LatinKeyboardView inputView = switcher.getKeyboardView();
286923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (msg.what) {
287d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SUGGESTIONS:
2884f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateSuggestions();
289d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
290d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SHIFT_STATE:
291de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                switcher.updateShiftState();
292d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
293cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            case MSG_SET_BIGRAM_PREDICTIONS:
2944f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateBigramPredictions();
29589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                break;
296d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_VOICE_RESULTS:
297433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                final Keyboard keyboard = switcher.getKeyboard();
2984f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.mVoiceProxy.handleVoiceResults(latinIme.preferCapitalization()
299433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                        || (keyboard != null && keyboard.isShiftedOrShiftLocked()));
300de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                break;
301de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR:
30223d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka                setSpacebarTextFadeFactor(inputView,
30323d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka                        (1.0f + mFinalFadeoutFactorOfLanguageOnSpacebar) / 2,
3043708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka                        (Keyboard)msg.obj);
305de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj),
306175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                        mDurationOfFadeoutLanguageOnSpacebar);
307de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                break;
308de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            case MSG_DISMISS_LANGUAGE_ON_SPACEBAR:
30923d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka                setSpacebarTextFadeFactor(inputView, mFinalFadeoutFactorOfLanguageOnSpacebar,
3103708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka                        (Keyboard)msg.obj);
311d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
312923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
313923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
314d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
315d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void postUpdateSuggestions() {
316d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
317175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions);
318d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
319d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
320d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateSuggestions() {
321d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
322d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
323d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
324d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public boolean hasPendingUpdateSuggestions() {
325d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            return hasMessages(MSG_UPDATE_SUGGESTIONS);
326d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
327d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
328beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void postUpdateShiftState() {
329d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
330175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState);
331d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
332d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
333d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateShiftState() {
334d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
335d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
336d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
337cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void postUpdateBigramPredictions() {
338cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
339175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions);
34089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
34189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
342cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void cancelUpdateBigramPredictions() {
343cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
34489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
34589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
346d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void updateVoiceResults() {
347d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            sendMessage(obtainMessage(MSG_VOICE_RESULTS));
348d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
349de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka
35023d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka        private static void setSpacebarTextFadeFactor(LatinKeyboardView inputView,
3513708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka                float fadeFactor, Keyboard oldKeyboard) {
35223d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka            if (inputView == null) return;
35323d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka            final Keyboard keyboard = inputView.getKeyboard();
3543708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            if (keyboard == oldKeyboard) {
3554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                inputView.updateSpacebar(fadeFactor,
3564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                        SubtypeSwitcher.getInstance().needsToDisplayLanguage(
3574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                                keyboard.mId.mLocale));
35823d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka            }
35923d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka        }
36023d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka
361d5a6b910e83de6dea3c5813cbf5e219abaccdf8aTadashi G. Takaoka        public void startDisplayLanguageOnSpacebar(boolean localeChanged) {
3624f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
363de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR);
364de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR);
365c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka            final LatinKeyboardView inputView = latinIme.mKeyboardSwitcher.getKeyboardView();
366de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            if (inputView != null) {
3673708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka                final Keyboard keyboard = latinIme.mKeyboardSwitcher.getKeyboard();
368c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                // The language is always displayed when the delay is negative.
369c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                final boolean needsToDisplayLanguage = localeChanged
370175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                        || mDelayBeforeFadeoutLanguageOnSpacebar < 0;
371de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                // The language is never displayed when the delay is zero.
372175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                if (mDelayBeforeFadeoutLanguageOnSpacebar != 0) {
37323d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka                    setSpacebarTextFadeFactor(inputView,
37423d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka                            needsToDisplayLanguage ? 1.0f : mFinalFadeoutFactorOfLanguageOnSpacebar,
37523d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka                                    keyboard);
37617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                }
377c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                // The fadeout animation will start when the delay is positive.
378175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                if (localeChanged && mDelayBeforeFadeoutLanguageOnSpacebar > 0) {
379de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR, keyboard),
380175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                            mDelayBeforeFadeoutLanguageOnSpacebar);
381de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                }
382de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            }
383de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        }
384fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
385fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void startDoubleSpacesTimer() {
386fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
387175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout);
388fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
389fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
390fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void cancelDoubleSpacesTimer() {
391fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
392fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
393fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
394fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public boolean isAcceptingDoubleSpaces() {
395fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            return hasMessages(MSG_SPACE_TYPED);
396fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
39738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
39859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        // Working variables for the following methods.
39959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mIsOrientationChanging;
4005fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard        private boolean mPendingSuccessiveImsCallback;
40159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingStartInput;
40259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInputView;
40359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInput;
404e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private EditorInfo mAppliedEditorInfo;
40559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
40659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void startOrientationChanging() {
407dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            removeMessages(MSG_PENDING_IMS_CALLBACK);
408dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            resetPendingImsCallback();
40959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mIsOrientationChanging = true;
410055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            final LatinIME latinIme = getOuterInstance();
411f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            if (latinIme.isInputViewShown()) {
412f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka                latinIme.mKeyboardSwitcher.saveKeyboardState();
413f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            }
41459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
41559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
41659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private void resetPendingImsCallback() {
41759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInputView = false;
41859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInput = false;
41959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingStartInput = false;
42059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
42159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
422e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private void executePendingImsCallback(LatinIME latinIme, EditorInfo editorInfo,
42359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                boolean restarting) {
42459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInputView)
42559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
42659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInput)
42759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
42859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingStartInput)
429e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
43059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            resetPendingImsCallback();
43159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
43259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
433e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInput(EditorInfo editorInfo, boolean restarting) {
43459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
43559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the second onStartInput after orientation changed.
43659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingStartInput = true;
43759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
43859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                if (mIsOrientationChanging && restarting) {
43959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    // This is the first onStartInput after orientation changed.
44059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    mIsOrientationChanging = false;
4415fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = true;
44259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                }
44359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
444e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
445e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
446055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
447055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
448055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
449e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
4506b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)
4516b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka                    && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
452e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                // Typically this is the second onStartInputView after orientation changed.
453e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                resetPendingImsCallback();
454e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            } else {
4555fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                if (mPendingSuccessiveImsCallback) {
456e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    // This is the first onStartInputView after orientation changed.
4575fd11a7f3367d51cea8dc83fb9675c93d82a2e4eJean Chalard                    mPendingSuccessiveImsCallback = false;
458e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    resetPendingImsCallback();
459e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
460e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            PENDING_IMS_CALLBACK_DURATION);
461e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                }
462e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
463e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
464e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputViewInternal(editorInfo, restarting);
465e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = editorInfo;
466e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            }
46759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
46859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
46959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInputView(boolean finishingInput) {
47059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
47159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInputView after orientation changed.
47259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInputView = true;
47359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
47459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
47559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(finishingInput);
476e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = null;
47738f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            }
47838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        }
479ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka
48059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInput() {
48159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
48259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInput after orientation changed.
48359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInput = true;
48459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
48559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
48659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                executePendingImsCallback(latinIme, null, false);
48759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
488ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka            }
489ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka        }
49038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    }
49138f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
4927e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
4937e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    public void onCreate() {
49427d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
49527d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        mPrefs = prefs;
49627d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        LatinImeLogger.init(this, prefs);
4976d9021527a38ba1e94225020389621a0d7227aa1satok        LanguageSwitcherProxy.init(this, prefs);
498bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        InputMethodManagerCompatWrapper.init(this);
499ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka        SubtypeSwitcher.init(this);
50027d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        KeyboardSwitcher.init(this, prefs);
5012ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka        AccessibilityUtils.init(this);
502363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
503923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onCreate();
504363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
505bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        mImm = InputMethodManagerCompatWrapper.getInstance();
5060ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
5070ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
50828f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        mVibrator = VibratorCompatWrapper.getInstance(this);
50910dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        mHandler.onCreate();
5109e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka        DEBUG = LatinImeLogger.sDBG;
511363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
512363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        final Resources res = getResources();
513363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        mResources = res;
514fd7d814c81132bdd59146a39dd668532f9514cd1Jean Chalard
51528f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        loadSettings();
51628f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
517dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        // TODO: remove the following when it's not needed by updateCorrectionMode() any more
518644c8b7c96627199c13297082e4566adae159bf3Jean Chalard        mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
5199502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
520979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        boolean tryGC = true;
5219502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
522979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            try {
5230ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok                initSuggest();
524979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = false;
525979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            } catch (OutOfMemoryError e) {
5269502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e);
527979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
528979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
529979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
530f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        mDisplayOrientation = res.getConfiguration().orientation;
531b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
532cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Register to receive ringer mode change and network state change.
533cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Also receive installation and removal of a dictionary pack.
534123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        final IntentFilter filter = new IntentFilter();
535123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
536123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
537923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        registerReceiver(mReceiver, filter);
538b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy = VoiceProxy.init(this, prefs, mHandler);
539cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
540cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final IntentFilter packageFilter = new IntentFilter();
541cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
542cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
543cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addDataScheme(SCHEME_PACKAGE);
544cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
545646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard
546646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        final IntentFilter newDictFilter = new IntentFilter();
547646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        newDictFilter.addAction(
548646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard                DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
549646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
550923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
55136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
55217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    // Has to be package-visible for unit tests
55317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    /* package */ void loadSettings() {
55417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
55517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance();
5567a16a061e622539e54d7d649dcb8d4965aea575aJean Chalard        mSettingsValues = new SettingsValues(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
55714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
55817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    }
55917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
5600ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private void initSuggest() {
561cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
562ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr);
56336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
564309bff562fbaf47488e6bf6636840f00574187d8Jean Chalard        final Resources res = mResources;
565ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        final Locale savedLocale = LocaleUtils.setSystemLocale(res, keyboardLocale);
56614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        final ContactsDictionary oldContactsDictionary;
56736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani        if (mSuggest != null) {
56814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            oldContactsDictionary = mSuggest.getContactsDictionary();
56936fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani            mSuggest.close();
57014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
57114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            oldContactsDictionary = null;
57236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani        }
573979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
5748efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka        int mainDicResId = Utils.getMainDictionaryResourceId(res);
575cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mSuggest = new Suggest(this, mainDicResId, keyboardLocale);
57617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (mSettingsValues.mAutoCorrectEnabled) {
57717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
57817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        }
579e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
580cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mUserDictionary = new UserDictionary(this, localeStr);
581923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mSuggest.setUserDictionary(mUserDictionary);
58288562bec54658840dcce352127bdc15705c20a89Jean Chalard        mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
583e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
58414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(oldContactsDictionary);
585e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
586f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        mUserUnigramDictionary
587f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                = new UserUnigramDictionary(this, this, localeStr, Suggest.DIC_USER_UNIGRAM);
588f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        mSuggest.setUserUnigramDictionary(mUserUnigramDictionary);
589e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
590f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        mUserBigramDictionary
591f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                = new UserBigramDictionary(this, this, localeStr, Suggest.DIC_USER_BIGRAM);
592e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        mSuggest.setUserBigramDictionary(mUserBigramDictionary);
593e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
594e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani        updateCorrectionMode();
59536fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
596ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        LocaleUtils.setSystemLocale(res, savedLocale);
597923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
59836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
59914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    /**
60014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * Resets the contacts dictionary in mSuggest according to the user settings.
60114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
60214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * This method takes an optional contacts dictionary to use. Since the contacts dictionary
60314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * does not depend on the locale, it can be reused across different instances of Suggest.
60414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * The dictionary will also be opened or closed as necessary depending on the settings.
60514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
60614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * @param oldContactsDictionary an optional dictionary to use, or null
60714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     */
60814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    private void resetContactsDictionary(final ContactsDictionary oldContactsDictionary) {
60914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict);
61014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
61114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        final ContactsDictionary dictionaryToUse;
61214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (!shouldSetDictionary) {
61314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the dictionary is closed. If it is already closed, this is a no-op,
61414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // so it's safe to call it anyways.
61514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            if (null != oldContactsDictionary) oldContactsDictionary.close();
61614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = null;
61714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else if (null != oldContactsDictionary) {
61814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the old contacts dictionary is opened. If it is already open, this is a
61914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // no-op, so it's safe to call it anyways.
62014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            oldContactsDictionary.reopen(this);
62114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = oldContactsDictionary;
62214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
62314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
62414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
62514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
62614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (null != mSuggest) {
62714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            mSuggest.setContactsDictionary(dictionaryToUse);
62814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
629699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard    }
630699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard
631cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /* package private */ void resetSuggestMainDict() {
632cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
633ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr);
634cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        int mainDicResId = Utils.getMainDictionaryResourceId(mResources);
635cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mSuggest.resetMainDict(this, mainDicResId, keyboardLocale);
636cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
637cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
638466741d8a78965b8509bf527344f289e50873092Mike LeBeau    @Override
639466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onDestroy() {
640e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        if (mSuggest != null) {
641e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest.close();
642e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest = null;
643979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
644923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        unregisterReceiver(mReceiver);
645cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        unregisterReceiver(mDictionaryPackInstallReceiver);
646b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.destroy();
647979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
648979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.onDestroy();
649923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onDestroy();
650923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
651923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
652923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
653923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onConfigurationChanged(Configuration conf) {
654dc64b138b5e3fb3706c0818d0a308fe6e36985b0Tadashi G. Takaoka        mSubtypeSwitcher.onConfigurationChanged(conf);
655604d80c67185954d4691ac775be59c499eee3b1csatok        mComposingStateManager.onFinishComposingText();
656b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        // If orientation changed while predicting, commit the change
657f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        if (mDisplayOrientation != conf.orientation) {
658f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mDisplayOrientation = conf.orientation;
659f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mHandler.startOrientationChanging();
6609351550dc6af7859e5280e16144c9386a37b976dKen Wakasa            final InputConnection ic = getCurrentInputConnection();
66166bb563535dbe3672f99f75bd71763a551444867Jean Chalard            commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
662466741d8a78965b8509bf527344f289e50873092Mike LeBeau            if (ic != null) ic.finishComposingText(); // For voice input
6632fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            if (isShowingOptionDialog())
6642fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                mOptionsDialog.dismiss();
665b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
6668b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
66781c52293f84ce475ac6b1661f4a4b92703405247Amith Yamasani        mConfigurationChanging = true;
668923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onConfigurationChanged(conf);
669b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.onConfigurationChanged(conf);
67081c52293f84ce475ac6b1661f4a4b92703405247Amith Yamasani        mConfigurationChanging = false;
67188fc9d44186120f9edc5cf7ec0e2af85260fed04satok
67288fc9d44186120f9edc5cf7ec0e2af85260fed04satok        // This will work only when the subtype is not supported.
67388fc9d44186120f9edc5cf7ec0e2af85260fed04satok        LanguageSwitcherProxy.onConfigurationChanged(conf);
674923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
675b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
676923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
677923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public View onCreateInputView() {
6786c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        return mKeyboardSwitcher.onCreateInputView();
6796c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    }
6806c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka
6816c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    @Override
6826c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    public void setInputView(View view) {
6836c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        super.setInputView(view);
684d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        mExtractArea = getWindow().getWindow().getDecorView()
685d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka                .findViewById(android.R.id.extractArea);
686abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
687913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
688913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
689913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
690913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setListener(this, view);
691f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        if (LatinImeLogger.sVISUALDEBUG) {
692f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka            mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
693f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        }
694923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
695923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
696923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
697c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public void setCandidatesView(View view) {
698c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        // To ensure that CandidatesView will never be set.
699c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return;
700923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
701923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
702a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker    @Override
703e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInput(EditorInfo editorInfo, boolean restarting) {
704e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInput(editorInfo, restarting);
70559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
70659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
70759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
708e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
709e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInputView(editorInfo, restarting);
71059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
71159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
71259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
71359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInputView(boolean finishingInput) {
71459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInputView(finishingInput);
71559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
71638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
71759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
71859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInput() {
71959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInput();
72059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
72159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
722e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
723e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInput(editorInfo, restarting);
72459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
72559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
726e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
727e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInputView(editorInfo, restarting);
72845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
729c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = switcher.getKeyboardView();
7308e09172df1bb176cc899940862c56bed9b9aec4esatok
73189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (DEBUG) {
732e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            Log.d(TAG, "onStartInputView: editorInfo:" + ((editorInfo == null) ? "none"
733f0f726464dcb5b3cef4f8e703659b35ca62430b5Tadashi G. Takaoka                    : String.format("inputType=0x%08x imeOptions=0x%08x",
734e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            editorInfo.inputType, editorInfo.imeOptions)));
735910b73127fa207dd26ec8124000262523b0aac0csatok        }
7364f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        if (Utils.inPrivateImeOptions(null, IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo)) {
7374f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
7384f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
7394f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Use " + getPackageName() + "." + IME_OPTION_NO_MICROPHONE + " instead");
7404f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
7414f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        if (Utils.inPrivateImeOptions(getPackageName(), IME_OPTION_FORCE_ASCII, editorInfo)) {
7424f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Deprecated private IME option specified: "
7434f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka                    + editorInfo.privateImeOptions);
7444f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka            Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
7454f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka        }
7464f5c3a2898315ff41cc735a195cfeb2937f22f70Tadashi G. Takaoka
7477ef235f53f2291f22ddf8c56be9860a218b25bbbTadashi G. Takaoka        LatinImeLogger.onStartInputView(editorInfo);
748923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // In landscape mode, this method gets called without the input view being created.
749979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (inputView == null) {
750923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
751923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
752923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
753b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        // Forward this event to the accessibility utilities, if enabled.
754b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
755b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        if (accessUtils.isTouchExplorationEnabled()) {
756e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            accessUtils.onStartInputViewInternal(editorInfo, restarting);
757b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        }
758b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette
7598d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        mSubtypeSwitcher.updateParametersOnStartInputView();
7604ab730dbd34fad323063f2ffd31ce33de746668dsatok
761055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        // Most such things we decide below in initializeInputAttributesAndGetMode, but we need to
762055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        // know now whether this is a password text field, because we need to know now whether we
763055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        // want to enable the voice button.
764e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        final int inputType = (editorInfo != null) ? editorInfo.inputType : 0;
765104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        mVoiceProxy.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType)
7663be0039164f5e2060b83d0699e293ffc76384732Tadashi G. Takaoka                || InputTypeCompatUtils.isVisiblePasswordInputType(inputType));
767c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
768b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // The EditorInfo might have a flag that affects fullscreen mode.
769b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // Note: This call should be done by InputMethodService?
770b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        updateFullscreenMode();
7711fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard        mLastSelectionStart = editorInfo.initialSelStart;
7721fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard        mLastSelectionEnd = editorInfo.initialSelEnd;
77380b66bb166f7f45adfcadcb84788477df9930828Jean Chalard        mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
774ccc35f7fa74860a8d737a4e9ff01fc0168dd329dJean Chalard        mApplicationSpecifiedCompletions = null;
775c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
776c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.closing();
777c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mEnteredText = null;
7782692a8700737d8eed268039aa27b22a31669da08Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
779c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mDeleteCount = 0;
780120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
781c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
78217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
78317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        updateCorrectionMode();
7842ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka        updateSuggestionVisibility(mResources);
78517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
78617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) {
78717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
788549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        }
789e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mVoiceProxy.loadSettings(editorInfo, mPrefs);
79017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        // This will work only when the subtype is not supported.
79117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        LanguageSwitcherProxy.loadSettings();
79217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
793c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (mSubtypeSwitcher.isKeyboardMode()) {
794e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            switcher.loadKeyboard(editorInfo, mSettingsValues);
795c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        }
796c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
797913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
798913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.clear();
799913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShownInternal(
800913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                isSuggestionsStripVisible(), /* needsInputViewShown */ false);
801c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        // Delay updating suggestions because keyboard input view may not be shown at this point.
802c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mHandler.postUpdateSuggestions();
803ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        mHandler.cancelDoubleSpacesTimer();
804c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
805240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard        inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
806240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard                mSettingsValues.mKeyPreviewPopupDismissDelay);
807c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.setProximityCorrectionEnabled(true);
808c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
809104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        mVoiceProxy.onStartInputView(inputView.getWindowToken());
810c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
811c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
812c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    }
813c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
814923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
815e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    public void onWindowHidden() {
816e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        super.onWindowHidden();
817f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
818e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        if (inputView != null) inputView.closing();
819e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
820e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
82159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputInternal() {
822923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onFinishInput();
823a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
824979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
825979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
826b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
827409220583333bdf06290dd9fd42f91b5c0d1b11asatok
828f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
829d024ea605cc6b5b0b9fa1bd838d5b0ebd3901a5dKen Wakasa        if (inputView != null) inputView.closing();
830f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        if (mUserUnigramDictionary != null) mUserUnigramDictionary.flushPendingWrites();
831979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites();
832466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
833466741d8a78965b8509bf527344f289e50873092Mike LeBeau
83459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputViewInternal(boolean finishingInput) {
8356495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa        super.onFinishInputView(finishingInput);
836055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardSwitcher.onFinishInputView();
837f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
8385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (inputView != null) inputView.cancelAllMessages();
839d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        // Remove pending messages related to update suggestions
840d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.cancelUpdateSuggestions();
8416495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    }
8426495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa
8436495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    @Override
844466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onUpdateExtractedText(int token, ExtractedText text) {
845466741d8a78965b8509bf527344f289e50873092Mike LeBeau        super.onUpdateExtractedText(token, text);
846b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.showPunctuationHintIfNecessary();
847923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
848923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
849923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
850923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
851923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            int newSelStart, int newSelEnd,
852104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            int composingSpanStart, int composingSpanEnd) {
853923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
854104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                composingSpanStart, composingSpanEnd);
855466741d8a78965b8509bf527344f289e50873092Mike LeBeau
856466741d8a78965b8509bf527344f289e50873092Mike LeBeau        if (DEBUG) {
857466741d8a78965b8509bf527344f289e50873092Mike LeBeau            Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
858466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", ose=" + oldSelEnd
859025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lss=" + mLastSelectionStart
860025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lse=" + mLastSelectionEnd
861466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nss=" + newSelStart
862466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nse=" + newSelEnd
863104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", cs=" + composingSpanStart
864104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                    + ", ce=" + composingSpanEnd);
865466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
866466741d8a78965b8509bf527344f289e50873092Mike LeBeau
867b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.setCursorAndSelection(newSelEnd, newSelStart);
8684f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
869104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // TODO: refactor the following code to be less contrived.
870104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "newSelStart != composingSpanEnd" || "newSelEnd != composingSpanEnd" means
871104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // that the cursor is not at the end of the composing span, or there is a selection.
872104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // "mLastSelectionStart != newSelStart" means that the cursor is not in the same place
873104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // as last time we were called (if there is a selection, it means the start hasn't
874104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // changed, so it's the end that did).
875104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean selectionChanged = (newSelStart != composingSpanEnd
876104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard                || newSelEnd != composingSpanEnd) && mLastSelectionStart != newSelStart;
877104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // if composingSpanStart and composingSpanEnd are -1, it means there is no composing
878104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // span in the view - we can use that to narrow down whether the cursor was moved
879104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // by us or not. If we are composing a word but there is no composing span, then
880104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // we know for sure the cursor moved while we were composing and we should reset
881104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        // the state.
882104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard        final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
8834733609947c0ec74e460bd714fffca0518ade93aJean Chalard        if (!mExpectingUpdateSelection) {
884cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // TAKE CARE: there is a race condition when we enter this test even when the user
885cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // did not explicitly move the cursor. This happens when typing fast, where two keys
886cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // turn this flag on in succession and both onUpdateSelection() calls arrive after
887cd2286fff1ce0aaa5e3f60e55afba2299101207cJean Chalard            // the second one - the first call successfully avoids this test, but the second one
888104453908064dcdbb4f10aa4150f1d79beafb408Jean Chalard            // enters. For the moment we rely on noComposingSpan to further reduce the impact.
88951fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
8909a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // TODO: the following is probably better done in resetEntireInputState().
8919a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // it should only happen when the cursor moved, and the very purpose of the
8929a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // test below is to narrow down whether this happened or not. Likewise with
8939a2a11e65bd32d82754b3fcff74e9e3795327628Jean Chalard            // the call to postUpdateShiftState.
89451fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // We set this to NONE because after a cursor move, we don't want the space
89551fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // state-related special processing to kick in.
89651fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            mSpaceState = SPACE_STATE_NONE;
89751fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard
8988a3d369840d6061692a19ceb1eb7267a50a9e056Jean Chalard            if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) {
8992649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard                resetEntireInputState();
9004c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard            }
901beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
902beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            mHandler.postUpdateShiftState();
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;
9137a8dac55278cedd838be325f56b4c52d973c61f5satok    }
9147a8dac55278cedd838be325f56b4c52d973c61f5satok
915c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
916c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has clicked on the extracted text view,
917c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * when running in fullscreen mode.  The default implementation hides
918913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * the suggestions view when this happens, but only if the extracted text
919c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * editor has a vertical scroll bar because its text doesn't fit.
920c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
921913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
922c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
923c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
924c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedTextClicked() {
925fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
926c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
927c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedTextClicked();
928c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
929c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
930c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
931c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has performed a cursor movement in the
932c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * extracted text view, when it is running in fullscreen mode.  The default
933913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * implementation hides the suggestions view when a vertical movement
934c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * happens, but only if the extracted text editor has a vertical scroll bar
935c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * because its text doesn't fit.
936c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
937913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
938c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
939c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
940c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedCursorMovement(int dx, int dy) {
941fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
942c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
943c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedCursorMovement(dx, dy);
944c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
945c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
946923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
947923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void hideWindow() {
948979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
949c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mKeyboardSwitcher.onHideWindow();
950979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
951923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (TRACE) Debug.stopMethodTracing();
9526e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
9536e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog.dismiss();
9546e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog = null;
9556e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        }
956b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.hideVoiceWindow(mConfigurationChanging);
957923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.hideWindow();
958923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
959923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
960923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
9611b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onDisplayCompletions(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        }
970dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn) {
9711b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
9721b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            if (applicationSpecifiedCompletions == null) {
973b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani                clearSuggestions();
974923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                return;
975923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
976a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
9777e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka            SuggestedWords.Builder builder = new SuggestedWords.Builder()
9781b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                    .setApplicationSpecifiedCompletions(applicationSpecifiedCompletions)
9795238df54ad3f648d09d5288f00b0a9d3c0593832Tadashi G. Takaoka                    .setTypedWordValid(false)
9805238df54ad3f648d09d5288f00b0a9d3c0593832Tadashi G. Takaoka                    .setHasMinimalSuggestion(false);
981979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // When in fullscreen mode, show completions generated by the application
9827e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka            setSuggestions(builder.build());
983b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            // TODO: is this the right thing to do? What should we auto-correct to in
984b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            // this case? This says to keep whatever the user typed.
985b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
986c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            setSuggestionStripShown(true);
987923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
988923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
989923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
990c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
991913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // TODO: Modify this if we support suggestions with hard keyboard
992913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
993433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
994433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
995913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean shouldShowSuggestions = shown
996433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                    && (needsInputViewShown ? inputViewShown : true);
9974b1780fa9571409d65d9797d47949ffafaf0f083Tadashi G. Takaoka            if (isFullscreenMode()) {
998913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
999913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.GONE);
10007a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            } else {
1001913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
1002913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE);
10037a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            }
1004923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1005923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1006a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1007c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShown(boolean shown) {
1008c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
1009543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    }
1010543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa
1011543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    @Override
1012923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onComputeInsets(InputMethodService.Insets outInsets) {
1013923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onComputeInsets(outInsets);
1014f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
1015913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (inputView == null || mSuggestionsContainer == null)
101646ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka            return;
1017d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // In fullscreen mode, the height of the extract area managed by InputMethodService should
1018d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // be considered.
1019d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}.
1020d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0;
1021abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        final int backingHeight = (mKeyPreviewBackingView.getVisibility() == View.GONE) ? 0
1022abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka                : mKeyPreviewBackingView.getHeight();
102359010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0
102459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                : mSuggestionsContainer.getHeight();
1025d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
1026abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        int touchY = extraHeight;
10279e347d3d448e48229c46aad394ec9bd60cd5807bsatok        // Need to set touchable region only if input view is being shown
1028433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka        final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
1029433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka        if (keyboardView != null && keyboardView.isShown()) {
1030913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
103159010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                touchY -= suggestionsHeight;
10329e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
10337a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            final int touchWidth = inputView.getWidth();
1034abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka            final int touchHeight = inputView.getHeight() + extraHeight
10357a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    // Extend touchable region below the keyboard.
10367a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
10379e347d3d448e48229c46aad394ec9bd60cd5807bsatok            if (DEBUG) {
10387a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                Log.d(TAG, "Touchable region: y=" + touchY + " width=" + touchWidth
10397a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                        + " height=" + touchHeight);
10409e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
10417a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            setTouchableRegionCompat(outInsets, 0, touchY, touchWidth, touchHeight);
10429e347d3d448e48229c46aad394ec9bd60cd5807bsatok        }
104346ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.contentTopInsets = touchY;
104446ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.visibleTopInsets = touchY;
1045923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1046a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1047923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1048979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public boolean onEvaluateFullscreenMode() {
10499751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        // Reread resource value here, because this method is called by framework anytime as needed.
10509751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        final boolean isFullscreenModeAllowed =
10519751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka                mSettingsValues.isFullscreenModeAllowed(getResources());
10529751a626ec9b2e771afa5b3757e8f8498a328683Tadashi G. Takaoka        return super.onEvaluateFullscreenMode() && isFullscreenModeAllowed;
105359010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    }
105459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka
105559010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    @Override
105659010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    public void updateFullscreenMode() {
105759010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        super.updateFullscreenMode();
1058f80b6a06992ae08ca3601f4fbc6da550fd9ac730Tadashi G. Takaoka
105959010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        if (mKeyPreviewBackingView == null) return;
1060549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        // In fullscreen mode, no need to have extra space to show the key preview.
106159010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        // If not, we should have extra space above the keyboard to show the key preview.
1062549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
1063979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
1064979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1065979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
1066923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean onKeyDown(int keyCode, KeyEvent event) {
1067923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (keyCode) {
1068e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_BACK:
10692cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka            if (event.getRepeatCount() == 0) {
10702cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                if (mSuggestionsView != null && mSuggestionsView.handleBack()) {
10712cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                    return true;
10722cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                }
10732cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
10742cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                if (keyboardView != null && keyboardView.handleBack()) {
10756e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani                    return true;
10766e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani                }
1077e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            }
1078e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            break;
1079923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1080923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return super.onKeyDown(keyCode, event);
1081923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1082923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1083923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1084923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean onKeyUp(int keyCode, KeyEvent event) {
1085923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (keyCode) {
1086e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_DOWN:
1087e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_UP:
1088e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_LEFT:
1089e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_RIGHT:
1090433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
1091433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
1092e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            // Enable shift key and DPAD to do selections
1093433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka            if ((keyboardView != null && keyboardView.isShown())
1094433ca6a46db30a321715da0f457974916668dff5Tadashi G. Takaoka                    && (keyboard != null && keyboard.isShiftedOrShiftLocked())) {
1095e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                KeyEvent newEvent = new KeyEvent(event.getDownTime(), event.getEventTime(),
1096e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                        event.getAction(), event.getKeyCode(), event.getRepeatCount(),
1097e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                        event.getDeviceId(), event.getScanCode(),
1098e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                        KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
10999351550dc6af7859e5280e16144c9386a37b976dKen Wakasa                final InputConnection ic = getCurrentInputConnection();
1100e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                if (ic != null)
1101e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                    ic.sendKeyEvent(newEvent);
1102e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                return true;
1103e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            }
1104e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            break;
1105923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1106923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return super.onKeyUp(keyCode, event);
1107923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1108923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
11092649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // This will reset the whole input state to the starting state. It will clear
11102649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // the composing word, reset the last composed word, tell the inputconnection
11112649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    // and the composingStateManager about it.
11122649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    private void resetEntireInputState() {
11132649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
11142649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        updateSuggestions();
11152649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        final InputConnection ic = getCurrentInputConnection();
11162649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        if (ic != null) {
11172649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard            ic.finishComposingText();
11182649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        }
11192649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        mComposingStateManager.onFinishComposingText();
11202649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard        mVoiceProxy.setVoiceInputHighlighted(false);
11212649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard    }
11222649e42d756132ad763d6db5f9de1f901bbefd06Jean Chalard
11232692a8700737d8eed268039aa27b22a31669da08Jean Chalard    private void resetComposingState(final boolean alsoResetLastComposedWord) {
11242692a8700737d8eed268039aa27b22a31669da08Jean Chalard        mWordComposer.reset();
11252692a8700737d8eed268039aa27b22a31669da08Jean Chalard        if (alsoResetLastComposedWord)
11262692a8700737d8eed268039aa27b22a31669da08Jean Chalard            mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
11272692a8700737d8eed268039aa27b22a31669da08Jean Chalard    }
11282692a8700737d8eed268039aa27b22a31669da08Jean Chalard
112966bb563535dbe3672f99f75bd71763a551444867Jean Chalard    public void commitTyped(final InputConnection ic, final int separatorCode) {
1130196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) return;
11313651220327c051d8017045aa5e8919461507b3f8Jean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
11323651220327c051d8017045aa5e8919461507b3f8Jean Chalard        if (typedWord.length() > 0) {
1133cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard            mLastComposedWord = mWordComposer.commitWord(
113466bb563535dbe3672f99f75bd71763a551444867Jean Chalard                    LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
113566bb563535dbe3672f99f75bd71763a551444867Jean Chalard                    separatorCode);
11369351550dc6af7859e5280e16144c9386a37b976dKen Wakasa            if (ic != null) {
11373651220327c051d8017045aa5e8919461507b3f8Jean Chalard                ic.commitText(typedWord, 1);
1138923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
11393651220327c051d8017045aa5e8919461507b3f8Jean Chalard            addToUserUnigramAndBigramDictionaries(typedWord,
1140f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                    UserUnigramDictionary.FREQUENCY_FOR_TYPED);
1141923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
11428558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        updateSuggestions();
1143923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1144923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1145b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public boolean getCurrentAutoCapsState() {
11469351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
11471c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        EditorInfo ei = getCurrentInputEditorInfo();
114817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (mSettingsValues.mAutoCap && ic != null && ei != null
114917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                && ei.inputType != InputType.TYPE_NULL) {
1150b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return ic.getCursorCapsMode(ei.inputType) != 0;
11511c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        }
1152b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
11531c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
11541c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
1155b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    // "ic" may be null
1156b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) {
1157b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        if (null == ic) return;
1158923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
1159863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
1160923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastTwo != null && lastTwo.length() == 2
1161863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard                && lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
1162923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.deleteSurroundingText(2, 0);
1163923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.commitText(lastTwo.charAt(1) + " ", 1);
1164b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
11654ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa        }
11664ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa    }
11674ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1168120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
1169120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (mCorrectionMode == Suggest.CORRECTION_NONE) return false;
1170120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (ic == null) return false;
11712b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard        final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
1172923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastThree != null && lastThree.length() == 3
11739351550dc6af7859e5280e16144c9386a37b976dKen Wakasa                && Utils.canBeFollowedByPeriod(lastThree.charAt(0))
1174571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastThree.charAt(1) == Keyboard.CODE_SPACE
1175fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && lastThree.charAt(2) == Keyboard.CODE_SPACE
1176fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && mHandler.isAcceptingDoubleSpaces()) {
1177fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            mHandler.cancelDoubleSpacesTimer();
1178923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.deleteSurroundingText(2, 0);
1179923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.commitText(". ", 1);
1180b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1181120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            return true;
1182923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1183120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return false;
1184923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1185a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1186b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    // "ic" may be null
11878fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) {
11889a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (ic == null) return;
1189b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
11909a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (lastOne != null && lastOne.length() == 1
1191571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
11929a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            ic.deleteSurroundingText(1, 0);
11939a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        }
11949a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa    }
11959a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa
1196c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
1197923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean addWordToDictionary(String word) {
1198923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mUserDictionary.addWord(word, 128);
11996558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // Suggestion strip should be updated after the operation of adding word to the
12006558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // user dictionary
1201d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.postUpdateSuggestions();
1202923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
1203923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1204923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
12058fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean isAlphabet(int code) {
12068fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka        return Character.isLetter(code);
1207923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1208a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1209e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    private void onSettingsKeyPressed() {
1210cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return;
1211d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka        if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
1212d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka            showSubtypeSelectorAndSettings();
121323d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka        } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(false /* exclude aux subtypes */)) {
1214d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka            showOptionsMenu();
1215d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka        } else {
1216d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka            launchSettings();
12179a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
12189a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12199a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1220cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
1221cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
1222d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka    public static final int CODE_HAPTIC_AND_AUDIO_FEEDBACK = 2;
1223cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa
1224cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    @Override
1225cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public boolean onCustomRequest(int requestCode) {
1226cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return false;
1227cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        switch (requestCode) {
1228cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        case CODE_SHOW_INPUT_METHOD_PICKER:
122923d27fefcbfe04df8f8762d5d50117a58fb011e9Tadashi G. Takaoka            if (Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
123079efbed76f638be298493107fa2d0cd1b5eb529esatok                mImm.showInputMethodPicker();
1231cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa                return true;
12329a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok            }
1233cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa            return false;
1234d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka        case CODE_HAPTIC_AND_AUDIO_FEEDBACK:
1235d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka            hapticAndAudioFeedback(Keyboard.CODE_UNSPECIFIED);
1236d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka            return true;
12379a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
1238cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        return false;
12399a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12409a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
12419a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    private boolean isShowingOptionDialog() {
12429a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        return mOptionsDialog != null && mOptionsDialog.isShowing();
12439a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12449a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
12457a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    private void insertPunctuationFromSuggestionStrip(final int code) {
12467a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        onCodeInput(code, new int[] { code },
12477a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                KeyboardActionListener.SUGGESTION_STRIP_COORDINATE,
12487a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
12497a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12507a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12517a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    private static int getEditorActionId(EditorInfo editorInfo) {
12527a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (editorInfo == null) return 0;
12537a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        return (editorInfo.actionLabel != null)
12547a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                ? editorInfo.actionId
12557a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                : (editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION);
12567a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12577a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12587a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    private void performeEditorAction(int actionId) {
12597a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
12607a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (ic != null) {
12617a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            ic.performEditorAction(actionId);
12627a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12637a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12647a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12657a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    private void sendKeyCodePoint(int code) {
12667a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // TODO: Remove this special handling of digit letters.
12677a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
12687a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (code >= '0' && code <= '9') {
12697a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            super.sendKeyChar((char)code);
12707a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            return;
12717a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12727a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12737a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
12747a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        if (ic != null) {
12757a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            final String text = new String(new int[] { code }, 0, 1);
12767a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            ic.commitText(text, text.length());
12777a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
12787a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    }
12797a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
12805f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    // Implementation of {@link KeyboardActionListener}.
12815a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
12828aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
1283175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final long when = SystemClock.uptimeMillis();
1284571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
1285923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mDeleteCount = 0;
1286923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1287923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mLastKeyTime = when;
1288175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
1289120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // The space state depends only on the last character pressed and its own previous
1290120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // state. Here, we revert the space state to neutral if the key is actually modifying
1291120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // the input contents (any non-shift key), which is what we should do for
1292120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // all inputs that do not result in a special state. Each character handling is then
1293120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // free to override the state as they see fit.
1294120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final int spaceState = mSpaceState;
1295ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1296ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        // TODO: Consolidate the double space timer, mLastKeyTime, and the space state.
1297ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        if (primaryCode != Keyboard.CODE_SPACE) {
1298ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa            mHandler.cancelDoubleSpacesTimer();
1299ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        }
1300ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1301c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1302923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (primaryCode) {
1303571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_DELETE:
1304120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1305120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            handleBackspace(spaceState);
13064189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            mDeleteCount++;
13074733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
13084189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            LatinImeLogger.logOnDelete();
13094189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1310571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_SHIFT:
1311e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
13122a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            // Shift and symbol key is handled in onPressKey() and onReleaseKey().
13134189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1314e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SETTINGS:
131593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            onSettingsKeyPressed();
13164189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1317d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka        case Keyboard.CODE_SHORTCUT:
131893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            mSubtypeSwitcher.switchToShortcutIME();
13194189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
13207a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        case Keyboard.CODE_ACTION_ENTER:
13217a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            performeEditorAction(getEditorActionId(getCurrentInputEditorInfo()));
13227a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            break;
1323571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_TAB:
132445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            handleTab();
13254733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // There are two cases for tab. Either we send a "next" event, that may change the
13264733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // focus but will never move the cursor. Or, we send a real tab keycode, which some
13274733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // applications may accept or ignore, and we don't know whether this will move the
13284733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // cursor or not. So actually, we don't really know.
13294733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // So to go with the safer option, we'd rather behave as if the user moved the
13304733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // cursor when they didn't than the opposite. We also expect that most applications
13314733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // will actually use tab only for focus movement.
13324733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // To sum it up: do not update mExpectingUpdateSelection here.
13334189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
13344189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        default:
1335120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
133617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (mSettingsValues.isWordSeparator(primaryCode)) {
1337c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
13384189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            } else {
1339120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                handleCharacter(primaryCode, keyCodes, x, y, spaceState);
13404189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            }
13414733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
13424733609947c0ec74e460bd714fffca0518ade93aJean Chalard            break;
1343923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1344eef6238f94b5046054d9ae9c06f775362893c0eeTadashi G. Takaoka        switcher.onCodeInput(primaryCode);
1345dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        // Reset after any single keystroke
1346c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        if (!didAutoCorrect)
1347c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard            mLastComposedWord.deactivate();
1348dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = null;
1349923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1350a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
13515a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13528aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onTextInput(CharSequence text) {
1353b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.commitVoiceInput();
13549351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
1355923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic == null) return;
1356923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.beginBatchEdit();
135766bb563535dbe3672f99f75bd71763a551444867Jean Chalard        commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
1358fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        text = specificTldProcessingOnTextInput(ic, text);
1359fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == mSpaceState) {
13607a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(Keyboard.CODE_SPACE);
1361fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1362923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.commitText(text, 1);
1363923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.endBatchEdit();
1364b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
13658cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
1366120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
1367dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = text;
13682692a8700737d8eed268039aa27b22a31669da08Jean Chalard        resetComposingState(true /* alsoResetLastComposedWord */);
1369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1370923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1371fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    // ic may not be null
1372fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    private CharSequence specificTldProcessingOnTextInput(final InputConnection ic,
1373fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            final CharSequence text) {
1374fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
1375fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                || !Character.isLetter(text.charAt(1))) {
1376fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            // Not a tld: do nothing.
1377fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1378fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
137912d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // We have a TLD (or something that looks like this): make sure we don't add
138012d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        // a space even if currently in phantom mode.
138112d67e6d5d5bd764117038f3f666d4d7da0c10eaJean Chalard        mSpaceState = SPACE_STATE_NONE;
1382fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
1383fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (lastOne != null && lastOne.length() == 1
1384fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                && lastOne.charAt(0) == Keyboard.CODE_PERIOD) {
1385fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text.subSequence(1, text.length());
1386fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        } else {
1387fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            return text;
1388fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1389fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard    }
1390fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
13915a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13928aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCancelInput() {
139383e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        // User released a finger outside any key
13945f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        mKeyboardSwitcher.onCancelInput();
139583e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka    }
139683e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka
1397120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleBackspace(final int spaceState) {
1398b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        if (mVoiceProxy.logAndRevertVoiceInput()) return;
1399504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
1400504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka        if (ic == null) return;
1401979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        ic.beginBatchEdit();
1402a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard        handleBackspaceWhileInBatchEdit(spaceState, ic);
1403a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard        ic.endBatchEdit();
1404a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    }
1405979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1406a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    // "ic" may not be null.
1407a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
1408b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleBackspace();
14094f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
14102245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard        // In many cases, we may have to put the keyboard in auto-shift state again.
1411beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        mHandler.postUpdateShiftState();
14122245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard
14135c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
14145c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // Cancel multi-character input: remove the text we just entered.
14155c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // This is triggered on backspace after a key that inputs multiple characters,
14165c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // like the smiley key or the .com key.
14175c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            ic.deleteSurroundingText(mEnteredText.length(), 0);
14185c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
14195c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // In addition we know that spaceState is false, and that we should not be
14205c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // reverting any autocorrect at this point. So we can safely return.
14215c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            return;
14225c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        }
14235c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard
1424196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
14253651220327c051d8017045aa5e8919461507b3f8Jean Chalard            final int length = mWordComposer.size();
1426923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (length > 0) {
14279318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                mWordComposer.deleteLast();
142877d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1429196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                // If we have deleted the last remaining character of a word, then we are not
1430196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                // isComposingWord() any more.
1431196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                if (!mWordComposer.isComposingWord()) {
1432196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                    // Not composing word any more, so we can show bigrams.
1433cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                    mHandler.postUpdateBigramPredictions();
143489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                } else {
1435196d82cdd740580ed79d801483dbc282be85d076Jean Chalard                    // Still composing a word, so we still have letters to deduce a suggestion from.
143689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    mHandler.postUpdateSuggestions();
143789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                }
1438923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1439923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                ic.deleteSurroundingText(1, 0);
1440923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1441890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        } else {
144272d285f4d758dd1ceafb7d9bd3dd330efd24c1f8Jean Chalard            // We should be very careful about auto-correction cancellation and suggestion
144372d285f4d758dd1ceafb7d9bd3dd330efd24c1f8Jean Chalard            // resuming here. The behavior needs to be different according to text field types,
144472d285f4d758dd1ceafb7d9bd3dd330efd24c1f8Jean Chalard            // and it would be much clearer to test for them explicitly here rather than
144572d285f4d758dd1ceafb7d9bd3dd330efd24c1f8Jean Chalard            // relying on implicit values like "whether the suggestion strip is displayed".
1446b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            if (mLastComposedWord.canCancelAutoCorrect()) {
1447d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                Utils.Stats.onAutoCorrectionCancellation();
1448d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                cancelAutoCorrect(ic);
1449120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                return;
1450120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1451d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard
1452d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            if (SPACE_STATE_DOUBLE == spaceState) {
14532124bc5bf5af31cf3d2789b70ebd2f24c815f5f4Jean Chalard                if (revertDoubleSpaceWhileInBatchEdit(ic)) {
1454d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // No need to reset mSpaceState, it has already be done (that's why we
1455d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // receive it as a parameter)
1456d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1457d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
1458d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1459d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                if (revertSwapPunctuation(ic)) {
1460d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // Likewise
1461d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1462d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
14634733609947c0ec74e460bd714fffca0518ade93aJean Chalard            }
1464504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka
146572d285f4d758dd1ceafb7d9bd3dd330efd24c1f8Jean Chalard            // See the comment above: must be careful about resuming auto-suggestion.
1466913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
14676558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // Go back to the suggestion mode if the user canceled the
146855b10796522b871c1e04d6f2254fdff5dc7aced4Ken Wakasa                // "Touch again to save".
1469a8ba49c2534220105ce302a50b3a9ddaf831ef20Jean Chalard                // NOTE: In general, we don't revert the word when backspacing
14706558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // from a manual suggestion pick.  We deliberately chose a
14716558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // different behavior only in the case of picking the first
14726558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // suggestion (typed word).  It's intentional to have made this
14736558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // inconsistent with backspacing after selecting other suggestions.
1474890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                restartSuggestionsOnManuallyPickedTypedWord(ic);
14756558253160e2039c87f424bd814f402ecd31de3bKen Wakasa            } else {
147677da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                // Here we must check whether there is a selection. If so we should remove the
147777da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                // selected text, otherwise we should just delete the character before the cursor.
147877da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                if (mLastSelectionStart != mLastSelectionEnd) {
147977da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                    final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
148077da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                    ic.setSelection(mLastSelectionEnd, mLastSelectionEnd);
148177da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                    ic.deleteSurroundingText(lengthToDelete, 0);
148277da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                } else {
148377da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                    if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
14841fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard                        // This should never happen.
14851fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard                        Log.e(TAG, "Backspace when we don't know the selection position");
148677da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                    }
14871fead1d5f12928f90c723b5f7b88490cc7cd2a67Jean Chalard                    ic.deleteSurroundingText(1, 0);
148877da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                    if (mDeleteCount > DELETE_ACCELERATE_AT) {
148977da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                        ic.deleteSurroundingText(1, 0);
149077da3d5a3baab0a88f78db5800bb9ede0b39ff60Jean Chalard                    }
14916558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                }
1492edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                if (isSuggestionsRequested()) {
1493edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                    restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
1494edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                }
1495923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1496923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1497923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1498923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
14997a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    // TODO: Implement next and previous actions using other key code than tab's code.
150045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka    private void handleTab() {
150145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final int imeOptions = getCurrentInputEditorInfo().imeOptions;
1502b2707856aba4fc9b063f26305f1fb603b19c1701satok        if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
1503b2707856aba4fc9b063f26305f1fb603b19c1701satok                && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) {
15047a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            // TODO: This should be {@link #sendKeyCodePoint(int)}.
150545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
150645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            return;
150745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        }
150845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka
150945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
151045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (ic == null)
151145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            return;
151245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka
1513ca2f051cc173acc3bce384ebfe08068564bc8e07Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
1514ca2f051cc173acc3bce384ebfe08068564bc8e07Tadashi G. Takaoka        // True if keyboard is in either shift chording or manual shifted state.
1515ca2f051cc173acc3bce384ebfe08068564bc8e07Tadashi G. Takaoka        final boolean isManualShifted = (keyboard != null  && keyboard.isManualShifted());
1516ca2f051cc173acc3bce384ebfe08068564bc8e07Tadashi G. Takaoka        if (EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) && !isManualShifted) {
1517b2707856aba4fc9b063f26305f1fb603b19c1701satok            EditorInfoCompatUtils.performEditorActionNext(ic);
1518ca2f051cc173acc3bce384ebfe08068564bc8e07Tadashi G. Takaoka        } else if (EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions) && isManualShifted) {
1519b2707856aba4fc9b063f26305f1fb603b19c1701satok            EditorInfoCompatUtils.performEditorActionPrevious(ic);
152045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        }
152145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka    }
152245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka
1523e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    // ic may be null
1524e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code,
1525e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final int spaceState, final boolean isFromSuggestionStrip) {
1526e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1527e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            removeTrailingSpaceWhileInBatchEdit(ic);
1528e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1529e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else if ((SPACE_STATE_WEAK == spaceState
1530e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                || SPACE_STATE_SWAP_PUNCTUATION == spaceState)
1531e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                && isFromSuggestionStrip) {
1532b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard            if (mSettingsValues.isWeakSpaceSwapper(code)) {
1533e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return true;
1534e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            } else {
1535b141c9cd3f6785bc0a4e1f5385f4e6e2e17955bbJean Chalard                if (mSettingsValues.isWeakSpaceStripper(code)) {
1536e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                    removeTrailingSpaceWhileInBatchEdit(ic);
1537e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                }
1538e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                return false;
1539e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
1540e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        } else {
1541e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            return false;
1542e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1543e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard    }
1544e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1545120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleCharacter(final int primaryCode, final int[] keyCodes, final int x,
1546120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int y, final int spaceState) {
1547b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleCharacter();
1548b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        final InputConnection ic = getCurrentInputConnection();
1549dafa7a8e15447544842975047f831883e67700c5Jean Chalard        if (null != ic) ic.beginBatchEdit();
1550dafa7a8e15447544842975047f831883e67700c5Jean Chalard        // TODO: if ic is null, does it make any sense to call this?
1551dafa7a8e15447544842975047f831883e67700c5Jean Chalard        handleCharacterWhileInBatchEdit(primaryCode, keyCodes, x, y, spaceState, ic);
1552dafa7a8e15447544842975047f831883e67700c5Jean Chalard        if (null != ic) ic.endBatchEdit();
1553dafa7a8e15447544842975047f831883e67700c5Jean Chalard    }
1554dafa7a8e15447544842975047f831883e67700c5Jean Chalard
1555dafa7a8e15447544842975047f831883e67700c5Jean Chalard    // "ic" may be null without this crashing, but the behavior will be really strange
1556dafa7a8e15447544842975047f831883e67700c5Jean Chalard    private void handleCharacterWhileInBatchEdit(final int primaryCode, final int[] keyCodes,
1557dafa7a8e15447544842975047f831883e67700c5Jean Chalard            final int x, final int y, final int spaceState, final InputConnection ic) {
1558196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        boolean isComposingWord = mWordComposer.isComposingWord();
1559fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1560fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        if (SPACE_STATE_PHANTOM == spaceState &&
1561fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                !mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) {
1562fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (isComposingWord) {
1563fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // Sanity check
1564fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                throw new RuntimeException("Should not be composing here");
1565fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            }
15667a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(Keyboard.CODE_SPACE);
1567fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        }
1568fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard
1569e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        if ((isAlphabet(primaryCode)
1570e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                || mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode))
15713889462439357fd76c0b82dfd52e1ca6e0bafd2dKen Wakasa                && isSuggestionsRequested() && !isCursorTouchingWord()) {
15727b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard            if (!isComposingWord) {
15736d1cbbc2ffb9d7046189174ec68b5b72bbc154b3Jean Chalard                // Reset entirely the composing state anyway, then start composing a new word unless
15747b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard                // the character is a single quote. The idea here is, single quote is not a
15757b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard                // separator and it should be treated as a normal character, except in the first
15767b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard                // position where it should not start composing a word.
1577e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != primaryCode);
15782692a8700737d8eed268039aa27b22a31669da08Jean Chalard                // Here we don't need to reset the last composed word. It will be reset
15792692a8700737d8eed268039aa27b22a31669da08Jean Chalard                // when we commit this one, if we ever do; if on the other hand we backspace
15802692a8700737d8eed268039aa27b22a31669da08Jean Chalard                // it entirely and resume suggestions on the previous word, we'd like to still
15812692a8700737d8eed268039aa27b22a31669da08Jean Chalard                // have touch coordinates for it.
15822692a8700737d8eed268039aa27b22a31669da08Jean Chalard                resetComposingState(false /* alsoResetLastComposedWord */);
15837e99a28dfcc14f5b19220442db972ca8d786b4feKen Wakasa                clearSuggestions();
1584604d80c67185954d4691ac775be59c499eee3b1csatok                mComposingStateManager.onFinishComposingText();
1585923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1586923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
15877b5bc1ff4d4694045e69e6aff8a2b5365ff882d1Jean Chalard        if (isComposingWord) {
1588e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            mWordComposer.add(primaryCode, keyCodes, x, y);
1589923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (ic != null) {
15901c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani                // If it's the first letter, make note of auto-caps state
15919318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                if (mWordComposer.size() == 1) {
15929318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                    mWordComposer.setAutoCapitalized(getCurrentAutoCapsState());
1593604d80c67185954d4691ac775be59c499eee3b1csatok                    mComposingStateManager.onStartComposingText();
15941c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani                }
159577d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1596923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1597d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
1598923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1599e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode,
1600e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                    spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
1601863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
16027a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            sendKeyCodePoint(primaryCode);
1603e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard
1604e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            if (swapWeakSpace) {
1605e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                swapSwapperAndSpaceWhileInBatchEdit(ic);
1606e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                mSpaceState = SPACE_STATE_WEAK;
1607e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            }
16085262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // Some characters are not word separators, yet they don't start a new
16095262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // composing span. For these, we haven't changed the suggestion strip, and
16105262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // if the "add to dictionary" hint is shown, we should do so now. Examples of
16115262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // such characters include single quote, dollar, and others; the exact list is
16125262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // the list of characters for which we enter handleCharacterWhileInBatchEdit
16135262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            // that don't match the test if ((isAlphabet...)) at the top of this method.
16145262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) {
16155262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard                mHandler.postUpdateBigramPredictions();
16165262fe8832e69b5d4b2c575524da3757435644f0Jean Chalard            }
1617e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        }
1618e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        Utils.Stats.onNonSeparator((char)primaryCode, x, y);
1619923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1620923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1621c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    // Returns true if we did an autocorrection, false otherwise.
1622c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard    private boolean handleSeparator(final int primaryCode, final int x, final int y,
1623120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int spaceState) {
1624b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleSeparator();
1625604d80c67185954d4691ac775be59c499eee3b1csatok        mComposingStateManager.onFinishComposingText();
16264f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
162755b10796522b871c1e04d6f2254fdff5dc7aced4Ken Wakasa        // Should dismiss the "Touch again to save" message when handling separator
1628913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
1629cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            mHandler.cancelUpdateBigramPredictions();
1630d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
16316558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        }
16326558253160e2039c87f424bd814f402ecd31de3bKen Wakasa
1633c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        boolean didAutoCorrect = false;
1634923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Handle separator
16351b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
1636923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
1637923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.beginBatchEdit();
1638923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1639196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (mWordComposer.isComposingWord()) {
1640923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // In certain languages where single quote is a separator, it's better
1641a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker            // not to auto correct, but accept the typed word. For instance,
1642923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // in Italian dov' should not be expanded to dove' because the elision
1643923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // requires the last vowel to be removed.
1644f50aa193377492e9c4afb3cc6e7f3448ab5a97a4Jean Chalard            final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
1645dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                    && !mInputAttributes.mInputTypeNoAutoCorrect;
164617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
1647f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard                commitCurrentAutoCorrection(primaryCode, ic);
1648c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard                didAutoCorrect = true;
1649923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
165066bb563535dbe3672f99f75bd71763a551444867Jean Chalard                commitTyped(ic, primaryCode);
1651923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1652923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
16534ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1654e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState,
1655e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
1656863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
16577a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        sendKeyCodePoint(primaryCode);
165889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
165989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (Keyboard.CODE_SPACE == primaryCode) {
1660120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            if (isSuggestionsRequested()) {
1661120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                if (maybeDoubleSpaceWhileInBatchEdit(ic)) {
1662120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_DOUBLE;
1663120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                } else if (!isShowingPunctuationList()) {
1664120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_WEAK;
1665120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                }
1666126698fdd256a2e3734634d3b923cabd800064baJean Chalard            }
1667120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1668120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mHandler.startDoubleSpacesTimer();
166989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            if (!isCursorTouchingWord()) {
167089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                mHandler.cancelUpdateSuggestions();
1671cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                mHandler.postUpdateBigramPredictions();
167289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            }
167389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
1674fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            if (swapWeakSpace) {
1675120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                swapSwapperAndSpaceWhileInBatchEdit(ic);
16764721427c7de3600f6fe7dfff16508a6a974fb3e4Jean Chalard                mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
1677fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard            } else if (SPACE_STATE_PHANTOM == spaceState) {
1678fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // If we are in phantom space state, and the user presses a separator, we want to
1679fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // stay in phantom space state so that the next keypress has a chance to add the
1680fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // space. For example, if I type "Good dat", pick "day" from the suggestion strip
1681fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // then insert a comma and go on to typing the next word, I want the space to be
1682fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // inserted automatically before the next word, the same way it is when I don't
1683fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                // input the comma.
1684fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard                mSpaceState = SPACE_STATE_PHANTOM;
1685120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1686120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
168789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Set punctuation right away. onUpdateSelection will fire but tests whether it is
168889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // already displayed or not, so it's okay.
168955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            setPunctuationSuggestions();
1690923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1691120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1692406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)primaryCode, x, y);
1693120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1694923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
1695923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.endBatchEdit();
1696923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1697c7c152de4b42853086fc6fd918387ad0583d0e3eJean Chalard        return didAutoCorrect;
1698923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1699466741d8a78965b8509bf527344f289e50873092Mike LeBeau
170077d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    private CharSequence getTextWithUnderline(final CharSequence text) {
170177d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard        return mComposingStateManager.isAutoCorrectionIndicatorOn()
170277d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
1703fda847a870829f1491cbd5325f9c985213081149Jean Chalard                : text;
170477d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    }
170577d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard
1706923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void handleClose() {
170766bb563535dbe3672f99f75bd71763a551444867Jean Chalard        commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR);
1708b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleClose();
1709923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        requestHideSelf(0);
1710c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
17111679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        if (inputView != null)
17121679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka            inputView.closing();
1713923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1714923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
17157a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isSuggestionsRequested() {
1716dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        return mInputAttributes.mIsSettingsSuggestionStripOn
1717c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka                && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
1718923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1719a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
17207a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingPunctuationList() {
1721cb8cb95d0afd340de9f1a0e15948f0068d2450bcKen Wakasa        if (mSuggestionsView == null) return false;
1722913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions();
17237599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
17247599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
17257a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingSuggestionsStrip() {
17267599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
17277599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
172838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka                        && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
17297599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
17307599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1731913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    public boolean isSuggestionsStripVisible() {
1732913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView == null)
1733b5e00d5841b946de8970875231456228ae0eb6b1Ken Wakasa            return false;
1734d0c5f9395a1b94e8425982e353d090f972dc44f0Jean Chalard        if (mSuggestionsView.isShowingAddToDictionaryHint())
17359fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
17369fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        if (!isShowingSuggestionsStrip())
17379fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return false;
1738dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn)
17399fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
17409fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        return isSuggestionsRequested();
1741923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1742923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1743409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void switchToKeyboardView() {
1744060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (DEBUG) {
1745060efb6d82af1f896d90cb845c8ae07c726f85e1satok            Log.d(TAG, "Switch to keyboard view.");
1746060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
1747c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        View v = mKeyboardSwitcher.getKeyboardView();
1748060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (v != null) {
1749060efb6d82af1f896d90cb845c8ae07c726f85e1satok            // Confirms that the keyboard view doesn't have parent view.
1750060efb6d82af1f896d90cb845c8ae07c726f85e1satok            ViewParent p = v.getParent();
1751060efb6d82af1f896d90cb845c8ae07c726f85e1satok            if (p != null && p instanceof ViewGroup) {
1752060efb6d82af1f896d90cb845c8ae07c726f85e1satok                ((ViewGroup) p).removeView(v);
17535a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            }
1754060efb6d82af1f896d90cb845c8ae07c726f85e1satok            setInputView(v);
1755060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
1756913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1757060efb6d82af1f896d90cb845c8ae07c726f85e1satok        updateInputViewShown();
1758060efb6d82af1f896d90cb845c8ae07c726f85e1satok        mHandler.postUpdateSuggestions();
1759466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1760466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1761409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void clearSuggestions() {
17627e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        setSuggestions(SuggestedWords.EMPTY);
1763466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1764466741d8a78965b8509bf527344f289e50873092Mike LeBeau
17657e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    public void setSuggestions(SuggestedWords words) {
1766913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null) {
1767913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setSuggestions(words);
17685e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            mKeyboardSwitcher.onAutoCorrectionStateChanged(
17695e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                    words.hasWordAboveAutoCorrectionScoreThreshold());
1770466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
1771ec780e2868962bf17f0dfd35d36895f543bde40asatok
1772ec780e2868962bf17f0dfd35d36895f543bde40asatok        // Put a blue underline to a word in TextView which will be auto-corrected.
1773ec780e2868962bf17f0dfd35d36895f543bde40asatok        final InputConnection ic = getCurrentInputConnection();
1774604d80c67185954d4691ac775be59c499eee3b1csatok        if (ic != null) {
1775604d80c67185954d4691ac775be59c499eee3b1csatok            final boolean oldAutoCorrectionIndicator =
1776604d80c67185954d4691ac775be59c499eee3b1csatok                    mComposingStateManager.isAutoCorrectionIndicatorOn();
1777604d80c67185954d4691ac775be59c499eee3b1csatok            final boolean newAutoCorrectionIndicator = Utils.willAutoCorrect(words);
1778604d80c67185954d4691ac775be59c499eee3b1csatok            if (oldAutoCorrectionIndicator != newAutoCorrectionIndicator) {
177977d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
178077d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                if (DEBUG) {
1781fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok                    Log.d(TAG, "Flip the indicator. " + oldAutoCorrectionIndicator
1782fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok                            + " -> " + newAutoCorrectionIndicator);
17830bfe359ee42af9c4487ce56acf42c74a2510980dJean Chalard                    if (mComposingStateManager.isComposing() && newAutoCorrectionIndicator
178477d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                            != mComposingStateManager.isAutoCorrectionIndicatorOn()) {
17850bfe359ee42af9c4487ce56acf42c74a2510980dJean Chalard                        throw new RuntimeException("Couldn't flip the indicator!");
178677d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                    }
1787fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok                }
178877d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                final CharSequence textWithUnderline =
178977d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                        getTextWithUnderline(mWordComposer.getTypedWord());
1790604d80c67185954d4691ac775be59c499eee3b1csatok                if (!TextUtils.isEmpty(textWithUnderline)) {
1791604d80c67185954d4691ac775be59c499eee3b1csatok                    ic.setComposingText(textWithUnderline, 1);
1792604d80c67185954d4691ac775be59c499eee3b1csatok                }
1793ec780e2868962bf17f0dfd35d36895f543bde40asatok            }
1794ec780e2868962bf17f0dfd35d36895f543bde40asatok        }
1795466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1796466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1797409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void updateSuggestions() {
1798923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if we have a suggestion engine attached.
17999fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        if ((mSuggest == null || !isSuggestionsRequested())
1800b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok                && !mVoiceProxy.isVoiceInputHighlighted()) {
1801edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            if (mWordComposer.isComposingWord()) {
1802edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                Log.w(TAG, "Called updateSuggestions but suggestions were not requested!");
1803edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard                mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
1804edc6395d9bdbb46082c8582ee92f6ba184914d59Jean Chalard            }
1805923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1806923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1807466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1808cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateSuggestions();
1809cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateBigramPredictions();
1810cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard
1811196d82cdd740580ed79d801483dbc282be85d076Jean Chalard        if (!mWordComposer.isComposingWord()) {
1812ca26f20fa4903de46e374babbfba8c8a1a5cac93satok            setPunctuationSuggestions();
1813923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1814923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1815979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
18169f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        // TODO: May need a better way of retrieving previous word
181740f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        final InputConnection ic = getCurrentInputConnection();
181840f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        final CharSequence prevWord;
181940f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        if (null == ic) {
182040f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            prevWord = null;
182140f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        } else {
182240f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
182340f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        }
182440f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        // getSuggestedWordBuilder handles gracefully a null value of prevWord
1825f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(mWordComposer,
18263708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka                prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode);
1827923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1828dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        boolean autoCorrectionAvailable = !mInputAttributes.mInputTypeNoAutoCorrect
1829dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && mSuggest.hasAutoCorrection();
1830f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
1831bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        // Here, we want to promote a whitelisted word if exists.
18325f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
18335f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // but still autocorrected from - in the case the whitelist only capitalizes the word.
18345f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // The whitelist should be case-insensitive, so it's not possible to be consistent with
18355f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // a boolean flag. Right now this is handled with a slight hack in
18365f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
1837f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        final int quotesCount = mWordComposer.trailingSingleQuotesCount();
18385f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected(
1839c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                mSuggest.getUnigramDictionaries(),
1840c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // If the typed string ends with a single quote, for dictionary lookup purposes
1841c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // we behave as if the single quote was not here. Here, we are looking up the
1842c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // typed string in the dictionary (to avoid autocorrecting from an existing
1843c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // word, so for consistency this lookup should be made WITHOUT the trailing
1844c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // single quote.
1845117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard                quotesCount > 0
1846117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard                        ? typedWord.subSequence(0, typedWord.length() - quotesCount) : typedWord,
1847c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                preferCapitalization());
1848979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (mCorrectionMode == Suggest.CORRECTION_FULL
1849979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
18505f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard            autoCorrectionAvailable |= (!allowsToBeAutoCorrected);
1851923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
18524a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        // Don't auto-correct words with multiple capital letter
1853f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        autoCorrectionAvailable &= !mWordComposer.isMostlyCaps();
1854979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1855a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // Basically, we update the suggestion strip only when suggestion count > 1.  However,
1856a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // there is an exception: We update the suggestion strip whenever typed word's length
1857f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // is 1 or typed word is found in dictionary, regardless of suggestion count.  Actually,
1858f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
1859f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // need to clear the previous state when the user starts typing a word (i.e. typed word's
1860f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // length == 1).
1861fe1a6d961cf039357f061482461e4d2e951ad346satok        if (typedWord != null) {
18625f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard            if (builder.size() > 1 || typedWord.length() == 1 || (!allowsToBeAutoCorrected)
1863913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    || mSuggestionsView.isShowingAddToDictionaryHint()) {
18645f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard                builder.setTypedWordValid(!allowsToBeAutoCorrected).setHasMinimalSuggestion(
18658558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa                        autoCorrectionAvailable);
1866fe1a6d961cf039357f061482461e4d2e951ad346satok            } else {
1867074cda4d266b5d034d4348961c9183e32b16af5asatok                SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
1868074cda4d266b5d034d4348961c9183e32b16af5asatok                if (previousSuggestions == mSettingsValues.mSuggestPuncList) {
1869074cda4d266b5d034d4348961c9183e32b16af5asatok                    if (builder.size() == 0) {
1870074cda4d266b5d034d4348961c9183e32b16af5asatok                        return;
1871074cda4d266b5d034d4348961c9183e32b16af5asatok                    }
1872074cda4d266b5d034d4348961c9183e32b16af5asatok                    previousSuggestions = SuggestedWords.EMPTY;
1873074cda4d266b5d034d4348961c9183e32b16af5asatok                }
1874fe1a6d961cf039357f061482461e4d2e951ad346satok                builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions);
1875fe1a6d961cf039357f061482461e4d2e951ad346satok            }
18769fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        }
18777e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        showSuggestions(builder.build(), typedWord);
1878979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
18794a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
18809fbfd5877305ed19a20663630b498b6b3fdae942satok    public void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) {
18812aa1dd45c44295e2f7e8ece1b520032d86b9f908satok        final boolean shouldBlockAutoCorrectionBySafetyNet =
18822aa1dd45c44295e2f7e8ece1b520032d86b9f908satok                Utils.shouldBlockAutoCorrectionBySafetyNet(suggestedWords, mSuggest);
18832aa1dd45c44295e2f7e8ece1b520032d86b9f908satok        if (shouldBlockAutoCorrectionBySafetyNet) {
18842aa1dd45c44295e2f7e8ece1b520032d86b9f908satok            suggestedWords.setShouldBlockAutoCorrection();
18852aa1dd45c44295e2f7e8ece1b520032d86b9f908satok        }
18867e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        setSuggestions(suggestedWords);
18877e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        if (suggestedWords.size() > 0) {
18882aa1dd45c44295e2f7e8ece1b520032d86b9f908satok            if (shouldBlockAutoCorrectionBySafetyNet) {
1889117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                mWordComposer.setAutoCorrection(typedWord);
189082411d47ba7e8133ed2390c6920945e139a738cesatok            } else if (suggestedWords.hasAutoCorrectionWord()) {
1891117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                mWordComposer.setAutoCorrection(suggestedWords.getWord(1));
1892923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1893117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                mWordComposer.setAutoCorrection(typedWord);
1894923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1895923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1896117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            // TODO: replace with mWordComposer.deleteAutoCorrection()?
1897117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            mWordComposer.setAutoCorrection(null);
1898923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1899913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1900923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1901923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1902f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard    private void commitCurrentAutoCorrection(final int separatorCodePoint,
1903f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard            final InputConnection ic) {
1904913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // Complete any pending suggestions query first
1905d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        if (mHandler.hasPendingUpdateSuggestions()) {
1906d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.cancelUpdateSuggestions();
1907923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            updateSuggestions();
1908923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1909117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
1910117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        if (autoCorrection != null) {
1911117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            final String typedWord = mWordComposer.getTypedWord();
191246798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            if (TextUtils.isEmpty(typedWord)) {
191346798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                throw new RuntimeException("We have an auto-correction but the typed word "
191446798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard                        + "is empty? Impossible! I must commit suicide.");
191546798d8f1f3d9012433d000790ba30ba910c0fa0Jean Chalard            }
1916f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard            Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint);
19174733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
191866bb563535dbe3672f99f75bd71763a551444867Jean Chalard            commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
191966bb563535dbe3672f99f75bd71763a551444867Jean Chalard                    separatorCodePoint);
1920f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard            // Add the word to the user unigram dictionary if it's not a known word
1921117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            addToUserUnigramAndBigramDictionaries(autoCorrection,
1922f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                    UserUnigramDictionary.FREQUENCY_FOR_TYPED);
1923f3ca6a59a7d6336fc318fe15467a1b447dc3ec4bJean Chalard            if (!typedWord.equals(autoCorrection) && null != ic) {
19241c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // This will make the correction flash for a short while as a visual clue
19251c6cf26c3705e845418a29718c034598b52293ccJean Chalard                // to the user that auto-correction happened.
19261c6cf26c3705e845418a29718c034598b52293ccJean Chalard                InputConnectionCompatUtils.commitCorrection(ic,
19271c6cf26c3705e845418a29718c034598b52293ccJean Chalard                        mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection);
19281c6cf26c3705e845418a29718c034598b52293ccJean Chalard            }
1929923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1930923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1931923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1932c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
19332e2d6b4d4060ed3b956b4bd4c1f89b5d9c87b525Jean Chalard    public void pickSuggestionManually(final int index, final CharSequence suggestion) {
1934604d80c67185954d4691ac775be59c499eee3b1csatok        mComposingStateManager.onFinishComposingText();
19352e2d6b4d4060ed3b956b4bd4c1f89b5d9c87b525Jean Chalard        final SuggestedWords suggestions = mSuggestionsView.getSuggestions();
193617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        mVoiceProxy.flushAndLogAllTextModificationCounters(index, suggestion,
193717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                mSettingsValues.mWordSeparators);
19384f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
1939dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        if (mInputAttributes.mApplicationSpecifiedCompletionOn
1940dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && mApplicationSpecifiedCompletions != null
19411b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
1942913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsView != null) {
1943913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsView.clear();
1944923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1945b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1946e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            final InputConnection ic = getCurrentInputConnection();
19479a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            if (ic != null) {
1948e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                ic.beginBatchEdit();
1949e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
1950e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                ic.commitCompletion(completionInfo);
19519a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa                ic.endBatchEdit();
19529a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            }
1953923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1954923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
19556a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
1956e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
1957e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard        if (suggestion.length() == 1 && isShowingPunctuationList()) {
1958979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // Word separators are suggested before the user inputs something.
1959979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // So, LatinImeLogger logs "" as a user's input.
1960979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            LatinImeLogger.logOnManualSuggestion(
19617e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka                    "", suggestion.toString(), index, suggestions.mWords);
1962e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
19632e2d6b4d4060ed3b956b4bd4c1f89b5d9c87b525Jean Chalard            final int primaryCode = suggestion.charAt(0);
1964e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard            onCodeInput(primaryCode, new int[] { primaryCode },
1965e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                    KeyboardActionListener.SUGGESTION_STRIP_COORDINATE,
1966e771fc2f1ac7935befcc4d872cc974a47b50fdc7Jean Chalard                    KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
19676a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani            return;
19686a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani        }
1969af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // We need to log before we commit, because the word composer will store away the user
1970af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        // typed word.
1971af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard        LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(),
1972af9fe5c5fcad1db22f605e0568c9a77cef178d22Jean Chalard                suggestion.toString(), index, suggestions.mWords);
19734733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = true;
197466bb563535dbe3672f99f75bd71763a551444867Jean Chalard        commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
197566bb563535dbe3672f99f75bd71763a551444867Jean Chalard                LastComposedWord.NOT_A_SEPARATOR);
19769468335a06d2b0e3ef15f4f57f8c1b0857b34ebeAmith Yamasani        // Add the word to the auto dictionary if it's not a known word
19770c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        if (index == 0) {
1978f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard            addToUserUnigramAndBigramDictionaries(suggestion,
1979f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                    UserUnigramDictionary.FREQUENCY_FOR_PICKED);
1980979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
1981bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            addToOnlyBigramDictionary(suggestion, 1);
19820c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        }
1983fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mSpaceState = SPACE_STATE_PHANTOM;
1984fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        // TODO: is this necessary?
1985fb684cebe8f5dac1bdb6cfa7085a07ddc66acedeJean Chalard        mKeyboardSwitcher.updateShiftState();
1986979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1987c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // We should show the "Touch again to save" hint if the user pressed the first entry
1988c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // AND either:
19897f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is no dictionary (we know that because we tried to load it => null != mSuggest
1990c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        //   AND mSuggest.hasMainDictionary() is false)
19917f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is a dictionary and the word is not in it
19927f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // Please note that if mSuggest is null, it means that everything is off: suggestion
19937f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // and correction, so we shouldn't try to show the hint
19947f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // We used to look at mCorrectionMode here, but showing the hint should have nothing
19957f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // to do with the autocorrection setting.
1996bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
1997bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                // If there is no dictionary the hint should be shown.
1998c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa                && (!mSuggest.hasMainDictionary()
1999bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        // If "suggestion" is not in the dictionary, the hint should be shown.
2000bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        || !AutoCorrection.isValidWord(
2001bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                                mSuggest.getUnigramDictionaries(), suggestion, true));
2002b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
2003406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE,
2004406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard                WordComposer.NOT_A_COORDINATE);
2005777118a40a363ccab69a00016d3156066513cb78Jean Chalard        if (!showingAddToDictionaryHint) {
2006364da8c618303a7764595d2c15ee034a7671365dKen Wakasa            // If we're not showing the "Touch again to save", then show corrections again.
2007979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // In case the cursor position doesn't change, make sure we show the suggestions again.
200841ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            updateBigramPredictions();
200941ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // Updating the predictions right away may be slow and feel unresponsive on slower
201041ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // terminals. On the other hand if we just postUpdateBigramPredictions() it will
201141ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // take a noticeable delay to update them which may feel uneasy.
201295a6e58ebcd8f44b970a8238cf30e48b10fc4b61Jean Chalard        } else {
201388562bec54658840dcce352127bdc15705c20a89Jean Chalard            if (mIsUserDictionaryAvailable) {
2014644d33d60ea5a87501274488013d65f55238895eKen Wakasa                mSuggestionsView.showAddToDictionaryHint(
2015644d33d60ea5a87501274488013d65f55238895eKen Wakasa                        suggestion, mSettingsValues.mHintToSaveText);
2016ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            } else {
2017ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka                mHandler.postUpdateSuggestions();
2018ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            }
201966a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani        }
2020923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2021a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2022979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
20238558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa     * Commits the chosen word to the text field and saves it for later retrieval.
2024979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
202566bb563535dbe3672f99f75bd71763a551444867Jean Chalard    private void commitChosenWord(final CharSequence bestWord, final int commitType,
202666bb563535dbe3672f99f75bd71763a551444867Jean Chalard            final int separatorCode) {
20279351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
2028923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
20291fef530ec7626fa16777f52b48191e61db8f46d4satok            mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
20301531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            if (mSettingsValues.mEnableSuggestionSpanInsertion) {
20311531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
20321531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
20331531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                        this, bestWord, suggestedWords), 1);
20341531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            } else {
20351531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                ic.commitText(bestWord, 1);
20361531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            }
2037923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
20380fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // TODO: figure out here if this is an auto-correct or if the best word is actually
20390fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // what user typed. Note: currently this is done much later in
2040b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        // LastComposedWord#canCancelAutoCorrect by string equality of the remembered
20410fd625bcfdfac1c10e7bd7f9088bf425fec08989Jean Chalard        // strings.
204266bb563535dbe3672f99f75bd71763a551444867Jean Chalard        mLastComposedWord = mWordComposer.commitWord(commitType, bestWord.toString(),
204366bb563535dbe3672f99f75bd71763a551444867Jean Chalard                separatorCode);
2044923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2045923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
204689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    private static final WordComposer sEmptyWordComposer = new WordComposer();
204741ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard    public void updateBigramPredictions() {
204889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (mSuggest == null || !isSuggestionsRequested())
204989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            return;
205089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
205117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (!mSettingsValues.mBigramPredictionEnabled) {
2052cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            setPunctuationSuggestions();
2053cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            return;
2054cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        }
2055cb3320179d39a7983874697a0aa428b127675c9dJean Chalard
205689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
205717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                mSettingsValues.mWordSeparators);
2058904baab25a4c6ec5d9c4bf7e562154e3f544d296satok        SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(sEmptyWordComposer,
20593708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka                prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode);
206089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
206189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (builder.size() > 0) {
206289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Explicitly supply an empty typed word (the no-second-arg version of
206389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // showSuggestions will retrieve the word near the cursor, we don't want that here)
206489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            showSuggestions(builder.build(), "");
206589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
206689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            if (!isShowingPunctuationList()) setPunctuationSuggestions();
206789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
206889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    }
206989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
20707a8dac55278cedd838be325f56b4c52d973c61f5satok    public void setPunctuationSuggestions() {
207117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        setSuggestions(mSettingsValues.mSuggestPuncList);
2072913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
20736a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani    }
20746a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
2075f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard    private void addToUserUnigramAndBigramDictionaries(CharSequence suggestion,
2076f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard            int frequencyDelta) {
2077979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        checkAddToDictionary(suggestion, frequencyDelta, false);
2078979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
2079979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2080bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    private void addToOnlyBigramDictionary(CharSequence suggestion, int frequencyDelta) {
2081979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        checkAddToDictionary(suggestion, frequencyDelta, true);
2082979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
2083979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2084979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
2085f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard     * Adds to the UserBigramDictionary and/or UserUnigramDictionary
2086bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok     * @param selectedANotTypedWord true if it should be added to bigram dictionary if possible
2087979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
2088979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta,
2089bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            boolean selectedANotTypedWord) {
2090979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (suggestion == null || suggestion.length() < 1) return;
2091bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
20920c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
20930c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // adding words in situations where the user or application really didn't
20940c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // want corrections enabled or learned.
2095979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (!(mCorrectionMode == Suggest.CORRECTION_FULL
2096979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
2097979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            return;
2098979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
2099bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
21005955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard        if (null != mSuggest && null != mUserUnigramDictionary) {
21015955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            final boolean selectedATypedWordAndItsInUserUnigramDic =
21025955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                    !selectedANotTypedWord && mUserUnigramDictionary.isValidWord(suggestion);
21035955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            final boolean isValidWord = AutoCorrection.isValidWord(
21045955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                    mSuggest.getUnigramDictionaries(), suggestion, true);
21055955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            final boolean needsToAddToUserUnigramDictionary =
21065955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                    selectedATypedWordAndItsInUserUnigramDic || !isValidWord;
21075955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            if (needsToAddToUserUnigramDictionary) {
21085955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                mUserUnigramDictionary.addWord(suggestion.toString(), frequencyDelta);
21095955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            }
2110e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        }
2111979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2112e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        if (mUserBigramDictionary != null) {
2113863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // We don't want to register as bigrams words separated by a separator.
2114863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // For example "I will, and you too" : we don't want the pair ("will" "and") to be
2115863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // a bigram.
211640f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            final InputConnection ic = getCurrentInputConnection();
211740f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            if (null != ic) {
211840f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                final CharSequence prevWord =
211940f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                        EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
212040f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                if (!TextUtils.isEmpty(prevWord)) {
212140f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                    mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
212240f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                }
2123979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
212432e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani        }
212532e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani    }
212632e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani
21277a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isCursorTouchingWord() {
21289351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
2129923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic == null) return false;
2130923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
2131923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        CharSequence toRight = ic.getTextAfterCursor(1, 0);
2132923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (!TextUtils.isEmpty(toLeft)
21336a800b253f0ea329808b582033a0ec3fc240d692Jean Chalard                && !mSettingsValues.isWordSeparator(toLeft.charAt(0))) {
2134923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
2135923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2136a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker        if (!TextUtils.isEmpty(toRight)
21376a800b253f0ea329808b582033a0ec3fc240d692Jean Chalard                && !mSettingsValues.isWordSeparator(toRight.charAt(0))) {
2138923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
2139923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2140923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return false;
2141923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2142a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2143120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
21448ff0564f6a7c78e2fda517ea49b2ac2fa96d0f8eJean Chalard    private static boolean sameAsTextBeforeCursor(final InputConnection ic,
21458ff0564f6a7c78e2fda517ea49b2ac2fa96d0f8eJean Chalard            final CharSequence text) {
21468ff0564f6a7c78e2fda517ea49b2ac2fa96d0f8eJean Chalard        final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
2147dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        return TextUtils.equals(text, beforeText);
2148dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    }
2149dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
2150120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
21516b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
21526b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
21536b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * word, else do nothing.
21546b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
21556b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(
21566b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final InputConnection ic) {
21576b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if the cursor is not at the end of a word (cursor must be preceded by
21586b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // non-whitespace, non-separator, non-start-of-text)
21596b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
21606b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0);
21616b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (TextUtils.isEmpty(textBeforeCursor)
21626b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return;
21636b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21646b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
21656b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // separator or end of line/text)
21666b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Example: "test|"<EOL> "te|st" get rejected here
21676b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0);
21686b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (!TextUtils.isEmpty(textAfterCursor)
21696b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return;
21706b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21716b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
2172fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // Example: " -|" gets rejected here but "e-|" and "e|" are okay
2173fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators);
2174fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // We don't suggest on leading single quotes, so we have to remove them from the word if
2175fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // it starts with single quotes.
2176fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) {
2177fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard            word = word.subSequence(1, word.length());
2178fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        }
21796b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (TextUtils.isEmpty(word)) return;
2180fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        final char firstChar = word.charAt(0); // we just tested that word is not empty
2181fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        if (word.length() == 1 && !Character.isLetter(firstChar)) return;
2182fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard
2183fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // We only suggest on words that start with a letter or a symbol that is excluded from
2184fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        // word separators (see #handleCharacterWhileInBatchEdit).
2185fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        if (!(isAlphabet(firstChar)
2186fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard                || mSettingsValues.isSymbolExcludedFromWordSeparators(firstChar))) {
2187fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard            return;
2188fbd1ac80838abb47bca25203f05de3a364356f27Jean Chalard        }
21896b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21906b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Okay, we are at the end of a word. Restart suggestions.
21916b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        restartSuggestionsOnWordBeforeCursor(ic, word);
21926b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
21936b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21946b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    // "ic" must not be null
21956b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
21966b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final CharSequence word) {
21973708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
21986b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mComposingStateManager.onStartComposingText();
21996b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        ic.deleteSurroundingText(word.length(), 0);
22006b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        ic.setComposingText(word, 1);
22016b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mHandler.postUpdateSuggestions();
22026b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
22036b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
22046b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    // "ic" must not be null
2205890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    private void cancelAutoCorrect(final InputConnection ic) {
2206b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        final String originallyTypedWord = mLastComposedWord.mTypedWord;
2207cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final CharSequence committedWord = mLastComposedWord.mCommittedWord;
2208cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard        final int cancelLength = committedWord.length();
2209193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        final int separatorLength = mLastComposedWord.getSeparatorLength(
2210193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                mLastComposedWord.mSeparatorCode);
2211193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        // TODO: should we check our saved separator against the actual contents of the text view?
2212890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2213b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            if (mWordComposer.isComposingWord()) {
2214b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard                throw new RuntimeException("cancelAutoCorrect, but we are composing a word");
2215b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard            }
2216890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            final String wordBeforeCursor =
2217193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                    ic.getTextBeforeCursor(cancelLength + separatorLength, 0)
2218193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                            .subSequence(0, cancelLength).toString();
2219cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard            if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
2220890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                throw new RuntimeException("cancelAutoCorrect check failed: we thought we were "
2221cf9d92629cae88273805eaf7984fcfdd8afd11f5Jean Chalard                        + "reverting \"" + committedWord
2222890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
2223890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
22247546ff6e34d540e9529d785bfc691b9c155b4051Jean Chalard            if (TextUtils.equals(originallyTypedWord, wordBeforeCursor)) {
2225890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                throw new RuntimeException("cancelAutoCorrect check failed: we wanted to cancel "
22269e8761c4402ddc11c942ed2e583bd7d58f70c5eaJean Chalard                        + "auto correction and revert to \"" + originallyTypedWord
2227890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\" but we found this very string before the cursor");
2228890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
22298558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        }
2230193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        ic.deleteSurroundingText(cancelLength + separatorLength, 0);
2231193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) {
2232193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // This is the case when we cancel a manual pick.
2233193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // We should restart suggestion on the word right away.
223432f0af1fc48f67907f0e731e18359f29e2b1df14Jean Chalard            mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
223532f0af1fc48f67907f0e731e18359f29e2b1df14Jean Chalard            mComposingStateManager.onStartComposingText();
223632f0af1fc48f67907f0e731e18359f29e2b1df14Jean Chalard            ic.setComposingText(originallyTypedWord, 1);
2237193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        } else {
2238193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            ic.commitText(originallyTypedWord, 1);
2239193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // Re-insert the separator
2240193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
2241193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
2242193d23f40e1556074f323b7bd9695759f4798efeJean Chalard                    WordComposer.NOT_A_COORDINATE);
2243193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // Don't restart suggestion yet. We'll restart if the user deletes the
2244193d23f40e1556074f323b7bd9695759f4798efeJean Chalard            // separator.
2245193d23f40e1556074f323b7bd9695759f4798efeJean Chalard        }
2246b6b8729374dc68b153f00730c79828532acf1ee5Jean Chalard        mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
2247890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.cancelUpdateBigramPredictions();
2248890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.postUpdateSuggestions();
2249890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    }
2250890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard
2251890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    // "ic" must not be null
2252890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    private void restartSuggestionsOnManuallyPickedTypedWord(final InputConnection ic) {
22533b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // Note: this relies on the last word still being held in the WordComposer, in
22543b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // the field for suggestion resuming.
22553b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // Note: in the interest of code simplicity, we may want to just call
22563b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving
22573b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // the old WordComposer allows to reuse the actual typed coordinates.
22582712f23acbb197af3b125da4cc47108e71b7446dJean Chalard        mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
22593b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // We resume suggestion, and then we want to set the composing text to the content
22603b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // of the word composer again. But since we just manually picked a word, there is
22613b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // no composing text at the moment, so we have to delete the word before we set a
22623b2112e70aa7474dbd0a879bfba5f7b1ce0a89adJean Chalard        // new composing text.
2263c2a76a1529870b59bba133a7d76a800cbd20ecfaJean Chalard        final int restartLength = mWordComposer.size();
2264890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2265a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard            final String wordBeforeCursor = ic.getTextBeforeCursor(restartLength, 0).toString();
22667546ff6e34d540e9529d785bfc691b9c155b4051Jean Chalard            if (!TextUtils.equals(mWordComposer.getTypedWord(), wordBeforeCursor)) {
2267890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                throw new RuntimeException("restartSuggestionsOnManuallyPickedTypedWord "
2268890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "check failed: we thought we were reverting \""
2269890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + mWordComposer.getTypedWord()
2270890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \""
2271890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + wordBeforeCursor + "\"");
2272890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
2273923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2274a7f2500001c53dc5a6de9c2525a75229cc7c6645Jean Chalard        ic.deleteSurroundingText(restartLength, 0);
2275172a013ab5481dee22e0637bc997b8bcbc4436dbJean Chalard        mComposingStateManager.onStartComposingText();
2276890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        ic.setComposingText(mWordComposer.getTypedWord(), 1);
22778558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        mHandler.cancelUpdateBigramPredictions();
22788558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        mHandler.postUpdateSuggestions();
2279923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2280923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2281120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
22822124bc5bf5af31cf3d2789b70ebd2f24c815f5f4Jean Chalard    private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
22834733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mHandler.cancelDoubleSpacesTimer();
22844733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // Here we test whether we indeed have a period and a space before us. This should not
22854733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // be needed, but it's there just in case something went wrong.
22864733609947c0ec74e460bd714fffca0518ade93aJean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
22878ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        if (!". ".equals(textBeforeCursor)) {
228851fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // Theoretically we should not be coming here if there isn't ". " before the
228951fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // cursor, but the application may be changing the text while we are typing, so
229051fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            // anything goes. We should not crash.
229151fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            Log.d(TAG, "Tried to revert double-space combo but we didn't find "
22928ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "\". \" just before the cursor.");
229351fd1632f59bd9aaeb5c98ff031f1618e8c31c59Jean Chalard            return false;
22948ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
22954733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.deleteSurroundingText(2, 0);
22964733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.commitText("  ", 1);
22974733609947c0ec74e460bd714fffca0518ade93aJean Chalard        return true;
22984733609947c0ec74e460bd714fffca0518ade93aJean Chalard    }
22994733609947c0ec74e460bd714fffca0518ade93aJean Chalard
23008fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean revertSwapPunctuation(final InputConnection ic) {
2301120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // Here we test whether we indeed have a space and something else before us. This should not
2302120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // be needed, but it's there just in case something went wrong.
2303120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
2304120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
2305120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // enter surrogate pairs this code will have been removed.
23068be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard        if (TextUtils.isEmpty(textBeforeCursor)
23078be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard                || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
23088be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // We may only come here if the application is changing the text while we are typing.
23098be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // This is quite a broken case, but not logically impossible, so we shouldn't crash,
23108be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            // but some debugging log may be in order.
23118be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
23128ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "find a space just before the cursor.");
23138be16f78b10aa4a8975c4068db1b32f81f661a2cJean Chalard            return false;
23148ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
2315120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.beginBatchEdit();
2316120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.deleteSurroundingText(2, 0);
2317120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
2318120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.endBatchEdit();
2319120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return true;
2320120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    }
2321120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
2322923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean isWordSeparator(int code) {
232317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        return mSettingsValues.isWordSeparator(code);
2324923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2325923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2326923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean preferCapitalization() {
23279318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa        return mWordComposer.isFirstCharCapitalized();
2328923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2329923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
233088fc9d44186120f9edc5cf7ec0e2af85260fed04satok    // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
2331c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    // according to new language or mode.
2332c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    public void onRefreshKeyboard() {
233355303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka        if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
233455303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka            // Before Honeycomb, Voice IME is in LatinIME and it changes the current input view,
233555303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka            // so that we need to re-create the keyboard input view here.
233655303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka            setInputView(mKeyboardSwitcher.onCreateInputView());
233755303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka        }
23381e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // When the device locale is changed in SetupWizard etc., this method may get called via
23391e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // onConfigurationChanged before SoftInputWindow is shown.
23401e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        if (mKeyboardSwitcher.getKeyboardView() != null) {
23411e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            // Reload keyboard because the current language has been changed.
23421e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
23431e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        }
23440ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        initSuggest();
234517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
234636fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
234736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
2348a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka    public void hapticAndAudioFeedback(int primaryCode) {
2349d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka        vibrate();
2350d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka        playKeyClick(primaryCode);
2351d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka    }
2352d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka
23535a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
23542a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    public void onPressKey(int primaryCode) {
2355a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        mKeyboardSwitcher.onPressKey(primaryCode);
2356923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2357923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
23585a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
23592a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka    public void onReleaseKey(int primaryCode, boolean withSliding) {
23602a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
23618d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv
23628d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        // If accessibility is on, ensure the user receives keyboard state updates.
23638d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
23648d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            switch (primaryCode) {
23658d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            case Keyboard.CODE_SHIFT:
23668d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifyShiftState();
23678d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
23688d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
23698d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                AccessibleKeyboardViewProxy.getInstance().notifySymbolsState();
23708d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv                break;
23718d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv            }
23728d4f0d5d1df2e0ae0b6ac332fd6661b7fa903186alanv        }
2373923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2374a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2375123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka    // receive ringer mode change and network state change.
2376923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
2377923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
2378923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void onReceive(Context context, Intent intent) {
2379123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            final String action = intent.getAction();
2380123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
2381123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                updateRingerMode();
2382123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
2383123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                mSubtypeSwitcher.onNetworkStateChanged(intent);
2384123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            }
2385923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2386923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    };
2387923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2388923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // update flags for silent mode
2389923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void updateRingerMode() {
2390923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (mAudioManager == null) {
2391923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
2392bf7dd47329c23c846912b268d15e8083ffdaabdeKen Wakasa            if (mAudioManager == null) return;
2393923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2394bf7dd47329c23c846912b268d15e8083ffdaabdeKen Wakasa        mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
2395923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2396923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2397923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void playKeyClick(int primaryCode) {
2398923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // if mAudioManager is null, we don't have the ringer state yet
2399923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // mAudioManager will be set by updateRingerMode
2400923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (mAudioManager == null) {
2401c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            if (mKeyboardSwitcher.getKeyboardView() != null) {
2402923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                updateRingerMode();
2403923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
2404923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
240517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (isSoundOn()) {
2406f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            final int sound;
2407923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (primaryCode) {
2408f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            case Keyboard.CODE_DELETE:
2409f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_DELETE;
2410f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2411f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            case Keyboard.CODE_ENTER:
2412f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_RETURN;
2413f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2414f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            case Keyboard.CODE_SPACE:
2415f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_SPACEBAR;
2416f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2417f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            default:
2418f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_STANDARD;
2419f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2420923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
2421c207e0a7dad0bdae054be47cafe878698f9401fcJean Chalard            mAudioManager.playSoundEffect(sound, mSettingsValues.mFxVolume);
2422923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2423923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2424923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2425409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void vibrate() {
242617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (!mSettingsValues.mVibrateOn) {
2427923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
2428923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2429c207e0a7dad0bdae054be47cafe878698f9401fcJean Chalard        if (mSettingsValues.mKeypressVibrationDuration < 0) {
243028f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            // Go ahead with the system default
243128f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
243228f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            if (inputView != null) {
243328f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa                inputView.performHapticFeedback(
243428f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa                        HapticFeedbackConstants.KEYBOARD_TAP,
243528f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa                        HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
243628f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            }
243728f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        } else if (mVibrator != null) {
2438c207e0a7dad0bdae054be47cafe878698f9401fcJean Chalard            mVibrator.vibrate(mSettingsValues.mKeypressVibrationDuration);
2439923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2440923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2441923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2442f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard    public boolean isAutoCapitalized() {
2443f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        return mWordComposer.isAutoCapitalized();
24446516d0fdfcbaa4eb809a8a69bd876293043a68a4Amith Yamasani    }
24456516d0fdfcbaa4eb809a8a69bd876293043a68a4Amith Yamasani
244617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    boolean isSoundOn() {
244717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        return mSettingsValues.mSoundOn && !mSilentModeOn;
2448979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
2449979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2450e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    private void updateCorrectionMode() {
24519f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        // TODO: cleanup messy flags
2452f50aa193377492e9c4afb3cc6e7f3448ab5a97a4Jean Chalard        final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
2453dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard                && !mInputAttributes.mInputTypeNoAutoCorrect;
2454283b0c2b642030539ba3e41f3f54b6aed6bb9e2dJean Chalard        mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE;
2455283b0c2b642030539ba3e41f3f54b6aed6bb9e2dJean Chalard        mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect)
2456979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
2457e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    }
2458e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani
24592ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka    private void updateSuggestionVisibility(final Resources res) {
24600fe3611bee5095e7bd0fff2d0fdf8d5a13379132Jean Chalard        final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting;
24617599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
2462458249e703bded3a1cbd25a2ab2249f9366a8188Ken Wakasa            if (suggestionVisiblityStr.equals(res.getString(visibility))) {
24637599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                mSuggestionVisibility = visibility;
24647599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                break;
24657599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            }
24667599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        }
24677599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
24687599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
2469466741d8a78965b8509bf527344f289e50873092Mike LeBeau    protected void launchSettings() {
2470cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        launchSettingsClass(Settings.class);
2471466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2472466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2473bf96661d33d0126adb60a48880ceba1ff055d4a4satok    public void launchDebugSettings() {
2474cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        launchSettingsClass(DebugSettings.class);
2475bf96661d33d0126adb60a48880ceba1ff055d4a4satok    }
2476bf96661d33d0126adb60a48880ceba1ff055d4a4satok
2477cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    protected void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
2478923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        handleClose();
2479923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        Intent intent = new Intent();
2480466741d8a78965b8509bf527344f289e50873092Mike LeBeau        intent.setClass(LatinIME.this, settingsClass);
2481923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2482923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        startActivity(intent);
2483923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2484923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
24852fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showSubtypeSelectorAndSettings() {
248685996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
248785996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
248885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                // TODO: Should use new string "Select active input modes".
248985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.language_selection_title),
249085996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.english_ime_settings),
249185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
249285996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
24932fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            @Override
24942fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            public void onClick(DialogInterface di, int position) {
24952fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                di.dismiss();
24962fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                switch (position) {
249785996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
24982cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok                    Intent intent = CompatUtils.getInputLanguageSelectionIntent(
24992a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Utils.getInputMethodId(mImm, getPackageName()),
25002a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Intent.FLAG_ACTIVITY_NEW_TASK
25012fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
25022fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_CLEAR_TOP);
25032fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    startActivity(intent);
25042fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2505aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                case 1:
2506aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    launchSettings();
2507aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    break;
25082fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                }
25092fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            }
251085996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2511bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2512bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2513bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2514bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        showOptionDialogInternal(builder.create());
25152fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    }
2516923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
25172fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showOptionsMenu() {
251885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
251985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
252085996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.selectInputMethod),
252185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.english_ime_settings),
252285996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
252385996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
25245a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            @Override
2525923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            public void onClick(DialogInterface di, int position) {
2526923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                di.dismiss();
2527923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                switch (position) {
252885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
2529ea55bf5df7d26d12b36c47141bfbac5730c3929aTadashi G. Takaoka                    mImm.showInputMethodPicker();
25302fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
253185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 1:
2532ea55bf5df7d26d12b36c47141bfbac5730c3929aTadashi G. Takaoka                    launchSettings();
25332fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2534923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                }
2535923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
253685996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2537bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2538bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2539bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2540bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        showOptionDialogInternal(builder.create());
2541923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2542923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
25437e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
25447e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2545923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.dump(fd, fout, args);
2546a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2547923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        final Printer p = new PrintWriterPrinter(fout);
2548923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("LatinIME state :");
25493708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
2550df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
2551df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        p.println("  Keyboard mode = " + keyboardMode);
2552dc9d0fcc11f0886296c6eb325fe4f71beb87ff4cJean Chalard        p.println("  mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn);
2553923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("  mCorrectionMode=" + mCorrectionMode);
25544d0f03bd66742ee292da81a3e025e119f28b6940Jean Chalard        p.println("  isComposingWord=" + mWordComposer.isComposingWord());
255517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
255617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mSoundOn=" + mSettingsValues.mSoundOn);
255717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mVibrateOn=" + mSettingsValues.mVibrateOn);
2558240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard        p.println("  mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
25596fa003ee234c6e2ca6cd9ec555221ac5c71a5405Jean Chalard        p.println("  mInputAttributes=" + mInputAttributes.toString());
2560923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2561923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
2562