Merge "Remove PrevWordsInfo.getBigramsIteratorForPrediction()." into lmp-dev
diff --git a/java/Android.mk b/java/Android.mk
index b580624..9b8b2b4 100644
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4 jsr305
 
 # Do not compress dictionary files to mmap dict data runtime
 LOCAL_AAPT_FLAGS := -0 .dict
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 5434106..003b011 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -481,13 +481,24 @@
     <declare-styleable name="KeyboardLayoutSet_Feature">
         <!-- This should be aligned with ScriptUtils.SCRIPT_* -->
         <attr name="supportedScript" format="enum">
-            <enum name="latin" value="0" />
-            <enum name="cyrillic" value="1" />
-            <enum name="greek" value="2" />
-            <enum name="arabic" value="3" />
-            <enum name="hebrew" value="4" />
-            <enum name="armenian" value="5" />
-            <enum name="georgian" value="6" />
+            <enum name="arabic" value="0" />
+            <enum name="armenian" value="1" />
+            <enum name="bengali" value="2" />
+            <enum name="cyrillic" value="3" />
+            <enum name="devanagari" value="4" />
+            <enum name="georgian" value="5" />
+            <enum name="greek" value="6" />
+            <enum name="hebrew" value="7" />
+            <enum name="kannada" value="8" />
+            <enum name="khmer" value="9" />
+            <enum name="lao" value="10" />
+            <enum name="latin" value="11" />
+            <enum name="malayalam" value="12" />
+            <enum name="myanmar" value="13" />
+            <enum name="sinhala" value="14" />
+            <enum name="tamil" value="15" />
+            <enum name="telugu" value="16" />
+            <enum name="thai" value="17" />
         </attr>
     </declare-styleable>
 
diff --git a/java/res/xml/keyboard_layout_set_bengali.xml b/java/res/xml/keyboard_layout_set_bengali.xml
index 6e40e6d..de48a0c 100644
--- a/java/res/xml/keyboard_layout_set_bengali.xml
+++ b/java/res/xml/keyboard_layout_set_bengali.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="bengali" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_bengali"
diff --git a/java/res/xml/keyboard_layout_set_hindi.xml b/java/res/xml/keyboard_layout_set_hindi.xml
index e850c7e..ee41b2a 100644
--- a/java/res/xml/keyboard_layout_set_hindi.xml
+++ b/java/res/xml/keyboard_layout_set_hindi.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="devanagari" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_hindi"
diff --git a/java/res/xml/keyboard_layout_set_hindi_compact.xml b/java/res/xml/keyboard_layout_set_hindi_compact.xml
index 77d02fb..147b299 100644
--- a/java/res/xml/keyboard_layout_set_hindi_compact.xml
+++ b/java/res/xml/keyboard_layout_set_hindi_compact.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="devanagari" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_hindi_compact"
diff --git a/java/res/xml/keyboard_layout_set_kannada.xml b/java/res/xml/keyboard_layout_set_kannada.xml
index 8dcf996..14323e7 100644
--- a/java/res/xml/keyboard_layout_set_kannada.xml
+++ b/java/res/xml/keyboard_layout_set_kannada.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="kannada" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_kannada"
diff --git a/java/res/xml/keyboard_layout_set_khmer.xml b/java/res/xml/keyboard_layout_set_khmer.xml
index 181f98b..752c852 100644
--- a/java/res/xml/keyboard_layout_set_khmer.xml
+++ b/java/res/xml/keyboard_layout_set_khmer.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="khmer" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_khmer"
diff --git a/java/res/xml/keyboard_layout_set_lao.xml b/java/res/xml/keyboard_layout_set_lao.xml
index 2ffde45..6285f87 100644
--- a/java/res/xml/keyboard_layout_set_lao.xml
+++ b/java/res/xml/keyboard_layout_set_lao.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="lao" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_lao"
diff --git a/java/res/xml/keyboard_layout_set_malayalam.xml b/java/res/xml/keyboard_layout_set_malayalam.xml
index 14c76ba..f6086f6 100644
--- a/java/res/xml/keyboard_layout_set_malayalam.xml
+++ b/java/res/xml/keyboard_layout_set_malayalam.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="malayalam" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_malayalam"
diff --git a/java/res/xml/keyboard_layout_set_marathi.xml b/java/res/xml/keyboard_layout_set_marathi.xml
index e5c68e7..6aea175 100644
--- a/java/res/xml/keyboard_layout_set_marathi.xml
+++ b/java/res/xml/keyboard_layout_set_marathi.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="devanagari" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_marathi"
diff --git a/java/res/xml/keyboard_layout_set_myanmar.xml b/java/res/xml/keyboard_layout_set_myanmar.xml
index 5c823b2..5af8c06 100644
--- a/java/res/xml/keyboard_layout_set_myanmar.xml
+++ b/java/res/xml/keyboard_layout_set_myanmar.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="myanmar" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_myanmar"
diff --git a/java/res/xml/keyboard_layout_set_nepali_romanized.xml b/java/res/xml/keyboard_layout_set_nepali_romanized.xml
index fbbc6a5..5bad835 100644
--- a/java/res/xml/keyboard_layout_set_nepali_romanized.xml
+++ b/java/res/xml/keyboard_layout_set_nepali_romanized.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="devanagari" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_nepali_romanized"
diff --git a/java/res/xml/keyboard_layout_set_nepali_traditional.xml b/java/res/xml/keyboard_layout_set_nepali_traditional.xml
index 4a3b601..6f11dee 100644
--- a/java/res/xml/keyboard_layout_set_nepali_traditional.xml
+++ b/java/res/xml/keyboard_layout_set_nepali_traditional.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="devanagari" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_nepali_traditional"
diff --git a/java/res/xml/keyboard_layout_set_sinhala.xml b/java/res/xml/keyboard_layout_set_sinhala.xml
index 8e6e619..1955d21 100644
--- a/java/res/xml/keyboard_layout_set_sinhala.xml
+++ b/java/res/xml/keyboard_layout_set_sinhala.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="sinhala" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_sinhala"
diff --git a/java/res/xml/keyboard_layout_set_tamil.xml b/java/res/xml/keyboard_layout_set_tamil.xml
index 5c04915..27f4a32 100644
--- a/java/res/xml/keyboard_layout_set_tamil.xml
+++ b/java/res/xml/keyboard_layout_set_tamil.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="tamil" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_tamil"
diff --git a/java/res/xml/keyboard_layout_set_telugu.xml b/java/res/xml/keyboard_layout_set_telugu.xml
index aca47b9..2bf65bc 100644
--- a/java/res/xml/keyboard_layout_set_telugu.xml
+++ b/java/res/xml/keyboard_layout_set_telugu.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="telugu" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_telugu"
diff --git a/java/res/xml/keyboard_layout_set_thai.xml b/java/res/xml/keyboard_layout_set_thai.xml
index b8f9997..f69dfa6 100644
--- a/java/res/xml/keyboard_layout_set_thai.xml
+++ b/java/res/xml/keyboard_layout_set_thai.xml
@@ -20,6 +20,8 @@
 
 <KeyboardLayoutSet
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Feature
+        latin:supportedScript="thai" />
     <Element
         latin:elementName="alphabet"
         latin:elementKeyboard="@xml/kbd_thai"
