Merge "Close ResearchLogger upon onFinishInputView"
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 3fba39d..245e685 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -190,7 +190,7 @@
     <string name="dictionary_downloading" msgid="2982650524622620983">"Hiện đang tải xuống"</string>
     <string name="dictionary_installed" msgid="8081558343559342962">"Đã cài đặt"</string>
     <string name="dictionary_disabled" msgid="8950383219564621762">"Đã cài đặt, bị tắt"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Lỗi knối d.vụ t.điển"</string>
+    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Lỗi kết nối DV từ điển"</string>
     <string name="no_dictionaries_available" msgid="8039920716566132611">"Không có từ điển nào"</string>
     <string name="check_for_updates_now" msgid="8087688440916388581">"Làm mới"</string>
     <string name="last_update" msgid="730467549913588780">"Cập nhật lần cuối"</string>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index fd9edec..5e68c70 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -301,8 +301,10 @@
             final int xmlId = mResources.getIdentifier(keyboardLayoutSetName, "xml", packageName);
             try {
                 parseKeyboardLayoutSet(mResources, xmlId);
-            } catch (Exception e) {
-                throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName);
+            } catch (final IOException e) {
+                throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
+            } catch (final XmlPullParserException e) {
+                throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
             }
             return new KeyboardLayoutSet(mContext, mParams);
         }
@@ -311,14 +313,14 @@
                 throws XmlPullParserException, IOException {
             final XmlResourceParser parser = res.getXml(resId);
             try {
-                int event;
-                while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                    final int event = parser.next();
                     if (event == XmlPullParser.START_TAG) {
                         final String tag = parser.getName();
                         if (TAG_KEYBOARD_SET.equals(tag)) {
                             parseKeyboardLayoutSetContent(parser);
                         } else {
-                            throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET);
+                            throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
                         }
                     }
                 }
@@ -329,21 +331,21 @@
 
         private void parseKeyboardLayoutSetContent(final XmlPullParser parser)
                 throws XmlPullParserException, IOException {
-            int event;
-            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                final int event = parser.next();
                 if (event == XmlPullParser.START_TAG) {
                     final String tag = parser.getName();
                     if (TAG_ELEMENT.equals(tag)) {
                         parseKeyboardLayoutSetElement(parser);
                     } else {
-                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET);
+                        throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
                     }
                 } else if (event == XmlPullParser.END_TAG) {
                     final String tag = parser.getName();
                     if (TAG_KEYBOARD_SET.equals(tag)) {
                         break;
                     } else {
-                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEYBOARD_SET);
+                        throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_KEYBOARD_SET);
                     }
                 }
             }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 8ae1b88..be178f5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -164,10 +164,10 @@
             parseKeyboard(parser);
         } catch (XmlPullParserException e) {
             Log.w(BUILDER_TAG, "keyboard XML parse error", e);
-            throw new IllegalArgumentException(e);
+            throw new IllegalArgumentException(e.getMessage(), e);
         } catch (IOException e) {
             Log.w(BUILDER_TAG, "keyboard XML parse error", e);
-            throw new RuntimeException(e);
+            throw new RuntimeException(e.getMessage(), e);
         } finally {
             parser.close();
         }
@@ -210,8 +210,8 @@
     private void parseKeyboard(final XmlPullParser parser)
             throws XmlPullParserException, IOException {
         if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEYBOARD.equals(tag)) {
@@ -220,7 +220,7 @@
                     parseKeyboardContent(parser, false);
                     break;
                 } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
+                    throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD);
                 }
             }
         }
@@ -303,8 +303,8 @@
 
     private void parseKeyboardContent(final XmlPullParser parser, final boolean skip)
             throws XmlPullParserException, IOException {
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_ROW.equals(tag)) {
@@ -321,7 +321,7 @@
                 } else if (TAG_KEY_STYLE.equals(tag)) {
                     parseKeyStyle(parser, skip);
                 } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
+                    throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_ROW);
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
@@ -333,7 +333,7 @@
                         || TAG_MERGE.equals(tag)) {
                     break;
                 } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
+                    throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
                 }
             }
         }
@@ -345,10 +345,10 @@
                 R.styleable.Keyboard);
         try {
             if (a.hasValue(R.styleable.Keyboard_horizontalGap)) {
-                throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
+                throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "horizontalGap");
             }
             if (a.hasValue(R.styleable.Keyboard_verticalGap)) {
-                throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
+                throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "verticalGap");
             }
             return new KeyboardRow(mResources, mParams, parser, mCurrentY);
         } finally {
@@ -358,8 +358,8 @@
 
     private void parseRowContent(final XmlPullParser parser, final KeyboardRow row,
             final boolean skip) throws XmlPullParserException, IOException {
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEY.equals(tag)) {
@@ -373,7 +373,7 @@
                 } else if (TAG_KEY_STYLE.equals(tag)) {
                     parseKeyStyle(parser, skip);
                 } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
+                    throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_ROW);
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
@@ -387,7 +387,7 @@
                         || TAG_MERGE.equals(tag)) {
                     break;
                 } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
