Merge "Fix: "saomething" -> "something"."
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index 5e2a9df..ff78db7 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 33ef1c1..4edc967 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index c39f052..95a7369 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index 4b55261..0763b62 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_BR_wordlist.combined.gz b/dictionaries/pt_BR_wordlist.combined.gz
index 83dbe79..0dd8472 100644
--- a/dictionaries/pt_BR_wordlist.combined.gz
+++ b/dictionaries/pt_BR_wordlist.combined.gz
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index 120e19b..3a41257 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index fb43a1a..31fb2af 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/raw/main_pt_br.dict b/java/res/raw/main_pt_br.dict
index 8c14499..557d46e 100644
--- a/java/res/raw/main_pt_br.dict
+++ b/java/res/raw/main_pt_br.dict
Binary files differ
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
index e2f2b56..53448c3 100644
--- a/java/res/values/keypress-vibration-durations.xml
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -51,7 +51,8 @@
         <!-- HTC One X -->
         <item>MODEL=HTC One X:MANUFACTURER=HTC,20</item>
         <!-- HTC One -->
-        <item>MODEL=HTC One( special edition)?:MANUFACTURER=HTC,15</item>
+        <item>MODEL=HTC One:MANUFACTURER=HTC,15</item>
+        <item>MODEL=HTL22:MANUFACTURER=HTC,15</item>
         <!-- Motorola Razor M -->
         <item>MODEL=XT907:MANUFACTURER=motorola,30</item>
         <!-- Sony Xperia Z -->
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 20b8def..52d715a 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -24,7 +24,7 @@
     keyboard_locale: script_name/keyboard_layout_set
     af: Afrikaans/qwerty
     ar: Arabic/arabic
-    az: Azerbaijani/qwerty
+    (az: Azerbaijani/qwerty) # disabled temporarily. waiting for strnig resources.
     be: Belarusian/east_slavic
     bg: Bulgarian/bulgarian
     bg: Bulgarian/bulgarian_bds
@@ -52,7 +52,7 @@
     it: Italian/qwerty
     iw: Hebrew/hebrew        # "he" is official language code of Hebrew.
     ka: Georgian/georgian
-    kk: Kazakh/east_slavic
+    (kk: Kazakh/east_slavic) # disabled temporarily. waiting for strnig resources.
     ky: Kyrgyz/east_slavic
     lt: Lithuanian/qwerty
     lv: Latvian/qwerty
@@ -117,13 +117,15 @@
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
     />
+    <!--
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_generic"
-            android:subtypeId="0x2a362219"
+            android:subtypeId="0x70b0f974"
             android:imeSubtypeLocale="az"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
     />
+    -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_generic"
             android:subtypeId="0x1dc3a859"
@@ -303,6 +305,7 @@
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian"
     />
+    <!--
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2d73d2f6"
@@ -310,6 +313,7 @@
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
     />
+    -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2e391c04"
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index 29e7c28..a54344a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -389,7 +389,7 @@
      * @param node the node to compute the maximum size of.
      * @param options file format options.
      */