diff --git a/java/src/com/android/inputmethod/event/Combiner.java b/java/src/com/android/inputmethod/event/Combiner.java
index 8b808c6..fee93f0 100644
--- a/java/src/com/android/inputmethod/event/Combiner.java
+++ b/java/src/com/android/inputmethod/event/Combiner.java
@@ -18,6 +18,8 @@
 
 import java.util.ArrayList;
 
+import javax.annotation.Nonnull;
+
 /**
  * A generic interface for combiners. Combiners are objects that transform chains of input events
  * into committable strings and manage feedback to show to the user on the combining state.
@@ -33,6 +35,7 @@
      * @param event the event to combine with the existing state.
      * @return the resulting event.
      */
+    @Nonnull
     Event processEvent(ArrayList<Event> previousEvents, Event event);
 
     /**
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index 61bc11b..f69bf4f 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -24,6 +24,8 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 
+import javax.annotation.Nonnull;
+
 /**
  * This class implements the logic chain between receiving events and generating code points.
  *
@@ -81,22 +83,29 @@
     }
 
     /**
-     * Pass a new event through the whole chain.
+     * Process an event through the combining chain, and return a processed event to apply.
      * @param previousEvents the list of previous events in this composition
      * @param newEvent the new event to process
+     * @return the processed event. It may be the same event, or a consumed event, or a completely
+     *   new event. However it may never be null.
      */
-    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
+    @Nonnull
+    public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
         final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents);
         Event event = newEvent;
         for (final Combiner combiner : mCombiners) {
             // A combiner can never return more than one event; it can return several
             // code points, but they should be encapsulated within one event.
             event = combiner.processEvent(modifiablePreviousEvents, event);
-            if (null == event) {
-                // Combiners return null if they eat the event.
-                break;
-            }
         }
