Home | History | Annotate | Download | only in personalization
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.inputmethod.latin.personalization;
     18 
     19 import android.content.Context;
     20 
     21 import com.android.inputmethod.annotations.UsedForTesting;
     22 import com.android.inputmethod.compat.ActivityManagerCompatUtils;
     23 import com.android.inputmethod.keyboard.ProximityInfo;
     24 import com.android.inputmethod.latin.AbstractDictionaryWriter;
     25 import com.android.inputmethod.latin.ExpandableDictionary;
     26 import com.android.inputmethod.latin.WordComposer;
     27 import com.android.inputmethod.latin.ExpandableDictionary.NextWord;
     28 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
     29 import com.android.inputmethod.latin.makedict.DictEncoder;
     30 import com.android.inputmethod.latin.makedict.FormatSpec;
     31 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
     32 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
     33 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
     34 import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils;
     35 import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
     36 
     37 import java.io.IOException;
     38 import java.util.ArrayList;
     39 import java.util.Map;
     40 
     41 // Currently this class is used to implement dynamic prodiction dictionary.
     42 // TODO: Move to native code.
     43 public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWriter {
     44     private static final String TAG = DynamicPersonalizationDictionaryWriter.class.getSimpleName();
     45     /** Maximum number of pairs. Pruning will start when databases goes above this number. */
     46     public static final int DEFAULT_MAX_HISTORY_BIGRAMS = 10000;
     47     public static final int LOW_MEMORY_MAX_HISTORY_BIGRAMS = 2000;
     48 
     49     /** Any pair being typed or picked */
     50     private static final int FREQUENCY_FOR_TYPED = 2;
     51 
     52     private static final int BINARY_DICT_VERSION = 3;
     53     private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
     54             new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */);
     55 
     56     private final UserHistoryDictionaryBigramList mBigramList =
     57             new UserHistoryDictionaryBigramList();
     58     private final ExpandableDictionary mExpandableDictionary;
     59     private final int mMaxHistoryBigrams;
     60 
     61     public DynamicPersonalizationDictionaryWriter(final Context context, final String dictType) {
     62         super(context, dictType);
     63         mExpandableDictionary = new ExpandableDictionary(dictType);
     64         final boolean isLowRamDevice = ActivityManagerCompatUtils.isLowRamDevice(context);
     65         mMaxHistoryBigrams = isLowRamDevice ?
     66                 LOW_MEMORY_MAX_HISTORY_BIGRAMS : DEFAULT_MAX_HISTORY_BIGRAMS;
     67     }
     68 
     69     @Override
     70     public void clear() {
     71         mBigramList.evictAll();
     72         mExpandableDictionary.clearDictionary();
     73     }
     74 
     75     /**
     76      * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes
     77      * are done to update the binary dictionary.
     78      * @param word The word to add.
     79      * @param shortcutTarget A shortcut target for this word, or null if none.
     80      * @param frequency The frequency for this unigram.
     81      * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
     82      *   if shortcutTarget is null.
     83      * @param isNotAWord true if this is not a word, i.e. shortcut only.
     84      */
     85     @Override
     86     public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
     87             final int shortcutFreq, final boolean isNotAWord) {
     88         if (mBigramList.size() > mMaxHistoryBigrams * 2) {
     89             // Too many entries: just stop adding new vocabulary and wait next refresh.
     90             return;
     91         }
     92         mExpandableDictionary.addWord(word, shortcutTarget, frequency, shortcutFreq);
     93         mBigramList.addBigram(null, word, (byte)frequency);
     94     }
     95 
     96     @Override
     97     public void addBigramWords(final String word0, final String word1, final int frequency,
     98             final boolean isValid, final long lastModifiedTime) {
     99         if (mBigramList.size() > mMaxHistoryBigrams * 2) {
    100             // Too many entries: just stop adding new vocabulary and wait next refresh.
    101             return;
    102         }
    103         if (lastModifiedTime > 0) {
    104             mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
    105                     new ForgettingCurveParams(frequency, System.currentTimeMillis(),
    106                             lastModifiedTime));
    107             mBigramList.addBigram(word0, word1, (byte)frequency);
    108         } else {
    109             mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
    110                     new ForgettingCurveParams(isValid));
    111             mBigramList.addBigram(word0, word1, (byte)frequency);
    112         }
    113     }
    114 
    115     @Override
    116     public void removeBigramWords(final String word0, final String word1) {
    117         if (mBigramList.removeBigram(word0, word1)) {
    118             mExpandableDictionary.removeBigram(word0, word1);
    119         }
    120     }
    121 
    122     @Override
    123     protected void writeDictionary(final DictEncoder dictEncoder,
    124             final Map<String, String> attributeMap) throws IOException, UnsupportedFormatException {
    125         UserHistoryDictIOUtils.writeDictionary(dictEncoder,
    126                 new FrequencyProvider(mBigramList, mExpandableDictionary, mMaxHistoryBigrams),
    127                 mBigramList, FORMAT_OPTIONS);
    128     }
    129 
    130     private static class FrequencyProvider implements BigramDictionaryInterface {
    131         private final UserHistoryDictionaryBigramList mBigramList;
    132         private final ExpandableDictionary mExpandableDictionary;
    133         private final int mMaxHistoryBigrams;
    134 
    135         public FrequencyProvider(final UserHistoryDictionaryBigramList bigramList,
    136                 final ExpandableDictionary expandableDictionary, final int maxHistoryBigrams) {
    137             mBigramList = bigramList;
    138             mExpandableDictionary = expandableDictionary;
    139             mMaxHistoryBigrams = maxHistoryBigrams;
    140         }
    141 
    142         @Override
    143         public int getFrequency(final String word0, final String word1) {
    144             final int freq;
    145             if (word0 == null) { // unigram
    146                 freq = FREQUENCY_FOR_TYPED;
    147             } else { // bigram
    148                 final NextWord nw = mExpandableDictionary.getBigramWord(word0, word1);
    149                 if (nw != null) {
    150                     final ForgettingCurveParams forgettingCurveParams = nw.getFcParams();
    151                     final byte prevFc = mBigramList.getBigrams(word0).get(word1);
    152                     final byte fc = forgettingCurveParams.getFc();
    153                     final boolean isValid = forgettingCurveParams.isValid();
    154                     if (prevFc > 0 && prevFc == fc) {
    155                         freq = fc & 0xFF;
    156                     } else if (UserHistoryForgettingCurveUtils.
    157                             needsToSave(fc, isValid, mBigramList.size() <= mMaxHistoryBigrams)) {
    158                         freq = fc & 0xFF;
    159                     } else {
    160                         // Delete this entry
    161                         freq = -1;
    162                     }
    163                 } else {
    164                     // Delete this entry
    165                     freq = -1;
    166                 }
    167             }
    168             return freq;
    169         }
    170     }
    171 
    172     @Override
    173     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
    174             final String prevWord, final ProximityInfo proximityInfo,
    175             boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
    176         return mExpandableDictionary.getSuggestions(composer, prevWord, proximityInfo,
    177                 blockOffensiveWords, additionalFeaturesOptions);
    178     }
    179 
    180     @Override
    181     public boolean isValidWord(final String word) {
    182         return mExpandableDictionary.isValidWord(word);
    183     }
    184 
    185     @UsedForTesting
    186     public boolean isInBigramListForTests(final String word) {
    187         // TODO: Use native method to determine whether the word is in dictionary or not
    188         return mBigramList.containsKey(word) || mBigramList.getBigrams(null).containsKey(word);
    189     }
    190 }
    191