+                    throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
                 }
             }
         }
@@ -506,8 +506,8 @@
     private void parseMerge(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
             throws XmlPullParserException, IOException {
         if (DEBUG) startTag("<%s>", TAG_MERGE);
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_MERGE.equals(tag)) {
@@ -539,8 +539,8 @@
             final boolean skip) throws XmlPullParserException, IOException {
         if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
         boolean selected = false;
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_CASE.equals(tag)) {
@@ -548,7 +548,7 @@
                 } else if (TAG_DEFAULT.equals(tag)) {
                     selected |= parseDefault(parser, row, selected ? true : skip);
                 } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
+                    throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_SWITCH);
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
@@ -556,7 +556,7 @@
                     if (DEBUG) endTag("</%s>", TAG_SWITCH);
                     break;
                 } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
+                    throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_SWITCH);
                 }
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index e66bcae..92b68dc 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -156,7 +156,7 @@
     private PositionalInfoForUserDictPendingAddition
             mPositionalInfoForUserDictPendingAddition = null;
     private final WordComposer mWordComposer = new WordComposer();
-    private RichInputConnection mConnection = new RichInputConnection(this);
+    private final RichInputConnection mConnection = new RichInputConnection(this);
 
     // Keep track of the last selection range to decide if we need to show word alternatives
     private static final int NOT_A_CURSOR_POSITION = -1;
@@ -2295,25 +2295,27 @@
         // expect to receive non-words.
         if (!mSettings.getCurrent().mCorrectionEnabled) return null;
 
+        final Suggest suggest = mSuggest;
         final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
-        if (userHistoryDictionary != null) {
-            final String prevWord
-                    = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2);
-            final String secondWord;
-            if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
-                secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
-            } else {
-                secondWord = suggestion;
-            }
-            // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
-            // We don't add words with 0-frequency (assuming they would be profanity etc.).
-            final int maxFreq = AutoCorrection.getMaxFrequency(
-                    mSuggest.getUnigramDictionaries(), suggestion);
-            if (maxFreq == 0) return null;
-            userHistoryDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0);
-            return prevWord;
+        if (suggest == null || userHistoryDictionary == null) {
+            // Avoid concurrent issue
+            return null;
         }