+        return event;
+    }
+
+    /**
+     * Apply a processed event.
+     * @param event the event to be applied
+     */
+    public void applyProcessedEvent(final Event event) {
         if (null != event) {
             // TODO: figure out the generic way of doing this
             if (Constants.CODE_DELETE == event.mKeyCode) {
diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
index bef4d85..d816247 100644
--- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
+++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java
@@ -23,6 +23,8 @@
 
 import java.util.ArrayList;
 
+import javax.annotation.Nonnull;
+
 /**
  * A combiner that handles dead keys.
  */
@@ -31,13 +33,13 @@
     final StringBuilder mDeadSequence = new StringBuilder();
 
     @Override
+    @Nonnull
     public Event processEvent(final ArrayList<Event> previousEvents, final Event event) {
-        if (null == event) return null; // Just in case some combiner is broken
         if (TextUtils.isEmpty(mDeadSequence)) {
             if (event.isDead()) {
                 mDeadSequence.appendCodePoint(event.mCodePoint);
             }
-            return event;
+            return Event.createConsumedEvent(event);
         } else {
             // TODO: Allow combining for several dead chars rather than only the first one.
             // The framework doesn't know how to do this now.
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java
index d257441..98c8274 100644
--- a/java/src/com/android/inputmethod/event/Event.java
+++ b/java/src/com/android/inputmethod/event/Event.java
@@ -67,6 +67,8 @@
     final private static int FLAG_DEAD = 0x1;
     // This event is coming from a key repeat, software or hardware.
     final private static int FLAG_REPEAT = 0x2;
+    // This event has already been consumed.
+    final private static int FLAG_CONSUMED = 0x4;
 
     final private int mEventType; // The type of event - one of the constants above
     // The code point associated with the event, if relevant. This is a unicode code point, and
@@ -219,6 +221,17 @@
                 null /* next */);
     }
 
+    /**
+     * Creates an event identical to the passed event, but that has already been consumed.
+     * @param source the event to copy the properties of.
+     * @return an identical event marked as consumed.
+     */
+    public static Event createConsumedEvent(final Event source) {
+        return new Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode,
+                source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags | FLAG_CONSUMED,
+                source.mNextEvent);
+    }
+
     public static Event createNotHandledEvent() {
         return new Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE,
                 Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
@@ -241,6 +254,8 @@
         return 0 != (FLAG_REPEAT & mFlags);
     }
 
+    public boolean isConsumed() { return 0 != (FLAG_CONSUMED & mFlags); }
+
     // Returns whether this is a fake key press from the suggestion strip. This happens with
     // punctuation signs selected from the suggestion strip.
     public boolean isSuggestionStripPress() {
diff --git a/java/src/com/android/inputmethod/event/MyanmarReordering.java b/java/src/com/android/inputmethod/event/MyanmarReordering.java
index 3291993..dcd06c8 100644
--- a/java/src/com/android/inputmethod/event/MyanmarReordering.java
+++ b/java/src/com/android/inputmethod/event/MyanmarReordering.java
@@ -21,6 +21,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 
+import javax.annotation.Nonnull;
+
 /**
  * A combiner that reorders input for Myanmar.
  */
@@ -111,7 +113,7 @@
      * Clears the currently combining stream of events and returns the resulting software text
      * event corresponding to the stream. Optionally adds a new event to the cleared stream.
      * @param newEvent the new event to add to the stream. null if none.
-     * @return the resulting software text event. Null if none.
+     * @return the resulting software text event. Never null.
      */
     private Event clearAndGetResultingEvent(final Event newEvent) {
         final CharSequence combinedText;
@@ -124,18 +126,19 @@
         if (null != newEvent) {
             mCurrentEvents.add(newEvent);
         }
-        return null == combinedText ? null
+        return null == combinedText ? Event.createConsumedEvent(newEvent)
                 : Event.createSoftwareTextEvent(combinedText, Event.NOT_A_KEY_CODE);
     }
 
     @Override
+    @Nonnull
     public Event processEvent(ArrayList<Event> previousEvents, Event newEvent) {
         final int codePoint = newEvent.mCodePoint;
         if (VOWEL_E == codePoint) {
             final Event lastEvent = getLastEvent();
             if (null == lastEvent) {
                 mCurrentEvents.add(newEvent);
-                return null;
+                return Event.createConsumedEvent(newEvent);
             } else if (isConsonantOrMedial(lastEvent.mCodePoint)) {
                 final Event resultingEvent = clearAndGetResultingEvent(null);
                 mCurrentEvents.add(Event.createSoftwareKeypressEvent(ZERO_WIDTH_NON_JOINER,
@@ -151,7 +154,7 @@
             final Event lastEvent = getLastEvent();
             if (null == lastEvent) {
                 mCurrentEvents.add(newEvent);
-                return null;
+                return Event.createConsumedEvent(newEvent);
             } else if (VOWEL_E == lastEvent.mCodePoint) {
                 final int eventSize = mCurrentEvents.size();
                 if (eventSize >= 2
@@ -162,7 +165,7 @@
                     mCurrentEvents.remove(eventSize - 2);
                     mCurrentEvents.add(newEvent);
                     mCurrentEvents.add(lastEvent);
-                    return null;
+                    return Event.createConsumedEvent(newEvent);
                 }
                 // If there is already a consonant, then we are starting a new syllable.
                 for (int i = eventSize - 2; i >= 0; --i) {
@@ -174,7 +177,7 @@
                 mCurrentEvents.remove(eventSize - 1);
                 mCurrentEvents.add(newEvent);
                 mCurrentEvents.add(lastEvent);
-                return null;
+                return Event.createConsumedEvent(newEvent);
             } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine
                 return clearAndGetResultingEvent(newEvent);
             }
@@ -182,7 +185,7 @@
             final Event lastEvent = getLastEvent();
             if (null == lastEvent) {
                 mCurrentEvents.add(newEvent);
-                return null;
+                return Event.createConsumedEvent(newEvent);
             } else if (VOWEL_E == lastEvent.mCodePoint) {
                 final int eventSize = mCurrentEvents.size();
                 // If there is already a consonant, then we are in the middle of a syllable, and we
@@ -198,7 +201,7 @@
                     mCurrentEvents.remove(eventSize - 1);
                     mCurrentEvents.add(newEvent);
                     mCurrentEvents.add(lastEvent);
-                    return null;
+                    return Event.createConsumedEvent(newEvent);
                 }
                 // Otherwise, we just commit everything.
                 return clearAndGetResultingEvent(null);
@@ -228,10 +231,10 @@
                             mCurrentEvents.remove(eventSize - 1);
                         }
                     }
-                    return null;
+                    return Event.createConsumedEvent(newEvent);
                 } else if (eventSize > 0) {
                     mCurrentEvents.remove(eventSize - 1);
-                    return null;
+                    return Event.createConsumedEvent(newEvent);
                 }
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index cdd7822..3b69048 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -25,6 +25,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 
+import javax.annotation.Nonnull;
+
 /**
  * A place to store the currently composing word with information such as adjacent key codes as well
  */
@@ -175,20 +177,31 @@
     }
 
     /**
-     * Process an input event.
+     * Process an event and return an event, and return a processed event to apply.
+     * @param event the unprocessed event.
+     * @return the processed event. Never null, but may be marked as consumed.
+     */
+    @Nonnull
+    public Event processEvent(final Event event) {
+        final Event processedEvent = mCombinerChain.processEvent(mEvents, event);
+        mEvents.add(event);
+        return processedEvent;
+    }
+
+    /**
+     * Apply a processed input event.
      *
      * All input events should be supported, including software/hardware events, characters as well
      * as deletions, multiple inputs and gestures.
      *
-     * @param event the event to process.
+     * @param event the event to apply. Must not be null.
      */
-    public void processEvent(final Event event) {
+    public void applyProcessedEvent(final Event event) {
+        mCombinerChain.applyProcessedEvent(event);
         final int primaryCode = event.mCodePoint;
         final int keyX = event.mX;
         final int keyY = event.mY;
         final int newIndex = size();
-        mCombinerChain.processEvent(mEvents, event);
-        mEvents.add(event);
         refreshTypedWordCache();
         mCursorPositionWithinWord = mCodePointSize;
         // We may have deleted the last one.
@@ -281,7 +294,9 @@
             final int codePoint = Character.codePointAt(word, i);
             // We don't want to override the batch input points that are held in mInputPointers
             // (See {@link #add(int,int,int)}).
-            processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
+            final Event processedEvent =
+                    processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
+            applyProcessedEvent(processedEvent);
         }
     }
 
@@ -295,9 +310,11 @@
         reset();
         final int length = codePoints.length;
         for (int i = 0; i < length; ++i) {
-            processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
+            final Event processedEvent =
+                    processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
                     CoordinateUtils.xFromArray(coordinates, i),
                     CoordinateUtils.yFromArray(coordinates, i)));
+            applyProcessedEvent(processedEvent);
         }
         mIsResumed = true;
     }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 74d8799..f2a6f3b 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -712,7 +712,8 @@
      */
     private void handleNonSeparator(final SettingsValues settingsValues,
             final InputTransaction inputTransaction) {
-        final int codePoint = inputTransaction.mEvent.mCodePoint;
+        final Event processedEvent = mWordComposer.processEvent(inputTransaction.mEvent);
+        final int codePoint = processedEvent.mCodePoint;
         // TODO: refactor this method to stop flipping isComposingWord around all the time, and
         // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
         // which has the same name as other handle* methods but is not the same.
@@ -762,7 +763,7 @@
             resetComposingState(false /* alsoResetLastComposedWord */);
         }
         if (isComposingWord) {
-            mWordComposer.processEvent(inputTransaction.mEvent);
+            mWordComposer.applyProcessedEvent(processedEvent);
             // If it's the first letter, make note of auto-caps state
             if (mWordComposer.isSingleLetter()) {
                 mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
@@ -771,7 +772,7 @@
                     mWordComposer.getTypedWord()), 1);
         } else {
             final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(
-                    inputTransaction, inputTransaction.mEvent.isSuggestionStripPress());
+                    inputTransaction, processedEvent.isSuggestionStripPress());
 
             if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) {
                 mSpaceState = SpaceState.WEAK;
@@ -902,6 +903,7 @@
     private void handleBackspace(final InputTransaction inputTransaction,
             // TODO: remove this argument, put it into settingsValues
             final int currentKeyboardScriptId) {
+        final Event processedEvent = mWordComposer.processEvent(inputTransaction.mEvent);
         mSpaceState = SpaceState.NONE;
         mDeleteCount++;
 
@@ -913,7 +915,7 @@
         // Then again, even in the case of a key repeat, if the cursor is at start of text, it
         // can't go any further back, so we can update right away even if it's a key repeat.
         final int shiftUpdateKind =
-                inputTransaction.mEvent.isKeyRepeat() && mConnection.getExpectedSelectionStart() > 0
+                processedEvent.isKeyRepeat() && mConnection.getExpectedSelectionStart() > 0
                 ? InputTransaction.SHIFT_UPDATE_LATER : InputTransaction.SHIFT_UPDATE_NOW;
         inputTransaction.requireShiftUpdate(shiftUpdateKind);
 
@@ -933,7 +935,7 @@
                     mDictionaryFacilitator.removeWordFromPersonalizedDicts(rejectedSuggestion);
                 }
             } else {
-                mWordComposer.processEvent(inputTransaction.mEvent);
+                mWordComposer.applyProcessedEvent(processedEvent);
             }
             if (mWordComposer.isComposingWord()) {
                 mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index d151e40..6c4d80e 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -38,6 +38,7 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.keyboard.MoreKeysPanel;
@@ -368,13 +369,15 @@
         return true;
     }
 
