LatinIME.java revision f7d6517d6b1a1dd88e2142e1a15703bb839be01b
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;
52c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.CompatUtils;
53c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.EditorInfoCompatUtils;
54c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputConnectionCompatUtils;
55c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
56c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
57c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.compat.InputTypeCompatUtils;
581fef530ec7626fa16777f52b48191e61db8f46d4satokimport com.android.inputmethod.compat.SuggestionSpanUtils;
5928f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasaimport com.android.inputmethod.compat.VibratorCompatWrapper;
60c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.deprecated.LanguageSwitcherProxy;
61c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.deprecated.VoiceProxy;
6283ffff2a49beeb41874b7cb40819a75001f708e2Ken Wakasaimport com.android.inputmethod.keyboard.Key;
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;
68c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport com.android.inputmethod.keyboard.LatinKeyboard;
69c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokaimport com.android.inputmethod.keyboard.LatinKeyboardView;
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.
110af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     */
111af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka    public static final String IME_OPTION_FORCE_ASCII = "forceAscii";
112af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka
113af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka    /**
114af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     * The subtype extra value used to indicate that the subtype keyboard layout is capable for
115af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     * typing ASCII characters.
116af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka     */
117af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka    public static final String SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable";
118af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka
119d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka    /**
120294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima     * The subtype extra value used to indicate that the subtype keyboard layout supports touch
121294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima     * position correction.
122294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima     */
123294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima    public static final String SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION =
124294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            "SupportTouchPositionCorrection";
125294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima    /**
126d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka     * The subtype extra value used to indicate that the subtype keyboard layout should be loaded
127d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka     * from the specified locale.
128d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka     */
129d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka    public static final String SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE = "KeyboardLocale";
130d81479a340d76afaef14ce683322e1488167919cTadashi G. Takaoka
1319e347d3d448e48229c46aad394ec9bd60cd5807bsatok    private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
132fd0bd57deb53c4cce32810a61133fa44b45dbb7bKen Wakasa
133923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // How many continuous deletes at which to start deleting at a higher speed.
134923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private static final int DELETE_ACCELERATE_AT = 20;
135923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // Key events coming any faster than this are long-presses.
136d67fe0e7583f1be18b35b33b7658e4427f1e3dedAmith Yamasani    private static final int QUICK_PRESS = 200;
137a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
13859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private static final int PENDING_IMS_CALLBACK_DURATION = 800;
139055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
140cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /**
141cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * The name of the scheme used by the Package Manager to warn of a new package installation,
142cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     * replacement or removal.
143cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard     */
144cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private static final String SCHEME_PACKAGE = "package";
145cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
1460fe3611bee5095e7bd0fff2d0fdf8d5a13379132Jean Chalard    // TODO: migrate this to SettingsValues
1477599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private int mSuggestionVisibility;
1487599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
1497599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_show_value;
1507599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
1517599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_show_only_portrait_value;
1527599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int SUGGESTION_VISIBILILTY_HIDE_VALUE
1537599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            = R.string.prefs_suggestion_visibility_hide_value;
1547599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1557599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
1567599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_SHOW_VALUE,
1577599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE,
1587599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        SUGGESTION_VISIBILILTY_HIDE_VALUE
1597599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    };
1607599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
161120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Magic space: a space that should disappear on space/apostrophe insertion, move after the
162120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // punctuation on punctuation insertion, and become a real space on alpha char insertion.
163ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa    // Weak space: a space that should be swapped only by suggestion strip punctuation.
164120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Double space: the state where the user pressed space twice quickly, which LatinIME
165120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // resolved as period-space. Undoing this converts the period to a space.
166120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Swap punctuation: the state where a (weak or magic) space and a punctuation from the
167120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // suggestion strip have just been swapped. Undoing this swaps them back.
168120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_NONE = 0;
169120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_DOUBLE = 1;
170120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
171120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_MAGIC = 3;
172120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private static final int SPACE_STATE_WEAK = 4;
173120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
174120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // Current space state of the input method. This can be any of the above constants.
175120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private int mSpaceState;
176126698fdd256a2e3734634d3b923cabd800064baJean Chalard
1777a16a061e622539e54d7d649dcb8d4965aea575aJean Chalard    private SettingsValues mSettingsValues;
17817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
179d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka    private View mExtractArea;
180abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka    private View mKeyPreviewBackingView;
181913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private View mSuggestionsContainer;
182913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    private SuggestionsView mSuggestionsView;
183923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private Suggest mSuggest;
1841b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private CompletionInfo[] mApplicationSpecifiedCompletions;
185a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
186610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    private InputMethodManagerCompatWrapper mImm;
1872fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private Resources mResources;
1882fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private SharedPreferences mPrefs;
18971c353aa875f5237b1dce4e18bd4fe86ce28b58eTadashi G. Takaoka    private KeyboardSwitcher mKeyboardSwitcher;
1900ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private SubtypeSwitcher mSubtypeSwitcher;
191b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok    private VoiceProxy mVoiceProxy;
192a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
193923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private UserDictionary mUserDictionary;
194979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private UserBigramDictionary mUserBigramDictionary;
195f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard    private UserUnigramDictionary mUserUnigramDictionary;
19688562bec54658840dcce352127bdc15705c20a89Jean Chalard    private boolean mIsUserDictionaryAvailable;
19736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
1980730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    // TODO: Create an inner class to group options and pseudo-options to improve readability.
199c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    // These variables are initialized according to the {@link EditorInfo#inputType}.
20026a531c6fe2a6e058803b7102e2bc9e7ea12d8f3Jean Chalard    private boolean mInsertSpaceOnPickSuggestionManually;
201c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    private boolean mInputTypeNoAutoCorrect;
202c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    private boolean mIsSettingsSuggestionStripOn;
203c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    private boolean mApplicationSpecifiedCompletionOn;
204c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
2059318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa    private WordComposer mWordComposer = new WordComposer();
206f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard    private boolean mHasUncommittedTypedChars;
207409220583333bdf06290dd9fd42f91b5c0d1b11asatok
20879efbed76f638be298493107fa2d0cd1b5eb529esatok    private int mCorrectionMode;
209890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    private String mWordSavedForAutoCorrectCancellation;
210979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    // Keep track of the last selection range to decide if we need to show word alternatives
21179efbed76f638be298493107fa2d0cd1b5eb529esatok    private int mLastSelectionStart;
21279efbed76f638be298493107fa2d0cd1b5eb529esatok    private int mLastSelectionEnd;
213979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2144733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
2154733609947c0ec74e460bd714fffca0518ade93aJean Chalard    // "expect" it, it means the user actually moved the cursor.
2164733609947c0ec74e460bd714fffca0518ade93aJean Chalard    private boolean mExpectingUpdateSelection;
217923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private int mDeleteCount;
218923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private long mLastKeyTime;
219a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
220923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private AudioManager mAudioManager;
22117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    private boolean mSilentModeOn; // System-wide current configuration
222923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
22328f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa    private VibratorCompatWrapper mVibrator;
22428f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
225b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok    // TODO: Move this flag to VoiceProxy
22681c52293f84ce475ac6b1661f4a4b92703405247Amith Yamasani    private boolean mConfigurationChanging;
227466741d8a78965b8509bf527344f289e50873092Mike LeBeau
22838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    // Member variables for remembering the current device orientation.
22938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    private int mDisplayOrientation;
23038f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
231cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    // Object for reacting to adding/removing a dictionary pack.
232cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    private BroadcastReceiver mDictionaryPackInstallReceiver =
233cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard            new DictionaryPackInstallBroadcastReceiver(this);
234cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
235dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    // Keeps track of most recently inserted text (multi-character key) for reverting
236dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    private CharSequence mEnteredText;
237dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
238604d80c67185954d4691ac775be59c499eee3b1csatok    private final ComposingStateManager mComposingStateManager =
239fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok            ComposingStateManager.getInstance();
240604d80c67185954d4691ac775be59c499eee3b1csatok
2414f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public final UIHandler mHandler = new UIHandler(this);
242d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
2434f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa    public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
244d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        private static final int MSG_UPDATE_SUGGESTIONS = 0;
24545f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_UPDATE_SHIFT_STATE = 1;
24645f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_VOICE_RESULTS = 2;
24745f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_FADEOUT_LANGUAGE_ON_SPACEBAR = 3;
24845f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 4;
24945f45a4fdb16d0fa44b4a34b880e5fabb41c1492Jean Chalard        private static final int MSG_SPACE_TYPED = 5;
25093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        private static final int MSG_SET_BIGRAM_PREDICTIONS = 6;
25193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        private static final int MSG_PENDING_IMS_CALLBACK = 7;
252175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
25310dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayBeforeFadeoutLanguageOnSpacebar;
25410dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateSuggestions;
25510dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDelayUpdateShiftState;
25610dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private int mDurationOfFadeoutLanguageOnSpacebar;
25710dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private float mFinalFadeoutFactorOfLanguageOnSpacebar;
25810dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        private long mDoubleSpacesTurnIntoPeriodTimeout;
25938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
2604f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        public UIHandler(LatinIME outerInstance) {
2614f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            super(outerInstance);
26210dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        }
263175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka
26410dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        public void onCreate() {
26510dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka            final Resources res = getOuterInstance().getResources();
266175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger(
267175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_delay_before_fadeout_language_on_spacebar);
268175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateSuggestions =
269175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_suggestions);
270175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDelayUpdateShiftState =
271175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    res.getInteger(R.integer.config_delay_update_shift_state);
272175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDurationOfFadeoutLanguageOnSpacebar = res.getInteger(
273175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_duration_of_fadeout_language_on_spacebar);
274175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger(
275175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f;
276175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger(
277175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    R.integer.config_double_spaces_turn_into_period_timeout);
2784f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa        }
2794f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa
280923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
281923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void handleMessage(Message msg) {
2824f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
2834f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
284c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka            final LatinKeyboardView inputView = switcher.getKeyboardView();
285923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (msg.what) {
286d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SUGGESTIONS:
2874f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateSuggestions();
288d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
289d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_UPDATE_SHIFT_STATE:
290de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                switcher.updateShiftState();
291d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
292cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            case MSG_SET_BIGRAM_PREDICTIONS:
2934f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.updateBigramPredictions();
29489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                break;
295d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            case MSG_VOICE_RESULTS:
2964f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                latinIme.mVoiceProxy.handleVoiceResults(latinIme.preferCapitalization()
297de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                        || (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked()));
298de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                break;
299de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR:
30017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                if (inputView != null) {
30166432cfc9b7680a653bcf19d0d4250db21155eceTadashi G. Takaoka                    inputView.setSpacebarTextFadeFactor(
302175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                            (1.0f + mFinalFadeoutFactorOfLanguageOnSpacebar) / 2,
30366432cfc9b7680a653bcf19d0d4250db21155eceTadashi G. Takaoka                            (LatinKeyboard)msg.obj);
30417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                }
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:
30917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                if (inputView != null) {
310175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                    inputView.setSpacebarTextFadeFactor(mFinalFadeoutFactorOfLanguageOnSpacebar,
31117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                            (LatinKeyboard)msg.obj);
31217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                }
313d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka                break;
314923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
315923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
316d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
317d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void postUpdateSuggestions() {
318d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
319175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions);
320d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
321d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
322d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateSuggestions() {
323d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SUGGESTIONS);
324d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
325d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
326d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public boolean hasPendingUpdateSuggestions() {
327d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            return hasMessages(MSG_UPDATE_SUGGESTIONS);
328d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
329d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
330d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void postUpdateShiftKeyState() {
331d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
332175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState);
333d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
334d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
335d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void cancelUpdateShiftState() {
336d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            removeMessages(MSG_UPDATE_SHIFT_STATE);
337d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
338d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka
339cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void postUpdateBigramPredictions() {
340cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
341175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions);
34289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
34389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
344cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        public void cancelUpdateBigramPredictions() {
345cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
34689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
34789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
348d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        public void updateVoiceResults() {
349d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            sendMessage(obtainMessage(MSG_VOICE_RESULTS));
350d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        }
351de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka
352d5a6b910e83de6dea3c5813cbf5e219abaccdf8aTadashi G. Takaoka        public void startDisplayLanguageOnSpacebar(boolean localeChanged) {
3534f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa            final LatinIME latinIme = getOuterInstance();
354de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR);
355de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR);
356c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka            final LatinKeyboardView inputView = latinIme.mKeyboardSwitcher.getKeyboardView();
357de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            if (inputView != null) {
3584f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                final LatinKeyboard keyboard = latinIme.mKeyboardSwitcher.getLatinKeyboard();
359c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                // The language is always displayed when the delay is negative.
360c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                final boolean needsToDisplayLanguage = localeChanged
361175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                        || mDelayBeforeFadeoutLanguageOnSpacebar < 0;
362de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                // The language is never displayed when the delay is zero.
363175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                if (mDelayBeforeFadeoutLanguageOnSpacebar != 0) {
364c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                    inputView.setSpacebarTextFadeFactor(needsToDisplayLanguage ? 1.0f
365175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                            : mFinalFadeoutFactorOfLanguageOnSpacebar,
3664f0d290c5d112ebac434bd8de4635f7d42ea2df0Ken Wakasa                            keyboard);
36717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                }
368c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka                // The fadeout animation will start when the delay is positive.
369175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                if (localeChanged && mDelayBeforeFadeoutLanguageOnSpacebar > 0) {
370de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR, keyboard),
371175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka                            mDelayBeforeFadeoutLanguageOnSpacebar);
372de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka                }
373de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka            }
374de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        }
375fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
376fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void startDoubleSpacesTimer() {
377fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
378175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout);
379fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
380fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
381fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public void cancelDoubleSpacesTimer() {
382fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            removeMessages(MSG_SPACE_TYPED);
383fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
384fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka
385fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        public boolean isAcceptingDoubleSpaces() {
386fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            return hasMessages(MSG_SPACE_TYPED);
387fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka        }
38838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
38959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        // Working variables for the following methods.
39059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mIsOrientationChanging;
39159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mPendingSuccesiveImsCallback;
39259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingStartInput;
39359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInputView;
39459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private boolean mHasPendingFinishInput;
395e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private EditorInfo mAppliedEditorInfo;
39659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
39759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void startOrientationChanging() {
398dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            removeMessages(MSG_PENDING_IMS_CALLBACK);
399dd25e4fa2c7dd1e32a9e6f5fd21f54214919ef20Tadashi G. Takaoka            resetPendingImsCallback();
40059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mIsOrientationChanging = true;
401055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            final LatinIME latinIme = getOuterInstance();
402f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            if (latinIme.isInputViewShown()) {
403f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka                latinIme.mKeyboardSwitcher.saveKeyboardState();
404f2c9a88887a303f3df8bee60788870946dfa55f7Tadashi G. Takaoka            }
40559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
40659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
40759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        private void resetPendingImsCallback() {
40859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInputView = false;
40959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingFinishInput = false;
41059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            mHasPendingStartInput = false;
41159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
41259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
413e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        private void executePendingImsCallback(LatinIME latinIme, EditorInfo editorInfo,
41459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                boolean restarting) {
41559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInputView)
41659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
41759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingFinishInput)
41859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
41959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (mHasPendingStartInput)
420e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
42159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            resetPendingImsCallback();
42259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
42359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
424e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInput(EditorInfo editorInfo, boolean restarting) {
42559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
42659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the second onStartInput after orientation changed.
42759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingStartInput = true;
42859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
42959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                if (mIsOrientationChanging && restarting) {
43059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    // This is the first onStartInput after orientation changed.
43159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    mIsOrientationChanging = false;
43259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                    mPendingSuccesiveImsCallback = true;
43359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                }
43459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
435e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
436e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputInternal(editorInfo, restarting);
437055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
438055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
439055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
440e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
4416b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)
4426b7100fecaaaf0e8e42c4d2ccebac165e89e79bfTadashi G. Takaoka                    && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
443e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                // Typically this is the second onStartInputView after orientation changed.
444e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                resetPendingImsCallback();
445e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            } else {
446e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                if (mPendingSuccesiveImsCallback) {
447e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    // This is the first onStartInputView after orientation changed.
448e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    mPendingSuccesiveImsCallback = false;
449e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    resetPendingImsCallback();
450e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
451e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            PENDING_IMS_CALLBACK_DURATION);
452e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                }
453e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
454e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                executePendingImsCallback(latinIme, editorInfo, restarting);
455e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                latinIme.onStartInputViewInternal(editorInfo, restarting);
456e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = editorInfo;
457e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            }
45859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        }
45959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
46059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInputView(boolean finishingInput) {
46159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
46259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInputView after orientation changed.
46359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInputView = true;
46459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
46559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
46659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputViewInternal(finishingInput);
467e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                mAppliedEditorInfo = null;
46838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka            }
46938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        }
470ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka
47159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        public void onFinishInput() {
47259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
47359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                // Typically this is the first onFinishInput after orientation changed.
47459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                mHasPendingFinishInput = true;
47559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka            } else {
47659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                final LatinIME latinIme = getOuterInstance();
47759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                executePendingImsCallback(latinIme, null, false);
47859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka                latinIme.onFinishInputInternal();
479ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka            }
480ffd156c77e93a54b704780b46886ba597d1c77cbTadashi G. Takaoka        }
48138f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    }
48238f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
4837e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
4847e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    public void onCreate() {
48527d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
48627d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        mPrefs = prefs;
48727d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        LatinImeLogger.init(this, prefs);
4886d9021527a38ba1e94225020389621a0d7227aa1satok        LanguageSwitcherProxy.init(this, prefs);
489bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        InputMethodManagerCompatWrapper.init(this);
490ef5dfc480c7a3e3e34a20b7aacc731942e7a0578Tadashi G. Takaoka        SubtypeSwitcher.init(this);
49127d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        KeyboardSwitcher.init(this, prefs);
4925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        AccessibilityUtils.init(this, prefs);
493363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
494923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onCreate();
495363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
496bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        mImm = InputMethodManagerCompatWrapper.getInstance();
4970ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
4980ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        mKeyboardSwitcher = KeyboardSwitcher.getInstance();
49928f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        mVibrator = VibratorCompatWrapper.getInstance(this);
50010dff76856a98b6eec86a53d3d07fdc9708037b7Tadashi G. Takaoka        mHandler.onCreate();
5019e2d810dc524380ca1db6b384cfb00b4401585e5Tadashi G. Takaoka        DEBUG = LatinImeLogger.sDBG;
502363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka
503363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        final Resources res = getResources();
504363cd6ef0f8f8af41a01964a2f0e800f9305d3a2Tadashi G. Takaoka        mResources = res;
505fd7d814c81132bdd59146a39dd668532f9514cd1Jean Chalard
50628f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        loadSettings();
50728f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa
5089502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
509979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        boolean tryGC = true;
5109502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
511979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            try {
5120ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok                initSuggest();
513979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                tryGC = false;
514979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            } catch (OutOfMemoryError e) {
5159502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e);
516979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
517979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
518979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
519f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        mDisplayOrientation = res.getConfiguration().orientation;
520b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
521cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Register to receive ringer mode change and network state change.
522cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        // Also receive installation and removal of a dictionary pack.
523123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        final IntentFilter filter = new IntentFilter();
524123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
525123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
526923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        registerReceiver(mReceiver, filter);
527b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy = VoiceProxy.init(this, prefs, mHandler);
528cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
529cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final IntentFilter packageFilter = new IntentFilter();
530cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
531cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
532cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        packageFilter.addDataScheme(SCHEME_PACKAGE);
533cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
534646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard
535646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        final IntentFilter newDictFilter = new IntentFilter();
536646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        newDictFilter.addAction(
537646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard                DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
538646d950ed8d2b2555df92855e18b350fd7761b21Jean Chalard        registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
539923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
54036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
54117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    // Has to be package-visible for unit tests
54217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    /* package */ void loadSettings() {
54317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
54417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance();
5457a16a061e622539e54d7d649dcb8d4965aea575aJean Chalard        mSettingsValues = new SettingsValues(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
54614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
54717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    }
54817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
5490ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private void initSuggest() {
550cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
551ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr);
55236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
553309bff562fbaf47488e6bf6636840f00574187d8Jean Chalard        final Resources res = mResources;
554ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        final Locale savedLocale = LocaleUtils.setSystemLocale(res, keyboardLocale);
55514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        final ContactsDictionary oldContactsDictionary;
55636fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani        if (mSuggest != null) {
55714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            oldContactsDictionary = mSuggest.getContactsDictionary();
55836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani            mSuggest.close();
55914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
56014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            oldContactsDictionary = null;
56136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani        }
562979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
5638efc0addce3cd1a32618bf18bafabaaca2b498b0Tadashi G. Takaoka        int mainDicResId = Utils.getMainDictionaryResourceId(res);
564cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mSuggest = new Suggest(this, mainDicResId, keyboardLocale);
56517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (mSettingsValues.mAutoCorrectEnabled) {
56617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
56717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        }
568e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
569cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mUserDictionary = new UserDictionary(this, localeStr);
570923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mSuggest.setUserDictionary(mUserDictionary);
57188562bec54658840dcce352127bdc15705c20a89Jean Chalard        mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
572e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
57314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        resetContactsDictionary(oldContactsDictionary);
574e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
575f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        mUserUnigramDictionary
576f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                = new UserUnigramDictionary(this, this, localeStr, Suggest.DIC_USER_UNIGRAM);
577f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        mSuggest.setUserUnigramDictionary(mUserUnigramDictionary);
578e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
579f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        mUserBigramDictionary
580f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                = new UserBigramDictionary(this, this, localeStr, Suggest.DIC_USER_BIGRAM);
581e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        mSuggest.setUserBigramDictionary(mUserBigramDictionary);
582e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa
583e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani        updateCorrectionMode();
58436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
585ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        LocaleUtils.setSystemLocale(res, savedLocale);
586923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
58736fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
58814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    /**
58914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * Resets the contacts dictionary in mSuggest according to the user settings.
59014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
59114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * This method takes an optional contacts dictionary to use. Since the contacts dictionary
59214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * does not depend on the locale, it can be reused across different instances of Suggest.
59314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * The dictionary will also be opened or closed as necessary depending on the settings.
59414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     *
59514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     * @param oldContactsDictionary an optional dictionary to use, or null
59614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard     */
59714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard    private void resetContactsDictionary(final ContactsDictionary oldContactsDictionary) {
59814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict);
59914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
60014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        final ContactsDictionary dictionaryToUse;
60114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (!shouldSetDictionary) {
60214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the dictionary is closed. If it is already closed, this is a no-op,
60314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // so it's safe to call it anyways.
60414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            if (null != oldContactsDictionary) oldContactsDictionary.close();
60514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = null;
60614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else if (null != oldContactsDictionary) {
60714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // Make sure the old contacts dictionary is opened. If it is already open, this is a
60814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            // no-op, so it's safe to call it anyways.
60914051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            oldContactsDictionary.reopen(this);
61014051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = oldContactsDictionary;
61114051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        } else {
61214051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
61314051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
61414051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard
61514051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        if (null != mSuggest) {
61614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard            mSuggest.setContactsDictionary(dictionaryToUse);
61714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard        }
618699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard    }
619699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard
620cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    /* package private */ void resetSuggestMainDict() {
621cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
622ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963Jean Chalard        final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr);
623cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        int mainDicResId = Utils.getMainDictionaryResourceId(mResources);
624cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        mSuggest.resetMainDict(this, mainDicResId, keyboardLocale);
625cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard    }
626cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
627466741d8a78965b8509bf527344f289e50873092Mike LeBeau    @Override
628466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onDestroy() {
629e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa        if (mSuggest != null) {
630e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest.close();
631e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa            mSuggest = null;
632979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
633923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        unregisterReceiver(mReceiver);
634cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard        unregisterReceiver(mDictionaryPackInstallReceiver);
635b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.destroy();
636979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
637979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.onDestroy();
638923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onDestroy();
639923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
640923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
641923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
642923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onConfigurationChanged(Configuration conf) {
643dc64b138b5e3fb3706c0818d0a308fe6e36985b0Tadashi G. Takaoka        mSubtypeSwitcher.onConfigurationChanged(conf);
644604d80c67185954d4691ac775be59c499eee3b1csatok        mComposingStateManager.onFinishComposingText();
645b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        // If orientation changed while predicting, commit the change
646f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka        if (mDisplayOrientation != conf.orientation) {
647f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mDisplayOrientation = conf.orientation;
648f94df382fb3f73c03cfef5f0a28856bf3f1c73daTadashi G. Takaoka            mHandler.startOrientationChanging();
6499351550dc6af7859e5280e16144c9386a37b976dKen Wakasa            final InputConnection ic = getCurrentInputConnection();
650466741d8a78965b8509bf527344f289e50873092Mike LeBeau            commitTyped(ic);
651466741d8a78965b8509bf527344f289e50873092Mike LeBeau            if (ic != null) ic.finishComposingText(); // For voice input
6522fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            if (isShowingOptionDialog())
6532fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                mOptionsDialog.dismiss();
654b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
6558b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
65681c52293f84ce475ac6b1661f4a4b92703405247Amith Yamasani        mConfigurationChanging = true;
657923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onConfigurationChanged(conf);
658b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.onConfigurationChanged(conf);
65981c52293f84ce475ac6b1661f4a4b92703405247Amith Yamasani        mConfigurationChanging = false;
66088fc9d44186120f9edc5cf7ec0e2af85260fed04satok
66188fc9d44186120f9edc5cf7ec0e2af85260fed04satok        // This will work only when the subtype is not supported.
66288fc9d44186120f9edc5cf7ec0e2af85260fed04satok        LanguageSwitcherProxy.onConfigurationChanged(conf);
663923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
664b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
665923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
666923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public View onCreateInputView() {
6676c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        return mKeyboardSwitcher.onCreateInputView();
6686c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    }
6696c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka
6706c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    @Override
6716c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka    public void setInputView(View view) {
6726c4add55f94ff729ef1aa35c4f8df3b086344f8aTadashi G. Takaoka        super.setInputView(view);
673d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        mExtractArea = getWindow().getWindow().getDecorView()
674d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka                .findViewById(android.R.id.extractArea);
675abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
676913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
677913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
678913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
679913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setListener(this, view);
680f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        if (LatinImeLogger.sVISUALDEBUG) {
681f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka            mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
682f5fe245df0548ea0a8ec607f2392de0fe02c141dTadashi G. Takaoka        }
683923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
684923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
685923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
686c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public void setCandidatesView(View view) {
687c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        // To ensure that CandidatesView will never be set.
688c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return;
689923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
690923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
691a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker    @Override
692e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInput(EditorInfo editorInfo, boolean restarting) {
693e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInput(editorInfo, restarting);
69459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
69559f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
69659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
697e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
698e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mHandler.onStartInputView(editorInfo, restarting);
69959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
70059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
70159f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
70259f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInputView(boolean finishingInput) {
70359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInputView(finishingInput);
70459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
70538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka
70659f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    @Override
70759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    public void onFinishInput() {
70859f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka        mHandler.onFinishInput();
70959f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
71059f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
711e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
712e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInput(editorInfo, restarting);
71359f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    }
71459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka
715e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
716e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        super.onStartInputView(editorInfo, restarting);
71745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
718c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = switcher.getKeyboardView();
7198e09172df1bb176cc899940862c56bed9b9aec4esatok
72089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (DEBUG) {
721e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            Log.d(TAG, "onStartInputView: editorInfo:" + ((editorInfo == null) ? "none"
722f0f726464dcb5b3cef4f8e703659b35ca62430b5Tadashi G. Takaoka                    : String.format("inputType=0x%08x imeOptions=0x%08x",
723e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                            editorInfo.inputType, editorInfo.imeOptions)));
724910b73127fa207dd26ec8124000262523b0aac0csatok        }
725923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // In landscape mode, this method gets called without the input view being created.
726979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (inputView == null) {
727923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
728923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
729923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
730b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        // Forward this event to the accessibility utilities, if enabled.
731b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
732b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        if (accessUtils.isTouchExplorationEnabled()) {
733e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            accessUtils.onStartInputViewInternal(editorInfo, restarting);
734b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette        }
735b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette
7368d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        mSubtypeSwitcher.updateParametersOnStartInputView();
7374ab730dbd34fad323063f2ffd31ce33de746668dsatok
738055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        // Most such things we decide below in initializeInputAttributesAndGetMode, but we need to
739055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        // know now whether this is a password text field, because we need to know now whether we
740055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        // want to enable the voice button.
741055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        final VoiceProxy voiceIme = mVoiceProxy;
742e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        final int inputType = (editorInfo != null) ? editorInfo.inputType : 0;
7433be0039164f5e2060b83d0699e293ffc76384732Tadashi G. Takaoka        voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType)
7443be0039164f5e2060b83d0699e293ffc76384732Tadashi G. Takaoka                || InputTypeCompatUtils.isVisiblePasswordInputType(inputType));
745c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
746b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // The EditorInfo might have a flag that affects fullscreen mode.
747b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        // Note: This call should be done by InputMethodService?
748b2b1eb5ca5a52648eea70920b9bd745b07d3ddfaTadashi G. Takaoka        updateFullscreenMode();
749e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        initializeInputAttributes(editorInfo);
750c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
751c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.closing();
752c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mEnteredText = null;
7533651220327c051d8017045aa5e8919461507b3f8Jean Chalard        mWordComposer.reset();
754f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        mHasUncommittedTypedChars = false;
755c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mDeleteCount = 0;
756120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
757c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
75817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
75917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        updateCorrectionMode();
76017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        updateSuggestionVisibility(mPrefs, mResources);
76117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
76217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) {
76317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
764549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        }
765e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        mVoiceProxy.loadSettings(editorInfo, mPrefs);
76617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        // This will work only when the subtype is not supported.
76717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        LanguageSwitcherProxy.loadSettings();
76817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard
769c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (mSubtypeSwitcher.isKeyboardMode()) {
770e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka            switcher.loadKeyboard(editorInfo, mSettingsValues);
771c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        }
772c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
773913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null)
774913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.clear();
775913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShownInternal(
776913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                isSuggestionsStripVisible(), /* needsInputViewShown */ false);
777c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        // Delay updating suggestions because keyboard input view may not be shown at this point.
778c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        mHandler.postUpdateSuggestions();
779ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        mHandler.cancelDoubleSpacesTimer();
780c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
781240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard        inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
782240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard                mSettingsValues.mKeyPreviewPopupDismissDelay);
783c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        inputView.setProximityCorrectionEnabled(true);
784c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
785055265684bdc049db54c3ec2a7fa5404ff36a608Tadashi G. Takaoka        voiceIme.onStartInputView(inputView.getWindowToken());
786c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
787c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
788c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    }
789c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka
790e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka    private void initializeInputAttributes(EditorInfo editorInfo) {
791e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        if (editorInfo == null)
792cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            return;
793e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka        final int inputType = editorInfo.inputType;
79482c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka        if (inputType == InputType.TYPE_NULL) {
79582c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka            // TODO: We should honor TYPE_NULL specification.
79682c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka            Log.i(TAG, "InputType.TYPE_NULL is specified");
79782c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka        }
79882c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka        final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
799c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        final int variation = inputType & InputType.TYPE_MASK_VARIATION;
80082c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka        if (inputClass == 0) {
80182c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka            Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x imeOptions=0x%08x",
802e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka                    inputType, editorInfo.imeOptions));
80382c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka        }
80482c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka
80526a531c6fe2a6e058803b7102e2bc9e7ea12d8f3Jean Chalard        mInsertSpaceOnPickSuggestionManually = false;
806e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani        mInputTypeNoAutoCorrect = false;
8079fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        mIsSettingsSuggestionStripOn = false;
8081b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        mApplicationSpecifiedCompletionOn = false;
8091b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        mApplicationSpecifiedCompletions = null;
810dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
81182c8c2e6c00386ae9925fff68ea77bc448897cbdTadashi G. Takaoka        if (inputClass == InputType.TYPE_CLASS_TEXT) {
812cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            mIsSettingsSuggestionStripOn = true;
813913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            // Make sure that passwords are not displayed in {@link SuggestionsView}.
814e9957752bcaad048746c7a57bbd2c0a59e1918a0satok            if (InputTypeCompatUtils.isPasswordInputType(inputType)
815e9957752bcaad048746c7a57bbd2c0a59e1918a0satok                    || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)) {
816cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mIsSettingsSuggestionStripOn = false;
817cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            }
818e9957752bcaad048746c7a57bbd2c0a59e1918a0satok            if (InputTypeCompatUtils.isEmailVariation(variation)
819cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                    || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) {
82026a531c6fe2a6e058803b7102e2bc9e7ea12d8f3Jean Chalard                // The point in turning this off is that we don't want to insert a space after
82126a531c6fe2a6e058803b7102e2bc9e7ea12d8f3Jean Chalard                // a name when filling a form: we can't delete trailing spaces when changing fields
82226a531c6fe2a6e058803b7102e2bc9e7ea12d8f3Jean Chalard                mInsertSpaceOnPickSuggestionManually = false;
823cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            } else {
82426a531c6fe2a6e058803b7102e2bc9e7ea12d8f3Jean Chalard                mInsertSpaceOnPickSuggestionManually = true;
825cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            }
826e9957752bcaad048746c7a57bbd2c0a59e1918a0satok            if (InputTypeCompatUtils.isEmailVariation(variation)) {
827cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mIsSettingsSuggestionStripOn = false;
828cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
829cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mIsSettingsSuggestionStripOn = false;
830cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
831cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mIsSettingsSuggestionStripOn = false;
832cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
833cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                // If it's a browser edit field and auto correct is not ON explicitly, then
834cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                // disable auto correction, but keep suggestions on.
835cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
836e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani                    mInputTypeNoAutoCorrect = true;
83736ebae2e8d58d36d3f8dd2f8a2d34c0c06522a8eAmith Yamasani                }
838cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            }
839cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka
840cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            // If NO_SUGGESTIONS is set, don't do prediction.
841cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
842cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mIsSettingsSuggestionStripOn = false;
843cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mInputTypeNoAutoCorrect = true;
844cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            }
845cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            // If it's not multiline and the autoCorrect flag is not set, then don't correct
846cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0
847cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                    && (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
848cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mInputTypeNoAutoCorrect = true;
849cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            }
850cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
851cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mIsSettingsSuggestionStripOn = false;
852cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka                mApplicationSpecifiedCompletionOn = isFullscreenMode();
853cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            }
854923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
855923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
856923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
857923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
858e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    public void onWindowHidden() {
859e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        super.onWindowHidden();
860f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
861e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        if (inputView != null) inputView.closing();
862e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
863e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
86459f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputInternal() {
865923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onFinishInput();
866a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
867979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
868979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
869b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
870409220583333bdf06290dd9fd42f91b5c0d1b11asatok
871f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
872d024ea605cc6b5b0b9fa1bd838d5b0ebd3901a5dKen Wakasa        if (inputView != null) inputView.closing();
873f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard        if (mUserUnigramDictionary != null) mUserUnigramDictionary.flushPendingWrites();
874979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites();
875466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
876466741d8a78965b8509bf527344f289e50873092Mike LeBeau
87759f8ca752d5f79d4469519623590052c050c9d23Tadashi G. Takaoka    private void onFinishInputViewInternal(boolean finishingInput) {
8786495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa        super.onFinishInputView(finishingInput);
879055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardSwitcher.onFinishInputView();
880f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
8815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (inputView != null) inputView.cancelAllMessages();
882d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        // Remove pending messages related to update suggestions
883d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.cancelUpdateSuggestions();
8846495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    }
8856495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa
8866495bfc7a6f6bfa6ab82e7f062ee80f4b6b33368Ken Wakasa    @Override
887466741d8a78965b8509bf527344f289e50873092Mike LeBeau    public void onUpdateExtractedText(int token, ExtractedText text) {
888466741d8a78965b8509bf527344f289e50873092Mike LeBeau        super.onUpdateExtractedText(token, text);
889b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.showPunctuationHintIfNecessary();
890923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
891923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
892923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
893923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
894923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            int newSelStart, int newSelEnd,
895923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            int candidatesStart, int candidatesEnd) {
896923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
897923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                candidatesStart, candidatesEnd);
898466741d8a78965b8509bf527344f289e50873092Mike LeBeau
899466741d8a78965b8509bf527344f289e50873092Mike LeBeau        if (DEBUG) {
900466741d8a78965b8509bf527344f289e50873092Mike LeBeau            Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
901466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", ose=" + oldSelEnd
902025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lss=" + mLastSelectionStart
903025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                    + ", lse=" + mLastSelectionEnd
904466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nss=" + newSelStart
905466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", nse=" + newSelEnd
906466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", cs=" + candidatesStart
907466741d8a78965b8509bf527344f289e50873092Mike LeBeau                    + ", ce=" + candidatesEnd);
908466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
909466741d8a78965b8509bf527344f289e50873092Mike LeBeau
910b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.setCursorAndSelection(newSelEnd, newSelStart);
9114f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
912923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // If the current selection in the text view changes, we should
913923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // clear whatever candidate text we have.
914025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa        final boolean selectionChanged = (newSelStart != candidatesEnd
915025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa                || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart;
916025a5e356ec74dbed7f805a2e43adbb2a35320f8Ken Wakasa        final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1;
9174733609947c0ec74e460bd714fffca0518ade93aJean Chalard        if (!mExpectingUpdateSelection) {
918120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            if (SPACE_STATE_WEAK == mSpaceState) {
919120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                // Test for no WEAK_SPACE action because there is a race condition that may end up
920120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                // in coming here on a normal key press. We set this to NONE because after
921120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                // a cursor move, we don't want the suggestion strip to swap the space with the
922120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                // newly inserted punctuation.
923120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                mSpaceState = SPACE_STATE_NONE;
924126698fdd256a2e3734634d3b923cabd800064baJean Chalard            }
9253651220327c051d8017045aa5e8919461507b3f8Jean Chalard            if (((mWordComposer.size() > 0 && mHasUncommittedTypedChars)
9264c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                    || mVoiceProxy.isVoiceInputHighlighted())
9274c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                    && (selectionChanged || candidatesCleared)) {
9283651220327c051d8017045aa5e8919461507b3f8Jean Chalard                mWordComposer.reset();
9294c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                mHasUncommittedTypedChars = false;
930cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard                updateSuggestions();
9314c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                final InputConnection ic = getCurrentInputConnection();
9324c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                if (ic != null) {
9334c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                    ic.finishComposingText();
9344c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                }
935604d80c67185954d4691ac775be59c499eee3b1csatok                mComposingStateManager.onFinishComposingText();
9364c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard                mVoiceProxy.setVoiceInputHighlighted(false);
937cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard            } else if (!mHasUncommittedTypedChars) {
938cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard                updateSuggestions();
9394c0fe5719853325e51a8ff0652318e2afdaca97fJean Chalard            }
9404733609947c0ec74e460bd714fffca0518ade93aJean Chalard        }
9414733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = false;
942d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.postUpdateShiftKeyState();
9436b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not
9446b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // here. It would probably be too expensive to call directly here but we may want to post a
9456b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // message to delay it. The point would be to unify behavior between backspace to the
9466b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // end of a word and manually put the pointer at the end of the word.
947466741d8a78965b8509bf527344f289e50873092Mike LeBeau
948979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        // Make a note of the cursor position
949979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionStart = newSelStart;
950979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mLastSelectionEnd = newSelEnd;
9517a8dac55278cedd838be325f56b4c52d973c61f5satok    }
9527a8dac55278cedd838be325f56b4c52d973c61f5satok
953c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
954c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has clicked on the extracted text view,
955c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * when running in fullscreen mode.  The default implementation hides
956913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * the suggestions view when this happens, but only if the extracted text
957c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * editor has a vertical scroll bar because its text doesn't fit.
958c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
959913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
960c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
961c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
962c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedTextClicked() {
963fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
964c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
965c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedTextClicked();
966c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
967c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
968c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    /**
969c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * This is called when the user has performed a cursor movement in the
970c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * extracted text view, when it is running in fullscreen mode.  The default
971913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * implementation hides the suggestions view when a vertical movement
972c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * happens, but only if the extracted text editor has a vertical scroll bar
973c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * because its text doesn't fit.
974c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     * Here we override the behavior due to the possibility that a re-correction could
975913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka     * cause the suggestions strip to disappear and re-appear.
976c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani     */
977c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    @Override
978c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    public void onExtractedCursorMovement(int dx, int dy) {
979fe5364c825058f6c34c0f42135a5520b77525a28Jean Chalard        if (isSuggestionsRequested()) return;
980c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
981c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani        super.onExtractedCursorMovement(dx, dy);
982c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani    }
983c1020c3aa65b1eef4c672564750c020d012c4ec2Amith Yamasani
984923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
985923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void hideWindow() {
986979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        LatinImeLogger.commit();
987c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mKeyboardSwitcher.onHideWindow();
988979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
989923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (TRACE) Debug.stopMethodTracing();
9906e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
9916e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog.dismiss();
9926e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani            mOptionsDialog = null;
9936e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani        }
994b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.hideVoiceWindow(mConfigurationChanging);
995923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.hideWindow();
996923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
997923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
998923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
9991b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
1000979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (DEBUG) {
1001a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa            Log.i(TAG, "Received completions:");
1002bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            if (applicationSpecifiedCompletions != null) {
1003bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
1004bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                    Log.i(TAG, "  #" + i + ": " + applicationSpecifiedCompletions[i]);
1005bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                }
1006923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1007923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
10081b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        if (mApplicationSpecifiedCompletionOn) {
10091b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
10101b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            if (applicationSpecifiedCompletions == null) {
1011b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani                clearSuggestions();
1012923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                return;
1013923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1014a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
10157e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka            SuggestedWords.Builder builder = new SuggestedWords.Builder()
10161b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                    .setApplicationSpecifiedCompletions(applicationSpecifiedCompletions)
10175238df54ad3f648d09d5288f00b0a9d3c0593832Tadashi G. Takaoka                    .setTypedWordValid(false)
10185238df54ad3f648d09d5288f00b0a9d3c0593832Tadashi G. Takaoka                    .setHasMinimalSuggestion(false);
1019979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // When in fullscreen mode, show completions generated by the application
10207e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka            setSuggestions(builder.build());
1021117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            mWordComposer.deleteAutoCorrection();
1022c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            setSuggestionStripShown(true);
1023923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1024923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1025923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1026c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
1027913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // TODO: Modify this if we support suggestions with hard keyboard
1028913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
1029913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            final boolean shouldShowSuggestions = shown
10307a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true);
10314b1780fa9571409d65d9797d47949ffafaf0f083Tadashi G. Takaoka            if (isFullscreenMode()) {
1032913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
1033913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.GONE);
10347a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            } else {
1035913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsContainer.setVisibility(
1036913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                        shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE);
10377a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            }
1038923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1039923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1040a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1041c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private void setSuggestionStripShown(boolean shown) {
1042c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
1043543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    }
1044543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa
1045543dbdfdbda259aa56be9670480c7d802beba332Ken Wakasa    @Override
1046923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void onComputeInsets(InputMethodService.Insets outInsets) {
1047923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.onComputeInsets(outInsets);
1048f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
1049913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (inputView == null || mSuggestionsContainer == null)
105046ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka            return;
1051d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // In fullscreen mode, the height of the extract area managed by InputMethodService should
1052d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // be considered.
1053d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}.
1054d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0;
1055abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        final int backingHeight = (mKeyPreviewBackingView.getVisibility() == View.GONE) ? 0
1056abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka                : mKeyPreviewBackingView.getHeight();
105759010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0
105859010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                : mSuggestionsContainer.getHeight();
1059d44647e76f286256515ddac8cfc1ab29a2cbcf82Tadashi G. Takaoka        final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
1060abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka        int touchY = extraHeight;
10619e347d3d448e48229c46aad394ec9bd60cd5807bsatok        // Need to set touchable region only if input view is being shown
106246ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        if (mKeyboardSwitcher.isInputViewShown()) {
1063913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
106459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka                touchY -= suggestionsHeight;
10659e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
10667a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            final int touchWidth = inputView.getWidth();
1067abb0c77af15a22b5d0953e477da8747cd5f2259dTadashi G. Takaoka            final int touchHeight = inputView.getHeight() + extraHeight
10687a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    // Extend touchable region below the keyboard.
10697a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
10709e347d3d448e48229c46aad394ec9bd60cd5807bsatok            if (DEBUG) {
10717a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                Log.d(TAG, "Touchable region: y=" + touchY + " width=" + touchWidth
10727a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka                        + " height=" + touchHeight);
10739e347d3d448e48229c46aad394ec9bd60cd5807bsatok            }
10747a3d3ae17f7a8ca0b44e9c92328a7de7cbc80f92Tadashi G. Takaoka            setTouchableRegionCompat(outInsets, 0, touchY, touchWidth, touchHeight);
10759e347d3d448e48229c46aad394ec9bd60cd5807bsatok        }
107646ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.contentTopInsets = touchY;
107746ca84584810dfe606e709b3fe283cbde8aba5f5Tadashi G. Takaoka        outInsets.visibleTopInsets = touchY;
1078923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1079a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1080923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1081979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public boolean onEvaluateFullscreenMode() {
1082d567b230b055cb69186acf1ce0a1c4e1c7b61508Jean Chalard        return super.onEvaluateFullscreenMode() && mSettingsValues.mUseFullScreenMode;
108359010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    }
108459010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka
108559010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    @Override
108659010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka    public void updateFullscreenMode() {
108759010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        super.updateFullscreenMode();
1088f80b6a06992ae08ca3601f4fbc6da550fd9ac730Tadashi G. Takaoka
108959010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        if (mKeyPreviewBackingView == null) return;
1090549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        // In fullscreen mode, no need to have extra space to show the key preview.
109159010a89b839ea181dc07588df21f3db29ee8bd1Tadashi G. Takaoka        // If not, we should have extra space above the keyboard to show the key preview.
1092549e295dc113bff50077c3c355dcd94af09a1b2fTadashi G. Takaoka        mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
1093979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
1094979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1095979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
1096923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean onKeyDown(int keyCode, KeyEvent event) {
1097923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (keyCode) {
1098e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_BACK:
10992cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka            if (event.getRepeatCount() == 0) {
11002cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                if (mSuggestionsView != null && mSuggestionsView.handleBack()) {
11012cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                    return true;
11022cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                }
11032cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
11042cb36637f41418e4037023a47915dd64a30ab869Tadashi G. Takaoka                if (keyboardView != null && keyboardView.handleBack()) {
11056e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani                    return true;
11066e3d427c320e89c6155cbcf4045e399b9b55f48fAmith Yamasani                }
1107e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            }
1108e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            break;
1109923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1110923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return super.onKeyDown(keyCode, event);
1111923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1112923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1113923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
1114923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean onKeyUp(int keyCode, KeyEvent event) {
1115923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (keyCode) {
1116e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_DOWN:
1117e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_UP:
1118e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_LEFT:
1119e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        case KeyEvent.KEYCODE_DPAD_RIGHT:
1120e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            // Enable shift key and DPAD to do selections
1121e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            if (mKeyboardSwitcher.isInputViewShown()
1122e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                    && mKeyboardSwitcher.isShiftedOrShiftLocked()) {
1123e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                KeyEvent newEvent = new KeyEvent(event.getDownTime(), event.getEventTime(),
1124e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                        event.getAction(), event.getKeyCode(), event.getRepeatCount(),
1125e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                        event.getDeviceId(), event.getScanCode(),
1126e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                        KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
11279351550dc6af7859e5280e16144c9386a37b976dKen Wakasa                final InputConnection ic = getCurrentInputConnection();
1128e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                if (ic != null)
1129e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                    ic.sendKeyEvent(newEvent);
1130e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                return true;
1131e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            }
1132e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            break;
1133923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1134923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return super.onKeyUp(keyCode, event);
1135923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1136923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
11379351550dc6af7859e5280e16144c9386a37b976dKen Wakasa    public void commitTyped(final InputConnection ic) {
11388558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        if (!mHasUncommittedTypedChars) return;
11398558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        mHasUncommittedTypedChars = false;
11403651220327c051d8017045aa5e8919461507b3f8Jean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
11413651220327c051d8017045aa5e8919461507b3f8Jean Chalard        if (typedWord.length() > 0) {
11429351550dc6af7859e5280e16144c9386a37b976dKen Wakasa            if (ic != null) {
11433651220327c051d8017045aa5e8919461507b3f8Jean Chalard                ic.commitText(typedWord, 1);
1144923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
11453651220327c051d8017045aa5e8919461507b3f8Jean Chalard            addToUserUnigramAndBigramDictionaries(typedWord,
1146f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                    UserUnigramDictionary.FREQUENCY_FOR_TYPED);
1147923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
11488558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        updateSuggestions();
1149923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1150923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1151b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public boolean getCurrentAutoCapsState() {
11529351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
11531c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        EditorInfo ei = getCurrentInputEditorInfo();
115417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (mSettingsValues.mAutoCap && ic != null && ei != null
115517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                && ei.inputType != InputType.TYPE_NULL) {
1156b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return ic.getCursorCapsMode(ei.inputType) != 0;
11571c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani        }
1158b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
11591c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani    }
11601c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani
1161b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    // "ic" may be null
1162b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) {
1163b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        if (null == ic) return;
1164923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
1165863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
1166923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastTwo != null && lastTwo.length() == 2
1167863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard                && lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
1168923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.deleteSurroundingText(2, 0);
1169923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.commitText(lastTwo.charAt(1) + " ", 1);
1170b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
11714ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa        }
11724ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa    }
11734ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1174120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
1175120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (mCorrectionMode == Suggest.CORRECTION_NONE) return false;
1176120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (ic == null) return false;
11772b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard        final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
1178923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (lastThree != null && lastThree.length() == 3
11799351550dc6af7859e5280e16144c9386a37b976dKen Wakasa                && Utils.canBeFollowedByPeriod(lastThree.charAt(0))
1180571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastThree.charAt(1) == Keyboard.CODE_SPACE
1181fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && lastThree.charAt(2) == Keyboard.CODE_SPACE
1182fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka                && mHandler.isAcceptingDoubleSpaces()) {
1183fc20f2198d6703d65eab22f40f9b8aa2c8e3a0eaTadashi G. Takaoka            mHandler.cancelDoubleSpacesTimer();
1184923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.deleteSurroundingText(2, 0);
1185923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.commitText(". ", 1);
1186b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
1187120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            return true;
1188923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1189120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return false;
1190923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1191a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1192b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    // "ic" must not be null
11938fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static void maybeRemovePreviousPeriod(final InputConnection ic, CharSequence text) {
119441a519729505a877844f2c57a33509c302dddbceKen Wakasa        // When the text's first character is '.', remove the previous period
119541a519729505a877844f2c57a33509c302dddbceKen Wakasa        // if there is one.
1196b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
11974ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa        if (lastOne != null && lastOne.length() == 1
1198571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastOne.charAt(0) == Keyboard.CODE_PERIOD
1199571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && text.charAt(0) == Keyboard.CODE_PERIOD) {
120041a519729505a877844f2c57a33509c302dddbceKen Wakasa            ic.deleteSurroundingText(1, 0);
120141a519729505a877844f2c57a33509c302dddbceKen Wakasa        }
120241a519729505a877844f2c57a33509c302dddbceKen Wakasa    }
120341a519729505a877844f2c57a33509c302dddbceKen Wakasa
1204b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard    // "ic" may be null
12058fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) {
12069a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (ic == null) return;
1207b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
12089a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (lastOne != null && lastOne.length() == 1
1209571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka                && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
12109a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            ic.deleteSurroundingText(1, 0);
12119a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        }
12129a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa    }
12139a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa
1214c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
1215923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean addWordToDictionary(String word) {
1216923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mUserDictionary.addWord(word, 128);
12176558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // Suggestion strip should be updated after the operation of adding word to the
12186558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        // user dictionary
1219d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        mHandler.postUpdateSuggestions();
1220923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return true;
1221923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1222923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
12238fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean isAlphabet(int code) {
12248fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka        return Character.isLetter(code);
1225923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1226a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
1227e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    private void onSettingsKeyPressed() {
1228cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return;
1229d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka        if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
1230d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka            showSubtypeSelectorAndSettings();
123111b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa        } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, false /* exclude aux subtypes */)) {
1232d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka            showOptionsMenu();
1233d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka        } else {
1234d794c6f0788a65a4ec623de3f8f05122621d665fTadashi G. Takaoka            launchSettings();
12359a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
12369a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12379a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1238cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    // Virtual codes representing custom requests.  These are used in onCustomRequest() below.
1239cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
1240d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka    public static final int CODE_HAPTIC_AND_AUDIO_FEEDBACK = 2;
1241cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa
1242cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    @Override
1243cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public boolean onCustomRequest(int requestCode) {
1244cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        if (isShowingOptionDialog()) return false;
1245cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        switch (requestCode) {
1246cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        case CODE_SHOW_INPUT_METHOD_PICKER:
124711b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa            if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, true /* include aux subtypes */)) {
124879efbed76f638be298493107fa2d0cd1b5eb529esatok                mImm.showInputMethodPicker();
1249cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa                return true;
12509a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok            }
1251cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa            return false;
1252d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka        case CODE_HAPTIC_AND_AUDIO_FEEDBACK:
1253d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka            hapticAndAudioFeedback(Keyboard.CODE_UNSPECIFIED);
1254d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka            return true;
12559a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        }
1256cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        return false;
12579a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12589a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
12599a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    private boolean isShowingOptionDialog() {
12609a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok        return mOptionsDialog != null && mOptionsDialog.isShowing();
12619a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok    }
12629a6a4d3d5f1ee52b56d349c002aa61c264d010d3satok
1263120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void insertPunctuationFromSuggestionStrip(final InputConnection ic, final int code) {
1264120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : null;
1265120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final int toLeft = TextUtils.isEmpty(beforeText) ? 0 : beforeText.charAt(0);
1266120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final boolean shouldRegisterSwapPunctuation;
1267120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // If we have a space left of the cursor and it's a weak or a magic space, then we should
1268120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // swap it, and override the space state with SPACESTATE_SWAP_PUNCTUATION.
1269120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // To swap it, we fool handleSeparator to think the previous space state was a
1270120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // magic space.
1271120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (Keyboard.CODE_SPACE == toLeft && mSpaceState == SPACE_STATE_WEAK) {
1272120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_MAGIC;
1273120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            shouldRegisterSwapPunctuation = true;
1274120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        } else {
1275120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            shouldRegisterSwapPunctuation = false;
1276120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        }
1277120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        onCodeInput(code, new int[] { code },
1278120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
1279120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
1280120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (shouldRegisterSwapPunctuation) {
1281120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
1282120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        }
1283126698fdd256a2e3734634d3b923cabd800064baJean Chalard    }
1284126698fdd256a2e3734634d3b923cabd800064baJean Chalard
12855f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    // Implementation of {@link KeyboardActionListener}.
12865a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
12878aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
1288175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final long when = SystemClock.uptimeMillis();
1289571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
1290923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mDeleteCount = 0;
1291923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1292923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        mLastKeyTime = when;
1293175acb8205fcc2a91de4059e8a12af49484af784Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
1294b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
1295120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // The space state depends only on the last character pressed and its own previous
1296120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // state. Here, we revert the space state to neutral if the key is actually modifying
1297120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // the input contents (any non-shift key), which is what we should do for
1298120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // all inputs that do not result in a special state. Each character handling is then
1299120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // free to override the state as they see fit.
1300120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final int spaceState = mSpaceState;
1301ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1302ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        // TODO: Consolidate the double space timer, mLastKeyTime, and the space state.
1303ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        if (primaryCode != Keyboard.CODE_SPACE) {
1304ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa            mHandler.cancelDoubleSpacesTimer();
1305ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa        }
1306ed631627cb2337b025f0e80df6fecf1e6e511dc4Ken Wakasa
1307923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        switch (primaryCode) {
1308571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_DELETE:
1309120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
1310120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            handleBackspace(spaceState);
13114189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            mDeleteCount++;
13124733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
13134189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            LatinImeLogger.logOnDelete();
13144189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1315571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_SHIFT:
13164189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            // Shift key is handled in onPress() when device has distinct multi-touch panel.
13175619d7658459d13723b6d7ad8969b249adf9e717Tadashi G. Takaoka            if (!distinctMultiTouch) {
1318b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                switcher.toggleShift();
13195619d7658459d13723b6d7ad8969b249adf9e717Tadashi G. Takaoka            }
13204189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1321e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
13224189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            // Symbol key is handled in onPress() when device has distinct multi-touch panel.
13235619d7658459d13723b6d7ad8969b249adf9e717Tadashi G. Takaoka            if (!distinctMultiTouch) {
132496c56cb577ff6b76e2c182f45402842e828c3644Tadashi G. Takaoka                switcher.toggleAlphabetAndSymbols();
13255619d7658459d13723b6d7ad8969b249adf9e717Tadashi G. Takaoka            }
13264189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1327e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        case Keyboard.CODE_SETTINGS:
132893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            onSettingsKeyPressed();
13294189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1330571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_CAPSLOCK:
1331b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            switcher.toggleCapsLock();
1332d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka            hapticAndAudioFeedback(primaryCode);
13334189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1334d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka        case Keyboard.CODE_SHORTCUT:
133593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka            mSubtypeSwitcher.switchToShortcutIME();
13364189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
1337571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        case Keyboard.CODE_TAB:
133845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            handleTab();
13394733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // There are two cases for tab. Either we send a "next" event, that may change the
13404733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // focus but will never move the cursor. Or, we send a real tab keycode, which some
13414733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // applications may accept or ignore, and we don't know whether this will move the
13424733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // cursor or not. So actually, we don't really know.
13434733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // So to go with the safer option, we'd rather behave as if the user moved the
13444733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // cursor when they didn't than the opposite. We also expect that most applications
13454733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // will actually use tab only for focus movement.
13464733609947c0ec74e460bd714fffca0518ade93aJean Chalard            // To sum it up: do not update mExpectingUpdateSelection here.
13474189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            break;
13484189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka        default:
1349120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mSpaceState = SPACE_STATE_NONE;
135017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (mSettingsValues.isWordSeparator(primaryCode)) {
1351120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                handleSeparator(primaryCode, x, y, spaceState);
13524189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            } else {
1353120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                handleCharacter(primaryCode, keyCodes, x, y, spaceState);
13544189eb23082fcd4bf8cfb2085d18e226e0e7ce13Tadashi G. Takaoka            }
13554733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
13564733609947c0ec74e460bd714fffca0518ade93aJean Chalard            break;
1357923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1358eef6238f94b5046054d9ae9c06f775362893c0eeTadashi G. Takaoka        switcher.onCodeInput(primaryCode);
1359dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        // Reset after any single keystroke
1360dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = null;
1361923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1362a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
13635a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13648aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onTextInput(CharSequence text) {
1365b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.commitVoiceInput();
13669351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
1367923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic == null) return;
1368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.beginBatchEdit();
136955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka        commitTyped(ic);
13709351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        maybeRemovePreviousPeriod(ic, text);
1371923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.commitText(text, 1);
1372923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        ic.endBatchEdit();
1373b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
1374eef6238f94b5046054d9ae9c06f775362893c0eeTadashi G. Takaoka        mKeyboardSwitcher.onCodeInput(Keyboard.CODE_DUMMY);
1375120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_NONE;
13765c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        mWordSavedForAutoCorrectCancellation = null;
1377dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        mEnteredText = text;
1378923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1379923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
13805a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
13818aa3f5a3ad6095a3355841ce30bce4877319d0a0Tadashi G. Takaoka    public void onCancelInput() {
138283e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka        // User released a finger outside any key
13835f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        mKeyboardSwitcher.onCancelInput();
138483e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka    }
138583e63ace2a1bd5b3c27d26d914456c2b0def17c5Tadashi G. Takaoka
1386120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleBackspace(final int spaceState) {
1387b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        if (mVoiceProxy.logAndRevertVoiceInput()) return;
1388504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
1389504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka        if (ic == null) return;
1390979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        ic.beginBatchEdit();
1391a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard        handleBackspaceWhileInBatchEdit(spaceState, ic);
1392a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard        ic.endBatchEdit();
1393a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    }
1394979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1395a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    // "ic" may not be null.
1396a2a85d45e0618dc0dd7d224d5a0e7394d9003dc5Jean Chalard    private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
1397b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleBackspace();
13984f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
13992245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard        // In many cases, we may have to put the keyboard in auto-shift state again.
14002245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard        mHandler.postUpdateShiftKeyState();
14012245c3b5b3691928b08fd6accf8d4a21fb35e26bJean Chalard
14025c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
14035c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // Cancel multi-character input: remove the text we just entered.
14045c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // This is triggered on backspace after a key that inputs multiple characters,
14055c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // like the smiley key or the .com key.
14065c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            ic.deleteSurroundingText(mEnteredText.length(), 0);
14075c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
14085c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // In addition we know that spaceState is false, and that we should not be
14095c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            // reverting any autocorrect at this point. So we can safely return.
14105c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard            return;
14115c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        }
14125c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard
1413f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        if (mHasUncommittedTypedChars) {
14143651220327c051d8017045aa5e8919461507b3f8Jean Chalard            final int length = mWordComposer.size();
1415923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (length > 0) {
14169318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                mWordComposer.deleteLast();
141777d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
14183651220327c051d8017045aa5e8919461507b3f8Jean Chalard                if (mWordComposer.size() == 0) {
1419f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard                    mHasUncommittedTypedChars = false;
14202939063947982b77a942a5ee9882944b8b48dd69Jean Chalard                    // Remaining size equals zero means we just erased the last character of the
14212939063947982b77a942a5ee9882944b8b48dd69Jean Chalard                    // word, so we can show bigrams.
1422cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                    mHandler.postUpdateBigramPredictions();
142389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                } else {
142489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    // length > 1, so we still have letters to deduce a suggestion from.
142589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                    mHandler.postUpdateSuggestions();
142689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                }
1427923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1428923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                ic.deleteSurroundingText(1, 0);
1429923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1430890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        } else {
1431d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            if (null != mWordSavedForAutoCorrectCancellation) {
1432d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                Utils.Stats.onAutoCorrectionCancellation();
1433d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                cancelAutoCorrect(ic);
1434d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                mWordSavedForAutoCorrectCancellation = null;
1435120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                return;
1436d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            } else {
1437d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                mWordSavedForAutoCorrectCancellation = null;
1438120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1439d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard
1440d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            if (SPACE_STATE_DOUBLE == spaceState) {
1441d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                if (revertDoubleSpace(ic)) {
1442d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // No need to reset mSpaceState, it has already be done (that's why we
1443d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // receive it as a parameter)
1444d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1445d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
1446d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard            } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
1447d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                if (revertSwapPunctuation(ic)) {
1448d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    // Likewise
1449d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                    return;
1450d2c69c7d1231c238afc9196dcb9cc2359de520cfJean Chalard                }
14514733609947c0ec74e460bd714fffca0518ade93aJean Chalard            }
1452504e8d5171edae36ec464a5e0c72cee22bb9ac4dTadashi G. Takaoka
1453913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
14546558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // Go back to the suggestion mode if the user canceled the
145555b10796522b871c1e04d6f2254fdff5dc7aced4Ken Wakasa                // "Touch again to save".
14566558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // NOTE: In gerenal, we don't revert the word when backspacing
14576558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // from a manual suggestion pick.  We deliberately chose a
14586558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // different behavior only in the case of picking the first
14596558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // suggestion (typed word).  It's intentional to have made this
14606558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                // inconsistent with backspacing after selecting other suggestions.
1461890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                restartSuggestionsOnManuallyPickedTypedWord(ic);
14626558253160e2039c87f424bd814f402ecd31de3bKen Wakasa            } else {
14636b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                ic.deleteSurroundingText(1, 0);
14646558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                if (mDeleteCount > DELETE_ACCELERATE_AT) {
14656b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                    ic.deleteSurroundingText(1, 0);
14666558253160e2039c87f424bd814f402ecd31de3bKen Wakasa                }
14676b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
1468923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1469923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1470923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1471923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
147245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka    private void handleTab() {
147345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final int imeOptions = getCurrentInputEditorInfo().imeOptions;
1474b2707856aba4fc9b063f26305f1fb603b19c1701satok        if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
1475b2707856aba4fc9b063f26305f1fb603b19c1701satok                && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) {
147645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
147745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            return;
147845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        }
147945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka
148045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
148145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (ic == null)
148245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            return;
148345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka
148445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        // True if keyboard is in either chording shift or manual temporary upper case mode.
148545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final boolean isManualTemporaryUpperCase = mKeyboardSwitcher.isManualTemporaryUpperCase();
1486b2707856aba4fc9b063f26305f1fb603b19c1701satok        if (EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
148745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                && !isManualTemporaryUpperCase) {
1488b2707856aba4fc9b063f26305f1fb603b19c1701satok            EditorInfoCompatUtils.performEditorActionNext(ic);
1489b2707856aba4fc9b063f26305f1fb603b19c1701satok        } else if (EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)
149045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                && isManualTemporaryUpperCase) {
1491b2707856aba4fc9b063f26305f1fb603b19c1701satok            EditorInfoCompatUtils.performEditorActionPrevious(ic);
149245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        }
149345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka    }
149445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka
1495120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleCharacter(final int primaryCode, final int[] keyCodes, final int x,
1496120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int y, final int spaceState) {
1497b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleCharacter();
1498b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard        final InputConnection ic = getCurrentInputConnection();
1499dafa7a8e15447544842975047f831883e67700c5Jean Chalard        if (null != ic) ic.beginBatchEdit();
1500dafa7a8e15447544842975047f831883e67700c5Jean Chalard        // TODO: if ic is null, does it make any sense to call this?
1501dafa7a8e15447544842975047f831883e67700c5Jean Chalard        handleCharacterWhileInBatchEdit(primaryCode, keyCodes, x, y, spaceState, ic);
1502dafa7a8e15447544842975047f831883e67700c5Jean Chalard        if (null != ic) ic.endBatchEdit();
1503dafa7a8e15447544842975047f831883e67700c5Jean Chalard    }
1504dafa7a8e15447544842975047f831883e67700c5Jean Chalard
1505dafa7a8e15447544842975047f831883e67700c5Jean Chalard    // "ic" may be null without this crashing, but the behavior will be really strange
1506dafa7a8e15447544842975047f831883e67700c5Jean Chalard    private void handleCharacterWhileInBatchEdit(final int primaryCode, final int[] keyCodes,
1507dafa7a8e15447544842975047f831883e67700c5Jean Chalard            final int x, final int y, final int spaceState, final InputConnection ic) {
1508120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (SPACE_STATE_MAGIC == spaceState
1509126698fdd256a2e3734634d3b923cabd800064baJean Chalard                && mSettingsValues.isMagicSpaceStripper(primaryCode)) {
1510dafa7a8e15447544842975047f831883e67700c5Jean Chalard            if (null != ic) removeTrailingSpaceWhileInBatchEdit(ic);
15110730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard        }
15120730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard
1513e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        int code = primaryCode;
15143889462439357fd76c0b82dfd52e1ca6e0bafd2dKen Wakasa        if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code))
15153889462439357fd76c0b82dfd52e1ca6e0bafd2dKen Wakasa                && isSuggestionsRequested() && !isCursorTouchingWord()) {
1516f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard            if (!mHasUncommittedTypedChars) {
15176d1cbbc2ffb9d7046189174ec68b5b72bbc154b3Jean Chalard                // Reset entirely the composing state anyway, then start composing a new word unless
15186d1cbbc2ffb9d7046189174ec68b5b72bbc154b3Jean Chalard                // the character is a single quote.
15196d1cbbc2ffb9d7046189174ec68b5b72bbc154b3Jean Chalard                mHasUncommittedTypedChars = (Keyboard.CODE_SINGLE_QUOTE != code);
15209318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                mWordComposer.reset();
15217e99a28dfcc14f5b19220442db972ca8d786b4feKen Wakasa                clearSuggestions();
1522604d80c67185954d4691ac775be59c499eee3b1csatok                mComposingStateManager.onFinishComposingText();
1523923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1524923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
152535f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
1526f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (switcher.isShiftedOrShiftLocked()) {
1527eef539c3f2b60af74a3ce05ac68288e7f86709aeAmith Yamasani            if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
1528eef539c3f2b60af74a3ce05ac68288e7f86709aeAmith Yamasani                    || keyCodes[0] > Character.MAX_CODE_POINT) {
1529eef539c3f2b60af74a3ce05ac68288e7f86709aeAmith Yamasani                return;
1530eef539c3f2b60af74a3ce05ac68288e7f86709aeAmith Yamasani            }
1531e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            code = keyCodes[0];
1532e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            if (switcher.isAlphabetMode() && Character.isLowerCase(code)) {
153335f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                // In some locales, such as Turkish, Character.toUpperCase() may return a wrong
153435f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                // character because it doesn't take care of locale.
153535f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                final String upperCaseString = new String(new int[] {code}, 0, 1)
153635f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                        .toUpperCase(mSubtypeSwitcher.getInputLocale());
153735f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                if (upperCaseString.codePointCount(0, upperCaseString.length()) == 1) {
153835f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                    code = upperCaseString.codePointAt(0);
153912a4e08a6440c95f29dc04efe83515a4ed045487Tadashi G. Takaoka                } else {
154012a4e08a6440c95f29dc04efe83515a4ed045487Tadashi G. Takaoka                    // Some keys, such as [eszett], have upper case as multi-characters.
154135f20916e5348d7fa485ba8eb0a5cf2e67f4f354Tadashi G. Takaoka                    onTextInput(upperCaseString);
154212a4e08a6440c95f29dc04efe83515a4ed045487Tadashi G. Takaoka                    return;
154312a4e08a6440c95f29dc04efe83515a4ed045487Tadashi G. Takaoka                }
1544979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
1545923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1546f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        if (mHasUncommittedTypedChars) {
15479318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa            mWordComposer.add(code, keyCodes, x, y);
1548923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (ic != null) {
15491c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani                // If it's the first letter, make note of auto-caps state
15509318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                if (mWordComposer.size() == 1) {
15519318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                    mWordComposer.setAutoCapitalized(getCurrentAutoCapsState());
1552604d80c67185954d4691ac775be59c499eee3b1csatok                    mComposingStateManager.onStartComposingText();
15531c551251106e506c70fad7ba0cb8b1e2a7dff3a9Amith Yamasani                }
155477d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
1555923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1556d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
1557923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1558e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            sendKeyChar((char)code);
1559923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1560120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (SPACE_STATE_MAGIC == spaceState
1561126698fdd256a2e3734634d3b923cabd800064baJean Chalard                && mSettingsValues.isMagicSpaceSwapper(primaryCode)) {
1562b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard            if (null != ic) swapSwapperAndSpaceWhileInBatchEdit(ic);
1563863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        }
1564863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
1565b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        switcher.updateShiftState();
1566406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        if (mSettingsValues.isWordSeparator(code)) {
1567406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard            Utils.Stats.onSeparator((char)code, x, y);
1568406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        } else {
1569406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard            Utils.Stats.onNonSeparator((char)code, x, y);
1570406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        }
1571923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1572923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1573120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    private void handleSeparator(final int primaryCode, final int x, final int y,
1574120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            final int spaceState) {
1575b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleSeparator();
1576604d80c67185954d4691ac775be59c499eee3b1csatok        mComposingStateManager.onFinishComposingText();
15774f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
157855b10796522b871c1e04d6f2254fdff5dc7aced4Ken Wakasa        // Should dismiss the "Touch again to save" message when handling separator
1579913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
1580cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            mHandler.cancelUpdateBigramPredictions();
1581d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.postUpdateSuggestions();
15826558253160e2039c87f424bd814f402ecd31de3bKen Wakasa        }
15836558253160e2039c87f424bd814f402ecd31de3bKen Wakasa
1584923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        boolean pickedDefault = false;
1585923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Handle separator
15861b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        final InputConnection ic = getCurrentInputConnection();
1587923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
1588923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.beginBatchEdit();
1589923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
15905c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        // Reset the saved word in all cases. If this separator causes an autocorrection,
15915c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        // it will overwrite this null with the actual word we need to save.
15925c3ff4c9c86e073d994ad874abe9dae7a665d5c4Jean Chalard        mWordSavedForAutoCorrectCancellation = null;
1593f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        if (mHasUncommittedTypedChars) {
1594923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // In certain languages where single quote is a separator, it's better
1595a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker            // not to auto correct, but accept the typed word. For instance,
1596923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // in Italian dov' should not be expanded to dove' because the elision
1597923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            // requires the last vowel to be removed.
1598f50aa193377492e9c4afb3cc6e7f3448ab5a97a4Jean Chalard            final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
1599c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa                    && !mInputTypeNoAutoCorrect;
160017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
1601b5d17e52fcda39d540822e615bfb7ed5074e0034satok                pickedDefault = pickDefaultSuggestion(primaryCode);
1602923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1603923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                commitTyped(ic);
1604923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1605923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
16064ca6d9dc09cf7635c9283e42f155957021b9f9acKen Wakasa
1607120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final boolean swapMagicSpace;
16082c77216e22e5f9d1d2191ce577f2707bea5b815aJean Chalard        if (Keyboard.CODE_ENTER == primaryCode && (SPACE_STATE_MAGIC == spaceState
16092c77216e22e5f9d1d2191ce577f2707bea5b815aJean Chalard                || SPACE_STATE_SWAP_PUNCTUATION == spaceState)) {
16102c77216e22e5f9d1d2191ce577f2707bea5b815aJean Chalard            removeTrailingSpaceWhileInBatchEdit(ic);
16112c77216e22e5f9d1d2191ce577f2707bea5b815aJean Chalard            swapMagicSpace = false;
16122c77216e22e5f9d1d2191ce577f2707bea5b815aJean Chalard        } else if (SPACE_STATE_MAGIC == spaceState) {
161317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard            if (mSettingsValues.isMagicSpaceSwapper(primaryCode)) {
1614120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                swapMagicSpace = true;
1615863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            } else {
1616120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                swapMagicSpace = false;
1617b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard                if (mSettingsValues.isMagicSpaceStripper(primaryCode)) {
1618b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard                    removeTrailingSpaceWhileInBatchEdit(ic);
1619b715299125e8fbaaa941d994217baf823e6c4013Jean Chalard                }
1620863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            }
16210730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard        } else {
1622120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            swapMagicSpace = false;
1623863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard        }
1624863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard
1625120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        sendKeyChar((char)primaryCode);
162689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
162789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (Keyboard.CODE_SPACE == primaryCode) {
1628120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            if (isSuggestionsRequested()) {
1629120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                if (maybeDoubleSpaceWhileInBatchEdit(ic)) {
1630120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_DOUBLE;
1631120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                } else if (!isShowingPunctuationList()) {
1632120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                    mSpaceState = SPACE_STATE_WEAK;
1633120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                }
1634126698fdd256a2e3734634d3b923cabd800064baJean Chalard            }
1635120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1636120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            mHandler.startDoubleSpacesTimer();
163789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            if (!isCursorTouchingWord()) {
163889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard                mHandler.cancelUpdateSuggestions();
1639cb3320179d39a7983874697a0aa428b127675c9dJean Chalard                mHandler.postUpdateBigramPredictions();
164089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            }
164189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
1642120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            if (swapMagicSpace) {
1643120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                swapSwapperAndSpaceWhileInBatchEdit(ic);
1644120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                mSpaceState = SPACE_STATE_MAGIC;
1645120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1646120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
164789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Set punctuation right away. onUpdateSelection will fire but tests whether it is
164889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // already displayed or not, so it's okay.
164955b9d333c5d260cb5da3f6a2d872bda8c03478d7Tadashi G. Takaoka            setPunctuationSuggestions();
1650923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1651120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1652406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)primaryCode, x, y);
1653120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
1654120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        if (pickedDefault) {
1655117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
1656117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            final String typedWord = mWordComposer.getTypedWord();
1657117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            if (TextUtils.isEmpty(typedWord)) {
1658117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                throw new RuntimeException("We have non-committed chars but the typed word "
1659117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                        + "is empty? Impossible! I must commit suicide.");
1660117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            }
1661117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            if (!typedWord.equals(autoCorrection)) {
1662117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                // TODO: if the commitCorrection method is not supported by the platform
1663117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                // this will do nothing and the correction will not be committed at all. What
1664117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                // happens on Froyo/Gingerbread, where this API is not present?
1665120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard                InputConnectionCompatUtils.commitCorrection(
1666117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                        ic, mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection);
1667120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            }
1668120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        }
1669b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
1670923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
1671923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            ic.endBatchEdit();
1672923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1673923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1674466741d8a78965b8509bf527344f289e50873092Mike LeBeau
167577d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    private CharSequence getTextWithUnderline(final CharSequence text) {
167677d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard        return mComposingStateManager.isAutoCorrectionIndicatorOn()
167777d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
167877d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                : mWordComposer.getTypedWord();
167977d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard    }
168077d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard
1681923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void handleClose() {
1682923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        commitTyped(getCurrentInputConnection());
1683b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok        mVoiceProxy.handleClose();
1684923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        requestHideSelf(0);
1685c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
16861679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        if (inputView != null)
16871679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka            inputView.closing();
1688923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1689923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
16907a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isSuggestionsRequested() {
1691c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka        return mIsSettingsSuggestionStripOn
1692c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka                && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
1693923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1694a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
16957a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingPunctuationList() {
1696cb8cb95d0afd340de9f1a0e15948f0068d2450bcKen Wakasa        if (mSuggestionsView == null) return false;
1697913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions();
16987599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
16997599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
17007a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isShowingSuggestionsStrip() {
17017599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
17027599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
170338f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka                        && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
17047599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
17057599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
1706913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka    public boolean isSuggestionsStripVisible() {
1707913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView == null)
1708b5e00d5841b946de8970875231456228ae0eb6b1Ken Wakasa            return false;
1709d0c5f9395a1b94e8425982e353d090f972dc44f0Jean Chalard        if (mSuggestionsView.isShowingAddToDictionaryHint())
17109fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
17119fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        if (!isShowingSuggestionsStrip())
17129fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return false;
17139fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        if (mApplicationSpecifiedCompletionOn)
17149fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka            return true;
17159fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        return isSuggestionsRequested();
1716923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1717923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1718409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void switchToKeyboardView() {
1719060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (DEBUG) {
1720060efb6d82af1f896d90cb845c8ae07c726f85e1satok            Log.d(TAG, "Switch to keyboard view.");
1721060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
1722c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        View v = mKeyboardSwitcher.getKeyboardView();
1723060efb6d82af1f896d90cb845c8ae07c726f85e1satok        if (v != null) {
1724060efb6d82af1f896d90cb845c8ae07c726f85e1satok            // Confirms that the keyboard view doesn't have parent view.
1725060efb6d82af1f896d90cb845c8ae07c726f85e1satok            ViewParent p = v.getParent();
1726060efb6d82af1f896d90cb845c8ae07c726f85e1satok            if (p != null && p instanceof ViewGroup) {
1727060efb6d82af1f896d90cb845c8ae07c726f85e1satok                ((ViewGroup) p).removeView(v);
17285a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            }
1729060efb6d82af1f896d90cb845c8ae07c726f85e1satok            setInputView(v);
1730060efb6d82af1f896d90cb845c8ae07c726f85e1satok        }
1731913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1732060efb6d82af1f896d90cb845c8ae07c726f85e1satok        updateInputViewShown();
1733060efb6d82af1f896d90cb845c8ae07c726f85e1satok        mHandler.postUpdateSuggestions();
1734466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1735466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1736409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void clearSuggestions() {
17377e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        setSuggestions(SuggestedWords.EMPTY);
1738466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1739466741d8a78965b8509bf527344f289e50873092Mike LeBeau
17407e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka    public void setSuggestions(SuggestedWords words) {
1741913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        if (mSuggestionsView != null) {
1742913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            mSuggestionsView.setSuggestions(words);
17435e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            mKeyboardSwitcher.onAutoCorrectionStateChanged(
17445e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                    words.hasWordAboveAutoCorrectionScoreThreshold());
1745466741d8a78965b8509bf527344f289e50873092Mike LeBeau        }
1746ec780e2868962bf17f0dfd35d36895f543bde40asatok
1747ec780e2868962bf17f0dfd35d36895f543bde40asatok        // Put a blue underline to a word in TextView which will be auto-corrected.
1748ec780e2868962bf17f0dfd35d36895f543bde40asatok        final InputConnection ic = getCurrentInputConnection();
1749604d80c67185954d4691ac775be59c499eee3b1csatok        if (ic != null) {
1750604d80c67185954d4691ac775be59c499eee3b1csatok            final boolean oldAutoCorrectionIndicator =
1751604d80c67185954d4691ac775be59c499eee3b1csatok                    mComposingStateManager.isAutoCorrectionIndicatorOn();
1752604d80c67185954d4691ac775be59c499eee3b1csatok            final boolean newAutoCorrectionIndicator = Utils.willAutoCorrect(words);
1753604d80c67185954d4691ac775be59c499eee3b1csatok            if (oldAutoCorrectionIndicator != newAutoCorrectionIndicator) {
175477d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
175577d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                if (DEBUG) {
1756fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok                    Log.d(TAG, "Flip the indicator. " + oldAutoCorrectionIndicator
1757fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok                            + " -> " + newAutoCorrectionIndicator);
175877d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                    if (newAutoCorrectionIndicator
175977d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                            != mComposingStateManager.isAutoCorrectionIndicatorOn()) {
176077d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                        throw new RuntimeException("Couldn't flip the indicator! We are not "
176177d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                                + "composing a word right now.");
176277d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                    }
1763fe2d90798ea409ee39d6f63942eb01bb7eed98e3satok                }
176477d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                final CharSequence textWithUnderline =
176577d59b0691b8a9bd6cb336f07c175b88db53bbc0Jean Chalard                        getTextWithUnderline(mWordComposer.getTypedWord());
1766604d80c67185954d4691ac775be59c499eee3b1csatok                if (!TextUtils.isEmpty(textWithUnderline)) {
1767604d80c67185954d4691ac775be59c499eee3b1csatok                    ic.setComposingText(textWithUnderline, 1);
1768604d80c67185954d4691ac775be59c499eee3b1csatok                }
1769ec780e2868962bf17f0dfd35d36895f543bde40asatok            }
1770ec780e2868962bf17f0dfd35d36895f543bde40asatok        }
1771466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
1772466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1773409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void updateSuggestions() {
1774923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Check if we have a suggestion engine attached.
17759fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        if ((mSuggest == null || !isSuggestionsRequested())
1776b86905943c0f1cadb2b3df9f2a58e7af84f6b27fsatok                && !mVoiceProxy.isVoiceInputHighlighted()) {
1777923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1778923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1779466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1780cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateSuggestions();
1781cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard        mHandler.cancelUpdateBigramPredictions();
1782cd95a365586b2b5f9a3639b72a2befdac7ada8a4Jean Chalard
1783f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        if (!mHasUncommittedTypedChars) {
1784ca26f20fa4903de46e374babbfba8c8a1a5cac93satok            setPunctuationSuggestions();
1785923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1786923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1787979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
17889f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        // TODO: May need a better way of retrieving previous word
178940f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        final InputConnection ic = getCurrentInputConnection();
179040f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        final CharSequence prevWord;
179140f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        if (null == ic) {
179240f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            prevWord = null;
179340f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        } else {
179440f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
179540f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        }
179640f7efc172928bdd6048e91421a766abe5b22996Jean Chalard        // getSuggestedWordBuilder handles gracefully a null value of prevWord
1797f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(mWordComposer,
17984606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard                prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode);
1799923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
18008558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
1801f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        final CharSequence typedWord = mWordComposer.getTypedWord();
1802bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        // Here, we want to promote a whitelisted word if exists.
18035f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
18045f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // but still autocorrected from - in the case the whitelist only capitalizes the word.
18055f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // The whitelist should be case-insensitive, so it's not possible to be consistent with
18065f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // a boolean flag. Right now this is handled with a slight hack in
18075f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
1808f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        final int quotesCount = mWordComposer.trailingSingleQuotesCount();
18095f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard        final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected(
1810c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                mSuggest.getUnigramDictionaries(),
1811c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // If the typed string ends with a single quote, for dictionary lookup purposes
1812c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // we behave as if the single quote was not here. Here, we are looking up the
1813c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // typed string in the dictionary (to avoid autocorrecting from an existing
1814c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // word, so for consistency this lookup should be made WITHOUT the trailing
1815c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                // single quote.
1816117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard                quotesCount > 0
1817117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard                        ? typedWord.subSequence(0, typedWord.length() - quotesCount) : typedWord,
1818c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard                preferCapitalization());
1819979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (mCorrectionMode == Suggest.CORRECTION_FULL
1820979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
18215f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard            autoCorrectionAvailable |= (!allowsToBeAutoCorrected);
1822923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
18234a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani        // Don't auto-correct words with multiple capital letter
1824f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        autoCorrectionAvailable &= !mWordComposer.isMostlyCaps();
1825979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1826a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // Basically, we update the suggestion strip only when suggestion count > 1.  However,
1827a776b7fc4a0d2c7f05eaed107e9d5db3b575b9d0Ken Wakasa        // there is an exception: We update the suggestion strip whenever typed word's length
1828f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // is 1 or typed word is found in dictionary, regardless of suggestion count.  Actually,
1829f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
1830f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // need to clear the previous state when the user starts typing a word (i.e. typed word's
1831f3df63a93a8f623e2aca5895ee749bd297b58d12Tadashi G. Takaoka        // length == 1).
1832fe1a6d961cf039357f061482461e4d2e951ad346satok        if (typedWord != null) {
18335f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard            if (builder.size() > 1 || typedWord.length() == 1 || (!allowsToBeAutoCorrected)
1834913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                    || mSuggestionsView.isShowingAddToDictionaryHint()) {
18355f41b705fc95b21c8edd6226bb50c0fa78a39261Jean Chalard                builder.setTypedWordValid(!allowsToBeAutoCorrected).setHasMinimalSuggestion(
18368558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa                        autoCorrectionAvailable);
1837fe1a6d961cf039357f061482461e4d2e951ad346satok            } else {
1838074cda4d266b5d034d4348961c9183e32b16af5asatok                SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
1839074cda4d266b5d034d4348961c9183e32b16af5asatok                if (previousSuggestions == mSettingsValues.mSuggestPuncList) {
1840074cda4d266b5d034d4348961c9183e32b16af5asatok                    if (builder.size() == 0) {
1841074cda4d266b5d034d4348961c9183e32b16af5asatok                        return;
1842074cda4d266b5d034d4348961c9183e32b16af5asatok                    }
1843074cda4d266b5d034d4348961c9183e32b16af5asatok                    previousSuggestions = SuggestedWords.EMPTY;
1844074cda4d266b5d034d4348961c9183e32b16af5asatok                }
1845fe1a6d961cf039357f061482461e4d2e951ad346satok                builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions);
1846fe1a6d961cf039357f061482461e4d2e951ad346satok            }
18479fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        }
18487e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        showSuggestions(builder.build(), typedWord);
1849979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
18504a7ff90d513f8b6cbf39688c08be0828a57e311bAmith Yamasani
18519fbfd5877305ed19a20663630b498b6b3fdae942satok    public void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) {
18522aa1dd45c44295e2f7e8ece1b520032d86b9f908satok        final boolean shouldBlockAutoCorrectionBySafetyNet =
18532aa1dd45c44295e2f7e8ece1b520032d86b9f908satok                Utils.shouldBlockAutoCorrectionBySafetyNet(suggestedWords, mSuggest);
18542aa1dd45c44295e2f7e8ece1b520032d86b9f908satok        if (shouldBlockAutoCorrectionBySafetyNet) {
18552aa1dd45c44295e2f7e8ece1b520032d86b9f908satok            suggestedWords.setShouldBlockAutoCorrection();
18562aa1dd45c44295e2f7e8ece1b520032d86b9f908satok        }
18577e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        setSuggestions(suggestedWords);
18587e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka        if (suggestedWords.size() > 0) {
18592aa1dd45c44295e2f7e8ece1b520032d86b9f908satok            if (shouldBlockAutoCorrectionBySafetyNet) {
1860117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                mWordComposer.setAutoCorrection(typedWord);
186182411d47ba7e8133ed2390c6920945e139a738cesatok            } else if (suggestedWords.hasAutoCorrectionWord()) {
1862117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                mWordComposer.setAutoCorrection(suggestedWords.getWord(1));
1863923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            } else {
1864117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                mWordComposer.setAutoCorrection(typedWord);
1865923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1866923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        } else {
1867117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            // TODO: replace with mWordComposer.deleteAutoCorrection()?
1868117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            mWordComposer.setAutoCorrection(null);
1869923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1870913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
1871923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1872923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1873b5d17e52fcda39d540822e615bfb7ed5074e0034satok    private boolean pickDefaultSuggestion(int separatorCode) {
1874913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        // Complete any pending suggestions query first
1875d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka        if (mHandler.hasPendingUpdateSuggestions()) {
1876d7641636db8fe91d9847ac79f5f431963e876ec3Tadashi G. Takaoka            mHandler.cancelUpdateSuggestions();
1877923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            updateSuggestions();
1878923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1879117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
1880117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard        if (autoCorrection != null) {
1881117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            final String typedWord = mWordComposer.getTypedWord();
1882117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCode);
18834733609947c0ec74e460bd714fffca0518ade93aJean Chalard            mExpectingUpdateSelection = true;
1884117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            commitBestWord(autoCorrection);
1885117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            if (!autoCorrection.equals(typedWord)) {
1886117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard                mWordSavedForAutoCorrectCancellation = autoCorrection.toString();
1887890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
1888f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard            // Add the word to the user unigram dictionary if it's not a known word
1889117fc93f373cb86d4120c1261f9d0562c6529fecJean Chalard            addToUserUnigramAndBigramDictionaries(autoCorrection,
1890f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                    UserUnigramDictionary.FREQUENCY_FOR_TYPED);
1891979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            return true;
1892923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1893979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return false;
1894923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
1895923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1896c97810693dfe83bf37c09f73c8d4b40f2ba8dddbTadashi G. Takaoka    @Override
1897923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public void pickSuggestionManually(int index, CharSequence suggestion) {
1898604d80c67185954d4691ac775be59c499eee3b1csatok        mComposingStateManager.onFinishComposingText();
1899913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        SuggestedWords suggestions = mSuggestionsView.getSuggestions();
190017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        mVoiceProxy.flushAndLogAllTextModificationCounters(index, suggestion,
190117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                mSettingsValues.mWordSeparators);
19024f1f2201bdd0e63a19e686caa3d17b16eb134f5eMaryam Garrett
19039351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
19049a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (ic != null) {
19059a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            ic.beginBatchEdit();
19069a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        }
19071b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null
19081b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
1909923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            if (ic != null) {
19109351550dc6af7859e5280e16144c9386a37b976dKen Wakasa                final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
19119351550dc6af7859e5280e16144c9386a37b976dKen Wakasa                ic.commitCompletion(completionInfo);
1912923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1913913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka            if (mSuggestionsView != null) {
1914913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka                mSuggestionsView.clear();
1915923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
1916b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mKeyboardSwitcher.updateShiftState();
19179a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            if (ic != null) {
19189a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa                ic.endBatchEdit();
19199a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            }
1920923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
1921923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
19226a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
19236a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani        // If this is a punctuation, apply it through the normal key press
192417c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (suggestion.length() == 1 && (mSettingsValues.isWordSeparator(suggestion.charAt(0))
192517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                || mSettingsValues.isSuggestedPunctuation(suggestion.charAt(0)))) {
1926979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // Word separators are suggested before the user inputs something.
1927979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // So, LatinImeLogger logs "" as a user's input.
1928979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            LatinImeLogger.logOnManualSuggestion(
19297e181fe1010c8eac7814cc67a0c4b3864a10b151Tadashi G. Takaoka                    "", suggestion.toString(), index, suggestions.mWords);
1930863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // Find out whether the previous character is a space. If it is, as a special case
1931120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            // for punctuation entered through the suggestion strip, it should be swapped
1932120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            // if it was a magic or a weak space. This is meant to help in case the user
1933863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // pressed space on purpose of displaying the suggestion strip punctuation.
193483ffff2a49beeb41874b7cb40819a75001f708e2Ken Wakasa            final int rawPrimaryCode = suggestion.charAt(0);
193583ffff2a49beeb41874b7cb40819a75001f708e2Ken Wakasa            // Maybe apply the "bidi mirrored" conversions for parentheses
193683ffff2a49beeb41874b7cb40819a75001f708e2Ken Wakasa            final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
193749426a1ea941c0331a6e573e36cff3c3a8e8849cTadashi G. Takaoka            final boolean isRtl = keyboard != null && keyboard.mIsRtlKeyboard;
193849426a1ea941c0331a6e573e36cff3c3a8e8849cTadashi G. Takaoka            final int primaryCode = Key.getRtlParenthesisCode(rawPrimaryCode, isRtl);
193983ffff2a49beeb41874b7cb40819a75001f708e2Ken Wakasa
1940120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            insertPunctuationFromSuggestionStrip(ic, primaryCode);
1941120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard            // TODO: the following endBatchEdit seems useless, check
19429a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            if (ic != null) {
19439a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa                ic.endBatchEdit();
19449a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            }
19456a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani            return;
19466a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani        }
1947f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        if (!mHasUncommittedTypedChars) {
194889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // If we are not composing a word, then it was a suggestion inferred from
194989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // context - no user input. We should reset the word composer.
19509318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa            mWordComposer.reset();
195189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
19524733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mExpectingUpdateSelection = true;
19531fef530ec7626fa16777f52b48191e61db8f46d4satok        commitBestWord(suggestion);
19549468335a06d2b0e3ef15f4f57f8c1b0857b34ebeAmith Yamasani        // Add the word to the auto dictionary if it's not a known word
19550c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        if (index == 0) {
1956f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard            addToUserUnigramAndBigramDictionaries(suggestion,
1957f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard                    UserUnigramDictionary.FREQUENCY_FOR_PICKED);
1958979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } else {
1959bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            addToOnlyBigramDictionary(suggestion, 1);
19600c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        }
1961f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        // TODO: the following is fishy, because if !mHasUncommittedTypedChars we are
1962f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        // going to log an empty string
19633651220327c051d8017045aa5e8919461507b3f8Jean Chalard        LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(),
19649318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa                suggestion.toString(), index, suggestions.mWords);
1965923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // Follow it with a space
1966d0c5f9395a1b94e8425982e353d090f972dc44f0Jean Chalard        if (mInsertSpaceOnPickSuggestionManually) {
19670730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard            sendMagicSpace();
1968923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
1969979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1970c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // We should show the "Touch again to save" hint if the user pressed the first entry
1971c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        // AND either:
19727f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is no dictionary (we know that because we tried to load it => null != mSuggest
1973c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa        //   AND mSuggest.hasMainDictionary() is false)
19747f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // - There is a dictionary and the word is not in it
19757f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // Please note that if mSuggest is null, it means that everything is off: suggestion
19767f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // and correction, so we shouldn't try to show the hint
19777f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // We used to look at mCorrectionMode here, but showing the hint should have nothing
19787f2ba16aa2adba95e0575a2c6d58f6240154f313Jean Chalard        // to do with the autocorrection setting.
1979bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok        final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
1980bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                // If there is no dictionary the hint should be shown.
1981c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa                && (!mSuggest.hasMainDictionary()
1982bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        // If "suggestion" is not in the dictionary, the hint should be shown.
1983bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                        || !AutoCorrection.isValidWord(
1984bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok                                mSuggest.getUnigramDictionaries(), suggestion, true));
1985b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani
1986406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE,
1987406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard                WordComposer.NOT_A_COORDINATE);
1988777118a40a363ccab69a00016d3156066513cb78Jean Chalard        if (!showingAddToDictionaryHint) {
1989364da8c618303a7764595d2c15ee034a7671365dKen Wakasa            // If we're not showing the "Touch again to save", then show corrections again.
1990979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            // In case the cursor position doesn't change, make sure we show the suggestions again.
199141ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            updateBigramPredictions();
199241ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // Updating the predictions right away may be slow and feel unresponsive on slower
199341ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // terminals. On the other hand if we just postUpdateBigramPredictions() it will
199441ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard            // take a noticeable delay to update them which may feel uneasy.
1995979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
1996b00a1d0c0adbdfc507676772201e979e539a2801Amith Yamasani        if (showingAddToDictionaryHint) {
199788562bec54658840dcce352127bdc15705c20a89Jean Chalard            if (mIsUserDictionaryAvailable) {
1998644d33d60ea5a87501274488013d65f55238895eKen Wakasa                mSuggestionsView.showAddToDictionaryHint(
1999644d33d60ea5a87501274488013d65f55238895eKen Wakasa                        suggestion, mSettingsValues.mHintToSaveText);
2000ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            } else {
2001ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka                mHandler.postUpdateSuggestions();
2002ada26bb383f5b9de4717a980a3aa8f53d267df93Tadashi G. Takaoka            }
200366a787b953d703201c6b827abbee74e8cd9bb063Amith Yamasani        }
20049a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        if (ic != null) {
20059a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa            ic.endBatchEdit();
20069a503e07d594ddf3cd0c31609c5311440b975906Ken Wakasa        }
2007923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2008a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2009979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
20108558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa     * Commits the chosen word to the text field and saves it for later retrieval.
2011979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
20121fef530ec7626fa16777f52b48191e61db8f46d4satok    private void commitBestWord(CharSequence bestWord) {
20139351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final KeyboardSwitcher switcher = mKeyboardSwitcher;
2014b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!switcher.isKeyboardAvailable())
20151679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka            return;
20169351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
2017923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic != null) {
20181fef530ec7626fa16777f52b48191e61db8f46d4satok            mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
20191531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            if (mSettingsValues.mEnableSuggestionSpanInsertion) {
20201531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
20211531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
20221531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                        this, bestWord, suggestedWords), 1);
20231531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            } else {
20241531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard                ic.commitText(bestWord, 1);
20251531528bfe01d0ce88888d23952a4c7092a15f17Jean Chalard            }
2026923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2027f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        mHasUncommittedTypedChars = false;
2028923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2029923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
203089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    private static final WordComposer sEmptyWordComposer = new WordComposer();
203141ec3ec2f3a95f0af2697da92cee4920e6156763Jean Chalard    public void updateBigramPredictions() {
203289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (mSuggest == null || !isSuggestionsRequested())
203389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            return;
203489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
203517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (!mSettingsValues.mBigramPredictionEnabled) {
2036cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            setPunctuationSuggestions();
2037cb3320179d39a7983874697a0aa428b127675c9dJean Chalard            return;
2038cb3320179d39a7983874697a0aa428b127675c9dJean Chalard        }
2039cb3320179d39a7983874697a0aa428b127675c9dJean Chalard
204089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
204117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                mSettingsValues.mWordSeparators);
2042904baab25a4c6ec5d9c4bf7e562154e3f544d296satok        SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(sEmptyWordComposer,
20434606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard                prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode);
204489bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
204589bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        if (builder.size() > 0) {
204689bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // Explicitly supply an empty typed word (the no-second-arg version of
204789bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            // showSuggestions will retrieve the word near the cursor, we don't want that here)
204889bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            showSuggestions(builder.build(), "");
204989bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        } else {
205089bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard            if (!isShowingPunctuationList()) setPunctuationSuggestions();
205189bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard        }
205289bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard    }
205389bd776cf68150202d774d62cc1c88664aea5e9fJean Chalard
20547a8dac55278cedd838be325f56b4c52d973c61f5satok    public void setPunctuationSuggestions() {
205517c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        setSuggestions(mSettingsValues.mSuggestPuncList);
2056913e2aeef26f172d500a4ebfc644b5f47778841aTadashi G. Takaoka        setSuggestionStripShown(isSuggestionsStripVisible());
20576a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani    }
20586a6075caba3865383eeeb52cccc63a28e4ae5900Amith Yamasani
2059f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard    private void addToUserUnigramAndBigramDictionaries(CharSequence suggestion,
2060f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard            int frequencyDelta) {
2061979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        checkAddToDictionary(suggestion, frequencyDelta, false);
2062979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
2063979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2064bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok    private void addToOnlyBigramDictionary(CharSequence suggestion, int frequencyDelta) {
2065979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        checkAddToDictionary(suggestion, frequencyDelta, true);
2066979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
2067979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2068979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
2069f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard     * Adds to the UserBigramDictionary and/or UserUnigramDictionary
2070bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok     * @param selectedANotTypedWord true if it should be added to bigram dictionary if possible
2071979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
2072979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta,
2073bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok            boolean selectedANotTypedWord) {
2074979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (suggestion == null || suggestion.length() < 1) return;
2075bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
20760c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
20770c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // adding words in situations where the user or application really didn't
20780c05902e331b03426754a1cfffe28d185dd8338cAmith Yamasani        // want corrections enabled or learned.
2079979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (!(mCorrectionMode == Suggest.CORRECTION_FULL
2080979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
2081979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            return;
2082979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
2083bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok
20845955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard        if (null != mSuggest && null != mUserUnigramDictionary) {
20855955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            final boolean selectedATypedWordAndItsInUserUnigramDic =
20865955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                    !selectedANotTypedWord && mUserUnigramDictionary.isValidWord(suggestion);
20875955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            final boolean isValidWord = AutoCorrection.isValidWord(
20885955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                    mSuggest.getUnigramDictionaries(), suggestion, true);
20895955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            final boolean needsToAddToUserUnigramDictionary =
20905955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                    selectedATypedWordAndItsInUserUnigramDic || !isValidWord;
20915955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            if (needsToAddToUserUnigramDictionary) {
20925955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard                mUserUnigramDictionary.addWord(suggestion.toString(), frequencyDelta);
20935955cfdaeae9ce2120769a4be965bdbac413bf79Jean Chalard            }
2094e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        }
2095979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2096e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        if (mUserBigramDictionary != null) {
2097863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // We don't want to register as bigrams words separated by a separator.
2098863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // For example "I will, and you too" : we don't want the pair ("will" "and") to be
2099863f95b6052e5d9d4fb1ac5dc283c464db9f29c3Jean Chalard            // a bigram.
210040f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            final InputConnection ic = getCurrentInputConnection();
210140f7efc172928bdd6048e91421a766abe5b22996Jean Chalard            if (null != ic) {
210240f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                final CharSequence prevWord =
210340f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                        EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
210440f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                if (!TextUtils.isEmpty(prevWord)) {
210540f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                    mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
210640f7efc172928bdd6048e91421a766abe5b22996Jean Chalard                }
2107979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
210832e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani        }
210932e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani    }
211032e1c1c338155f8a83646d725a8512b95edad4f8Amith Yamasani
21117a8dac55278cedd838be325f56b4c52d973c61f5satok    public boolean isCursorTouchingWord() {
21129351550dc6af7859e5280e16144c9386a37b976dKen Wakasa        final InputConnection ic = getCurrentInputConnection();
2113923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (ic == null) return false;
2114923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
2115923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        CharSequence toRight = ic.getTextAfterCursor(1, 0);
2116923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (!TextUtils.isEmpty(toLeft)
211717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                && !mSettingsValues.isWordSeparator(toLeft.charAt(0))
211817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                && !mSettingsValues.isSuggestedPunctuation(toLeft.charAt(0))) {
2119923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
2120923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2121a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker        if (!TextUtils.isEmpty(toRight)
212217c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                && !mSettingsValues.isWordSeparator(toRight.charAt(0))
212317c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard                && !mSettingsValues.isSuggestedPunctuation(toRight.charAt(0))) {
2124923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return true;
2125923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2126923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        return false;
2127923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2128a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2129120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
21308fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean sameAsTextBeforeCursor(final InputConnection ic, CharSequence text) {
2131dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
2132dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani        return TextUtils.equals(text, beforeText);
2133dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani    }
2134dad0e792aa064765901224af56d2a53a25bc7b7dAmith Yamasani
2135120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
21366b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    /**
21376b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
21386b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     * word, else do nothing.
21396b1f500da451de56932a8b2a99c63857994ece85Jean Chalard     */
21406b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(
21416b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final InputConnection ic) {
21426b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if the cursor is not at the end of a word (cursor must be preceded by
21436b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // non-whitespace, non-separator, non-start-of-text)
21446b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
21456b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0);
21466b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (TextUtils.isEmpty(textBeforeCursor)
21476b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return;
21486b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21496b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
21506b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // separator or end of line/text)
21516b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Example: "test|"<EOL> "te|st" get rejected here
21526b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0);
21536b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (!TextUtils.isEmpty(textAfterCursor)
21546b1f500da451de56932a8b2a99c63857994ece85Jean Chalard                && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return;
21556b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21566b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
21576b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Example: " '|" gets rejected here but "I'|" and "I|" are okay
21586b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        final CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators);
21596b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (TextUtils.isEmpty(word)) return;
21606b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        if (word.length() == 1 && !Character.isLetter(word.charAt(0))) return;
21616b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21626b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        // Okay, we are at the end of a word. Restart suggestions.
21636b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        restartSuggestionsOnWordBeforeCursor(ic, word);
21646b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
21656b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21666b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    // "ic" must not be null
21676b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
21686b1f500da451de56932a8b2a99c63857994ece85Jean Chalard            final CharSequence word) {
21696b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mWordComposer.setComposingWord(word, mKeyboardSwitcher.getLatinKeyboard());
21706b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mHasUncommittedTypedChars = true;
21716b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mComposingStateManager.onStartComposingText();
21726b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        ic.deleteSurroundingText(word.length(), 0);
21736b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        ic.setComposingText(word, 1);
21746b1f500da451de56932a8b2a99c63857994ece85Jean Chalard        mHandler.postUpdateSuggestions();
21756b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    }
21766b1f500da451de56932a8b2a99c63857994ece85Jean Chalard
21776b1f500da451de56932a8b2a99c63857994ece85Jean Chalard    // "ic" must not be null
2178890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    private void cancelAutoCorrect(final InputConnection ic) {
2179890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        final int cancelLength = mWordSavedForAutoCorrectCancellation.length();
2180890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        final CharSequence separator = ic.getTextBeforeCursor(1, 0);
2181890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2182890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            final String wordBeforeCursor =
2183890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                    ic.getTextBeforeCursor(cancelLength + 1, 0).subSequence(0, cancelLength)
2184890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                    .toString();
2185890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            if (!mWordSavedForAutoCorrectCancellation.equals(wordBeforeCursor)) {
2186890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                throw new RuntimeException("cancelAutoCorrect check failed: we thought we were "
2187890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "reverting \"" + mWordSavedForAutoCorrectCancellation
2188890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
2189890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
2190890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            if (mWordComposer.getTypedWord().equals(wordBeforeCursor)) {
2191890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                throw new RuntimeException("cancelAutoCorrect check failed: we wanted to cancel "
2192890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "auto correction and revert to \"" + mWordComposer.getTypedWord()
2193890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\" but we found this very string before the cursor");
2194890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
21958558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        }
2196890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        ic.deleteSurroundingText(cancelLength + 1, 0);
21978558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa
2198890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        // Re-insert the separator
2199890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        ic.commitText(mWordComposer.getTypedWord(), 1);
2200890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        ic.commitText(separator, 1);
2201406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard        Utils.Stats.onSeparator(separator.charAt(0), WordComposer.NOT_A_COORDINATE,
2202406d192a9e8f07ed6c6a408650feb0a757ca388eJean Chalard                WordComposer.NOT_A_COORDINATE);
2203890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.cancelUpdateBigramPredictions();
2204890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHandler.postUpdateSuggestions();
2205890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    }
2206890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard
2207890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    // "ic" must not be null
2208890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard    private void restartSuggestionsOnManuallyPickedTypedWord(final InputConnection ic) {
2209c2a76a1529870b59bba133a7d76a800cbd20ecfaJean Chalard        final int restartLength = mWordComposer.size();
2210890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        if (DEBUG) {
2211890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            final String wordBeforeCursor =
2212890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                ic.getTextBeforeCursor(restartLength + 1, 0).subSequence(0, restartLength)
2213890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                .toString();
2214890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            if (!mWordComposer.getTypedWord().equals(wordBeforeCursor)) {
2215890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                throw new RuntimeException("restartSuggestionsOnManuallyPickedTypedWord "
2216890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "check failed: we thought we were reverting \""
2217890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + mWordComposer.getTypedWord()
2218890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + "\", but before the cursor we found \""
2219890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard                        + wordBeforeCursor + "\"");
2220890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard            }
2221923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2222890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        ic.deleteSurroundingText(restartLength + 1, 0);
2223890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard
2224890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        // Note: this relies on the last word still being held in the WordComposer
2225890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        // Note: in the interest of code simplicity, we may want to just call
2226890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving
2227890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        // the old WordComposer allows to reuse the actual typed coordinates.
2228890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        mHasUncommittedTypedChars = true;
2229890d13189d8dd305681bcb00d09f34d3ae68c648Jean Chalard        ic.setComposingText(mWordComposer.getTypedWord(), 1);
22308558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        mHandler.cancelUpdateBigramPredictions();
22318558cfb42a412010f2e5320ee50fe44afefdc1cfKen Wakasa        mHandler.postUpdateSuggestions();
2232923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2233923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2234120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    // "ic" must not be null
22359351550dc6af7859e5280e16144c9386a37b976dKen Wakasa    private boolean revertDoubleSpace(final InputConnection ic) {
22364733609947c0ec74e460bd714fffca0518ade93aJean Chalard        mHandler.cancelDoubleSpacesTimer();
22374733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // Here we test whether we indeed have a period and a space before us. This should not
22384733609947c0ec74e460bd714fffca0518ade93aJean Chalard        // be needed, but it's there just in case something went wrong.
22394733609947c0ec74e460bd714fffca0518ade93aJean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
22408ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        if (!". ".equals(textBeforeCursor)) {
22418ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard            // We should not have come here if we aren't just after a ". ".
22428ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard            throw new RuntimeException("Tried to revert double-space combo but we didn't find "
22438ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "\". \" just before the cursor.");
22448ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
22454733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.beginBatchEdit();
22464733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.deleteSurroundingText(2, 0);
22474733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.commitText("  ", 1);
22484733609947c0ec74e460bd714fffca0518ade93aJean Chalard        ic.endBatchEdit();
22494733609947c0ec74e460bd714fffca0518ade93aJean Chalard        return true;
22504733609947c0ec74e460bd714fffca0518ade93aJean Chalard    }
22514733609947c0ec74e460bd714fffca0518ade93aJean Chalard
22528fbf29e2d54027a17993cd0d4ad486e3454b56f6Tadashi G. Takaoka    private static boolean revertSwapPunctuation(final InputConnection ic) {
2253120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // Here we test whether we indeed have a space and something else before us. This should not
2254120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // be needed, but it's there just in case something went wrong.
2255120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
2256120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
2257120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        // enter surrogate pairs this code will have been removed.
22588ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1)) {
22598ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard            // We should not have come here if the text before the cursor is not a space.
22608ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard            throw new RuntimeException("Tried to revert a swap of punctiation but we didn't "
22618ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard                    + "find a space just before the cursor.");
22628ad4013406e5e94967bb74baae3b068187f62e4bJean Chalard        }
2263120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.beginBatchEdit();
2264120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.deleteSurroundingText(2, 0);
2265120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
2266120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        ic.endBatchEdit();
2267120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        return true;
2268120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard    }
2269120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard
2270923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean isWordSeparator(int code) {
227117c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        return mSettingsValues.isWordSeparator(code);
2272923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2273923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
22740730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    private void sendMagicSpace() {
2275571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        sendKeyChar((char)Keyboard.CODE_SPACE);
2276120586c226c416d2211b6ebb3f4b914a30f9a74fJean Chalard        mSpaceState = SPACE_STATE_MAGIC;
2277b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mKeyboardSwitcher.updateShiftState();
2278923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2279923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2280923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    public boolean preferCapitalization() {
22819318d33b6e501d4dd9ea860de04160ccb23a7501Ken Wakasa        return mWordComposer.isFirstCharCapitalized();
2282923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2283923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
228488fc9d44186120f9edc5cf7ec0e2af85260fed04satok    // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
2285c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    // according to new language or mode.
2286c1c4ee6b3a8c3ec42edefe42fd183f3cbf67b0bfTadashi G. Takaoka    public void onRefreshKeyboard() {
228755303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka        if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
228855303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka            // Before Honeycomb, Voice IME is in LatinIME and it changes the current input view,
228955303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka            // so that we need to re-create the keyboard input view here.
229055303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka            setInputView(mKeyboardSwitcher.onCreateInputView());
229155303bc63440c6a9547e94b4f3486a00696da9b0Tadashi G. Takaoka        }
22921e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // When the device locale is changed in SetupWizard etc., this method may get called via
22931e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        // onConfigurationChanged before SoftInputWindow is shown.
22941e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        if (mKeyboardSwitcher.getKeyboardView() != null) {
22951e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            // Reload keyboard because the current language has been changed.
22961e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
22971e39565bb48d23221145c494d4bf5665b514699dTadashi G. Takaoka        }
22980ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        initSuggest();
229917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        loadSettings();
230036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    }
230136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
2302d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka    private void hapticAndAudioFeedback(int primaryCode) {
2303d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka        vibrate();
2304d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka        playKeyClick(primaryCode);
2305d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka    }
2306d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka
23075a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
2308e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onPress(int primaryCode, boolean withSliding) {
2309055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        final KeyboardSwitcher switcher = mKeyboardSwitcher;
2310055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        if (switcher.isVibrateAndSoundFeedbackRequired()) {
2311d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka            hapticAndAudioFeedback(primaryCode);
2312cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        }
23131679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
2314571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) {
2315e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            switcher.onPressShift(withSliding);
2316e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        } else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
23176c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka            switcher.onPressSymbol();
231840a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka        } else {
23196c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka            switcher.onOtherKeyPressed();
2320681b102a492b7d5301c1ca87985b4c391eb5eb14Tadashi G. Takaoka        }
2321923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2322923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
23235a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    @Override
2324e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onRelease(int primaryCode, boolean withSliding) {
23251679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        KeyboardSwitcher switcher = mKeyboardSwitcher;
23263f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani        // Reset any drag flags in the keyboard
23271679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
2328571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) {
2329e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            switcher.onReleaseShift(withSliding);
2330e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka        } else if (distinctMultiTouch && primaryCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
23316c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka            switcher.onReleaseSymbol();
233240a05f62edc6cdedb4365a722b48a72826ef2bf6Tadashi G. Takaoka        }
2333923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2334a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2335a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2336123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka    // receive ringer mode change and network state change.
2337923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
2338923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        @Override
2339923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        public void onReceive(Context context, Intent intent) {
2340123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            final String action = intent.getAction();
2341123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
2342123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                updateRingerMode();
2343123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
2344123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka                mSubtypeSwitcher.onNetworkStateChanged(intent);
2345123c4a38514c003aac50caed7ad3cb6ab8f2515aTadashi G. Takaoka            }
2346923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2347923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    };
2348923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2349923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    // update flags for silent mode
2350923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void updateRingerMode() {
2351923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (mAudioManager == null) {
2352923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
2353bf7dd47329c23c846912b268d15e8083ffdaabdeKen Wakasa            if (mAudioManager == null) return;
2354923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2355bf7dd47329c23c846912b268d15e8083ffdaabdeKen Wakasa        mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
2356923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2357923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2358923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    private void playKeyClick(int primaryCode) {
2359923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // if mAudioManager is null, we don't have the ringer state yet
2360923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        // mAudioManager will be set by updateRingerMode
2361923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (mAudioManager == null) {
2362c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            if (mKeyboardSwitcher.getKeyboardView() != null) {
2363923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                updateRingerMode();
2364923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
2365923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
236617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (isSoundOn()) {
2367f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            final int sound;
2368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            switch (primaryCode) {
2369f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            case Keyboard.CODE_DELETE:
2370f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_DELETE;
2371f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2372f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            case Keyboard.CODE_ENTER:
2373f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_RETURN;
2374f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2375f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            case Keyboard.CODE_SPACE:
2376f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_SPACEBAR;
2377f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2378f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa            default:
2379f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                sound = AudioManager.FX_KEYPRESS_STANDARD;
2380f58293f6ebf821acd9a3cc594124e905c4592810Ken Wakasa                break;
2381923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
2382c207e0a7dad0bdae054be47cafe878698f9401fcJean Chalard            mAudioManager.playSoundEffect(sound, mSettingsValues.mFxVolume);
2383923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2384923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2385923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2386409220583333bdf06290dd9fd42f91b5c0d1b11asatok    public void vibrate() {
238717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        if (!mSettingsValues.mVibrateOn) {
2388923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            return;
2389923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2390c207e0a7dad0bdae054be47cafe878698f9401fcJean Chalard        if (mSettingsValues.mKeypressVibrationDuration < 0) {
239128f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            // Go ahead with the system default
239228f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
239328f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            if (inputView != null) {
239428f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa                inputView.performHapticFeedback(
239528f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa                        HapticFeedbackConstants.KEYBOARD_TAP,
239628f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa                        HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
239728f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa            }
239828f36d68afe8d323d1032d0e03fe3013449e21feKen Wakasa        } else if (mVibrator != null) {
2399c207e0a7dad0bdae054be47cafe878698f9401fcJean Chalard            mVibrator.vibrate(mSettingsValues.mKeypressVibrationDuration);
2400923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2401923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2402923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2403f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard    public boolean isAutoCapitalized() {
2404f7d6517d6b1a1dd88e2142e1a15703bb839be01bJean Chalard        return mWordComposer.isAutoCapitalized();
24056516d0fdfcbaa4eb809a8a69bd876293043a68a4Amith Yamasani    }
24066516d0fdfcbaa4eb809a8a69bd876293043a68a4Amith Yamasani
240717c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    boolean isSoundOn() {
240817c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        return mSettingsValues.mSoundOn && !mSilentModeOn;
2409979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
2410979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2411e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    private void updateCorrectionMode() {
24129f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok        // TODO: cleanup messy flags
2413f50aa193377492e9c4afb3cc6e7f3448ab5a97a4Jean Chalard        final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
2414c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa                && !mInputTypeNoAutoCorrect;
2415283b0c2b642030539ba3e41f3f54b6aed6bb9e2dJean Chalard        mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE;
2416283b0c2b642030539ba3e41f3f54b6aed6bb9e2dJean Chalard        mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect)
2417979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
2418e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani    }
2419e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani
242017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard    private void updateSuggestionVisibility(final SharedPreferences prefs, final Resources res) {
24210fe3611bee5095e7bd0fff2d0fdf8d5a13379132Jean Chalard        final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting;
24227599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
2423458249e703bded3a1cbd25a2ab2249f9366a8188Ken Wakasa            if (suggestionVisiblityStr.equals(res.getString(visibility))) {
24247599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                mSuggestionVisibility = visibility;
24257599cfea4a2d56f4779452ec8e8742f7b9629cc0satok                break;
24267599cfea4a2d56f4779452ec8e8742f7b9629cc0satok            }
24277599cfea4a2d56f4779452ec8e8742f7b9629cc0satok        }
24287599cfea4a2d56f4779452ec8e8742f7b9629cc0satok    }
24297599cfea4a2d56f4779452ec8e8742f7b9629cc0satok
2430466741d8a78965b8509bf527344f289e50873092Mike LeBeau    protected void launchSettings() {
2431cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        launchSettingsClass(Settings.class);
2432466741d8a78965b8509bf527344f289e50873092Mike LeBeau    }
2433466741d8a78965b8509bf527344f289e50873092Mike LeBeau
2434bf96661d33d0126adb60a48880ceba1ff055d4a4satok    public void launchDebugSettings() {
2435cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa        launchSettingsClass(DebugSettings.class);
2436bf96661d33d0126adb60a48880ceba1ff055d4a4satok    }
2437bf96661d33d0126adb60a48880ceba1ff055d4a4satok
2438cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    protected void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
2439923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        handleClose();
2440923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        Intent intent = new Intent();
2441466741d8a78965b8509bf527344f289e50873092Mike LeBeau        intent.setClass(LatinIME.this, settingsClass);
2442923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2443923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        startActivity(intent);
2444923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2445923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
24462fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showSubtypeSelectorAndSettings() {
244785996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
244885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
244985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                // TODO: Should use new string "Select active input modes".
245085996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.language_selection_title),
245185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.english_ime_settings),
245285996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
245385996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
24542fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            @Override
24552fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            public void onClick(DialogInterface di, int position) {
24562fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                di.dismiss();
24572fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                switch (position) {
245885996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
24592cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok                    Intent intent = CompatUtils.getInputLanguageSelectionIntent(
24602a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Utils.getInputMethodId(mImm, getPackageName()),
24612a659b8aa642b0832fa0ac9a93e0640592fcc239Jean Chalard                            Intent.FLAG_ACTIVITY_NEW_TASK
24622fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
24632fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                            | Intent.FLAG_ACTIVITY_CLEAR_TOP);
24642fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    startActivity(intent);
24652fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2466aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                case 1:
2467aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    launchSettings();
2468aa0458f917bb823efab6245f08c65e8f2f7327acKen Wakasa                    break;
24692fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                }
24702fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka            }
247185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2472bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2473bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2474bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2475bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        showOptionDialogInternal(builder.create());
24762fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    }
2477923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
24782fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka    private void showOptionsMenu() {
247985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence title = getString(R.string.english_ime_input_options);
248085996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final CharSequence[] items = new CharSequence[] {
248185996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.selectInputMethod),
248285996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                getString(R.string.english_ime_settings),
248385996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
248485996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
24855a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            @Override
2486923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            public void onClick(DialogInterface di, int position) {
2487923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                di.dismiss();
2488923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                switch (position) {
248985996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 0:
2490ea55bf5df7d26d12b36c47141bfbac5730c3929aTadashi G. Takaoka                    mImm.showInputMethodPicker();
24912fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
249285996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka                case 1:
2493ea55bf5df7d26d12b36c47141bfbac5730c3929aTadashi G. Takaoka                    launchSettings();
24942fa21f5854e1565deb139e0bf22719fecc5340bcTadashi G. Takaoka                    break;
2495923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project                }
2496923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            }
249785996b472a0ec5fc31e57c52aa46c8c7794689bbTadashi G. Takaoka        };
2498bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        final AlertDialog.Builder builder = new AlertDialog.Builder(this)
2499bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setItems(items, listener)
2500bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka                .setTitle(title);
2501bf9d8348d89be257ccc3db75333bfd4cdf0a9b7eTadashi G. Takaoka        showOptionDialogInternal(builder.create());
2502923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2503923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
25047e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    @Override
25057e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2506923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        super.dump(fd, fout, args);
2507a16ff1b19e8e012cf37a234bae67b994e805bfb1Doug Zongker
2508923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        final Printer p = new PrintWriterPrinter(fout);
2509923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("LatinIME state :");
2510df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        final Keyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
2511df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
2512df9deffba241d3f1527092212de02f5c77a0b24aTadashi G. Takaoka        p.println("  Keyboard mode = " + keyboardMode);
25139fb8c6dd48dfa4e45827628a866d9b13c4c6c799Tadashi G. Takaoka        p.println("  mIsSuggestionsRequested=" + mIsSettingsSuggestionStripOn);
2514923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        p.println("  mCorrectionMode=" + mCorrectionMode);
2515f108bf8fe924722ac6fc451f0235eb85a60f459dJean Chalard        p.println("  mHasUncommittedTypedChars=" + mHasUncommittedTypedChars);
251617c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
251726a531c6fe2a6e058803b7102e2bc9e7ea12d8f3Jean Chalard        p.println("  mInsertSpaceOnPickSuggestionManually=" + mInsertSpaceOnPickSuggestionManually);
25181b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        p.println("  mApplicationSpecifiedCompletionOn=" + mApplicationSpecifiedCompletionOn);
251917c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mSoundOn=" + mSettingsValues.mSoundOn);
252017c7ffeb1e10348227c5dff1c319d2099c1b5d0eJean Chalard        p.println("  mVibrateOn=" + mSettingsValues.mVibrateOn);
2521240297d0ee186b14e795016e9b1bd168c8d8acf8Jean Chalard        p.println("  mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
2522923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2523923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
2524