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

Commit c8ac8da4 authored by Keisuke Kuroyanagi's avatar Keisuke Kuroyanagi Committed by Android (Google) Code Review
Browse files

Merge "Prepare ExpandableBinaryDictionary to make it updatable"

parents 979f9f99 c8db6f21
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -42,8 +42,10 @@ abstract public class AbstractDictionaryWriter extends Dictionary {
    abstract public void addUnigramWord(final String word, final String shortcutTarget,
            final int frequency, final boolean isNotAWord);

    // TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve.
    abstract public void addBigramWords(final String word0, final String word1,
            final int frequency, final boolean isValid);
            final int frequency, final boolean isValid,
            final long lastModifiedTime);

    abstract public void removeBigramWords(final String word0, final String word1);

+4 −2
Original line number Diff line number Diff line
@@ -70,7 +70,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
    private final boolean mUseFirstLastBigrams;

    public ContactsBinaryDictionary(final Context context, final Locale locale) {
        super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
        super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS,
                false /* isUpdatable */);
        mLocale = locale;
        mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
        registerObserver(context);
@@ -208,7 +209,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
                            false /* isNotAWord */);
                    if (!TextUtils.isEmpty(prevWord)) {
                        if (mUseFirstLastBigrams) {
                            super.setBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM);
                            super.addBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
                                    0 /* lastModifiedTime */);
                        }
                    }
                    prevWord = word;