-    // Working variables for {@link #onLongClick(View)} and
-    // {@link onInterceptTouchEvent(MotionEvent)}.
+    // Working variables for {@link onInterceptTouchEvent(MotionEvent)} and
+    // {@link onTouchEvent(MotionEvent)}.
     private int mLastX;
     private int mLastY;
     private int mOriginX;
     private int mOriginY;
     private final int mMoreSuggestionsModalTolerance;
+    private boolean mNeedsToTransformTouchEventToHoverEvent;
+    private boolean mIsDispatchingHoverEventToMoreSuggestions;
     private final GestureDetector mMoreSuggestionsSlidingDetector;
     private final GestureDetector.OnGestureListener mMoreSuggestionsSlidingListener =
             new GestureDetector.SimpleOnGestureListener() {
@@ -402,9 +405,12 @@
         final int y = (int)me.getY(index);
         if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance
                 || mOriginY - y >= mMoreSuggestionsModalTolerance) {
-            // Decided to be in the sliding input mode only when the touch point has been moved
+            // Decided to be in the sliding suggestion mode only when the touch point has been moved
             // upward. Further {@link MotionEvent}s will be delivered to
             // {@link #onTouchEvent(MotionEvent)}.
+            mNeedsToTransformTouchEventToHoverEvent = AccessibilityUtils.getInstance()
+                    .isTouchExplorationEnabled();
+            mIsDispatchingHoverEventToMoreSuggestions = false;
             return true;
         }
 
