Merge "Support Spanish keyboard"
diff --git a/java/res/values-hr/donottranslate-altchars.xml b/java/res/values-hr/donottranslate-altchars.xml
new file mode 100644
index 0000000..d0c9d40
--- /dev/null
+++ b/java/res/values-hr/donottranslate-altchars.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="alternates_for_s">š,ś,ß</string>
+    <string name="alternates_for_n">ñ,ń</string>
+    <string name="alternates_for_y"></string>
+    <string name="alternates_for_z">6,ž,ź,ż</string>
+    <string name="alternates_for_c">č,ć,ç</string>
+    <string name="alternates_for_d">đ</string>
+</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 3d03271..93da137 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -258,12 +258,18 @@
     <string name="subtype_mode_en_US_keyboard">English (US) Keyboard</string>
     <!-- Description for Spanish keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_es_keyboard">Spanish Keyboard</string>
+    <!-- Description for Finnish keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_fi_keyboard">Finnish Keyboard</string>
     <!-- Description for French keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_fr_keyboard">French Keyboard</string>
     <!-- Description for French (Canada) keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_fr_CA_keyboard">French (Canada) Keyboard</string>
     <!-- Description for French (Switzerland) keyboard subtype [CHAR LIMIT=35] -->
     <string name="subtype_mode_fr_CH_keyboard">French (Switzerland) Keyboard</string>
+    <!-- Description for Croatian keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_hr_keyboard">Croatian Keyboard</string>
+    <!-- Description for Hungarian keyboard subtype [CHAR LIMIT=35] -->
+    <string name="subtype_mode_hu_keyboard">Hungarian Keyboard</string>
     <!-- Description for Hebrew keyboard subtype [CHAR LIMIT=35] -->
     <!-- Java uses the deprecated "iw" code instead of the standard "he" code -->
     <string name="subtype_mode_iw_keyboard">Hebrew Keyboard</string>
diff --git a/java/res/xml-hr/kbd_qwerty.xml b/java/res/xml-hr/kbd_qwerty.xml
new file mode 100644
index 0000000..ca92e86
--- /dev/null
+++ b/java/res/xml-hr/kbd_qwerty.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="hr"
+>
+    <!-- TODO: Dedicated Croatian layout especially for tablet. -->
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_qwertz" />
+</Keyboard>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index aba6974..9c41617 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -20,7 +20,7 @@
 <!-- The attributes in this XML file provide configuration information -->
 <!-- for the Input Method Manager. -->
 
-<!-- Keyboard: en_US, en_GB, cs, da, de, es, es_US, fr, fr_CA, fr_CH, it, nb, nl, sr, sv -->
+<!-- Keyboard: en_US, en_GB, ar, cs, da, de, es, es_US, fi, fr, fr_CA, fr_CH, hr, hu, it, iw, nb, nl, pl, pt, ru, sr, sv -->
 <!-- Voice: af, cs, da, de, en, es, fr, it, ja, ko, nl, pl, pt, ru, tr, yue, zh, zu -->
 <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
 <!-- TODO: use <lang>_mic icon instead of a common mic icon. -->
@@ -91,6 +91,11 @@
 <!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
 <!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_fi_keyboard"
+            android:imeSubtypeLocale="fi"
+            android:imeSubtypeMode="keyboard"
+    />
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_fr_keyboard"
             android:imeSubtypeLocale="fr"
             android:imeSubtypeMode="keyboard"
@@ -112,6 +117,16 @@
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_hr_keyboard"
+            android:imeSubtypeLocale="hr"
+            android:imeSubtypeMode="keyboard"
+    />
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_mode_hu_keyboard"
+            android:imeSubtypeLocale="hu"
+            android:imeSubtypeMode="keyboard"
+    />
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_mode_it_keyboard"
             android:imeSubtypeLocale="it"
             android:imeSubtypeMode="keyboard"
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index aa159b5..ef52ceb 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -272,6 +272,7 @@
 }
 
 // TODO: We need to optimize addWord by using STL or something
