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

Commit dca305dd authored by satok's avatar satok Committed by Android (Google) Code Review
Browse files

Merge "Move the auto correction functionalities to AutoCorrection.java"

parents 4503e2ea 9f67e12a
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.inputmethod.latin;

import android.util.Log;

import java.util.ArrayList;

public class AutoCorrection {
    private static final boolean DBG = LatinImeLogger.sDBG;
    private static final String TAG = AutoCorrection.class.getSimpleName();
    private boolean mHasAutoCorrection;
    private CharSequence mAutoCorrectionWord;
    private double mNormalizedScore;

    public void init() {
        mHasAutoCorrection = false;
        mAutoCorrectionWord = null;
        mNormalizedScore = Integer.MIN_VALUE;
    }

    public boolean hasAutoCorrection() {
        return mHasAutoCorrection;
    }

    public CharSequence getAutoCorrectionWord() {
        return mAutoCorrectionWord;
    }

    public double getNormalizedScore() {
        return mNormalizedScore;
    }

    public void updateAutoCorrectionStatus(Suggest suggest,
            WordComposer wordComposer, ArrayList<CharSequence> suggestions, int[] priorities,
            CharSequence typedWord, double autoCorrectionThreshold, int correctionMode,
            CharSequence quickFixedWord) {
        if (hasAutoCorrectionForTypedWord(
                suggest, wordComposer, suggestions, typedWord, correctionMode)) {
            mHasAutoCorrection = true;
            mAutoCorrectionWord = typedWord;
        } else if (hasAutoCorrectForBinaryDictionary(wordComposer, suggestions, correctionMode,
                priorities, typedWord, autoCorrectionThreshold)) {
            mHasAutoCorrection = true;
            mAutoCorrectionWord = suggestions.get(0);
        } else if (hasAutoCorrectionForQuickFix(quickFixedWord)) {
            mHasAutoCorrection = true;
            mAutoCorrectionWord = quickFixedWord;
        }
    }

    private boolean hasAutoCorrectionForTypedWord(Suggest suggest, WordComposer wordComposer,
            ArrayList<CharSequence> suggestions, CharSequence typedWord, int correctionMode) {
        return wordComposer.size() > 1 && suggestions.size() > 0 && suggest.isValidWord(typedWord)
                && (correctionMode == Suggest.CORRECTION_FULL
                || correctionMode == Suggest.CORRECTION_FULL_BIGRAM);
    }

    private boolean hasAutoCorrectForBinaryDictionary(WordComposer wordComposer,
            ArrayList<CharSequence> suggestions, int correctionMode, int[] priorities,
            CharSequence typedWord, double autoCorrectionThreshold) {
        if (wordComposer.size() > 1 && (correctionMode == Suggest.CORRECTION_FULL
                || correctionMode == Suggest.CORRECTION_FULL_BIGRAM)
                && typedWord != null && suggestions.size() > 0 && priorities.length > 0) {
            final CharSequence autoCorrectionCandidate = suggestions.get(0);
            final int autoCorrectionCandidateScore = priorities[0];
            // TODO: when the normalized score of the first suggestion is nearly equals to
            //       the normalized score of the second suggestion, behave less aggressive.
            mNormalizedScore = Utils.calcNormalizedScore(
                    typedWord,autoCorrectionCandidate, autoCorrectionCandidateScore);
            if (DBG) {
                Log.d(TAG, "Normalized " + typedWord + "," + autoCorrectionCandidate + ","
                        + autoCorrectionCandidateScore + ", " + mNormalizedScore
                        + "(" + autoCorrectionThreshold + ")");
            }
            if (mNormalizedScore >= autoCorrectionThreshold) {
                if (DBG) {
                    Log.d(TAG, "Auto corrected by S-threshold.");
                }
                return true;
            }
        }
        return false;
    }

    private boolean hasAutoCorrectionForQuickFix(CharSequence quickFixedWord) {
        return quickFixedWord != null;
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -1530,7 +1530,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    }