+1 −1
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ public class DictionaryWriter extends AbstractDictionaryWriter {

    @Override
    public void addBigramWords(final String word0, final String word1, final int frequency,
            final boolean isValid) {
            final boolean isValid, final long lastModifiedTime) {
        mFusionDictionary.setBigram(word0, word1, frequency);
    }

+161 −53
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import android.os.SystemClock;
import android.util.Log;

import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.CollectionUtils;
@@ -78,6 +79,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     */
    private final String mFilename;

    /** Whether to support dynamically updating the dictionary */
    private final boolean mIsUpdatable;

    /** Controls access to the shared binary dictionary file across multiple instances. */
    private final DictionaryController mSharedDictionaryController;

@@ -113,6 +117,16 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
        return controller;
    }

    private static AbstractDictionaryWriter getDictionaryWriter(final Context context,
            final String dictType, final boolean isUpdatable) {
        if (isUpdatable) {
            // TODO: Employ dynamically updatable DictionaryWriter.
            return new DictionaryWriter(context, dictType);
        } else {
            return new DictionaryWriter(context, dictType);
        }
    }

    /**
     * Creates a new expandable binary dictionary.
     *
@@ -120,15 +134,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     * @param filename The filename for this binary dictionary. Multiple dictionaries with the same
     *        filename is supported.
     * @param dictType the dictionary type, as a human-readable string
     * @param isUpdatable whether to support dynamically updating the dictionary. Please note that
     *        dynamic dictionary has negative effects on memory space and computation time.
     */
    public ExpandableBinaryDictionary(
            final Context context, final String filename, final String dictType) {
    public ExpandableBinaryDictionary(final Context context, final String filename,
            final String dictType, final boolean isUpdatable) {
        super(dictType);
        mFilename = filename;
        mContext = context;
        mIsUpdatable = isUpdatable;
        mBinaryDictionary = null;
        mSharedDictionaryController = getSharedDictionaryController(filename);
        mDictionaryWriter = new DictionaryWriter(context, dictType);
        mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
    }

    protected static String getFilenameWithLocale(final String name, final String localeStr) {
@@ -140,6 +157,16 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
     */
    @Override
    public void close() {
        closeBinaryDictionary();
        mLocalDictionaryController.writeLock().lock();
        try {
            mDictionaryWriter.close();
        } finally {
            mLocalDictionaryController.writeLock().unlock();
        }
    }

    protected void closeBinaryDictionary() {
        // Ensure that no other threads are accessing the local binary dictionary.
        mLocalDictionaryController.writeLock().lock();
        try {
@@ -147,7 +174,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
                mBinaryDictionary.close();
                mBinaryDictionary = null;
            }
            mDictionaryWriter.close();
        } finally {
            mLocalDictionaryController.writeLock().unlock();
        }
@@ -162,37 +188,72 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    }

    /**
     * Sets a word bigram in the dictionary. Used for loading a dictionary.
     * Adds a word bigram in the dictionary. Used for loading a dictionary.
     */
    protected void setBigram(final String prevWord, final String word, final int frequency) {
        mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */);
    protected void addBigram(final String prevWord, final String word, final int frequency,
            final long lastModifiedTime) {
        mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */,
                lastModifiedTime);
    }

    /**
     * Dynamically adds a word unigram to the dictionary.
     * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
     */
    protected void addWordDynamically(final String word, final String shortcutTarget,
            final int frequency, final boolean isNotAWord) {
        mLocalDictionaryController.writeLock().lock();
        if (!mIsUpdatable) {
            Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
            return;
        }
        // TODO: Use a queue to reflect what needs to be reflected.
        if (mLocalDictionaryController.writeLock().tryLock()) {
            try {
                mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
            } finally {
                mLocalDictionaryController.writeLock().unlock();
            }
        }
    }

    /**
     * Dynamically sets a word bigram in the dictionary.
     * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry.
     */
    protected void setBigramDynamically(final String prevWord, final String word,
            final int frequency) {
        mLocalDictionaryController.writeLock().lock();
    protected void addBigramDynamically(final String word0, final String word1,
            final int frequency, final boolean isValid) {
        if (!mIsUpdatable) {
            Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: "
                    + mFilename);
            return;
        }
        // TODO: Use a queue to reflect what needs to be reflected.
        if (mLocalDictionaryController.writeLock().tryLock()) {
            try {
                mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid,
                        0 /* lastTouchedTime */);
            } finally {
                mLocalDictionaryController.writeLock().unlock();
            }
        }
    }

    /**
     * Dynamically remove a word bigram in the dictionary.
     */
    protected void removeBigramDynamically(final String word0, final String word1) {
        if (!mIsUpdatable) {
            Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: "
                    + mFilename);
            return;
        }
        // TODO: Use a queue to reflect what needs to be reflected.
        if (mLocalDictionaryController.writeLock().tryLock()) {
            try {
            mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */);
                mDictionaryWriter.removeBigramWords(word0, word1);
            } finally {
                mLocalDictionaryController.writeLock().unlock();
            }
        }
    }

    @Override
    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
