| // 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], ×[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 ¶meters) { |
| return new SuggestEngineImpl(keyboard_size, common_key_size, |
| keylist, parameters); |
| } |
| |
| } // namespace suggest |