    private void showSuggestions(WordComposer word) {
        // TODO Maybe need better way of retrieving previous word
        // TODO: May need a better way of retrieving previous word
        CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
                mWordSeparators);
        SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
@@ -2052,6 +2052,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    }

    private void updateCorrectionMode() {
        // TODO: cleanup messy flags
        mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
        mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes)
                && !mInputTypeNoAutoCorrect && mHasDictionary;
+49 −65
Original line number Diff line number Diff line
@@ -67,6 +67,8 @@ public class Suggest implements Dictionary.WordCallback {

    private static final boolean DBG = LatinImeLogger.sDBG;

    private AutoCorrection mAutoCorrection;

    private BinaryDictionary mMainDict;

    private Dictionary mUserDictionary;
@@ -90,7 +92,6 @@ public class Suggest implements Dictionary.WordCallback {
    private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
    ArrayList<CharSequence> mBigramSuggestions  = new ArrayList<CharSequence>();
    private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
    private boolean mHasAutoCorrection;
    private String mLowerOriginalWord;

    // TODO: Remove these member variables by passing more context to addWord() callback method
@@ -101,12 +102,16 @@ public class Suggest implements Dictionary.WordCallback {

    public Suggest(Context context, int dictionaryResId) {
        mMainDict = BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN);
        initPool();
        init();
    }

    // For unit test
    /* package */ Suggest(File dictionary, long startOffset, long length) {
    /* package for test */ Suggest(File dictionary, long startOffset, long length) {
        mMainDict = BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN);
        init();
    }

    private void init() {
        mAutoCorrection = new AutoCorrection();
        initPool();
    }

@@ -205,7 +210,7 @@ public class Suggest implements Dictionary.WordCallback {
    public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
            CharSequence prevWordForBigram) {
        LatinImeLogger.onStartSuggestion(prevWordForBigram);
        mHasAutoCorrection = false;
        mAutoCorrection.init();
        mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
        mIsAllUpperCase = wordComposer.isAllUpperCase();
        collectGarbage(mSuggestions, mPrefMaxSuggestions);
@@ -224,7 +229,6 @@ public class Suggest implements Dictionary.WordCallback {
            mLowerOriginalWord = "";
        }

        double normalizedScore = Integer.MIN_VALUE;
        if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
                || mCorrectionMode == CORRECTION_BASIC)) {
            // At first character typed, search only the bigrams
@@ -273,86 +277,67 @@ public class Suggest implements Dictionary.WordCallback {
                if (mContactsDictionary != null) {
                    mContactsDictionary.getWords(wordComposer, this);
                }

                if (mSuggestions.size() > 0 && isValidWord(typedWord)
                        && (mCorrectionMode == CORRECTION_FULL
                        || mCorrectionMode == CORRECTION_FULL_BIGRAM)) {
                    if (DBG) {
                        Log.d(TAG, "Auto corrected by CORRECTION_FULL.");
                    }
                    mHasAutoCorrection = true;
                }
            }
            if (mMainDict != null) mMainDict.getWords(wordComposer, this);
            if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM)
                    && mSuggestions.size() > 0 && mPriorities.length > 0) {
                // TODO: when the normalized score of the first suggestion is nearly equals to
                //       the normalized score of the second suggestion, behave less aggressive.
                normalizedScore = Utils.calcNormalizedScore(
                        typedWord, mSuggestions.get(0), mPriorities[0]);
                if (DBG) {
                    Log.d(TAG, "Normalized " + typedWord + "," + mSuggestions.get(0) + ","
                            + mPriorities[0] + ", " + normalizedScore
                            + "(" + mAutoCorrectionThreshold + ")");
                }
                if (normalizedScore >= mAutoCorrectionThreshold) {
                    if (DBG) {
                        Log.d(TAG, "Auto corrected by S-threshold.");
                    }
                    mHasAutoCorrection = true;
                }
            }
        }
        CharSequence autoText = null;
        final String typedWordString = typedWord == null ? null : typedWord.toString();
        if (typedWord != null) {
            mSuggestions.add(0, typedWord.toString());
        }
            // Apply quick fix only for the typed word.
            if (mQuickFixesEnabled) {
            int i = 0;
            int max = 6;
            // Don't autotext the suggestions from the dictionaries
            if (mCorrectionMode == CORRECTION_BASIC) max = 1;
            while (i < mSuggestions.size() && i < max) {
                String suggestedWord = mSuggestions.get(i).toString().toLowerCase();
                CharSequence autoText =
                        AutoText.get(suggestedWord, 0, suggestedWord.length(), view);
                final String lowerCaseTypedWord = typedWordString.toLowerCase();
                CharSequence tempAutoText =
                        AutoText.get(lowerCaseTypedWord, 0, lowerCaseTypedWord.length(), view);
                // Is there an AutoText (also known as Quick Fixes) correction?
                boolean canAdd = autoText != null;
                // Capitalize as needed
                final int autoTextLength = autoText != null ? autoText.length() : 0;
                if (autoTextLength > 0 && (mIsAllUpperCase || mIsFirstCharCapitalized)) {
                    int poolSize = mStringPool.size();
                    StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(
                            poolSize - 1) : new StringBuilder(getApproxMaxWordLength());
                if (!TextUtils.isEmpty(tempAutoText)
                        && (mIsAllUpperCase || mIsFirstCharCapitalized)) {
                    final int tempAutoTextLength = tempAutoText.length();
                    final int poolSize = mStringPool.size();
                    final StringBuilder sb =
                            poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
                                    : new StringBuilder(getApproxMaxWordLength());
                    sb.setLength(0);
                    if (mIsAllUpperCase) {
                        sb.append(autoText.toString().toUpperCase());
                        sb.append(tempAutoText.toString().toUpperCase());
                    } else if (mIsFirstCharCapitalized) {
                        sb.append(Character.toUpperCase(autoText.charAt(0)));
                        if (autoTextLength > 1) {
                            sb.append(autoText.subSequence(1, autoTextLength));
                        sb.append(Character.toUpperCase(tempAutoText.charAt(0)));
                        if (tempAutoTextLength > 1) {
                            sb.append(tempAutoText.subSequence(1, tempAutoTextLength));
                        }
                    }
                    autoText = sb.toString();
                    tempAutoText = sb.toString();
                }
                boolean canAdd = tempAutoText != null;
                // Is that correction already the current prediction (or original word)?
                canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i));
                canAdd &= !TextUtils.equals(tempAutoText, typedWord);
                // Is that correction already the next predicted word?
                if (canAdd && i + 1 < mSuggestions.size() && mCorrectionMode != CORRECTION_BASIC) {
                    canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1));
                if (canAdd && mSuggestions.size() > 0 && mCorrectionMode != CORRECTION_BASIC) {
                    canAdd &= !TextUtils.equals(tempAutoText, mSuggestions.get(0));
                }
                if (canAdd) {
                    if (DBG) {
                        Log.d(TAG, "Auto corrected by AUTOTEXT.");
                    }
                    mHasAutoCorrection = true;
                    mSuggestions.add(i + 1, autoText);
                    i++;
                    autoText = tempAutoText;
                }
                i++;
            }
        }

        mAutoCorrection.updateAutoCorrectionStatus(this, wordComposer, mSuggestions, mPriorities,
                typedWord, mAutoCorrectionThreshold, mCorrectionMode, autoText);

        if (autoText != null) {
            mSuggestions.add(0, autoText);
        }

        if (typedWord != null) {
            mSuggestions.add(0, typedWordString);
        }
        removeDupes();

        if (DBG) {
            double normalizedScore = mAutoCorrection.getNormalizedScore();
            ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList =
                    new ArrayList<SuggestedWords.SuggestedWordInfo>();
            frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
@@ -373,9 +358,8 @@ public class Suggest implements Dictionary.WordCallback {
                frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
            }
            return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList);
        } else {
            return new SuggestedWords.Builder().addWords(mSuggestions, null);
        }
        return new SuggestedWords.Builder().addWords(mSuggestions, null);
    }

    private void removeDupes() {
@@ -406,7 +390,7 @@ public class Suggest implements Dictionary.WordCallback {
    }

    public boolean hasAutoCorrection() {
        return mHasAutoCorrection;
        return mAutoCorrection.hasAutoCorrection();
    }

    private static boolean compareCaseInsensitive(final String lowerOriginalWord,