@@ -426,10 +432,41 @@
         // In the sliding input mode. {@link MotionEvent} should be forwarded to
         // {@link MoreSuggestionsView}.
         final int index = me.getActionIndex();
-        final int x = (int)me.getX(index);
-        final int y = (int)me.getY(index);
-        me.setLocation(mMoreSuggestionsView.translateX(x), mMoreSuggestionsView.translateY(y));
-        mMoreSuggestionsView.onTouchEvent(me);
+        final int x = mMoreSuggestionsView.translateX((int)me.getX(index));
+        final int y = mMoreSuggestionsView.translateY((int)me.getY(index));
+        me.setLocation(x, y);
+        if (!mNeedsToTransformTouchEventToHoverEvent) {
+            mMoreSuggestionsView.onTouchEvent(me);
+            return true;
+        }
+        // In sliding suggestion mode with accessibility mode on, a touch event should be
+        // transformed to a hover event.
+        final int width = mMoreSuggestionsView.getWidth();
+        final int height = mMoreSuggestionsView.getHeight();
+        final boolean onMoreSuggestions = (x >= 0 && x < width && y >= 0 && y < height);
+        if (!onMoreSuggestions && !mIsDispatchingHoverEventToMoreSuggestions) {
+            // Just drop this touch event because dispatching hover event isn't started yet and
+            // the touch event isn't on {@link MoreSuggestionsView}.
+            return true;
+        }
+        final int hoverAction;
+        if (onMoreSuggestions && !mIsDispatchingHoverEventToMoreSuggestions) {
+            // Transform this touch event to a hover enter event and start dispatching a hover
+            // event to {@link MoreSuggestionsView}.
+            mIsDispatchingHoverEventToMoreSuggestions = true;
+            hoverAction = MotionEvent.ACTION_HOVER_ENTER;
+        } else if (me.getActionMasked() == MotionEvent.ACTION_UP) {
+            // Transform this touch event to a hover exit event and stop dispatching a hover event
+            // after this.
+            mIsDispatchingHoverEventToMoreSuggestions = false;
+            mNeedsToTransformTouchEventToHoverEvent = false;
+            hoverAction = MotionEvent.ACTION_HOVER_EXIT;
+        } else {
+            // Transform this touch event to a hover move event.
+            hoverAction = MotionEvent.ACTION_HOVER_MOVE;
+        }
+        me.setAction(hoverAction);
+        mMoreSuggestionsView.onHoverEvent(me);
         return true;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
index a76a6df..0e24466 100644
--- a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java
@@ -26,13 +26,24 @@
     // Used for hardware keyboards
     public static final int SCRIPT_UNKNOWN = -1;
     // TODO: should we use ISO 15924 identifiers instead?
