AndroidSpellCheckerService.java revision b86ca76cea9aedf47a81f9272fb59897de3bbbe7
1022c1cc20379767966f4915e2dea65fc0b67c0d8satok/* 2022c1cc20379767966f4915e2dea65fc0b67c0d8satok * Copyright (C) 2011 The Android Open Source Project 3022c1cc20379767966f4915e2dea65fc0b67c0d8satok * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 7022c1cc20379767966f4915e2dea65fc0b67c0d8satok * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9022c1cc20379767966f4915e2dea65fc0b67c0d8satok * 10022c1cc20379767966f4915e2dea65fc0b67c0d8satok * Unless required by applicable law or agreed to in writing, software 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License. 15022c1cc20379767966f4915e2dea65fc0b67c0d8satok */ 16022c1cc20379767966f4915e2dea65fc0b67c0d8satok 17022c1cc20379767966f4915e2dea65fc0b67c0d8satokpackage com.android.inputmethod.latin.spellcheck; 18022c1cc20379767966f4915e2dea65fc0b67c0d8satok 19c160373b6a8e8a536ad8aa2798a33a41d3050f3bJean Chalardimport android.content.Intent; 20db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalardimport android.content.SharedPreferences; 21db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalardimport android.preference.PreferenceManager; 22022c1cc20379767966f4915e2dea65fc0b67c0d8satokimport android.service.textservice.SpellCheckerService; 23204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaokaimport android.text.InputType; 24204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaokaimport android.view.inputmethod.EditorInfo; 25204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype; 26022c1cc20379767966f4915e2dea65fc0b67c0d8satokimport android.view.textservice.SuggestionsInfo; 278aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanevimport android.util.Log; 28022c1cc20379767966f4915e2dea65fc0b67c0d8satok 299e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.keyboard.Keyboard; 309e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.keyboard.KeyboardId; 31244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataokaimport com.android.inputmethod.keyboard.KeyboardLayoutSet; 329e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.DictionaryFacilitator; 33d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagiimport com.android.inputmethod.latin.DictionaryFacilitatorLruCache; 34bb0eca57054758ef17b032d2654c1fc5f6b32101Keisuke Kuroyanagiimport com.android.inputmethod.latin.NgramContext; 3559b501a05078e5a9de7cdace19c51ca693076a17Jean Chalardimport com.android.inputmethod.latin.R; 3685ddfe1317a4475269e53f62c2338c335e02e839Jean Chalardimport com.android.inputmethod.latin.RichInputMethodSubtype; 37b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheikimport com.android.inputmethod.latin.SuggestedWords; 382b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevovimport com.android.inputmethod.latin.common.ComposedData; 399e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; 40204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaokaimport com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; 41289299bf66de5fb0c8a378f2366c0760da27077bJean Chalardimport com.android.inputmethod.latin.utils.ScriptUtils; 429e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.utils.SuggestionResults; 433234123fba901243990972158d023a5d1c273316Jean Chalard 443234123fba901243990972158d023a5d1c273316Jean Chalardimport java.util.Locale; 459e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.ConcurrentHashMap; 469e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.ConcurrentLinkedQueue; 479e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.Semaphore; 483234123fba901243990972158d023a5d1c273316Jean Chalard 49487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanevimport javax.annotation.Nonnull; 50487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev 51022c1cc20379767966f4915e2dea65fc0b67c0d8satok/** 52022c1cc20379767966f4915e2dea65fc0b67c0d8satok * Service for spell checking, using LatinIME's dictionaries and mechanisms. 53022c1cc20379767966f4915e2dea65fc0b67c0d8satok */ 54a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class AndroidSpellCheckerService extends SpellCheckerService 55db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard implements SharedPreferences.OnSharedPreferenceChangeListener { 568aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); 578aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev private static final boolean DEBUG = false; 588aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev 59db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts"; 60db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard 61204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480; 626cc318bd6a2c5993cce128d75fe753c3686331c1Keisuke Kuroyanagi private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 301; 63204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka 649e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private static final String DICTIONARY_NAME_PREFIX = "spellcheck_"; 659e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 669e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private static final String[] EMPTY_STRING_ARRAY = new String[0]; 679e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 689e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final int MAX_NUM_OF_THREADS_READ_DICTIONARY = 2; 699e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final Semaphore mSemaphore = new Semaphore(MAX_NUM_OF_THREADS_READ_DICTIONARY, 709e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi true /* fair */); 719e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi // TODO: Make each spell checker session has its own session id. 729e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final ConcurrentLinkedQueue<Integer> mSessionIdPool = new ConcurrentLinkedQueue<>(); 739e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 74d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi private final DictionaryFacilitatorLruCache mDictionaryFacilitatorCache = 75d6a8adcb044dd8b73a1c96776a835b411a978b46Dan Zivkovic new DictionaryFacilitatorLruCache(this /* context */, DICTIONARY_NAME_PREFIX); 769e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final ConcurrentHashMap<Locale, Keyboard> mKeyboardCache = new ConcurrentHashMap<>(); 773234123fba901243990972158d023a5d1c273316Jean Chalard 78a409f009fa410019ad10b1134ff57393443eba33Jean Chalard // The threshold for a suggestion to be considered "recommended". 790028ed3627ff4f37a62a80f3b2c857e373cd5090satok private float mRecommendedThreshold; 809e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi // TODO: make a spell checker option to block offensive words or not 819e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final SettingsValuesForSuggestion mSettingsValuesForSuggestion = 829e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi new SettingsValuesForSuggestion(true /* blockPotentiallyOffensive */, 8377e0fa1d6708fa53549d80fcd2035da5a30e4140Dan Zivkovic true /* spaceAwareGestureEnabled */); 8459b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard 8584ed0966417d93b07c4da2b295244b160d223ce9Satoshi Kataoka public static final String SINGLE_QUOTE = "\u0027"; 8684ed0966417d93b07c4da2b295244b160d223ce9Satoshi Kataoka public static final String APOSTROPHE = "\u2019"; 878aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev private UserDictionaryLookup mUserDictionaryLookup; 881830cd1dc8259aa57175f1cf2a3d8797a7a35935Jean Chalard 899e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public AndroidSpellCheckerService() { 909e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi super(); 919e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi for (int i = 0; i < MAX_NUM_OF_THREADS_READ_DICTIONARY; i++) { 929e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSessionIdPool.add(i); 939e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 949e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 959e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 9659b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard @Override public void onCreate() { 9759b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard super.onCreate(); 98a409f009fa410019ad10b1134ff57393443eba33Jean Chalard mRecommendedThreshold = 990028ed3627ff4f37a62a80f3b2c857e373cd5090satok Float.parseFloat(getString(R.string.spellchecker_recommended_threshold_value)); 100db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 101db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard prefs.registerOnSharedPreferenceChangeListener(this); 102db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY); 1038aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev // Create a UserDictionaryLookup. It needs to be close()d and set to null in onDestroy. 1048aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev if (mUserDictionaryLookup == null) { 1058aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev if (DEBUG) { 1068aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev Log.d(TAG, "Creating mUserDictionaryLookup in onCreate"); 1078aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } 1088aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev mUserDictionaryLookup = new UserDictionaryLookup(this); 1098aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } else if (DEBUG) { 1108aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev Log.d(TAG, "mUserDictionaryLookup already created before onCreate"); 1118aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } 1128aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } 1138aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev 1148aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev @Override public void onDestroy() { 1158aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev if (DEBUG) { 1168aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev Log.d(TAG, "Closing and dereferencing mUserDictionaryLookup in onDestroy"); 1178aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } 1188aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev mUserDictionaryLookup.close(); 1198aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev mUserDictionaryLookup = null; 1208aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev super.onDestroy(); 121db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard } 122db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard 1231467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi public float getRecommendedThreshold() { 1241467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi return mRecommendedThreshold; 1251467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi } 1261467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi 127244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka private static String getKeyboardLayoutNameForScript(final int script) { 128244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka switch (script) { 129289299bf66de5fb0c8a378f2366c0760da27077bJean Chalard case ScriptUtils.SCRIPT_LATIN: 130244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka return "qwerty"; 131289299bf66de5fb0c8a378f2366c0760da27077bJean Chalard case ScriptUtils.SCRIPT_CYRILLIC: 132244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka return "east_slavic"; 133289299bf66de5fb0c8a378f2366c0760da27077bJean Chalard case ScriptUtils.SCRIPT_GREEK: 134244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka return "greek"; 135244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka default: 136244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka throw new RuntimeException("Wrong script supplied: " + script); 137244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka } 138244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka } 139244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka 140db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard @Override 141db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { 142db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard if (!PREF_USE_CONTACTS_KEY.equals(key)) return; 14329aa3df3dadeb5829a70652a24b0756e2c9e45caDan Zivkovic final boolean useContactsDictionary = prefs.getBoolean(PREF_USE_CONTACTS_KEY, true); 14429aa3df3dadeb5829a70652a24b0756e2c9e45caDan Zivkovic mDictionaryFacilitatorCache.setUseContactsDictionary(useContactsDictionary); 14559b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard } 14659b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard 1475bcf8ee66ceb38675a6b70fefcb574978e0fae92satok @Override 1485bcf8ee66ceb38675a6b70fefcb574978e0fae92satok public Session createSession() { 14937b19ffe6c9d8335cc0e1c1c50f5b08c778b287cSatoshi Kataoka // Should not refer to AndroidSpellCheckerSession directly considering 15037b19ffe6c9d8335cc0e1c1c50f5b08c778b287cSatoshi Kataoka // that AndroidSpellCheckerSession may be overlaid. 15137b19ffe6c9d8335cc0e1c1c50f5b08c778b287cSatoshi Kataoka return AndroidSpellCheckerSessionFactory.newInstance(this); 1525bcf8ee66ceb38675a6b70fefcb574978e0fae92satok } 1535bcf8ee66ceb38675a6b70fefcb574978e0fae92satok 154df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard /** 155df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * Returns an empty SuggestionsInfo with flags signaling the word is not in the dictionary. 156df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * @param reportAsTypo whether this should include the flag LOOKS_LIKE_TYPO, for red underline. 157df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * @return the empty SuggestionsInfo with the appropriate flags set. 158df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard */ 159df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard public static SuggestionsInfo getNotInDictEmptySuggestions(final boolean reportAsTypo) { 160df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard return new SuggestionsInfo(reportAsTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0, 161df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard EMPTY_STRING_ARRAY); 162cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard } 163cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard 164df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard /** 165df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * Returns an empty suggestionInfo with flags signaling the word is in the dictionary. 166df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * @return the empty SuggestionsInfo with the appropriate flags set. 167df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard */ 16884ed0966417d93b07c4da2b295244b160d223ce9Satoshi Kataoka public static SuggestionsInfo getInDictEmptySuggestions() { 169cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY, 170cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard EMPTY_STRING_ARRAY); 171cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard } 172cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard 1739e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public boolean isValidWord(final Locale locale, final String word) { 1749e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(); 1759e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 1768aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev if (mUserDictionaryLookup.isValidWord(word, locale)) { 1778aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev if (DEBUG) { 1788aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev Log.d(TAG, "mUserDictionaryLookup.isValidWord(" + word + ")=true"); 1798aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } 1808aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev return true; 1818aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } else { 1828aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev if (DEBUG) { 1838aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev Log.d(TAG, "mUserDictionaryLookup.isValidWord(" + word + ")=false"); 1848aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } 1858aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev } 1869e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi DictionaryFacilitator dictionaryFacilitatorForLocale = 187d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi mDictionaryFacilitatorCache.get(locale); 188c0eb57124fd295ceb85c3350de3189c40594ee96Dan Zivkovic return dictionaryFacilitatorForLocale.isValidSpellingWord(word); 1899e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 1909e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(); 1919e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 1928403611960cd0b2a40b77275c536e8088c098830Jean Chalard } 1938403611960cd0b2a40b77275c536e8088c098830Jean Chalard 1942b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevov public SuggestionResults getSuggestionResults(final Locale locale, 1952b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevov final ComposedData composedData, final NgramContext ngramContext, 196487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev @Nonnull final Keyboard keyboard) { 1979e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi Integer sessionId = null; 1989e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(); 1999e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 2009e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi sessionId = mSessionIdPool.poll(); 2019e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi DictionaryFacilitator dictionaryFacilitatorForLocale = 202d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi mDictionaryFacilitatorCache.get(locale); 2032b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevov return dictionaryFacilitatorForLocale.getSuggestionResults(composedData, ngramContext, 204487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev keyboard, mSettingsValuesForSuggestion, 205487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev sessionId, SuggestedWords.INPUT_STYLE_TYPING); 2069e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 2079e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi if (sessionId != null) { 2089e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSessionIdPool.add(sessionId); 2099e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2109e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(); 2119e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2129e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2139e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 2149e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public boolean hasMainDictionaryForLocale(final Locale locale) { 2159e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(); 2169e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 2179e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi final DictionaryFacilitator dictionaryFacilitator = 218d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi mDictionaryFacilitatorCache.get(locale); 2198cd53266229895a3e0c6618e3765d57fc5d0b392Jean Chalard return dictionaryFacilitator.hasAtLeastOneInitializedMainDictionary(); 2209e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 2219e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(); 2229e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2239e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2249e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 2259e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi @Override 2269e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public boolean onUnbind(final Intent intent) { 2279e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(MAX_NUM_OF_THREADS_READ_DICTIONARY); 2289e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 22929aa3df3dadeb5829a70652a24b0756e2c9e45caDan Zivkovic mDictionaryFacilitatorCache.closeDictionaries(); 2309e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 2319e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(MAX_NUM_OF_THREADS_READ_DICTIONARY); 2329e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2339e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mKeyboardCache.clear(); 2349e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi return false; 235c160373b6a8e8a536ad8aa2798a33a41d3050f3bJean Chalard } 236c160373b6a8e8a536ad8aa2798a33a41d3050f3bJean Chalard 2379e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public Keyboard getKeyboardForLocale(final Locale locale) { 2389e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi Keyboard keyboard = mKeyboardCache.get(locale); 2399e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi if (keyboard == null) { 2409e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi keyboard = createKeyboardForLocale(locale); 2419e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi if (keyboard != null) { 2429e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mKeyboardCache.put(locale, keyboard); 2439e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2443234123fba901243990972158d023a5d1c273316Jean Chalard } 2459e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi return keyboard; 246a562767a14c7bbac95b25e69e360fc28d6ce9e33Jean Chalard } 247a562767a14c7bbac95b25e69e360fc28d6ce9e33Jean Chalard 2489e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private Keyboard createKeyboardForLocale(final Locale locale) { 2490dab3171d442a4d0acc87cc0019bfcbd4ea4123fJean Chalard final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale); 250244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka final String keyboardLayoutName = getKeyboardLayoutNameForScript(script); 2513895d7f8dc2e4999947f61220b86fa148f433413Yohei Yukawa final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype( 2523895d7f8dc2e4999947f61220b86fa148f433413Yohei Yukawa locale.toString(), keyboardLayoutName); 253204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype); 2549e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi return keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); 2553234123fba901243990972158d023a5d1c273316Jean Chalard } 256204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka 257204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka private KeyboardLayoutSet createKeyboardSetForSpellChecker(final InputMethodSubtype subtype) { 258204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka final EditorInfo editorInfo = new EditorInfo(); 259204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka editorInfo.inputType = InputType.TYPE_CLASS_TEXT; 260204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(this, editorInfo); 261204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka builder.setKeyboardGeometry( 262204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka SPELLCHECKER_DUMMY_KEYBOARD_WIDTH, SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT); 263b86ca76cea9aedf47a81f9272fb59897de3bbbe7Dan Zivkovic builder.setSubtype(RichInputMethodSubtype.getRichInputMethodSubtype(subtype)); 264204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka builder.setIsSpellChecker(true /* isSpellChecker */); 265204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka builder.disableTouchPositionCorrectionData(); 266204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka return builder.build(); 267204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka } 268022c1cc20379767966f4915e2dea65fc0b67c0d8satok} 269