AndroidSpellCheckerService.java revision 29aa3df3dadeb5829a70652a24b0756e2c9e45ca
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;
31b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheikimport com.android.inputmethod.keyboard.KeyboardLayout;
32244a24e3685f3fc1d0cbfaf375ad137f917740c2Satoshi Kataokaimport com.android.inputmethod.keyboard.KeyboardLayoutSet;
339e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.keyboard.ProximityInfo;
349e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.DictionaryFacilitator;
35d267764d5abf12f95d09d1ec8f02548d992ca612Keisuke Kuroyanagiimport com.android.inputmethod.latin.DictionaryFacilitatorLruCache;
36bb0eca57054758ef17b032d2654c1fc5f6b32101Keisuke Kuroyanagiimport com.android.inputmethod.latin.NgramContext;
3759b501a05078e5a9de7cdace19c51ca693076a17Jean Chalardimport com.android.inputmethod.latin.R;
3885ddfe1317a4475269e53f62c2338c335e02e839Jean Chalardimport com.android.inputmethod.latin.RichInputMethodSubtype;
39b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheikimport com.android.inputmethod.latin.SuggestedWords;
409e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
41204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaokaimport com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
42289299bf66de5fb0c8a378f2366c0760da27077bJean Chalardimport com.android.inputmethod.latin.utils.ScriptUtils;
439e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.utils.SuggestionResults;
449e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport com.android.inputmethod.latin.WordComposer;
453234123fba901243990972158d023a5d1c273316Jean Chalard
463234123fba901243990972158d023a5d1c273316Jean Chalardimport java.util.Locale;
479e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.ConcurrentHashMap;
489e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.ConcurrentLinkedQueue;
499e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagiimport java.util.concurrent.Semaphore;
503234123fba901243990972158d023a5d1c273316Jean Chalard
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
1949e76304d6004c43c3149bc2df460af2a00b18a4fKeisuke Kuroyanagi    public SuggestionResults getSuggestionResults(final Locale locale, final WordComposer composer,
195b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik            final NgramContext ngramContext, final ProximityInfo proximityInfo,
196b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik            final KeyboardLayout keyboardLayout) {
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);
203bb0eca57054758ef17b032d2654c1fc5f6b32101Keisuke Kuroyanagi            return dictionaryFacilitatorForLocale.getSuggestionResults(composer, ngramContext,
204ecab6aff5908bfd5b34670d2e2bb3696627fa47cJean Chalard                    proximityInfo.getNativeProximityInfo(), mSettingsValuesForSuggestion,
205b00c054125d9f2aa31c2147920cc52cbf2a45cccMohammadinamul Sheik                    sessionId, SuggestedWords.INPUT_STYLE_TYPING, keyboardLayout);
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);
26385ddfe1317a4475269e53f62c2338c335e02e839Jean Chalard        builder.setSubtype(new RichInputMethodSubtype(subtype));
264204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka        builder.setIsSpellChecker(true /* isSpellChecker */);
265204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka        builder.disableTouchPositionCorrectionData();
266204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka        return builder.build();
267204e7b140171a0a8b954cf508da139e93c3b2b2cTadashi G. Takaoka    }
268022c1cc20379767966f4915e2dea65fc0b67c0d8satok}
269