blob: 820532d4069e244baf08065e452233be34120519 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <suggest/suggest.h>
#include <memory>
#include <fstream>
#include <iostream>
#include "aosp/suggest/core/layout/proximity_info.h"
#include "aosp/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h"
#include "aosp/suggest/core/dictionary/dictionary.h"
#include "aosp/suggest/core/session/dic_traverse_session.h"
#include "aosp/suggest/core/suggest_options.h"
namespace {
std::vector<int> FillVector(int size, std::function<int (int)> fillFunc) {
std::vector<int> array(size);
for(int i=0; i<size; ++i) {
array[i] = fillFunc(i);
}
return array;
}
}
namespace suggest {
using namespace latinime;
Touch::Touch(vec2f pos, charcode code) : pos(pos), code(code) {}
Touch::Touch(vec2f pos, const SuggestEngine& engine) : pos(pos) {
code = engine.GetKeyAt(pos).code;
}
Touch::Touch(charcode code, const SuggestEngine& engine) : code(code) {
pos = engine.GetKey(code).rect.center();
}
class SuggestEngineImpl;
class SuggestSessionImpl : public SuggestSession {
public:
SuggestSessionImpl(SuggestEngineImpl* engine);
virtual const std::list<Suggestion>& GetSuggestions(
const std::vector<Touch> &touches,
std::string previous_word);
private:
std::unique_ptr<SuggestOptions> options_;
std::unique_ptr<DicTraverseSession> session_;
std::list<Suggestion> suggestions_;
SuggestEngineImpl* engine_;
};
class SuggestEngineImpl : public SuggestEngine {
public:
SuggestEngineImpl(vec2f keyboard_size, vec2f common_key_size,
const std::vector<Key> &keys,
const SuggestParameters &p) : parameters_(p), keys_(keys) {
JNIEnv fakeEnv;
int num_keys = keys.size();
// build proximity chars grid
std::vector<int> proximityChars(p.grid_cells.x * p.grid_cells.y * MAX_PROXIMITY_CHARS_SIZE, 0);
vec2f cell_size = div_elem(keyboard_size, p.grid_cells);
vec2f search_box_size = mul(common_key_size, p.search_box_size_factor);
for (int y=0; y<p.grid_cells.y; ++y) {
for (int x=0; x<p.grid_cells.x; ++x) {
rect2f cell(mul_elem(vec2f(x, y), cell_size), cell_size);
rect2f search_rect = cell.resized(search_box_size);
int i = ((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE;
for (int k=0; k<num_keys; ++k) {
if (search_rect.intersects(keys[k].rect)) {
proximityChars[i++] = keys[k].code;
}
}
}
}
#ifdef FLAG_DEBUG
for (int y=0; y<p.grid_cells.y; ++y) {
for (int x=0; x<p.grid_cells.x; ++x) {
AKLOGI("(%d, %d) = %c %c %c %c %c %c %c %c", x, y,
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 0],
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 1],
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 2],
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 3],
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 4],
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 5],
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 6],
(char)proximityChars[((y * p.grid_cells.x) + x) * MAX_PROXIMITY_CHARS_SIZE + 7]);
}
}
#endif
// build aosp compabtile data structures
std::vector<int> keyXCoordinates = FillVector(num_keys, [&](int i) -> int { return keys[i].rect.pos.x; });
std::vector<int> keyYCoordinates = FillVector(num_keys, [&](int i) -> int { return keys[i].rect.pos.y; });
std::vector<int> keyWidths = FillVector(num_keys, [&](int i) -> int { return keys[i].rect.size.x; });
std::vector<int> keyHeights = FillVector(num_keys, [&](int i) -> int { return keys[i].rect.size.y; });
std::vector<int> keyCharCodes = FillVector(num_keys, [&](int i) -> int { return keys[i].code; });
#ifdef DEBUG
for (int y=0; y<grid_cells.y; ++y) {
for (int x=0; x<grid_cells.x; ++x) {
AKLOGI("(%d, %d) = %c", keyXCoordinates[y * 5 + x], keyYCoordinates[y * 5 + x], (char)keyCharCodes[y * 5 + x]);
}
}
#endif
// initialize aosp dictionary and proximity info
proximity_info_.reset(new ProximityInfo(&fakeEnv, p.locale.c_str(),
keyboard_size.x, keyboard_size.y,
p.grid_cells.x, p.grid_cells.y,
common_key_size.x, common_key_size.y,
&proximityChars,
num_keys,
&keyXCoordinates, &keyYCoordinates,
&keyWidths, &keyHeights,
&keyCharCodes,
NULL, NULL, NULL));
}
virtual bool LoadDictionary(std::string locale) {
JNIEnv fakeEnv;
std::string dict_filename = "/usr/share/libsuggest/" + locale + ".dict";
std::cout << "Loading: " << dict_filename << std::endl;
std::ifstream stream(dict_filename);
if (!stream.good())
return false;
stream.seekg(0, stream.end);
int filesize = stream.tellg();
stream.close();
std::cout << "Size: " << filesize << std::endl;
dict_structure_ =
DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy(
dict_filename.c_str(), 0, filesize, false);
dictionary_.reset(new Dictionary(&fakeEnv, dict_structure_));
return true;
}
virtual Key GetKeyAt(vec2f pos) const {
for(const Key &key: keys_) {
if (key.rect.contains(pos)) {
return key;
}
}
return Key::InvalidKey;
}
virtual Key GetKey(charcode code) const {
for(const Key &key: keys_) {
if (key.code == code) {
return key;
}
}
return Key::InvalidKey;
}
virtual SuggestSession* NewSession() {
return new SuggestSessionImpl(this);
}
public:
ProximityInfo* proximity_info(){ return proximity_info_.get(); }
Dictionary* dictionary(){ return dictionary_.get(); }
const SuggestParameters& parameters() { return parameters_; }
private:
std::unique_ptr<ProximityInfo> proximity_info_;
DictionaryStructureWithBufferPolicy *dict_structure_;
std::unique_ptr<Dictionary> dictionary_;
SuggestParameters parameters_;
std::vector<Key> keys_;
};
SuggestSessionImpl::SuggestSessionImpl(SuggestEngineImpl* engine) : engine_(engine) {
JNIEnv fakeEnv;
options_.reset(new SuggestOptions(NULL, 0));
session_.reset(new DicTraverseSession(&fakeEnv,
engine->parameters().locale.c_str(), true));
}
const std::list<Suggestion>& SuggestSessionImpl::GetSuggestions(
const std::vector<Touch> &touches,
std::string previous_word="") {
suggestions_.clear();
std::vector<int> x_coords = FillVector(touches.size(),
[&](int i) { return touches[i].pos.x; });
std::vector<int> y_coords = FillVector(touches.size(),
[&](int i) { return touches[i].pos.y; });
std::vector<int> codes = FillVector(touches.size(),
[&](int i) { return touches[i].code; });
std::vector<int> times = FillVector(touches.size(),
[&](int i) { return i; });
std::vector<int> pointer_ids(touches.size(), 0);
int outWords[1024] = {0};
int frequencies[1024] = {0};
int spaceIndices[1024] = {0};
int outputTypes[1024] = {0};
int outputCommitFirstWordConfidence[1024] = {0};
engine_->dictionary()->getSuggestions(
engine_->proximity_info(), session_.get(),
&x_coords[0], &y_coords[0], &times[0], &pointer_ids[0], &codes[0],
touches.size(),
NULL, 0,
0,
options_.get(),
outWords, frequencies, spaceIndices, outputTypes,
outputCommitFirstWordConfidence);
for (int i = 0; i < MAX_RESULTS; ++i) {
Suggestion suggestion;
for (int j=0; j<MAX_WORD_LENGTH; ++j) {
char code = outWords[i * MAX_WORD_LENGTH + j];
if (code == 0)
break;
suggestion.word.append(1, (char)outWords[i * MAX_WORD_LENGTH + j]);
}
if (suggestion.word.size() > 0) {
suggestion.frequency = frequencies[i];
suggestions_.push_back(suggestion);
} else {
break;
}
}
return suggestions_;
}
Key Key::InvalidKey(vec2f(0, 0), vec2f(0, 0), 0);
SuggestParameters::SuggestParameters(std::string locale)
: grid_cells(10, 10),
search_box_size_factor(1.5),
locale(locale) {}
SuggestEngine* NewSuggestEngine(vec2f keyboard_size, vec2f common_key_size,
const std::vector<Key> &keylist,
const SuggestParameters &parameters) {
return new SuggestEngineImpl(keyboard_size, common_key_size,
keylist, parameters);
}
} // namespace suggest