Android Open Source - WriteRight Predictor






From Project

Back to project page WriteRight.

License

The source code is released under:

GNU General Public License

If you think the Android project WriteRight listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 *    Alternative input method that resizes its virtual keys according to
 *    usage statistics by the user (when, where and which words were typed.
 *    Copyright (C) June 2011  Panagiotis Sakkos <panos.sakkos@gmail.com>
 */*from  w  ww  . jav a2 s  .co m*/
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package panos.sakkos.softkeyboard.writeright;

import java.util.HashMap;
import android.database.Cursor;
import android.util.Log;

/**
 * The class that produces the predictions for the next letter
 * @author Panos Sakkos
 */

public class Predictor
{
    private Trie root;
    private HashMap<String, Statistics> knowledge;

    /* Current word typed */

    private String word;
    private int wordsTyped;

    /*Prefix tree for the current word typed */

    private Trie subTrie;
    public static char[] latinLetters = {
                            'a','b', 'c', 'd', 'e', 'f', 'g',
                         'h','i', 'j', 'k', 'l', 'm', 'n',
                       'o', 'p', 'q', 'r', 's', 't', 'u',
                         'v', 'w', 'x', 'y', 'z'
                 };
    private int personalizationFactor = 1;
    private boolean unknownWord;

    private boolean idle = false;
    DataBaseHelper db;
    
    public Predictor(DataBaseHelper db)
    {
      this.db = db;
      Cursor cursor = db.SelectAllWords();
      
      root = new Trie();
      root.LoadFromDB(cursor);

    knowledge = new HashMap<String, Statistics>();

        wordsTyped = 0;

        word = "";
        subTrie = root;
        unknownWord = false;      
        cursor = db.SelectAllWords();
        String key;
        while(cursor.moveToNext())
        {
          key = cursor.getString(0);
          Statistics statistics = new Statistics(cursor.getInt(1), Long.parseLong(cursor.getString(2)));
          
          knowledge.put(key, statistics);
        }
        
        GetTrained();
    }
    
    /**
     * Returns a dictionary containing the probability of each possible next character
     * @return containing pairs of possible next letter and it's probability to be typed
     */

    public HashMap<Character, Float> GetPredictions()
    {
        int popularity;
        int postfixesCounter;
        float evaluation;

        float evaluationSum = 0;

        /* If subTrie is null, then the word being typed is not in the dictionary */

        if(subTrie == null || idle)
        {
            unknownWord = true;
            return new HashMap<Character, Float>();
        }

        /* Compute total evaluation amount */

        for(char possibleNextLetter : latinLetters)
        {
            popularity = subTrie.GetPopularity(possibleNextLetter);
            postfixesCounter = subTrie.GetSubTrieSize(possibleNextLetter);

            evaluation = Evaluate(popularity, postfixesCounter);

            evaluationSum += evaluation;
        }

        HashMap<Character, Float> predictions = new HashMap<Character, Float>();

        /* If there are no predictions */

        if(evaluationSum == 0)
        {
            for(char letter : latinLetters)
            {
                predictions.put(new Character(letter), new Float(0.0));
            }

            return predictions;
        }

        else
        {
            for(char possibleNextLetter : latinLetters)
            {
                popularity = subTrie.GetPopularity(possibleNextLetter);
                postfixesCounter = subTrie.GetSubTrieSize(possibleNextLetter);

                evaluation = Evaluate(popularity, postfixesCounter);

                /* Normalize evaluation in order to express probability */

                predictions.put(new Character(possibleNextLetter), (float) Math.round(evaluation / evaluationSum * 100) / 100 );
            }
        }

        return predictions;
    }

    /**
     * Informs the predictor that the parameter letter is typed. In order to
     * give right predictions this method must be called whenever  there is a
     * new input, even if this input is space
     * @param character
     */

    public void CharacterTyped(char character) throws Exception
    {
      /* Ignore invalid input */
      
      if(Trie.ValidWord((new Character(character)).toString()) == false)
      {
            Log.d("DEBUG", "CHARACTER IGNORED");
        return;
      }

        word += character;

        /* If subTrie is null, then the word being typed is not in the dictionary */

        if (subTrie != null)
        {
          subTrie = subTrie.GetSubTrie(character);
        }
        else
        {
          /* Unknown word detected! */

          unknownWord = true;
        }
    }

    /**
     * If the user cancels the prediction, this method MUST be called in order
     * to inform the prediction infrastructure
     */

    public void PredictionCanceled()
    {
        word = "";
        subTrie = root;
        unknownWord = false;
        idle = true;
    }

    /**
     * Trains the prefix tree with the knowledge in order to give back
     * personalized data
     */

    private void GetTrained()
    {
        for(Object key : knowledge.keySet())
        {
            Statistics value = (Statistics) knowledge.get(key);
            root.WasTyped(key.toString(), value.GetPopularity());
            wordsTyped += value.GetPopularity();
        }
    }

    /**
     * This method is called by the CharacterTyped method when the typed
     * character is space (' '), in order to store the knowledge drained from this action
     */

    public void WordTyped() throws Exception
    {
      if(word == null || word.length() == 0)
        return;

        word = word.toLowerCase();

        if(knowledge.containsKey(word) == false)
        {
            Statistics statistics = new Statistics();
            knowledge.put(word, statistics);
        }
        else
        {
            ((Statistics) knowledge.get(word)).WordTyped();
        }

        /* If the word that was typed is known */

        if (unknownWord == false)
        {
            /* Train the Trie with the new knowledge */

            root.WasTyped(word, 1);
        }
        else
        {
            /* If the word is unknown, add it to the Trie */

            root.Add(word);

            /* Add word to wordnet */

            AddNewWordToWordNet();
        }

        wordsTyped++;

        word = "";
        subTrie = root;
        unknownWord = false;
    }


    /**
     * Given the popularity of a possible next letter and the number of the
     * postfixes that start with this letter, returns an evaluation
     * @param popularity Indicates the popularity of the possible next letter
     * @param prefixesCounter Indicates the number of the postfixes that start
     * with the target possible next letter
     * @return Evaluation for the target possible next letter
     */

    private float Evaluate(int popularity, int prefixesCounter)
    {
        float usageRatio = personalizationFactor * wordsTyped / root.Size();

        /* If there are more typed words than the stored words (usageRatio > 1 ),
        * the evaluation is computed based only on letter's popularity
        */

        if (usageRatio > 1)
        {
            usageRatio = 1;
        }

        return usageRatio * popularity + (1 - usageRatio) * prefixesCounter;
    }

    private void AddNewWordToWordNet() throws Exception
    {
      db.AddNewWord(word);
        Log.d("DEBUG", "NEW WORD ADDED TO WORDNET");          
    }

    private void AddNewWordToWordNet(String newWord) throws Exception
    {
      /* prevent bad use */
      
      if(knowledge.containsKey(newWord))
        return;

      db.AddNewWord(newWord);
        Log.d("DEBUG", "NEW WORD ADDED TO WORDNET");          
    }
        
    public void Save()
    {
        for(Object key : knowledge.keySet())
        {
          if(knowledge.get(key).GetPopularity() > 0)
          {
            db.UpdateWords(key.toString(), knowledge.get(key));
          }
        }      
    }
    
    public void SetNotIdle()
    {
      idle = false;
      word = "";
        subTrie = root;
        unknownWord = false;
    }
    
    public void SetIdle()
    {
      word = "";
      idle = true;
    }
    
    public boolean IsIdle()
    {
      return idle;
    }
    
    public void LearnNewWord(String newWord)
    {      
        Statistics statistics = new Statistics();
        knowledge.put(newWord, statistics);
        root.Add(newWord);

        try 
        {
      AddNewWordToWordNet(newWord);
    }
        catch (Exception e) 
        {
          Log.d("DEBUG", "ERROR WHILE ADDING NEW WORD TO WORDNET AFTER PREDICTION CANCELLING");
    }
    }
}




Java Source Code List

panos.sakkos.softkeyboard.writeright.DataBaseHelper.java
panos.sakkos.softkeyboard.writeright.LatinKeyboardView.java
panos.sakkos.softkeyboard.writeright.LatinKeyboard.java
panos.sakkos.softkeyboard.writeright.Predictor.java
panos.sakkos.softkeyboard.writeright.SoftKeyboard.java
panos.sakkos.softkeyboard.writeright.Statistics.java
panos.sakkos.softkeyboard.writeright.Trie.java