+// TODO: This needs to take an const unsigned short* and not tinker with its contents
 bool UnigramDictionary::addWord(unsigned short *word, int length, int frequency) {
     word[length] = 0;
     if (DEBUG_DICT && DEBUG_SHOW_FOUND_WORD) {
@@ -321,6 +322,16 @@
     return false;
 }
 
+inline void UnigramDictionary::addWordAlternatesSpellings(const uint8_t* const root, int pos,
+        int depth, int finalFreq) {
+    // TODO: actually add alternates when the format supports it.
+}
+
+static inline bool hasAlternateSpellings(uint8_t flags) {
+    // TODO: when the format supports it, return the actual value.
+    return false;
+}
+
 static inline unsigned short toBaseLowerCase(unsigned short c) {
     if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) {
         c = BASE_CHARS[c];
@@ -333,7 +344,7 @@
     return c;
 }
 
-bool UnigramDictionary::sameAsTyped(unsigned short *word, int length) {
+bool UnigramDictionary::sameAsTyped(const unsigned short *word, int length) const {
     if (length != mInputLength) {
         return false;
     }
@@ -656,28 +667,6 @@
     return finalFreq;
 }
 
-inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsGreaterThanInputLength(
-        unsigned short *word, const int inputIndex, const int depth, const int matchWeight,
-        int *nextLetters, const int nextLettersSize, const int skipPos, const int excessivePos,
-        const int transposedPos, const int freq) {
-    const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos, excessivePos,
-            transposedPos, freq, false);
-    if (depth >= MIN_SUGGEST_DEPTH) addWord(word, depth + 1, finalFreq);
-    if (depth >= mInputLength && skipPos < 0) {
-        registerNextLetter(mWord[mInputLength], nextLetters, nextLettersSize);
-    }
-}
-
-inline void UnigramDictionary::onTerminalWhenUserTypedLengthIsSameAsInputLength(
-        unsigned short *word, const int inputIndex, const int depth, const int matchWeight,
-        const int skipPos, const int excessivePos, const int transposedPos, const int freq) {
-    if (sameAsTyped(word, depth + 1)) return;
-    const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos,
-            excessivePos, transposedPos, freq, true);
-    // Proximity collection will promote a word of the same length as what user typed.
-    if (depth >= MIN_SUGGEST_DEPTH) addWord(word, depth + 1, finalFreq);
-}
-
 inline bool UnigramDictionary::needsToSkipCurrentNode(const unsigned short c,
         const int inputIndex, const int skipPos, const int depth) {
     const unsigned short userTypedChar = getInputCharsAt(inputIndex)[0];
@@ -708,7 +697,6 @@
     return false;
 }
 
-
 // In the following function, c is the current character of the dictionary word
 // currently examined.
 // currentChars is an array containing the keys close to the character the
@@ -751,6 +739,30 @@
     return UNRELATED_CHAR;
 }
 
+inline void UnigramDictionary::onTerminal(unsigned short int* word, const int depth,
+        const uint8_t* const root, const uint8_t flags, int pos,
+        const int inputIndex, const int matchWeight, const int skipPos,
+        const int excessivePos, const int transposedPos, const int freq, const bool sameLength,
+        int* nextLetters, const int nextLettersSize) {
+
+    const bool isSameAsTyped = sameLength ? sameAsTyped(word, depth + 1) : false;
+    const bool hasAlternates = hasAlternateSpellings(flags);
+    if (isSameAsTyped && !hasAlternates) return;
+
+    if (depth >= MIN_SUGGEST_DEPTH) {
+        const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos,
+                excessivePos, transposedPos, freq, sameLength);
+        if (!isSameAsTyped)
+            addWord(word, depth + 1, finalFreq);
+        if (hasAlternates)
+            addWordAlternatesSpellings(DICT_ROOT, pos, flags, finalFreq);
+    }
+
+    if (sameLength && depth >= mInputLength && skipPos < 0) {
+        registerNextLetter(word[mInputLength], nextLetters, nextLettersSize);
+    }
+}
+
 inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth,
         const int maxDepth, const bool traverseAllNodes, int matchWeight, int inputIndex,
         const int diffs, const int skipPos, const int excessivePos, const int transposedPos,