-        return null;
+        final String prevWord
+                = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2);
+        final String secondWord;
+        if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
+            secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
+        } else {
+            secondWord = suggestion;
+        }
+        // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
+        // We don't add words with 0-frequency (assuming they would be profanity etc.).
+        final int maxFreq = AutoCorrection.getMaxFrequency(
+                suggest.getUnigramDictionaries(), suggestion);
+        if (maxFreq == 0) return null;
+        userHistoryDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0);
+        return prevWord;
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
index f01d4c5..48e5ed3 100644
--- a/java/src/com/android/inputmethod/latin/XmlParseUtils.java
+++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
@@ -30,50 +30,53 @@
 
     @SuppressWarnings("serial")
     public static class ParseException extends XmlPullParserException {
-        public ParseException(String msg, XmlPullParser parser) {
+        public ParseException(final String msg, final XmlPullParser parser) {
             super(msg + " at " + parser.getPositionDescription());
         }
     }
 
     @SuppressWarnings("serial")
     public static final class IllegalStartTag extends ParseException {
-        public IllegalStartTag(XmlPullParser parser, String parent) {
-            super("Illegal start tag " + parser.getName() + " in " + parent, parser);
+        public IllegalStartTag(final XmlPullParser parser, final String tag, final String parent) {
+            super("Illegal start tag " + tag + " in " + parent, parser);
         }
     }
 
     @SuppressWarnings("serial")
     public static final class IllegalEndTag extends ParseException {
-        public IllegalEndTag(XmlPullParser parser, String parent) {
-            super("Illegal end tag " + parser.getName() + " in " + parent, parser);
+        public IllegalEndTag(final XmlPullParser parser, final String tag, final String parent) {
+            super("Illegal end tag " + tag + " in " + parent, parser);
         }
     }
 
     @SuppressWarnings("serial")
     public static final class IllegalAttribute extends ParseException {
-        public IllegalAttribute(XmlPullParser parser, String attribute) {
-            super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser);
+        public IllegalAttribute(final XmlPullParser parser, final String tag,
+                final String attribute) {
+            super("Tag " + tag + " has illegal attribute " + attribute, parser);
         }
     }
 
     @SuppressWarnings("serial")
     public static final class NonEmptyTag extends ParseException{
-        public NonEmptyTag(String tag, XmlPullParser parser) {
+        public NonEmptyTag(final XmlPullParser parser, final String tag) {
             super(tag + " must be empty tag", parser);
         }
     }
 
-    public static void checkEndTag(String tag, XmlPullParser parser)
+    public static void checkEndTag(final String tag, final XmlPullParser parser)
             throws XmlPullParserException, IOException {
         if (parser.next() == XmlPullParser.END_TAG && tag.equals(parser.getName()))
             return;
-        throw new NonEmptyTag(tag, parser);
+        throw new NonEmptyTag(parser, tag);
     }
 
-    public static void checkAttributeExists(TypedArray attr, int attrId, String attrName,
-            String tag, XmlPullParser parser) throws XmlPullParserException {
-        if (attr.hasValue(attrId))
+    public static void checkAttributeExists(final TypedArray attr, final int attrId,
+            final String attrName, final String tag, final XmlPullParser parser)
+                    throws XmlPullParserException {
+        if (attr.hasValue(attrId)) {
             return;
+        }
         throw new ParseException(
                 "No " + attrName + " attribute found in <" + tag + "/>", parser);
     }
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index 30ca3f1..3c482ca 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -43,9 +43,12 @@
 }
 
 static JNINativeMethod sMethods[] = {
-    {"setProximityInfoNative", "(Ljava/lang/String;IIIII[II[I[I[I[I[I[F[F[F)J",
-            reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)},
-    {"releaseProximityInfoNative", "(J)V", reinterpret_cast<void *>(latinime_Keyboard_release)}
+    {const_cast<char *>("setProximityInfoNative"),
+     const_cast<char *>("(Ljava/lang/String;IIIII[II[I[I[I[I[I[F[F[F)J"),
+     reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)},
+    {const_cast<char *>("releaseProximityInfoNative"),
+     const_cast<char *>("(J)V"),
+     reinterpret_cast<void *>(latinime_Keyboard_release)}
 };
 
 int register_ProximityInfo(JNIEnv *env) {
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 9321c4b..11fa3da 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -280,19 +280,27 @@
 }
 
 static JNINativeMethod sMethods[] = {
-    {"openNative", "(Ljava/lang/String;JJ)J",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_open)},
-    {"closeNative", "(J)V", reinterpret_cast<void *>(latinime_BinaryDictionary_close)},
-    {"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[I[I[I[I)I",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)},
-    {"getProbabilityNative", "(J[I)I",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)},
-    {"isValidBigramNative", "(J[I[I)Z",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)},
-    {"calcNormalizedScoreNative", "([I[II)F",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore)},
-    {"editDistanceNative", "([I[I)I",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance)}
+    {const_cast<char *>("openNative"),
+     const_cast<char *>("(Ljava/lang/String;JJ)J"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_open)},
+    {const_cast<char *>("closeNative"),
+     const_cast<char *>("(J)V"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_close)},
+    {const_cast<char *>("getSuggestionsNative"),
+     const_cast<char *>("(JJJ[I[I[I[I[IIIZ[IZ[I[I[I[I)I"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)},
+    {const_cast<char *>("getProbabilityNative"),
+     const_cast<char *>("(J[I)I"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)},
+    {const_cast<char *>("isValidBigramNative"),
+     const_cast<char *>("(J[I[I)Z"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)},
+    {const_cast<char *>("calcNormalizedScoreNative"),
+     const_cast<char *>("([I[II)F"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore)},
+    {const_cast<char *>("editDistanceNative"),
+     const_cast<char *>("([I[I)I"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance)}
 };
 
 int register_BinaryDictionary(JNIEnv *env) {
diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
index 9b39245..dfe3b09 100644
--- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
+++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
@@ -48,12 +48,15 @@
 }
 
 static JNINativeMethod sMethods[] = {
-    {"setDicTraverseSessionNative", "(Ljava/lang/String;)J",
-            reinterpret_cast<void *>(latinime_setDicTraverseSession)},
-    {"initDicTraverseSessionNative", "(JJ[II)V",
-            reinterpret_cast<void *>(latinime_initDicTraverseSession)},
-    {"releaseDicTraverseSessionNative", "(J)V",
-            reinterpret_cast<void *>(latinime_releaseDicTraverseSession)}
+    {const_cast<char *>("setDicTraverseSessionNative"),
+     const_cast<char *>("(Ljava/lang/String;)J"),
+     reinterpret_cast<void *>(latinime_setDicTraverseSession)},
+    {const_cast<char *>("initDicTraverseSessionNative"),
+     const_cast<char *>("(JJ[II)V"),
+     reinterpret_cast<void *>(latinime_initDicTraverseSession)},
+    {const_cast<char *>("releaseDicTraverseSessionNative"),
+     const_cast<char *>("(J)V"),
+     reinterpret_cast<void *>(latinime_releaseDicTraverseSession)}
 };
 
 int register_DicTraverseSession(JNIEnv *env) {