Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7d5df0a authored by Dan Yu's avatar Dan Yu Committed by Janet Sun
Browse files

LatinIME: add shortcut functionality

Add shortcut functionality by creating property
method/property calls to accommodate.

CYNGNOS-3129
Change-Id: I19d07846a405fd3e53dddb2c6db43cb350435c54
(cherry picked from commit 877f8b48)
parent fae97933
Loading
Loading
Loading
Loading
+12 −7
Original line number Diff line number Diff line
@@ -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;
+6 −4
Original line number Diff line number Diff line
@@ -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) {
+8 −4
Original line number Diff line number Diff line
@@ -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);
        }
+40 −3
Original line number Diff line number Diff line
@@ -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();
            }
+34 −16
Original line number Diff line number Diff line
@@ -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,
                        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,6 +70,7 @@ 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) {
@@ -79,17 +84,21 @@ public final class WordProperty implements Comparable<WordProperty> {
    // 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 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[]> 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();
@@ -112,6 +121,14 @@ public final class WordProperty implements Comparable<WordProperty> {
            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.