-    private static void setNodeMaximumSize(final Node node, final FormatOptions options) {
+    private static void calculateNodeMaximumSize(final Node node, final FormatOptions options) {
         int size = getGroupCountSize(node);
         for (CharGroup g : node.mData) {
             final int groupSize = getCharGroupMaximumSize(g, options);
@@ -548,17 +548,17 @@
         boolean changed = false;
         int size = getGroupCountSize(node);
         for (CharGroup group : node.mData) {
-            if (group.mCachedAddress != node.mCachedAddress + size) {
+            if (group.mCachedAddress != node.mCachedAddressBeforeUpdate + size) {
                 changed = true;
-                group.mCachedAddress = node.mCachedAddress + size;
+                group.mCachedAddress = node.mCachedAddressBeforeUpdate + size;
             }
             int groupSize = getGroupHeaderSize(group, formatOptions);
             if (group.isTerminal()) groupSize += FormatSpec.GROUP_FREQUENCY_SIZE;
             if (null == group.mChildren && formatOptions.mSupportsDynamicUpdate) {
                 groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
             } else if (null != group.mChildren) {
-                final int offsetBasePoint = groupSize + node.mCachedAddress + size;
-                final int offset = group.mChildren.mCachedAddress - offsetBasePoint;
+                final int offsetBasePoint = groupSize + node.mCachedAddressBeforeUpdate + size;
+                final int offset = group.mChildren.mCachedAddressBeforeUpdate - offsetBasePoint;
                 if (formatOptions.mSupportsDynamicUpdate) {
                     groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
                 } else {
@@ -568,7 +568,7 @@
             groupSize += getShortcutListSize(group.mShortcutTargets);
             if (null != group.mBigrams) {
                 for (WeightedString bigram : group.mBigrams) {
-                    final int offsetBasePoint = groupSize + node.mCachedAddress + size
+                    final int offsetBasePoint = groupSize + node.mCachedAddressBeforeUpdate + size
                             + FormatSpec.GROUP_FLAGS_SIZE;
                     final int addressOfBigram = findAddressOfWord(dict, bigram.mWord);
                     final int offset = addressOfBigram - offsetBasePoint;
@@ -589,20 +589,48 @@
     }
 
     /**
-     * Computes the byte size of a list of nodes and updates each node cached position.
+     * Initializes the cached addresses of nodes from their size.
      *
      * @param flatNodes the array of nodes.
      * @param formatOptions file format options.
      * @return the byte size of the entire stack.
      */
-    private static int stackNodes(final ArrayList<Node> flatNodes,
+    private static int initializeNodesCachedAddresses(final ArrayList<Node> flatNodes,
             final FormatOptions formatOptions) {
         int nodeOffset = 0;
-        for (Node n : flatNodes) {
-            n.mCachedAddress = nodeOffset;
+        for (final Node n : flatNodes) {
+            n.mCachedAddressBeforeUpdate = nodeOffset;
             int groupCountSize = getGroupCountSize(n);
             int groupOffset = 0;
-            for (CharGroup g : n.mData) {
+            for (final CharGroup g : n.mData) {
+                g.mCachedAddress = groupCountSize + nodeOffset + groupOffset;
+                groupOffset += g.mCachedSize;
+            }
+            final int nodeSize = groupCountSize + groupOffset
+                    + (formatOptions.mSupportsDynamicUpdate
+                            ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
+            nodeOffset += n.mCachedSize;
+        }
+        return nodeOffset;
+    }
+
+    /**
+     * Updates the cached addresses of nodes after recomputing their new positions.
+     *
+     * @param flatNodes the array of nodes.
+     * @param formatOptions file format options.
+     * @return the byte size of the entire stack.
+     */
+    private static int updateNodeCachedAddresses(final ArrayList<Node> flatNodes,
+            final FormatOptions formatOptions) {
+        int nodeOffset = 0;
+        for (final Node n : flatNodes) {
+            n.mCachedAddressBeforeUpdate = n.mCachedAddressAfterUpdate;
+            int groupCountSize = getGroupCountSize(n);
+            int groupOffset = 0;
+            for (final CharGroup g : n.mData) {
+                // TODO: just copy cached address after update into cached address before update
+                // when the two fields are separated.
                 g.mCachedAddress = groupCountSize + nodeOffset + groupOffset;
                 groupOffset += g.mCachedSize;
             }
@@ -610,8 +638,13 @@
                     + (formatOptions.mSupportsDynamicUpdate
                             ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
             if (nodeSize != n.mCachedSize) {
+                // TODO: remove this test when the addresses are separated
                 throw new RuntimeException("Bug : Stored and computed node size differ");
             }
+            if (nodeOffset != n.mCachedAddressAfterUpdate) {
+                // TODO: remove this test when the code is well tested
+                throw new RuntimeException("Bug : Stored and computed node address differ");
+            }
             nodeOffset += n.mCachedSize;
         }
         return nodeOffset;
@@ -627,11 +660,13 @@
      */
     private static void computeParentAddresses(final ArrayList<Node> flatNodes) {
         for (final Node node : flatNodes) {
-            for (CharGroup group : node.mData) {
+            for (final CharGroup group : node.mData) {
                 if (null != group.mChildren) {
-                    // assign my address to children's parent address
+                    // Assign my address to children's parent address
+                    // Here BeforeUpdate and AfterUpdate addresses have the same value, so it
+                    // does not matter which we use.
                     group.mChildren.mCachedParentAddress = group.mCachedAddress
-                            - group.mChildren.mCachedAddress;
+                            - group.mChildren.mCachedAddressAfterUpdate;
                 }
             }
         }
@@ -654,9 +689,9 @@
      */
     private static ArrayList<Node> computeAddresses(final FusionDictionary dict,
             final ArrayList<Node> flatNodes, final FormatOptions formatOptions) {
-        // First get the worst sizes and offsets
-        for (Node n : flatNodes) setNodeMaximumSize(n, formatOptions);
-        final int offset = stackNodes(flatNodes, formatOptions);
+        // First get the worst possible sizes and offsets
+        for (final Node n : flatNodes) calculateNodeMaximumSize(n, formatOptions);
+        final int offset = initializeNodesCachedAddresses(flatNodes, formatOptions);
 
         MakedictLog.i("Compressing the array addresses. Original size : " + offset);
         MakedictLog.i("(Recursively seen size : " + offset + ")");
@@ -665,14 +700,17 @@
         boolean changesDone = false;
         do {
             changesDone = false;
-            for (Node n : flatNodes) {
+            int nodeStartOffset = 0;
+            for (final Node n : flatNodes) {
+                n.mCachedAddressAfterUpdate = nodeStartOffset;
                 final int oldNodeSize = n.mCachedSize;
                 final boolean changed = computeActualNodeSize(n, dict, formatOptions);
                 final int newNodeSize = n.mCachedSize;
                 if (oldNodeSize < newNodeSize) throw new RuntimeException("Increased size ?!");
+                nodeStartOffset += newNodeSize;
                 changesDone |= changed;
             }
-            stackNodes(flatNodes, formatOptions);
+            updateNodeCachedAddresses(flatNodes, formatOptions);
             ++passes;
             if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
         } while (changesDone);
@@ -683,7 +721,7 @@
         final Node lastNode = flatNodes.get(flatNodes.size() - 1);
         MakedictLog.i("Compression complete in " + passes + " passes.");
         MakedictLog.i("After address compression : "
-                + (lastNode.mCachedAddress + lastNode.mCachedSize));
+                + (lastNode.mCachedAddressAfterUpdate + lastNode.mCachedSize));
 
         return flatNodes;
     }
@@ -701,10 +739,12 @@
     private static void checkFlatNodeArray(final ArrayList<Node> array) {
         int offset = 0;
         int index = 0;
-        for (Node n : array) {
-            if (n.mCachedAddress != offset) {
+        for (final Node n : array) {
+            // BeforeUpdate and AfterUpdate addresses are the same here, so it does not matter
+            // which we use.
+            if (n.mCachedAddressAfterUpdate != offset) {
                 throw new RuntimeException("Wrong address for node " + index
-                        + " : expected " + offset + ", got " + n.mCachedAddress);
+                        + " : expected " + offset + ", got " + n.mCachedAddressAfterUpdate);
             }
             ++index;
             offset += n.mCachedSize;
@@ -946,7 +986,7 @@
     private static int writePlacedNode(final FusionDictionary dict, byte[] buffer,
             final Node node, final FormatOptions formatOptions) {
         // TODO: Make the code in common with BinaryDictIOUtils#writeCharGroup
-        int index = node.mCachedAddress;
+        int index = node.mCachedAddressAfterUpdate;
 
         final int groupCount = node.mData.size();
         final int countSize = getGroupCountSize(node);
@@ -977,7 +1017,7 @@
             if (group.mFrequency >= 0) groupAddress += FormatSpec.GROUP_FREQUENCY_SIZE;
             final int childrenOffset = null == group.mChildren
                     ? FormatSpec.NO_CHILDREN_ADDRESS
-                            : group.mChildren.mCachedAddress - groupAddress;
+                            : group.mChildren.mCachedAddressAfterUpdate - groupAddress;
             byte flags = makeCharGroupFlags(group, groupAddress, childrenOffset, formatOptions);
             buffer[index++] = flags;
 
@@ -985,7 +1025,7 @@
                 index = writeParentAddress(buffer, index, parentAddress, formatOptions);
             } else {
                 index = writeParentAddress(buffer, index,
-                        parentAddress + (node.mCachedAddress - group.mCachedAddress),
+                        parentAddress + (node.mCachedAddressAfterUpdate - group.mCachedAddress),
                         formatOptions);
             }
 
@@ -1055,9 +1095,9 @@
                     = FormatSpec.NO_FORWARD_LINK_ADDRESS;
             index += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
         }
-        if (index != node.mCachedAddress + node.mCachedSize) throw new RuntimeException(
+        if (index != node.mCachedAddressAfterUpdate + node.mCachedSize) throw new RuntimeException(
                 "Not the same size : written "
-                + (index - node.mCachedAddress) + " bytes out of a node that should have "
+                + (index - node.mCachedAddressAfterUpdate) + " bytes from a node that should have "
                 + node.mCachedSize + " bytes");
         return index;
     }
@@ -1077,25 +1117,27 @@
         int charGroups = 0;
         int maxGroups = 0;
         int maxRuns = 0;
-        for (Node n : nodes) {
+        for (final Node n : nodes) {
             if (maxGroups < n.mData.size()) maxGroups = n.mData.size();
-            for (CharGroup cg : n.mData) {
+            for (final CharGroup cg : n.mData) {
                 ++charGroups;
                 if (cg.mChars.length > maxRuns) maxRuns = cg.mChars.length;
                 if (cg.mFrequency >= 0) {
-                    if (n.mCachedAddress < firstTerminalAddress)
-                        firstTerminalAddress = n.mCachedAddress;
-                    if (n.mCachedAddress > lastTerminalAddress)
-                        lastTerminalAddress = n.mCachedAddress;
+                    if (n.mCachedAddressAfterUpdate < firstTerminalAddress)
+                        firstTerminalAddress = n.mCachedAddressAfterUpdate;
+                    if (n.mCachedAddressAfterUpdate > lastTerminalAddress)
+                        lastTerminalAddress = n.mCachedAddressAfterUpdate;
                 }
             }
-            if (n.mCachedAddress + n.mCachedSize > size) size = n.mCachedAddress + n.mCachedSize;
+            if (n.mCachedAddressAfterUpdate + n.mCachedSize > size) {
+                size = n.mCachedAddressAfterUpdate + n.mCachedSize;
+            }
         }
         final int[] groupCounts = new int[maxGroups + 1];
         final int[] runCounts = new int[maxRuns + 1];
-        for (Node n : nodes) {
+        for (final Node n : nodes) {
             ++groupCounts[n.mData.size()];
-            for (CharGroup cg : n.mData) {
+            for (final CharGroup cg : n.mData) {
                 ++runCounts[cg.mChars.length];
             }
         }
@@ -1205,7 +1247,7 @@
 
         // Create a buffer that matches the final dictionary size.
         final Node lastNode = flatNodes.get(flatNodes.size() - 1);
-        final int bufferSize = lastNode.mCachedAddress + lastNode.mCachedSize;
+        final int bufferSize = lastNode.mCachedAddressAfterUpdate + lastNode.mCachedSize;
         final byte[] buffer = new byte[bufferSize];
         int index = 0;
 
@@ -1584,8 +1626,9 @@
                 buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
 
         final Node node = new Node(nodeContents);
-        node.mCachedAddress = nodeOrigin;
-        reverseNodeMap.put(node.mCachedAddress, node);
+        node.mCachedAddressBeforeUpdate = nodeOrigin;
+        node.mCachedAddressAfterUpdate = nodeOrigin;
+        reverseNodeMap.put(node.mCachedAddressAfterUpdate, node);
         return node;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 17d2815..5530b7a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -46,7 +46,13 @@
         ArrayList<CharGroup> mData;
         // To help with binary generation
         int mCachedSize = Integer.MIN_VALUE;
-        int mCachedAddress = Integer.MIN_VALUE;
+        // mCachedAddressBefore/AfterUpdate are helpers for binary dictionary generation. They
+        // always hold the same value except between dictionary address compression, during which
+        // the update process needs to know about both values at the same time. Updating will
+        // update the AfterUpdate value, and the code will move them to BeforeUpdate before
+        // the next update pass.
+        int mCachedAddressBeforeUpdate = Integer.MIN_VALUE;
+        int mCachedAddressAfterUpdate = Integer.MIN_VALUE;
         int mCachedParentAddress = 0;
 
         public Node() {
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
index 50dda96..64f860e 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java
@@ -1,17 +1,17 @@
-/**
- * Copyright (C) 2013 Google Inc.
+/*
+ * Copyright (C) 2013 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
+ * 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
+ *      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.
+ * 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.
  */
 
 package com.android.inputmethod.latin.userdictionary;
diff --git a/tests/res/raw/dummy_resource_for_testing.txt b/tests/res/raw/dummy_resource_for_testing.txt
new file mode 100644
index 0000000..05da86b
--- /dev/null
+++ b/tests/res/raw/dummy_resource_for_testing.txt
@@ -0,0 +1,3 @@
+/* This dummy raw resource is needed to be able to load string resources from a test APK
+ * successfully. (See {@link KeySpecParserSplitTests#setUp()}.
+ */
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java
index f04c7e9..b6ee71a 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java
@@ -51,9 +51,10 @@
         }.runInLocale(targetContext.getResources(), TEST_LOCALE);
         final String[] testResourceNames = getAllResourceIdNames(
                 com.android.inputmethod.latin.tests.R.string.class);
-        mTextsSet.loadStringResourcesInternal(instrumentation.getContext(),
-                testResourceNames,
-                com.android.inputmethod.latin.tests.R.string.empty_string);
+        mTextsSet.loadStringResourcesInternal(instrumentation.getContext(), testResourceNames,
+                // This dummy raw resource is needed to be able to load string resources from a test
+                // APK successfully.
+                com.android.inputmethod.latin.tests.R.raw.dummy_resource_for_testing);
     }
 
     private static String[] getAllResourceIdNames(final Class<?> resourceIdClass) {