@@ -280,7 +341,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {

        // Build the new binary dictionary
        final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length,
                true /* useFullEditDistance */, null, mDictType, false /* isUpdatable */);
                true /* useFullEditDistance */, null, mDictType, mIsUpdatable);

        if (mBinaryDictionary != null) {
            // Ensure all threads accessing the current dictionary have finished before swapping in
@@ -305,9 +366,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    abstract protected boolean needsToReloadBeforeWriting();

    /**
     * Generates and writes a new binary dictionary based on the contents of the fusion dictionary.
     * Writes a new binary dictionary based on the contents of the fusion dictionary.
     */
    private void generateBinaryDictionary() {
    private void writeBinaryDictionary() {
        if (DEBUG) {
            Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
                    + mSharedDictionaryController.mLastUpdateRequestTime + " update="
@@ -370,6 +431,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
    private final void syncReloadDictionaryInternal() {
        // Ensure that only one thread attempts to read or write to the shared binary dictionary
        // file at the same time.
        mLocalDictionaryController.writeLock().lock();
        try {
            mSharedDictionaryController.writeLock().lock();
            try {
                final long time = SystemClock.uptimeMillis();
@@ -378,34 +441,38 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
                    // If the shared dictionary file does not exist or is out of date, the first
                    // instance that acquires the lock will generate a new one.
                    if (hasContentChanged() || !dictionaryFileExists) {
                    // If the source content has changed or the dictionary does not exist, rebuild
                    // the binary dictionary. Empty dictionaries are supported (in the case where
                    // loadDictionaryAsync() adds nothing) in order to provide a uniform framework.
                        // If the source content has changed or the dictionary does not exist,
                        // rebuild the binary dictionary. Empty dictionaries are supported (in the
                        // case where loadDictionaryAsync() adds nothing) in order to provide a
                        // uniform framework.
                        mSharedDictionaryController.mLastUpdateTime = time;
                    generateBinaryDictionary();
                        writeBinaryDictionary();
                        loadBinaryDictionary();
                    } else {
                    // If not, the reload request was unnecessary so revert LastUpdateRequestTime
                    // to LastUpdateTime.
                        // If not, the reload request was unnecessary so revert
                        // LastUpdateRequestTime to LastUpdateTime.
                        mSharedDictionaryController.mLastUpdateRequestTime =
                                mSharedDictionaryController.mLastUpdateTime;
                    }
                } else if (mBinaryDictionary == null || mLocalDictionaryController.mLastUpdateTime
                        < mSharedDictionaryController.mLastUpdateTime) {
                // Otherwise, if the local dictionary is older than the shared dictionary, load the
                // shared dictionary.
                    // Otherwise, if the local dictionary is older than the shared dictionary, load
                    // the shared dictionary.
                    loadBinaryDictionary();
                }
                if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
                    // Binary dictionary is not valid. Regenerate the dictionary file.
                    mSharedDictionaryController.mLastUpdateTime = time;
                generateBinaryDictionary();
                    writeBinaryDictionary();
                    loadBinaryDictionary();
                }
                mLocalDictionaryController.mLastUpdateTime = time;
            } finally {
                mSharedDictionaryController.writeLock().unlock();
            }
        } finally {
            mLocalDictionaryController.writeLock().unlock();
        }
    }

    // TODO: cache the file's existence so that we avoid doing a disk access each time.
@@ -437,4 +504,45 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
            return (mLastUpdateRequestTime > mLastUpdateTime);
        }
    }

    /**
     * Dynamically adds a word unigram to the dictionary for testing with blocking-lock.
     */
    @UsedForTesting
    protected void addWordDynamicallyForTests(final String word, final String shortcutTarget,
            final int frequency, final boolean isNotAWord) {
        mLocalDictionaryController.writeLock().lock();
        try {
            addWordDynamically(word, shortcutTarget, frequency, isNotAWord);
        } finally {
            mLocalDictionaryController.writeLock().unlock();
        }
    }

    /**
     * Dynamically adds a word bigram in the dictionary for testing with blocking-lock.
     */
    @UsedForTesting
    protected void addBigramDynamicallyForTests(final String word0, final String word1,
            final int frequency, final boolean isValid) {
        mLocalDictionaryController.writeLock().lock();
        try {
            addBigramDynamically(word0, word1, frequency, isValid);
        } finally {
            mLocalDictionaryController.writeLock().unlock();
        }
    }

    /**
     * Dynamically remove a word bigram in the dictionary for testing with blocking-lock.
     */
    @UsedForTesting
    protected void removeBigramDynamicallyForTests(final String word0, final String word1) {
        mLocalDictionaryController.writeLock().lock();
        try {
            removeBigramDynamically(word0, word1);
        } finally {
            mLocalDictionaryController.writeLock().unlock();
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -75,7 +75,8 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {

    public UserBinaryDictionary(final Context context, final String locale,
            final boolean alsoUseMoreRestrictiveLocales) {
        super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER);
        super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER,
                false /* isUpdatable */);
        if (null == locale) throw new NullPointerException(); // Catch the error earlier
        if (SubtypeLocaleUtils.NO_LANGUAGE.equals(locale)) {
            // If we don't have a locale, insert into the "all locales" user dictionary.
Loading