diff options
author | Dan Yu <dyu@cyngn.com> | 2016-08-03 16:50:47 -0700 |
---|---|---|
committer | Gerrit Code Review <gerrit@cyanogenmod.org> | 2016-08-17 15:21:56 -0700 |
commit | 877f8b481a6c6452a3894df14ec527c21d8e2d8c (patch) | |
tree | 464f403d68a2e48db6c32a985dd284c8cefeab46 | |
parent | fa3518fb978580a4eb15b1e9f746afa812ba47df (diff) | |
download | android_packages_inputmethods_LatinIME-877f8b481a6c6452a3894df14ec527c21d8e2d8c.tar.gz android_packages_inputmethods_LatinIME-877f8b481a6c6452a3894df14ec527c21d8e2d8c.tar.bz2 android_packages_inputmethods_LatinIME-877f8b481a6c6452a3894df14ec527c21d8e2d8c.zip |
LatinIME: add shortcut functionality
Add shortcut functionality by creating property
method/property calls to accommodate.
CYNGNOS-3129
Change-Id: I19d07846a405fd3e53dddb2c6db43cb350435c54
5 files changed, 100 insertions, 34 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 9a3ac674e..4171b05ba 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -412,9 +412,11 @@ public final class BinaryDictionary extends Dictionary { outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX], outFlags[FORMAT_WORD_PROPERTY_IS_POSSIBLY_OFFENSIVE_INDEX], outFlags[FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX], + outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], outFlags[FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX], outProbabilityInfo, outNgramPrevWordsArray, outNgramPrevWordIsBeginningOfSentenceArray, - outNgramTargets, outNgramProbabilityInfo); + outNgramTargets, outNgramProbabilityInfo, outShortcutTargets, + outShortcutProbabilities); } public static class GetNextWordPropertyResult { @@ -442,16 +444,19 @@ public final class BinaryDictionary extends Dictionary { } // Add a unigram entry to binary dictionary with unigram attributes in native code. - public boolean addUnigramEntry( - final String word, final int probability, final boolean isBeginningOfSentence, - final boolean isNotAWord, final boolean isPossiblyOffensive, final int timestamp) { + public boolean addUnigramEntry(final String word, final int probability, + final String shortcutTarget, final int shortcutProbability, + final boolean isBeginningOfSentence, final boolean isNotAWord, + final boolean isPossiblyOffensive, final int timestamp) { if (word == null || (word.isEmpty() && !isBeginningOfSentence)) { return false; } final int[] codePoints = StringUtils.toCodePointArray(word); - if (!addUnigramEntryNative(mNativeDict, codePoints, probability, - null /* shortcutTargetCodePoints */, 0 /* shortcutProbability */, - isBeginningOfSentence, isNotAWord, isPossiblyOffensive, timestamp)) { + final int[] shortcutTargetCodePoints = (shortcutTarget != null) ? + StringUtils.toCodePointArray(shortcutTarget) : null; + if (!addUnigramEntryNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, + shortcutProbability, isBeginningOfSentence, isNotAWord, isPossiblyOffensive, + timestamp)) { return false; } mHasUpdated = true; diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index dbd639fe8..f38c44ca3 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -41,6 +41,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary private static final String TAG = ContactsBinaryDictionary.class.getSimpleName(); private static final String NAME = "contacts"; + private static final int FREQUENCY_FOR_CONTACTS = 40; + private static final boolean DEBUG = false; private static final boolean DEBUG_DUMP = false; @@ -100,8 +102,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary Log.d(TAG, "loadAccountVocabulary: " + word); } runGCIfRequiredLocked(true /* mindsBlockByGC */); - addUnigramLocked(word, ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS, - false /* isNotAWord */, false /* isPossiblyOffensive */, + addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */, + 0 /* shortcutFreq */, false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); } } @@ -151,8 +153,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary Log.d(TAG, "addName " + name + ", " + word + ", " + ngramContext); } runGCIfRequiredLocked(true /* mindsBlockByGC */); - addUnigramLocked(word, - ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS, false /* isNotAWord */, + addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, + null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); if (ngramContext.isValid() && mUseFirstLastBigrams) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 907095746..9ec283ec7 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -294,19 +294,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** * Adds unigram information of a word to the dictionary. May overwrite an existing entry. */ + // Add a unigram entry to binary dictionary with unigram attributes in native code. public void addUnigramEntry(final String word, final int frequency, - final boolean isNotAWord, final boolean isPossiblyOffensive, final int timestamp) { + final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, + final boolean isPossiblyOffensive, final int timestamp) { updateDictionaryWithWriteLock(new Runnable() { @Override public void run() { - addUnigramLocked(word, frequency, isNotAWord, isPossiblyOffensive, timestamp); + addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, + isNotAWord, isPossiblyOffensive, timestamp); } }); } protected void addUnigramLocked(final String word, final int frequency, - final boolean isNotAWord, final boolean isPossiblyOffensive, final int timestamp) { - if (!mBinaryDictionary.addUnigramEntry(word, frequency, + final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, + final boolean isPossiblyOffensive, final int timestamp) { + if (!mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq, false /* isBeginningOfSentence */, isNotAWord, isPossiblyOffensive, timestamp)) { Log.e(TAG, "Cannot add unigram entry. word: " + word); } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index fe24ccfc2..bd0e75f6b 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -25,6 +25,7 @@ import android.net.Uri; import android.provider.UserDictionary.Words; import android.text.TextUtils; import android.util.Log; +import android.os.Build; import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -47,7 +48,19 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { private static final int HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY = 250; private static final int LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY = 160; - private static final String[] PROJECTION_QUERY = new String[] {Words.WORD, Words.FREQUENCY}; + // Shortcut frequency is 0~15, with 15 = whitelist. We don't want user dictionary entries + // to auto-correct, so we set this to the highest frequency that won't, i.e. 14. + private static final int USER_DICT_SHORTCUT_FREQUENCY = 14; + + private static final String[] PROJECTION_QUERY_WITH_SHORTCUT = new String[] { + Words.WORD, + Words.SHORTCUT, + Words.FREQUENCY, + }; + private static final String[] PROJECTION_QUERY_WITHOUT_SHORTCUT = new String[] { + Words.WORD, + Words.FREQUENCY, + }; private static final String NAME = "userunigram"; @@ -159,7 +172,20 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { requestArguments = localeElements; } final String requestString = request.toString(); - addWordsFromProjectionLocked(PROJECTION_QUERY, requestString, requestArguments); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + try { + addWordsFromProjectionLocked(PROJECTION_QUERY_WITH_SHORTCUT, requestString, + requestArguments); + } catch (IllegalArgumentException e) { + // This may happen on some non-compliant devices where the declared API is JB+ but + // the SHORTCUT column is not present for some reason. + addWordsFromProjectionLocked(PROJECTION_QUERY_WITHOUT_SHORTCUT, requestString, + requestArguments); + } + } else { + addWordsFromProjectionLocked(PROJECTION_QUERY_WITHOUT_SHORTCUT, requestString, + requestArguments); + } } private void addWordsFromProjectionLocked(final String[] query, String request, @@ -194,20 +220,31 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { } private void addWordsLocked(final Cursor cursor) { + final boolean hasShortcutColumn = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; if (cursor == null) return; if (cursor.moveToFirst()) { final int indexWord = cursor.getColumnIndex(Words.WORD); + final int indexShortcut = hasShortcutColumn ? cursor.getColumnIndex(Words.SHORTCUT) : 0; final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY); while (!cursor.isAfterLast()) { final String word = cursor.getString(indexWord); + final String shortcut = hasShortcutColumn ? cursor.getString(indexShortcut) : null; final int frequency = cursor.getInt(indexFrequency); final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency); // Safeguard against adding really long words. if (word.length() <= MAX_WORD_LENGTH) { runGCIfRequiredLocked(true /* mindsBlockByGC */); - addUnigramLocked(word, adjustedFrequency, false /* isNotAWord */, + addUnigramLocked(word, adjustedFrequency, null /* shortcutTarget */, + 0 /* shortcutFreq */, false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); + if (null != shortcut && shortcut.length() <= MAX_WORD_LENGTH) { + runGCIfRequiredLocked(true /* mindsBlockByGC */); + addUnigramLocked(shortcut, adjustedFrequency, word, + USER_DICT_SHORTCUT_FREQUENCY, true /* isNotAWord */, + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); + } } cursor.moveToNext(); } diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java index 264e75710..ac03dffba 100644 --- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -37,11 +37,13 @@ import javax.annotation.Nullable; public final class WordProperty implements Comparable<WordProperty> { public final String mWord; public final ProbabilityInfo mProbabilityInfo; + public final ArrayList<WeightedString> mShortcutTargets; public final ArrayList<NgramProperty> mNgrams; // TODO: Support mIsBeginningOfSentence. public final boolean mIsBeginningOfSentence; public final boolean mIsNotAWord; public final boolean mIsPossiblyOffensive; + public final boolean mHasShortcuts; public final boolean mHasNgrams; private int mHashCode = 0; @@ -49,10 +51,12 @@ public final class WordProperty implements Comparable<WordProperty> { // TODO: Support n-gram. @UsedForTesting public WordProperty(final String word, final ProbabilityInfo probabilityInfo, - @Nullable final ArrayList<WeightedString> bigrams, - final boolean isNotAWord, final boolean isPossiblyOffensive) { + final ArrayList<WeightedString> shortcutTargets, + @Nullable final ArrayList<WeightedString> bigrams, + final boolean isNotAWord, final boolean isPossiblyOffensive) { mWord = word; mProbabilityInfo = probabilityInfo; + mShortcutTargets = shortcutTargets; if (null == bigrams) { mNgrams = null; } else { @@ -66,30 +70,35 @@ public final class WordProperty implements Comparable<WordProperty> { mIsNotAWord = isNotAWord; mIsPossiblyOffensive = isPossiblyOffensive; mHasNgrams = bigrams != null && !bigrams.isEmpty(); + mHasShortcuts = shortcutTargets != null && !shortcutTargets.isEmpty(); } private static ProbabilityInfo createProbabilityInfoFromArray(final int[] probabilityInfo) { - return new ProbabilityInfo( - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX], - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX], - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX], - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]); + return new ProbabilityInfo( + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX], + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX], + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX], + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]); } // Construct word property using information from native code. // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY. public WordProperty(final int[] codePoints, final boolean isNotAWord, - final boolean isPossiblyOffensive, final boolean hasBigram, - final boolean isBeginningOfSentence, final int[] probabilityInfo, - final ArrayList<int[][]> ngramPrevWordsArray, - final ArrayList<boolean[]> ngramPrevWordIsBeginningOfSentenceArray, - final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo) { + final boolean isPossiblyOffensive, final boolean hasBigram, final boolean hasShortcuts, + final boolean isBeginningOfSentence, final int[] probabilityInfo, + final ArrayList<int[][]> ngramPrevWordsArray, + final ArrayList<boolean[]> ngramPrevWordIsBeginningOfSentenceArray, + final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo, + final ArrayList<int[]> shortcutTargets, + final ArrayList<Integer> shortcutProbabilities) { mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo); + mShortcutTargets = new ArrayList<>(); final ArrayList<NgramProperty> ngrams = new ArrayList<>(); mIsBeginningOfSentence = isBeginningOfSentence; mIsNotAWord = isNotAWord; mIsPossiblyOffensive = isPossiblyOffensive; + mHasShortcuts = hasShortcuts; mHasNgrams = hasBigram; final int relatedNgramCount = ngramTargets.size(); @@ -106,12 +115,20 @@ public final class WordProperty implements Comparable<WordProperty> { wordInfoArray[j] = isBeginningOfSentenceArray[j] ? WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO : new WordInfo(StringUtils.getStringFromNullTerminatedCodePointArray( - prevWords[j])); + prevWords[j])); } final NgramContext ngramContext = new NgramContext(wordInfoArray); ngrams.add(new NgramProperty(ngramTarget, ngramContext)); } mNgrams = ngrams.isEmpty() ? null : ngrams; + + final int shortcutTargetCount = shortcutTargets.size(); + for (int i = 0; i < shortcutTargetCount; i++) { + final String shortcutTargetString = + StringUtils.getStringFromNullTerminatedCodePointArray(shortcutTargets.get(i)); + mShortcutTargets.add( + new WeightedString(shortcutTargetString, shortcutProbabilities.get(i))); + } } // TODO: Remove @@ -137,6 +154,7 @@ public final class WordProperty implements Comparable<WordProperty> { return Arrays.hashCode(new Object[] { word.mWord, word.mProbabilityInfo, + word.mShortcutTargets, word.mNgrams, word.mIsNotAWord, word.mIsPossiblyOffensive @@ -167,10 +185,10 @@ public final class WordProperty implements Comparable<WordProperty> { if (o == this) return true; if (!(o instanceof WordProperty)) return false; WordProperty w = (WordProperty)o; - return mProbabilityInfo.equals(w.mProbabilityInfo) - && mWord.equals(w.mWord) && equals(mNgrams, w.mNgrams) + return mProbabilityInfo.equals(w.mProbabilityInfo) && mWord.equals(w.mWord) + && mShortcutTargets.equals(w.mShortcutTargets) && equals(mNgrams, w.mNgrams) && mIsNotAWord == w.mIsNotAWord && mIsPossiblyOffensive == w.mIsPossiblyOffensive - && mHasNgrams == w.mHasNgrams; + && mHasNgrams == w.mHasNgrams && mHasShortcuts && w.mHasNgrams; } // TDOO: Have a utility method like java.util.Objects.equals. |