-    public static final int SCRIPT_LATIN = 0;
-    public static final int SCRIPT_CYRILLIC = 1;
-    public static final int SCRIPT_GREEK = 2;
-    public static final int SCRIPT_ARABIC = 3;
-    public static final int SCRIPT_HEBREW = 4;
-    public static final int SCRIPT_ARMENIAN = 5;
-    public static final int SCRIPT_GEORGIAN = 6;
+    public static final int SCRIPT_ARABIC = 0;
+    public static final int SCRIPT_ARMENIAN = 1;
+    public static final int SCRIPT_BENGALI = 2;
+    public static final int SCRIPT_CYRILLIC = 3;
+    public static final int SCRIPT_DEVANAGARI = 4;
+    public static final int SCRIPT_GEORGIAN = 5;
+    public static final int SCRIPT_GREEK = 6;
+    public static final int SCRIPT_HEBREW = 7;
+    public static final int SCRIPT_KANNADA = 8;
+    public static final int SCRIPT_KHMER = 9;
+    public static final int SCRIPT_LAO = 10;
+    public static final int SCRIPT_LATIN = 11;
+    public static final int SCRIPT_MALAYALAM = 12;
+    public static final int SCRIPT_MYANMAR = 13;
+    public static final int SCRIPT_SINHALA = 14;
+    public static final int SCRIPT_TAMIL = 15;
+    public static final int SCRIPT_TELUGU = 16;
+    public static final int SCRIPT_THAI = 17;
     public static final TreeMap<String, Integer> mSpellCheckerLanguageToScript;
     static {
         // List of the supported languages and their associated script. We won't check
@@ -72,18 +83,40 @@
      */
     public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) {
         switch (scriptId) {
-        case SCRIPT_LATIN:
-            // Our supported latin script dictionaries (EFIGS) at the moment only include
-            // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
-            // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
-            // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
-            // excluded from isLetter anyway.
-            return codePoint <= 0x2AF && Character.isLetter(codePoint);
+        case SCRIPT_ARABIC:
+            // Arabic letters can be in any of the following blocks:
+            // Arabic U+0600..U+06FF
+            // Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF
+            // Arabic Extended-A U+08A0..U+08FF
+            // Arabic Presentation Forms-A U+FB50..U+FDFF
+            // Arabic Presentation Forms-B U+FE70..U+FEFF
+            return (codePoint >= 0x600 && codePoint <= 0x6FF)
+                    || (codePoint >= 0x750 && codePoint <= 0x7BF)
+                    || (codePoint >= 0x8A0 && codePoint <= 0x8FF)
+                    || (codePoint >= 0xFB50 && codePoint <= 0xFDFF)
+                    || (codePoint >= 0xFE70 && codePoint <= 0xFEFF);
+        case SCRIPT_ARMENIAN:
+            // Armenian letters are in the Armenian unicode block, U+0530..U+058F and
+            // Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part
+            // of that block, which is U+FB13..U+FB17.
+            return (codePoint >= 0x530 && codePoint <= 0x58F
+                    || codePoint >= 0xFB13 && codePoint <= 0xFB17);
+        case SCRIPT_BENGALI:
+            // Bengali unicode block is U+0980..U+09FF
+            return (codePoint >= 0x980 && codePoint <= 0x9FF);
         case SCRIPT_CYRILLIC:
             // All Cyrillic characters are in the 400~52F block. There are some in the upper
             // Unicode range, but they are archaic characters that are not used in modern
             // Russian and are not used by our dictionary.
             return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
+        case SCRIPT_DEVANAGARI:
+            // Devanagari unicode block is +0900..U+097F
+            return (codePoint >= 0x900 && codePoint <= 0x97F);
+        case SCRIPT_GEORGIAN:
+            // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF,
+            // or Georgian supplement block, U+2D00..U+2D2F
+            return (codePoint >= 0x10A0 && codePoint <= 0x10FF
+                    || codePoint >= 0x2D00 && codePoint <= 0x2D2F);
         case SCRIPT_GREEK:
             // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
             // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
@@ -93,35 +126,53 @@
             return (codePoint >= 0x370 && codePoint <= 0x3FF)
                     || (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
                     || codePoint == 0xF2;
-        case SCRIPT_ARABIC:
-            // Arabic letters can be in any of the following blocks:
-            // Arabic U+0600..U+06FF
-            // Arabic Supplement U+0750..U+077F
-            // Arabic Extended-A U+08A0..U+08FF
-            // Arabic Presentation Forms-A U+FB50..U+FDFF
-            // Arabic Presentation Forms-B U+FE70..U+FEFF
-            return (codePoint >= 0x600 && codePoint <= 0x6FF)
-                    || (codePoint >= 0x750 && codePoint <= 0x77F)
-                    || (codePoint >= 0x8A0 && codePoint <= 0x8FF)
-                    || (codePoint >= 0xFB50 && codePoint <= 0xFDFF)
-                    || (codePoint >= 0xFE70 && codePoint <= 0xFEFF);
         case SCRIPT_HEBREW:
             // Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF,
             // or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the
             // Hebrew part of that block, which is U+FB1D..U+FB4F.
             return (codePoint >= 0x590 && codePoint <= 0x5FF
                     || codePoint >= 0xFB1D && codePoint <= 0xFB4F);
-        case SCRIPT_ARMENIAN:
-            // Armenian letters are in the Armenian unicode block, U+0530..U+058F and
-            // Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part
-            // of that block, which is U+FB13..U+FB17.
-            return (codePoint >= 0x530 && codePoint <= 0x58F
-                    || codePoint >= 0xFB13 && codePoint <= 0xFB17);
-        case SCRIPT_GEORGIAN:
-            // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF,
-            // or Georgian supplement block, U+2D00..U+2D2F
-            return (codePoint >= 0x10A0 && codePoint <= 0x10FF
-                    || codePoint >= 0x2D00 && codePoint <= 0x2D2F);
+        case SCRIPT_KANNADA:
+            // Kannada unicode block is U+0C80..U+0CFF
+            return (codePoint >= 0xC80 && codePoint <= 0xCFF);
+        case SCRIPT_KHMER:
+            // Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block
+            // is U+19E0..U+19FF
+            return (codePoint >= 0x1780 && codePoint <= 0x17FF
+                    || codePoint >= 0x19E0 && codePoint <= 0x19FF);
+        case SCRIPT_LAO:
+            // The Lao block is U+0E80..U+0EFF
+            return (codePoint >= 0xE80 && codePoint <= 0xEFF);
+        case SCRIPT_LATIN:
+            // Our supported latin script dictionaries (EFIGS) at the moment only include
+            // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
+            // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
+            // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
+            // excluded from isLetter anyway.
+            return codePoint <= 0x2AF && Character.isLetter(codePoint);
+        case SCRIPT_MALAYALAM:
+            // Malayalam unicode block is U+0D00..U+0D7F
+            return (codePoint >= 0xD00 && codePoint <= 0xD7F);
+        case SCRIPT_MYANMAR:
+            // Myanmar has three unicode blocks :
+            // Myanmar U+1000..U+109F
+            // Myanmar extended-A U+AA60..U+AA7F
+            // Myanmar extended-B U+A9E0..U+A9FF
+            return (codePoint >= 0x1000 && codePoint <= 0x109F
+                    || codePoint >= 0xAA60 && codePoint <= 0xAA7F
+                    || codePoint >= 0xA9E0 && codePoint <= 0xA9FF);
+        case SCRIPT_SINHALA:
+            // Sinhala unicode block is U+0D80..U+0DFF
+            return (codePoint >= 0xD80 && codePoint <= 0xDFF);
+        case SCRIPT_TAMIL:
+            // Tamil unicode block is U+0B80..U+0BFF
+            return (codePoint >= 0xB80 && codePoint <= 0xBFF);
+        case SCRIPT_TELUGU:
+            // Telugu unicode block is U+0C00..U+0C7F
+            return (codePoint >= 0xC00 && codePoint <= 0xC7F);
+        case SCRIPT_THAI:
+            // Thai unicode block is U+0E00..U+0E7F
+            return (codePoint >= 0xE00 && codePoint <= 0xE7F);
         case SCRIPT_UNKNOWN:
             return true;
         default:
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.h b/native/jni/src/suggest/core/layout/proximity_info_state.h
index 6b1a319..e6180fe 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_state.h
@@ -215,13 +215,13 @@
     std::vector<float> mSpeedRates;
     std::vector<float> mDirections;
     // probabilities of skipping or mapping to a key for each point.
-    std::vector<std::unordered_map<int, float> > mCharProbabilities;
+    std::vector<std::unordered_map<int, float>> mCharProbabilities;
     // The vector for the key code set which holds nearby keys of some trailing sampled input points
     // for each sampled input point. These nearby keys contain the next characters which can be in
     // the dictionary. Specifically, currently we are looking for keys nearby trailing sampled
     // inputs including the current input point.
     std::vector<ProximityInfoStateUtils::NearKeycodesSet> mSampledSearchKeySets;
-    std::vector<std::vector<int> > mSampledSearchKeyVectors;
+    std::vector<std::vector<int>> mSampledSearchKeyVectors;
     bool mTouchPositionCorrectionEnabled;
     int mInputProximities[MAX_PROXIMITY_CHARS_SIZE * MAX_WORD_LENGTH];
     int mSampledInputSize;
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
index ea3b022..0aeb36a 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
@@ -621,7 +621,7 @@
         const std::vector<int> *const sampledLengthCache,
         const std::vector<float> *const sampledNormalizedSquaredLengthCache,
         const ProximityInfo *const proximityInfo,
-        std::vector<std::unordered_map<int, float> > *charProbabilities) {
+        std::vector<std::unordered_map<int, float>> *charProbabilities) {
     charProbabilities->resize(sampledInputSize);
     // Calculates probabilities of using a point as a correlated point with the character
     // for each point.
@@ -822,9 +822,9 @@
 /* static */ void ProximityInfoStateUtils::updateSampledSearchKeySets(
         const ProximityInfo *const proximityInfo, const int sampledInputSize,
         const int lastSavedInputSize, const std::vector<int> *const sampledLengthCache,
-        const std::vector<std::unordered_map<int, float> > *const charProbabilities,
+        const std::vector<std::unordered_map<int, float>> *const charProbabilities,
         std::vector<NearKeycodesSet> *sampledSearchKeySets,
-        std::vector<std::vector<int> > *sampledSearchKeyVectors) {
+        std::vector<std::vector<int>> *sampledSearchKeyVectors) {
     sampledSearchKeySets->resize(sampledInputSize);
     sampledSearchKeyVectors->resize(sampledInputSize);
     const int readForwordLength = static_cast<int>(
@@ -868,7 +868,7 @@
 /* static */ bool ProximityInfoStateUtils::suppressCharProbabilities(const int mostCommonKeyWidth,
         const int sampledInputSize, const std::vector<int> *const lengthCache,
         const int index0, const int index1,
-        std::vector<std::unordered_map<int, float> > *charProbabilities) {
+        std::vector<std::unordered_map<int, float>> *charProbabilities) {
     ASSERT(0 <= index0 && index0 < sampledInputSize);
     ASSERT(0 <= index1 && index1 < sampledInputSize);
     const float keyWidthFloat = static_cast<float>(mostCommonKeyWidth);
@@ -933,7 +933,7 @@
 // returns probability of generating the word.
 /* static */ float ProximityInfoStateUtils::getMostProbableString(
         const ProximityInfo *const proximityInfo, const int sampledInputSize,
-        const std::vector<std::unordered_map<int, float> > *const charProbabilities,
+        const std::vector<std::unordered_map<int, float>> *const charProbabilities,
         int *const codePointBuf) {
     ASSERT(sampledInputSize >= 0);
     memset(codePointBuf, 0, sizeof(codePointBuf[0]) * MAX_WORD_LENGTH);
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h
index 211a797..4043334 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h
@@ -72,13 +72,13 @@
             const std::vector<int> *const sampledLengthCache,
             const std::vector<float> *const sampledNormalizedSquaredLengthCache,
             const ProximityInfo *const proximityInfo,
-            std::vector<std::unordered_map<int, float> > *charProbabilities);
+            std::vector<std::unordered_map<int, float>> *charProbabilities);
     static void updateSampledSearchKeySets(const ProximityInfo *const proximityInfo,
             const int sampledInputSize, const int lastSavedInputSize,
             const std::vector<int> *const sampledLengthCache,
-            const std::vector<std::unordered_map<int, float> > *const charProbabilities,
+            const std::vector<std::unordered_map<int, float>> *const charProbabilities,
             std::vector<NearKeycodesSet> *sampledSearchKeySets,
-            std::vector<std::vector<int> > *sampledSearchKeyVectors);
+            std::vector<std::vector<int>> *sampledSearchKeyVectors);
     static float getPointToKeyByIdLength(const float maxPointToKeyLength,
             const std::vector<float> *const sampledNormalizedSquaredLengthCache, const int keyCount,
             const int inputIndex, const int keyId);
@@ -105,7 +105,7 @@
     // TODO: Move to most_probable_string_utils.h
     static float getMostProbableString(const ProximityInfo *const proximityInfo,
             const int sampledInputSize,
-            const std::vector<std::unordered_map<int, float> > *const charProbabilities,
+            const std::vector<std::unordered_map<int, float>> *const charProbabilities,
             int *const codePointBuf);
 
  private:
@@ -147,7 +147,7 @@
             const int index2);
     static bool suppressCharProbabilities(const int mostCommonKeyWidth,
             const int sampledInputSize, const std::vector<int> *const lengthCache, const int index0,
-            const int index1, std::vector<std::unordered_map<int, float> > *charProbabilities);
+            const int index1, std::vector<std::unordered_map<int, float>> *charProbabilities);
     static float calculateSquaredDistanceFromSweetSpotCenter(
             const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int keyIndex,
diff --git a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
index a612276..6da390e 100644
--- a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
+++ b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
@@ -30,7 +30,7 @@
  */
 class DictionaryHeaderStructurePolicy {
  public:
-    typedef std::map<std::vector<int>, std::vector<int> > AttributeMap;
+    typedef std::map<std::vector<int>, std::vector<int>> AttributeMap;
 
     virtual ~DictionaryHeaderStructurePolicy() {}
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
index 3ff80ae..9910777 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
@@ -84,7 +84,7 @@
         static const int STRONG_BASE_PROBABILITY;
         static const int AGGRESSIVE_BASE_PROBABILITY;
 
-        std::vector<std::vector<std::vector<int> > > mTables;
+        std::vector<std::vector<std::vector<int>>> mTables;
 
         static int getBaseProbabilityForLevel(const int tableId, const int level);
     };
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index e4de59d..0f1f344 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -88,6 +88,7 @@
         $(call all-java-files-under, $(DICTTOOL_ONDEVICE_TESTS_DIRECTORY))
 
 LOCAL_JAVA_LIBRARIES := junit
+LOCAL_STATIC_JAVA_LIBRARIES := jsr305lib
 LOCAL_REQUIRED_MODULES := $(LATINIME_HOST_NATIVE_LIBNAME)
 LOCAL_JAR_MANIFEST := etc/manifest.txt
 LOCAL_MODULE := dicttool_aosp
diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
index 458f22c..c4457a1 100644
--- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
+++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
@@ -31,8 +31,12 @@
         mComposingWord = new StringBuilder(initialText);
     }
 
-    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
-        mComposingWord.append(newEvent.getTextToCommit());
+    public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
+        return newEvent;
+    }
+
+    public void applyProcessedEvent(final Event event) {
+        mComposingWord.append(event.getTextToCommit());
     }
 
     public CharSequence getComposingWordWithCombiningFeedback() {