Esempio n. 1
0
 private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
     final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) {
   final SuggestedWordInfo typedWordInfo = suggestions.get(0);
   typedWordInfo.setDebugString("+");
   final int suggestionsSize = suggestions.size();
   final ArrayList<SuggestedWordInfo> suggestionsList =
       CollectionUtils.newArrayList(suggestionsSize);
   suggestionsList.add(typedWordInfo);
   // Note: i here is the index in mScores[], but the index in mSuggestions is one more
   // than i because we added the typed word to mSuggestions without touching mScores.
   for (int i = 0; i < suggestionsSize - 1; ++i) {
     final SuggestedWordInfo cur = suggestions.get(i + 1);
     final float normalizedScore =
         BinaryDictionary.calcNormalizedScore(typedWord, cur.toString(), cur.mScore);
     final String scoreInfoString;
     if (normalizedScore > 0) {
       scoreInfoString = String.format(Locale.ROOT, "%d (%4.2f)", cur.mScore, normalizedScore);
     } else {
       scoreInfoString = Integer.toString(cur.mScore);
     }
     cur.setDebugString(scoreInfoString);
     suggestionsList.add(cur);
   }
   return suggestionsList;
 }
  private ArrayList<WeightedString> readShortcuts(final int terminalId) {
    if (mShortcutAddressTable.get(0, terminalId) == SparseTable.NOT_EXIST) return null;

    final ArrayList<WeightedString> ret = CollectionUtils.newArrayList();
    final int posOfShortcuts =
        mShortcutAddressTable.get(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId);
    mShortcutBuffer.position(posOfShortcuts);
    while (true) {
      final int flags = mShortcutBuffer.readUnsignedByte();
      final String word = CharEncoding.readString(mShortcutBuffer);
      ret.add(new WeightedString(word, flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
      if (0 == (flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
    }
    return ret;
  }
Esempio n. 3
0
  // Retrieves suggestions for the batch input
  // and calls the callback function with the suggestions.
  private void getSuggestedWordsForBatchInput(
      final WordComposer wordComposer,
      final String prevWordForBigram,
      final ProximityInfo proximityInfo,
      final boolean blockOffensiveWords,
      final int[] additionalFeaturesOptions,
      final int sessionId,
      final int sequenceNumber,
      final OnGetSuggestedWordsCallback callback) {
    final BoundedTreeSet suggestionsSet =
        new BoundedTreeSet(sSuggestedWordInfoComparator, MAX_SUGGESTIONS);

    // At second character typed, search the unigrams (scores being affected by bigrams)
    for (final String key : mDictionaries.keySet()) {
      final Dictionary dictionary = mDictionaries.get(key);
      suggestionsSet.addAll(
          dictionary.getSuggestionsWithSessionId(
              wordComposer,
              prevWordForBigram,
              proximityInfo,
              blockOffensiveWords,
              additionalFeaturesOptions,
              sessionId));
    }

    for (SuggestedWordInfo wordInfo : suggestionsSet) {
      LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType);
    }

    final ArrayList<SuggestedWordInfo> suggestionsContainer =
        CollectionUtils.newArrayList(suggestionsSet);
    final int suggestionsCount = suggestionsContainer.size();
    final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock();
    final boolean isAllUpperCase = wordComposer.isAllUpperCase();
    if (isFirstCharCapitalized || isAllUpperCase) {
      for (int i = 0; i < suggestionsCount; ++i) {
        final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
        final SuggestedWordInfo transformedWordInfo =
            getTransformedSuggestedWordInfo(
                wordInfo,
                mLocale,
                isAllUpperCase,
                isFirstCharCapitalized,
                0 /* trailingSingleQuotesCount */);
        suggestionsContainer.set(i, transformedWordInfo);
      }
    }

    if (suggestionsContainer.size() > 1
        && TextUtils.equals(
            suggestionsContainer.get(0).mWord, wordComposer.getRejectedBatchModeSuggestion())) {
      final SuggestedWordInfo rejected = suggestionsContainer.remove(0);
      suggestionsContainer.add(1, rejected);
    }
    SuggestedWordInfo.removeDups(suggestionsContainer);

    // For some reason some suggestions with MIN_VALUE are making their way here.
    // TODO: Find a more robust way to detect distractors.
    for (int i = suggestionsContainer.size() - 1; i >= 0; --i) {
      if (suggestionsContainer.get(i).mScore < SUPPRESS_SUGGEST_THRESHOLD) {
        suggestionsContainer.remove(i);
      }
    }

    // In the batch input mode, the most relevant suggested word should act as a "typed word"
    // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
    callback.onGetSuggestedWords(
        new SuggestedWords(
            suggestionsContainer,
            true /* typedWordValid */,
            false /* willAutoCorrect */,
            false /* isPunctuationSuggestions */,
            false /* isObsoleteSuggestions */,
            false /* isPrediction */,
            sequenceNumber));
  }
Esempio n. 4
0
  // Retrieves suggestions for the typing input
  // and calls the callback function with the suggestions.
  private void getSuggestedWordsForTypingInput(
      final WordComposer wordComposer,
      final String prevWordForBigram,
      final ProximityInfo proximityInfo,
      final boolean blockOffensiveWords,
      final boolean isCorrectionEnabled,
      final int[] additionalFeaturesOptions,
      final int sequenceNumber,
      final OnGetSuggestedWordsCallback callback) {
    final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
    final BoundedTreeSet suggestionsSet =
        new BoundedTreeSet(sSuggestedWordInfoComparator, MAX_SUGGESTIONS);

    final String typedWord = wordComposer.getTypedWord();
    final String consideredWord =
        trailingSingleQuotesCount > 0
            ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount)
            : typedWord;
    LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED);

    final WordComposer wordComposerForLookup;
    if (trailingSingleQuotesCount > 0) {
      wordComposerForLookup = new WordComposer(wordComposer);
      for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
        wordComposerForLookup.deleteLast();
      }
    } else {
      wordComposerForLookup = wordComposer;
    }

    for (final String key : mDictionaries.keySet()) {
      final Dictionary dictionary = mDictionaries.get(key);
      suggestionsSet.addAll(
          dictionary.getSuggestions(
              wordComposerForLookup,
              prevWordForBigram,
              proximityInfo,
              blockOffensiveWords,
              additionalFeaturesOptions));
    }

    final String whitelistedWord;
    if (suggestionsSet.isEmpty()) {
      whitelistedWord = null;
    } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) {
      whitelistedWord = null;
    } else {
      whitelistedWord = suggestionsSet.first().mWord;
    }

    // The word can be auto-corrected if it has a whitelist entry that is not itself,
    // or if it's a 2+ characters non-word (i.e. it's not in the dictionary).
    final boolean allowsToBeAutoCorrected =
        (null != whitelistedWord && !whitelistedWord.equals(consideredWord))
            || (consideredWord.length() > 1
                && !AutoCorrectionUtils.isValidWord(
                    this, consideredWord, wordComposer.isFirstCharCapitalized()));

    final boolean hasAutoCorrection;
    // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
    // any attempt to do auto-correction is already shielded with a test for this flag; at the
    // same time, it feels wrong that the SuggestedWord object includes information about
    // the current settings. It may also be useful to know, when the setting is off, whether
    // the word *would* have been auto-corrected.
    if (!isCorrectionEnabled
        || !allowsToBeAutoCorrected
        || !wordComposer.isComposingWord()
        || suggestionsSet.isEmpty()
        || wordComposer.hasDigits()
        || wordComposer.isMostlyCaps()
        || wordComposer.isResumed()
        || !hasMainDictionary()
        || SuggestedWordInfo.KIND_SHORTCUT == suggestionsSet.first().mKind) {
      // If we don't have a main dictionary, we never want to auto-correct. The reason for
      // this is, the user may have a contact whose name happens to match a valid word in
      // their language, and it will unexpectedly auto-correct. For example, if the user
      // types in English with no dictionary and has a "Will" in their contact list, "will"
      // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no
      // auto-correct.
      // Also, shortcuts should never auto-correct unless they are whitelist entries.
      // TODO: we may want to have shortcut-only entries auto-correct in the future.
      hasAutoCorrection = false;
    } else {
      hasAutoCorrection =
          AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
              suggestionsSet.first(), consideredWord, mAutoCorrectionThreshold);
    }

    final ArrayList<SuggestedWordInfo> suggestionsContainer =
        CollectionUtils.newArrayList(suggestionsSet);
    final int suggestionsCount = suggestionsContainer.size();
    final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
    final boolean isAllUpperCase = wordComposer.isAllUpperCase();
    if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
      for (int i = 0; i < suggestionsCount; ++i) {
        final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
        final SuggestedWordInfo transformedWordInfo =
            getTransformedSuggestedWordInfo(
                wordInfo,
                mLocale,
                isAllUpperCase,
                isFirstCharCapitalized,
                trailingSingleQuotesCount);
        suggestionsContainer.set(i, transformedWordInfo);
      }
    }

    for (int i = 0; i < suggestionsCount; ++i) {
      final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
      LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict.mDictType);
    }

    if (!TextUtils.isEmpty(typedWord)) {
      suggestionsContainer.add(
          0,
          new SuggestedWordInfo(
              typedWord,
              SuggestedWordInfo.MAX_SCORE,
              SuggestedWordInfo.KIND_TYPED,
              Dictionary.DICTIONARY_USER_TYPED,
              SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
              SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
    }
    SuggestedWordInfo.removeDups(suggestionsContainer);

    final ArrayList<SuggestedWordInfo> suggestionsList;
    if (DBG && !suggestionsContainer.isEmpty()) {
      suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer);
    } else {
      suggestionsList = suggestionsContainer;
    }

    callback.onGetSuggestedWords(
        new SuggestedWords(
            suggestionsList,
            // TODO: this first argument is lying. If this is a whitelisted word which is an
            // actual word, it says typedWordValid = false, which looks wrong. We should either
            // rename the attribute or change the value.
            !allowsToBeAutoCorrected /* typedWordValid */,
            hasAutoCorrection, /* willAutoCorrect */
            false /* isPunctuationSuggestions */,
            false /* isObsoleteSuggestions */,
            !wordComposer.isComposingWord() /* isPrediction */,
            sequenceNumber));
  }