Suggest.java revision 2dae79b1966a7970c25c8b79beec1c95c13f6c87
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/* 2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project 3e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 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 7e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * 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. 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.text.TextUtils; 20923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 21043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalardimport com.android.inputmethod.keyboard.ProximityInfo; 22def4551c2a570e7f575b2e9303506d790c2f335fJean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 232dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaokaimport com.android.inputmethod.latin.define.DebugFlags; 24b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagiimport com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; 25b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasaimport com.android.inputmethod.latin.utils.AutoCorrectionUtils; 26e784148ae6872942434eaa55ca32b4c6442cc8e8Keisuke Kuroyanagiimport com.android.inputmethod.latin.utils.BinaryDictionaryUtils; 27e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.StringUtils; 28adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagiimport com.android.inputmethod.latin.utils.SuggestionResults; 29043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard 30fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.ArrayList; 31cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalardimport java.util.Locale; 32fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka 33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 34e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * This class loads a dictionary and provides a list of suggestions for a given sequence of 35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * characters. This includes corrections and completions. 36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 37a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class Suggest { 3882411d47ba7e8133ed2390c6920945e139a738cesatok public static final String TAG = Suggest.class.getSimpleName(); 39cdbbea735f590784791f0c1fe33a514c4e864836satok 40f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka // Session id for 41bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}. 42f1233b58c2d81b575c92339f146cfe0f73a992faKeisuke Kuroyanagi // We are sharing the same ID between typing and gesture to save RAM footprint. 43f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka public static final int SESSION_TYPING = 0; 44f1233b58c2d81b575c92339f146cfe0f73a992faKeisuke Kuroyanagi public static final int SESSION_GESTURE = 0; 45f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka 465475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_OFF 47923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public static final int CORRECTION_NONE = 0; 485475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_ON 494606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard public static final int CORRECTION_FULL = 1; 50979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 51a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // Close to -2**31 52a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; 53a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 542dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaoka private static final boolean DBG = DebugFlags.DEBUG_ENABLED; 55a1035be6d877cafda95b2761f9697474b79deeb8Keisuke Kuroyanagi private final DictionaryFacilitator mDictionaryFacilitator; 56979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 570028ed3627ff4f37a62a80f3b2c857e373cd5090satok private float mAutoCorrectionThreshold; 58979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 59a1035be6d877cafda95b2761f9697474b79deeb8Keisuke Kuroyanagi public Suggest(final DictionaryFacilitator dictionaryFacilitator) { 60a1035be6d877cafda95b2761f9697474b79deeb8Keisuke Kuroyanagi mDictionaryFacilitator = dictionaryFacilitator; 61a1035be6d877cafda95b2761f9697474b79deeb8Keisuke Kuroyanagi } 62a1035be6d877cafda95b2761f9697474b79deeb8Keisuke Kuroyanagi 63adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagi public Locale getLocale() { 64adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagi return mDictionaryFacilitator.getLocale(); 65366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka } 66366c0c5198f43279f4671a196556124f41297c0cSatoshi Kataoka 67adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagi public void setAutoCorrectionThreshold(final float threshold) { 681b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka mAutoCorrectionThreshold = threshold; 69b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda } 70b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda 719666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada public interface OnGetSuggestedWordsCallback { 729666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada public void onGetSuggestedWords(final SuggestedWords suggestedWords); 739666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada } 749666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada 759666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada public void getSuggestedWords(final WordComposer wordComposer, 7683c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, 77b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi final SettingsValuesForSuggestion settingsValuesForSuggestion, 78b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi final boolean isCorrectionEnabled, final int sessionId, final int sequenceNumber, 799666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada final OnGetSuggestedWordsCallback callback) { 80d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka if (wordComposer.isBatchMode()) { 8183c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi getSuggestedWordsForBatchInput(wordComposer, prevWordsInfo, proximityInfo, 82b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi settingsValuesForSuggestion, sessionId, sequenceNumber, callback); 83d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } else { 8483c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi getSuggestedWordsForTypingInput(wordComposer, prevWordsInfo, proximityInfo, 85b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi settingsValuesForSuggestion, isCorrectionEnabled, sequenceNumber, callback); 86d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 87d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 88d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 89e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard private static ArrayList<SuggestedWordInfo> getTransformedSuggestedWordInfoList( 90e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final WordComposer wordComposer, final SuggestionResults results, 91e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final int trailingSingleQuotesCount) { 92e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final boolean shouldMakeSuggestionsAllUpperCase = wordComposer.isAllUpperCase() 93e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard && !wordComposer.isResumed(); 94e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final boolean isOnlyFirstCharCapitalized = 95e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard wordComposer.isOrWillBeOnlyFirstCharCapitalized(); 96e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard 97e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final ArrayList<SuggestedWordInfo> suggestionsContainer = new ArrayList<>(results); 98e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final int suggestionsCount = suggestionsContainer.size(); 99e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard if (isOnlyFirstCharCapitalized || shouldMakeSuggestionsAllUpperCase 100e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard || 0 != trailingSingleQuotesCount) { 101e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard for (int i = 0; i < suggestionsCount; ++i) { 102e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 103e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 104e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard wordInfo, results.mLocale, shouldMakeSuggestionsAllUpperCase, 105e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard isOnlyFirstCharCapitalized, trailingSingleQuotesCount); 106e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard suggestionsContainer.set(i, transformedWordInfo); 107e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard } 108e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard } 109e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard return suggestionsContainer; 110e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard } 111e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard 112e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard private static String getWhitelistedWordOrNull(final ArrayList<SuggestedWordInfo> suggestions) { 113e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard if (suggestions.isEmpty()) { 114e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard return null; 115e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard } 116e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final SuggestedWordInfo firstSuggestedWordInfo = suggestions.get(0); 117e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) { 118e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard return null; 119e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard } 120e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard return firstSuggestedWordInfo.mWord; 121e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard } 122e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard 1239666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // Retrieves suggestions for the typing input 1249666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // and calls the callback function with the suggestions. 1259666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada private void getSuggestedWordsForTypingInput(final WordComposer wordComposer, 12683c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, 127b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi final SettingsValuesForSuggestion settingsValuesForSuggestion, 128b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi final boolean isCorrectionEnabled, final int sequenceNumber, 129c130be877987afe675869d2cbea9d5a49b4ad419Jean Chalard final OnGetSuggestedWordsCallback callback) { 130c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard final String typedWord = wordComposer.getTypedWord(); 13134873a66f03e0b9945474213fa2bc48cc272a7caKeisuke Kuroyanagi final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(typedWord); 13210abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard final String consideredWord = trailingSingleQuotesCount > 0 13310abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) 134117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard : typedWord; 135979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 136adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagi final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( 137b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, 138b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi SESSION_TYPING); 1391ec3f158d2532d71c8660f02710b342c7a16f330Jean Chalard final ArrayList<SuggestedWordInfo> suggestionsContainer = 140e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, 141e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard trailingSingleQuotesCount); 142b740886aeb47f8f3fb32bca7a7faa13d5756bd74Jean Chalard final boolean didRemoveTypedWord = 143e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard SuggestedWordInfo.removeDups(wordComposer.getTypedWord(), suggestionsContainer); 1441ec3f158d2532d71c8660f02710b342c7a16f330Jean Chalard 145e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final String whitelistedWord = getWhitelistedWordOrNull(suggestionsContainer); 146e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard final boolean resultsArePredictions = !wordComposer.isComposingWord(); 1477b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard 148b740886aeb47f8f3fb32bca7a7faa13d5756bd74Jean Chalard // We allow auto-correction if we have a whitelisted word, or if the word had more than 149b740886aeb47f8f3fb32bca7a7faa13d5756bd74Jean Chalard // one char and was not suggested. 150b740886aeb47f8f3fb32bca7a7faa13d5756bd74Jean Chalard final boolean allowsToBeAutoCorrected = (null != whitelistedWord) 151b740886aeb47f8f3fb32bca7a7faa13d5756bd74Jean Chalard || (consideredWord.length() > 1 && !didRemoveTypedWord); 152caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard 15367af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard final boolean hasAutoCorrection; 154966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because 155966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // any attempt to do auto-correction is already shielded with a test for this flag; at the 156966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // same time, it feels wrong that the SuggestedWord object includes information about 157966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the current settings. It may also be useful to know, when the setting is off, whether 158966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the word *would* have been auto-corrected. 159e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard if (!isCorrectionEnabled || !allowsToBeAutoCorrected || resultsArePredictions 160e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard || suggestionResults.isEmpty() || wordComposer.hasDigits() 161a374482719ef9f38395e7e39884433cc72e9cdb7Keisuke Kuroyanagi || wordComposer.isMostlyCaps() || wordComposer.isResumed() 162adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagi || !mDictionaryFacilitator.hasInitializedMainDictionary() 163e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { 16490d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // If we don't have a main dictionary, we never want to auto-correct. The reason for 16590d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // this is, the user may have a contact whose name happens to match a valid word in 16690d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // their language, and it will unexpectedly auto-correct. For example, if the user 16790d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // types in English with no dictionary and has a "Will" in their contact list, "will" 16890d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no 16990d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // auto-correct. 170f309f2288b0652df0b611ed3f86897c939e68d63Jean Chalard // Also, shortcuts should never auto-correct unless they are whitelist entries. 171f309f2288b0652df0b611ed3f86897c939e68d63Jean Chalard // TODO: we may want to have shortcut-only entries auto-correct in the future. 172ea578f6b1dbcf04ffcc9c673f72a38ed2cfecdfcJean Chalard hasAutoCorrection = false; 17394b20c90d86aa042c2f361597665045271956decJean Chalard } else { 174b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasa hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( 175e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard suggestionResults.first(), consideredWord, mAutoCorrectionThreshold); 17694b20c90d86aa042c2f361597665045271956decJean Chalard } 1779f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 178f89a75134b03bd2675c85249a184c09f83c6f80cJean Chalard if (!TextUtils.isEmpty(typedWord)) { 179bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, 18024eec0fa680f97e64d1fa0df754acbad95ed9a76Jean Chalard SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, 181e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard Dictionary.DICTIONARY_USER_TYPED, 18224aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, 18324aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); 18428eeb35d149468514a65379e9d0d1672cf26981eJean Chalard } 1859f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 186def4551c2a570e7f575b2e9303506d790c2f335fJean Chalard final ArrayList<SuggestedWordInfo> suggestionsList; 187bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard if (DBG && !suggestionsContainer.isEmpty()) { 188bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer); 189ed9986824e1339855376771ad29fae4de921a029Jean Chalard } else { 190bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = suggestionsContainer; 1918553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 192ed9986824e1339855376771ad29fae4de921a029Jean Chalard 193e83e79cb055fbfe5171fb79a2224e7d9e2cda4d2Jean Chalard callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, 194e83e79cb055fbfe5171fb79a2224e7d9e2cda4d2Jean Chalard suggestionResults.mRawSuggestions, 195b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // TODO: this first argument is lying. If this is a whitelisted word which is an 196b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // actual word, it says typedWordValid = false, which looks wrong. We should either 197b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // rename the attribute or change the value. 198e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard !resultsArePredictions && !allowsToBeAutoCorrected /* typedWordValid */, 199e83e79cb055fbfe5171fb79a2224e7d9e2cda4d2Jean Chalard hasAutoCorrection /* willAutoCorrect */, 200e5a8615de706e47ec0a25022aed4df44f4d4b155Jean Chalard false /* isObsoleteSuggestions */, resultsArePredictions, sequenceNumber)); 201923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 202923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 2039666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // Retrieves suggestions for the batch input 2049666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada // and calls the callback function with the suggestions. 2059666a228153bb2269da8983762bdd47e448f2cecYuichiro Hanada private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, 20683c40a2301a0b5a42a75eecada48e7887a7c940eKeisuke Kuroyanagi final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, 207b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi final SettingsValuesForSuggestion settingsValuesForSuggestion, 208c130be877987afe675869d2cbea9d5a49b4ad419Jean Chalard final int sessionId, final int sequenceNumber, 209c130be877987afe675869d2cbea9d5a49b4ad419Jean Chalard final OnGetSuggestedWordsCallback callback) { 210adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagi final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( 211b8a9479b57007edb5cb12c628797f89a8164f596Keisuke Kuroyanagi wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, sessionId); 212d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final ArrayList<SuggestedWordInfo> suggestionsContainer = 213a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka new ArrayList<>(suggestionResults); 214eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int suggestionsCount = suggestionsContainer.size(); 2151eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); 2161eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isAllUpperCase = wordComposer.isAllUpperCase(); 217eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang if (isFirstCharCapitalized || isAllUpperCase) { 218eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang for (int i = 0; i < suggestionsCount; ++i) { 219eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 220eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 221adfb262797023c4ca57bb470e547f90c88f638caKeisuke Kuroyanagi wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized, 222eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang 0 /* trailingSingleQuotesCount */); 223eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang suggestionsContainer.set(i, transformedWordInfo); 224eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 225eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 226d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 227d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard if (suggestionsContainer.size() > 1 && TextUtils.equals(suggestionsContainer.get(0).mWord, 228d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard wordComposer.getRejectedBatchModeSuggestion())) { 229d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard final SuggestedWordInfo rejected = suggestionsContainer.remove(0); 230d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard suggestionsContainer.add(1, rejected); 231d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard } 232fdebf4005f849a4a2875b686d239a817ca043842Jean Chalard SuggestedWordInfo.removeDups(null /* typedWord */, suggestionsContainer); 233a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 234a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // For some reason some suggestions with MIN_VALUE are making their way here. 235a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // TODO: Find a more robust way to detect distractors. 236a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard for (int i = suggestionsContainer.size() - 1; i >= 0; --i) { 237a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard if (suggestionsContainer.get(i).mScore < SUPPRESS_SUGGEST_THRESHOLD) { 238a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard suggestionsContainer.remove(i); 239a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard } 240a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard } 241a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 242eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // In the batch input mode, the most relevant suggested word should act as a "typed word" 243eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). 244e83e79cb055fbfe5171fb79a2224e7d9e2cda4d2Jean Chalard callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, 245e83e79cb055fbfe5171fb79a2224e7d9e2cda4d2Jean Chalard suggestionResults.mRawSuggestions, 246d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka true /* typedWordValid */, 247eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang false /* willAutoCorrect */, 248d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isObsoleteSuggestions */, 249c130be877987afe675869d2cbea9d5a49b4ad419Jean Chalard false /* isPrediction */, sequenceNumber)); 250d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 251d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 2520d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( 2537e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) { 2547e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo typedWordInfo = suggestions.get(0); 2557e518d8b8358c96b94b900f0917cdc5fd8190ce1satok typedWordInfo.setDebugString("+"); 2560d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final int suggestionsSize = suggestions.size(); 257a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>(suggestionsSize); 2587e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(typedWordInfo); 2590d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // Note: i here is the index in mScores[], but the index in mSuggestions is one more 2600d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // than i because we added the typed word to mSuggestions without touching mScores. 2617e518d8b8358c96b94b900f0917cdc5fd8190ce1satok for (int i = 0; i < suggestionsSize - 1; ++i) { 2627e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo cur = suggestions.get(i + 1); 263e784148ae6872942434eaa55ca32b4c6442cc8e8Keisuke Kuroyanagi final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore( 264db1939dbaa1de59eaf5693e2c89b02b323e9aac8satok typedWord, cur.toString(), cur.mScore); 2650d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final String scoreInfoString; 2660d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard if (normalizedScore > 0) { 26794027c7201a376107a35ec78cd21db1905662601Tadashi G. Takaoka scoreInfoString = String.format( 2682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa Locale.ROOT, "%d (%4.2f), %s", cur.mScore, normalizedScore, 2692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa cur.mSourceDict.mDictType); 2700d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } else { 2717e518d8b8358c96b94b900f0917cdc5fd8190ce1satok scoreInfoString = Integer.toString(cur.mScore); 2720d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 2737e518d8b8358c96b94b900f0917cdc5fd8190ce1satok cur.setDebugString(scoreInfoString); 2747e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(cur); 2750d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 2760d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard return suggestionsList; 2770d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 2780d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard 279e8f717943f7063444cd1c777e8dd03dc738f3c4aJean Chalard /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo( 280ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, 281367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) { 2829011b89f4ea0d73f1ad78b2dd0a6557b950fddd9Jean Chalard final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); 283ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard if (isAllUpperCase) { 284bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka sb.append(wordInfo.mWord.toUpperCase(locale)); 285367c199de16f7ce8e608bdf38bf35df8995e18a0Jean Chalard } else if (isOnlyFirstCharCapitalized) { 28699b93d17d53c2d587c45373831b327f7851ec0a8Jean Chalard sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale)); 287ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } else { 288ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard sb.append(wordInfo.mWord); 289ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 290e8f717943f7063444cd1c777e8dd03dc738f3c4aJean Chalard // Appending quotes is here to help people quote words. However, it's not helpful 291e8f717943f7063444cd1c777e8dd03dc738f3c4aJean Chalard // when they type words with quotes toward the end like "it's" or "didn't", where 292e8f717943f7063444cd1c777e8dd03dc738f3c4aJean Chalard // it's more likely the user missed the last character (or didn't type it yet). 293e8f717943f7063444cd1c777e8dd03dc738f3c4aJean Chalard final int quotesToAppend = trailingSingleQuotesCount 294e8f717943f7063444cd1c777e8dd03dc738f3c4aJean Chalard - (-1 == wordInfo.mWord.indexOf(Constants.CODE_SINGLE_QUOTE) ? 0 : 1); 295e8f717943f7063444cd1c777e8dd03dc738f3c4aJean Chalard for (int i = quotesToAppend - 1; i >= 0; --i) { 296240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE); 297ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 2984bffac6db9e5800ecc58ba12d86b98a184779ebaKeisuke Kuroyanagi return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKindAndFlags, 29924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord, 3005b5ed3d6092ea539d8cfebd786c63ec0c784040bJean Chalard wordInfo.mAutoCommitFirstWordConfidence); 301ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 302923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 303