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; 27022c1cc20379767966f4915e2dea65fc0b67c0d8satok 289e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.keyboard.Keyboard; 299e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.keyboard.KeyboardId; 30244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataokaimport com.android.inputmethod.keyboard.KeyboardLayoutSet; 319e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.DictionaryFacilitator; 32d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagiimport com.android.inputmethod.latin.DictionaryFacilitatorLruCache; 33bb0eca57054758ef17b032d2654c1fc5f6b32101Keisuke Kuroyanagiimport com.android.inputmethod.latin.NgramContext; 3459b501a05078e5a9de7cdace19c51ca693076a17Jean Chalardimport com.android.inputmethod.latin.R; 3585ddfe1317a4475269e53f62c2338c335e02e839Jean Chalardimport com.android.inputmethod.latin.RichInputMethodSubtype; 36b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheikimport com.android.inputmethod.latin.SuggestedWords; 372b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevovimport com.android.inputmethod.latin.common.ComposedData; 389e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; 39204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaokaimport com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; 40289299bf66de5fb0c8a378f2366c0760da27077bJean Chalardimport com.android.inputmethod.latin.utils.ScriptUtils; 419e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.utils.SuggestionResults; 423234123fba901243990972158d023a5d1c273316Jean Chalard 433234123fba901243990972158d023a5d1c273316Jean Chalardimport java.util.Locale; 449e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.ConcurrentHashMap; 459e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.ConcurrentLinkedQueue; 469e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.Semaphore; 473234123fba901243990972158d023a5d1c273316Jean Chalard 48487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanevimport javax.annotation.Nonnull; 49487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev 50022c1cc20379767966f4915e2dea65fc0b67c0d8satok/** 51022c1cc20379767966f4915e2dea65fc0b67c0d8satok * Service for spell checking, using LatinIME's dictionaries and mechanisms. 52022c1cc20379767966f4915e2dea65fc0b67c0d8satok */ 53a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class AndroidSpellCheckerService extends SpellCheckerService 54db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard implements SharedPreferences.OnSharedPreferenceChangeListener { 558aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); 568aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev private static final boolean DEBUG = false; 578aa310aa5a1b8e726e78c57361d496a82c569bf6Mario Tanev 58db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts"; 59db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard 60204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480; 616cc318bd6a2c5993cce128d75fe753c3686331c1Keisuke Kuroyanagi private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 301; 62204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka 639e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private static final String DICTIONARY_NAME_PREFIX = "spellcheck_"; 649e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 659e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private static final String[] EMPTY_STRING_ARRAY = new String[0]; 669e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 679e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final int MAX_NUM_OF_THREADS_READ_DICTIONARY = 2; 689e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final Semaphore mSemaphore = new Semaphore(MAX_NUM_OF_THREADS_READ_DICTIONARY, 699e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi true /* fair */); 709e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi // TODO: Make each spell checker session has its own session id. 719e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final ConcurrentLinkedQueue<Integer> mSessionIdPool = new ConcurrentLinkedQueue<>(); 729e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 73d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi private final DictionaryFacilitatorLruCache mDictionaryFacilitatorCache = 74d6a8adcb044dd8b73a1c96776a835b411a978b46Dan Zivkovic new DictionaryFacilitatorLruCache(this /* context */, DICTIONARY_NAME_PREFIX); 759e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final ConcurrentHashMap<Locale, Keyboard> mKeyboardCache = new ConcurrentHashMap<>(); 763234123fba901243990972158d023a5d1c273316Jean Chalard 77a409f009fa410019ad10b1134ff57393443eba33Jean Chalard // The threshold for a suggestion to be considered "recommended". 780028ed3627ff4f37a62a80f3b2c857e373cd5090satok private float mRecommendedThreshold; 799e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi // TODO: make a spell checker option to block offensive words or not 809e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private final SettingsValuesForSuggestion mSettingsValuesForSuggestion = 814e0af43673936c336fff298bc9aeee1d75c3bc92Chieu Nguyen new SettingsValuesForSuggestion(true /* blockPotentiallyOffensive */); 8259b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard 8384ed0966417d93b07c4da2b295244b160d223ce9Satoshi Kataoka public static final String SINGLE_QUOTE = "\u0027"; 8484ed0966417d93b07c4da2b295244b160d223ce9Satoshi Kataoka public static final String APOSTROPHE = "\u2019"; 851830cd1dc8259aa57175f1cf2a3d8797a7a35935Jean Chalard 869e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public AndroidSpellCheckerService() { 879e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi super(); 889e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi for (int i = 0; i < MAX_NUM_OF_THREADS_READ_DICTIONARY; i++) { 899e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSessionIdPool.add(i); 909e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 919e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 929e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 9326fb83c481034cb9dbc9504e60fde40c6b213e97Dan Zivkovic @Override 9426fb83c481034cb9dbc9504e60fde40c6b213e97Dan Zivkovic public void onCreate() { 9559b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard super.onCreate(); 9687eb7ac29c51ba4c341cb663cdbbc5ea74595f2dDan Zivkovic mRecommendedThreshold = Float.parseFloat( 9787eb7ac29c51ba4c341cb663cdbbc5ea74595f2dDan Zivkovic getString(R.string.spellchecker_recommended_threshold_value)); 98db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 99db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard prefs.registerOnSharedPreferenceChangeListener(this); 100db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY); 101db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard } 102db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard 1031467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi public float getRecommendedThreshold() { 1041467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi return mRecommendedThreshold; 1051467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi } 1061467fa0c2642e05aef6f588eef0f704b6da3aee2Keisuke Kuroyanagi 10723a7998edde3b25d4dc7a14b8a653ccd325d2405Dan Zivkovic private static String getKeyboardLayoutNameForLocale(final Locale locale) { 10823a7998edde3b25d4dc7a14b8a653ccd325d2405Dan Zivkovic // See b/19963288. 10923a7998edde3b25d4dc7a14b8a653ccd325d2405Dan Zivkovic if (locale.getLanguage().equals("sr")) { 11023a7998edde3b25d4dc7a14b8a653ccd325d2405Dan Zivkovic return "south_slavic"; 11123a7998edde3b25d4dc7a14b8a653ccd325d2405Dan Zivkovic } 11223a7998edde3b25d4dc7a14b8a653ccd325d2405Dan Zivkovic final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale); 113244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka switch (script) { 114289299bf66de5fb0c8a378f2366c0760da27077bJean Chalard case ScriptUtils.SCRIPT_LATIN: 115244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka return "qwerty"; 116289299bf66de5fb0c8a378f2366c0760da27077bJean Chalard case ScriptUtils.SCRIPT_CYRILLIC: 117244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka return "east_slavic"; 118289299bf66de5fb0c8a378f2366c0760da27077bJean Chalard case ScriptUtils.SCRIPT_GREEK: 119244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka return "greek"; 12016c262abe57a1cc481930391e800c35867f3d0b4Chieu Nguyen case ScriptUtils.SCRIPT_HEBREW: 12116c262abe57a1cc481930391e800c35867f3d0b4Chieu Nguyen return "hebrew"; 122244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka default: 123244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka throw new RuntimeException("Wrong script supplied: " + script); 124244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka } 125244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka } 126244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataoka 127db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard @Override 128db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { 129db5aedb5a5eea5224e5a732b689c97eead2e35f4Jean Chalard if (!PREF_USE_CONTACTS_KEY.equals(key)) return; 13029aa3df3dadeb5829a70652a24b0756e2c9e45caDan Zivkovic final boolean useContactsDictionary = prefs.getBoolean(PREF_USE_CONTACTS_KEY, true); 13129aa3df3dadeb5829a70652a24b0756e2c9e45caDan Zivkovic mDictionaryFacilitatorCache.setUseContactsDictionary(useContactsDictionary); 13259b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard } 13359b501a05078e5a9de7cdace19c51ca693076a17Jean Chalard 1345bcf8ee66ceb38675a6b70fefcb574978e0fae92satok @Override 1355bcf8ee66ceb38675a6b70fefcb574978e0fae92satok public Session createSession() { 13637b19ffe6c9d8335cc0e1c1c50f5b08c778b287cSatoshi Kataoka // Should not refer to AndroidSpellCheckerSession directly considering 13737b19ffe6c9d8335cc0e1c1c50f5b08c778b287cSatoshi Kataoka // that AndroidSpellCheckerSession may be overlaid. 13837b19ffe6c9d8335cc0e1c1c50f5b08c778b287cSatoshi Kataoka return AndroidSpellCheckerSessionFactory.newInstance(this); 1395bcf8ee66ceb38675a6b70fefcb574978e0fae92satok } 1405bcf8ee66ceb38675a6b70fefcb574978e0fae92satok 141df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard /** 142df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * Returns an empty SuggestionsInfo with flags signaling the word is not in the dictionary. 143df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * @param reportAsTypo whether this should include the flag LOOKS_LIKE_TYPO, for red underline. 144df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * @return the empty SuggestionsInfo with the appropriate flags set. 145df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard */ 146df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard public static SuggestionsInfo getNotInDictEmptySuggestions(final boolean reportAsTypo) { 147df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard return new SuggestionsInfo(reportAsTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0, 148df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard EMPTY_STRING_ARRAY); 149cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard } 150cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard 151df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard /** 152df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * Returns an empty suggestionInfo with flags signaling the word is in the dictionary. 153df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard * @return the empty SuggestionsInfo with the appropriate flags set. 154df33982fce6312203ed7446926f31ed92a8ae1caJean Chalard */ 15584ed0966417d93b07c4da2b295244b160d223ce9Satoshi Kataoka public static SuggestionsInfo getInDictEmptySuggestions() { 156cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY, 157cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard EMPTY_STRING_ARRAY); 158cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard } 159cba1af9c5626a2cb1e611735deb72db72d02c4c1Jean Chalard 1609e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public boolean isValidWord(final Locale locale, final String word) { 1619e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(); 1629e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 1639e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi DictionaryFacilitator dictionaryFacilitatorForLocale = 164d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi mDictionaryFacilitatorCache.get(locale); 165c0eb57124fd295ceb85c3350de3189c40594ee96Dan Zivkovic return dictionaryFacilitatorForLocale.isValidSpellingWord(word); 1669e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 1679e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(); 1689e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 1698403611960cd0b2a40b77275c536e8088c098830Jean Chalard } 1708403611960cd0b2a40b77275c536e8088c098830Jean Chalard 1712b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevov public SuggestionResults getSuggestionResults(final Locale locale, 1722b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevov final ComposedData composedData, final NgramContext ngramContext, 173487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev @Nonnull final Keyboard keyboard) { 1749e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi Integer sessionId = null; 1759e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(); 1769e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 1779e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi sessionId = mSessionIdPool.poll(); 1789e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi DictionaryFacilitator dictionaryFacilitatorForLocale = 179d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi mDictionaryFacilitatorCache.get(locale); 1802b8d763c65b2482fcdc7efe301907ac18133fa42Martin Paraskevov return dictionaryFacilitatorForLocale.getSuggestionResults(composedData, ngramContext, 181487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev keyboard, mSettingsValuesForSuggestion, 182487e038ff329b6099ff5343fb2d7bdc60a6fd699Mario Tanev sessionId, SuggestedWords.INPUT_STYLE_TYPING); 1839e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 1849e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi if (sessionId != null) { 1859e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSessionIdPool.add(sessionId); 1869e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 1879e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(); 1889e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 1899e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 1909e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 1919e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public boolean hasMainDictionaryForLocale(final Locale locale) { 1929e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(); 1939e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 1949e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi final DictionaryFacilitator dictionaryFacilitator = 195d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagi mDictionaryFacilitatorCache.get(locale); 1968cd53266229895a3e0c6618e3765d57fc5d0b392Jean Chalard return dictionaryFacilitator.hasAtLeastOneInitializedMainDictionary(); 1979e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 1989e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(); 1999e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2009e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2019e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi 2029e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi @Override 2039e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public boolean onUnbind(final Intent intent) { 2049e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.acquireUninterruptibly(MAX_NUM_OF_THREADS_READ_DICTIONARY); 2059e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi try { 20629aa3df3dadeb5829a70652a24b0756e2c9e45caDan Zivkovic mDictionaryFacilitatorCache.closeDictionaries(); 2079e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } finally { 2089e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mSemaphore.release(MAX_NUM_OF_THREADS_READ_DICTIONARY); 2099e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2109e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mKeyboardCache.clear(); 2119e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi return false; 212c160373b6a8e8a536ad8aa2798a33a41d3050f3bJean Chalard } 213c160373b6a8e8a536ad8aa2798a33a41d3050f3bJean Chalard 2149e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi public Keyboard getKeyboardForLocale(final Locale locale) { 2159e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi Keyboard keyboard = mKeyboardCache.get(locale); 2169e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi if (keyboard == null) { 2179e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi keyboard = createKeyboardForLocale(locale); 2189e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi if (keyboard != null) { 2199e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi mKeyboardCache.put(locale, keyboard); 2209e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi } 2213234123fba901243990972158d023a5d1c273316Jean Chalard } 2229e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi return keyboard; 223a562767a14c7bbac95b25e69e360fc28d6ce9e33Jean Chalard } 224a562767a14c7bbac95b25e69e360fc28d6ce9e33Jean Chalard 2259e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi private Keyboard createKeyboardForLocale(final Locale locale) { 22623a7998edde3b25d4dc7a14b8a653ccd325d2405Dan Zivkovic final String keyboardLayoutName = getKeyboardLayoutNameForLocale(locale); 2273895d7f8dc2e4999947f61220b86fa148f433413Yohei Yukawa final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype( 2283895d7f8dc2e4999947f61220b86fa148f433413Yohei Yukawa locale.toString(), keyboardLayoutName); 229204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype); 2309e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi return keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); 2313234123fba901243990972158d023a5d1c273316Jean Chalard } 232204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka 233204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka private KeyboardLayoutSet createKeyboardSetForSpellChecker(final InputMethodSubtype subtype) { 234204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka final EditorInfo editorInfo = new EditorInfo(); 235204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka editorInfo.inputType = InputType.TYPE_CLASS_TEXT; 236204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(this, editorInfo); 237204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka builder.setKeyboardGeometry( 238204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka SPELLCHECKER_DUMMY_KEYBOARD_WIDTH, SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT); 239b86ca76cea9aedf47a81f9272fb59897de3bbbe7Dan Zivkovic builder.setSubtype(RichInputMethodSubtype.getRichInputMethodSubtype(subtype)); 240204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka builder.setIsSpellChecker(true /* isSpellChecker */); 241204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka builder.disableTouchPositionCorrectionData(); 242204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka return builder.build(); 243204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka } 244022c1cc20379767966f4915e2dea65fc0b67c0d8satok} 245