@@ -770,6 +782,8 @@
     int freq;
     bool isSameAsUserTypedLength = false;
 
+    const uint8_t flags = 0; // No flags for now
+
     if (excessivePos == depth && inputIndex < mInputLength - 1) ++inputIndex;
 
     *nextSiblingPosition = Dictionary::setDictionaryValues(DICT_ROOT, IS_LATEST_DICT_VERSION, pos,
@@ -782,9 +796,8 @@
     if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, depth)) {
         mWord[depth] = c;
         if (traverseAllNodes && terminal) {
-            onTerminalWhenUserTypedLengthIsGreaterThanInputLength(mWord, inputIndex, depth,
-                    matchWeight, nextLetters, nextLettersSize, skipPos, excessivePos, transposedPos,
-                    freq);
+            onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
+                       excessivePos, transposedPos, freq, false, nextLetters, nextLettersSize);
         }
         if (!needsToTraverseChildrenNodes) return false;
         *newTraverseAllNodes = traverseAllNodes;
@@ -811,8 +824,8 @@
         bool isSameAsUserTypedLength = mInputLength == inputIndex + 1
                 || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2);
         if (isSameAsUserTypedLength && terminal) {
-            onTerminalWhenUserTypedLengthIsSameAsInputLength(mWord, inputIndex, depth, matchWeight,
-                    skipPos, excessivePos, transposedPos, freq);
+            onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos,
+                    excessivePos, transposedPos, freq, true, nextLetters, nextLettersSize);
         }
         if (!needsToTraverseChildrenNodes) return false;
         // Start traversing all nodes after the index exceeds the user typed length
diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h
index c47db1a..3593dd6 100644
--- a/native/src/unigram_dictionary.h
+++ b/native/src/unigram_dictionary.h
@@ -64,9 +64,9 @@
     bool checkIfDictVersionIsLatest();
     int getAddress(int *pos);
     int getFreq(int *pos);
-    int wideStrLen(unsigned short *str);
-    bool sameAsTyped(unsigned short *word, int length);
+    bool sameAsTyped(const unsigned short *word, int length) const;
     bool addWord(unsigned short *word, int length, int frequency);
+    void addWordAlternatesSpellings(const uint8_t* const root, int pos, int depth, int finalFreq);
     void getWordsRec(const int childrenCount, const int pos, const int depth, const int maxDepth,
             const bool traverseAllNodes, const int snr, const int inputIndex, const int diffs,
             const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters,
@@ -83,13 +83,11 @@
     int calculateFinalFreq(const int inputIndex, const int depth, const int snr, const int skipPos,
             const int excessivePos, const int transposedPos, const int freq,
             const bool sameLength) const;
-    void onTerminalWhenUserTypedLengthIsGreaterThanInputLength(unsigned short *word,
-            const int inputIndex, const int depth, const int snr, int *nextLetters,
-            const int nextLettersSize, const int skipPos, const int excessivePos,
-            const int transposedPos, const int freq);
-    void onTerminalWhenUserTypedLengthIsSameAsInputLength(unsigned short *word,
-            const int inputIndex, const int depth, const int snr, const int skipPos,
-            const int excessivePos, const int transposedPos, const int freq);
+    void onTerminal(unsigned short int* word, const int depth,
+            const uint8_t* const root, const uint8_t flags, int pos,
+            const int inputIndex, const int matchWeight, const int skipPos,
+            const int excessivePos, const int transposedPos, const int freq, const bool sameLength,
+            int *nextLetters, const int nextLettersSize);
     bool needsToSkipCurrentNode(const unsigned short c,
             const int inputIndex, const int skipPos, const int depth);
     ProximityType getMatchedProximityId(const int *currentChars, const unsigned short c,