diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index da7d01af4b2fe77b529c1700592ee778e384e1d9..dca15decdb73ff6f1ad0a549e1457d33fdba8d2b 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -54,7 +54,6 @@ import com.android.inputmethod.latin.StringUtils;
 import com.android.inputmethod.latin.SubtypeUtils;
 import com.android.inputmethod.latin.Utils;
 import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
-import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils.LogGroup;
 
 import java.util.Locale;
 import java.util.WeakHashMap;
@@ -70,9 +69,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
         SuddenJumpingTouchEventHandler.ProcessMotionEvent {
     private static final String TAG = LatinKeyboardView.class.getSimpleName();
 
-    // TODO: Kill process when the usability study mode was changed.
-    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
-
     /** Listener for {@link KeyboardActionListener}. */
     private KeyboardActionListener mKeyboardActionListener;
 
@@ -672,6 +668,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
         final int index = me.getActionIndex();
         final int id = me.getPointerId(index);
         final int x, y;
+        final float size = me.getSize(index);
+        final float pressure = me.getPressure(index);
         if (mMoreKeysPanel != null && id == mMoreKeysPanelPointerTrackerId) {
             x = mMoreKeysPanel.translateX((int)me.getX(index));
             y = mMoreKeysPanel.translateY((int)me.getY(index));
@@ -679,32 +677,11 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
             x = (int)me.getX(index);
             y = (int)me.getY(index);
         }
-        if (ENABLE_USABILITY_STUDY_LOG) {
-            final String eventTag;
-            switch (action) {
-                case MotionEvent.ACTION_UP:
-                    eventTag = "[Up]";
-                    break;
-                case MotionEvent.ACTION_DOWN:
-                    eventTag = "[Down]";
-                    break;
-                case MotionEvent.ACTION_POINTER_UP:
-                    eventTag = "[PointerUp]";
-                    break;
-                case MotionEvent.ACTION_POINTER_DOWN:
-                    eventTag = "[PointerDown]";
-                    break;
-                case MotionEvent.ACTION_MOVE: // Skip this as being logged below
-                    eventTag = "";
-                    break;
-                default:
-                    eventTag = "[Action" + action + "]";
-                    break;
-            }
-            if (!TextUtils.isEmpty(eventTag)) {
-                UsabilityStudyLogUtils.getInstance().write(LogGroup.MOTION_EVENT,
-                        eventTag + eventTime + "," + id + "," + x + "," + y + ","
-                        + me.getSize(index) + "," + me.getPressure(index));
+        if (LatinImeLogger.sUsabilityStudy) {
+            if (action != MotionEvent.ACTION_MOVE) {
+                // Skip ACTION_MOVE events as they are logged below
+                UsabilityStudyLogUtils.getInstance().writeMotionEvent(action, eventTime, id, x,
+                        y, size, pressure);
             }
         }
 
@@ -764,11 +741,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
                     py = (int)me.getY(i);
                 }
                 tracker.onMoveEvent(px, py, eventTime);
-                if (ENABLE_USABILITY_STUDY_LOG) {
-                    UsabilityStudyLogUtils.getInstance().write(
-                            LogGroup.MOTION_EVENT,
-                            "[Move]" + eventTime + "," + me.getPointerId(i) + "," + px + "," + py
-                                    + "," + me.getSize(i) + "," + me.getPressure(i));
+                if (LatinImeLogger.sUsabilityStudy) {
+                    UsabilityStudyLogUtils.getInstance().writeMotionEvent(action, eventTime, id,
+                            px, py, size, pressure);
                 }
             }
         } else {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index e67f0ea05268f727f7bab59d02b95d4d5122b2a9..48fb798098b8226ba6bf9ba43bde8e638d525ebc 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -68,6 +68,7 @@ import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.LatinKeyboardView;
+import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
 import com.android.inputmethod.latin.suggestions.SuggestionsView;
 
 import java.io.FileDescriptor;
@@ -1266,6 +1267,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
             mDeleteCount = 0;
         }
         mLastKeyTime = when;
+
+        if (LatinImeLogger.sUsabilityStudy) {
+            UsabilityStudyLogUtils.getInstance().writeKeyEvent(primaryCode, x, y);
+        }
+
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         // The space state depends only on the last character pressed and its own previous
         // state. Here, we revert the space state to neutral if the key is actually modifying
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index e2ce083235e3a49d7fd4984f3d3328672c4dc9c1..a3589da0a46c5347207497c128fe9b367e9fe3a7 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -31,7 +31,9 @@ import android.os.Process;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.view.MotionEvent;
 
+import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 
 import java.io.BufferedReader;
@@ -138,9 +140,6 @@ public class Utils {
         // TODO: accept code points
         public void push(char c, int x, int y) {
             if (!mEnabled) return;
-            if (mUsabilityStudy) {
-                UsabilityStudyLogUtils.getInstance().writeChar(c, x, y);
-            }
             mCharBuf[mEnd] = c;
             mXBuf[mEnd] = x;
             mYBuf[mEnd] = y;
@@ -279,30 +278,57 @@ public class Utils {
             }
         }
 
-        public static void writeBackSpace(int x, int y) {
-            UsabilityStudyLogUtils.getInstance().write(
-                    LogGroup.KEY, "<backspace>\t" + x + "\t" + y);
+        public void writeMotionEvent(final int action, final long eventTime, final int id,
+                final int x, final int y, final float size, final float pressure) {
+            final String eventTag;
+            switch (action) {
+                case MotionEvent.ACTION_CANCEL: eventTag = "[Cancel]"; break;
+                case MotionEvent.ACTION_UP: eventTag = "[Up]"; break;
+                case MotionEvent.ACTION_DOWN: eventTag = "[Down]"; break;
+                case MotionEvent.ACTION_POINTER_UP: eventTag = "[PointerUp]"; break;
+                case MotionEvent.ACTION_POINTER_DOWN: eventTag = "[PointerDown]"; break;
+                case MotionEvent.ACTION_MOVE: eventTag = "[Move]"; break;
+                case MotionEvent.ACTION_OUTSIDE: eventTag = "[Outside]"; break;
+                default: eventTag = "[Action" + action + "]"; break;
+            }
+            if (!TextUtils.isEmpty(eventTag)) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(eventTag);
+                sb.append('\t'); sb.append(eventTime);
+                sb.append('\t'); sb.append(id);
+                sb.append('\t'); sb.append(x);
+                sb.append('\t'); sb.append(y);
+                sb.append('\t'); sb.append(size);
+                sb.append('\t'); sb.append(pressure);
+                write(LogGroup.MOTION_EVENT, sb.toString());
+            }
         }
 
-        public void writeChar(char c, int x, int y) {
-            String inputChar = String.valueOf(c);
-            switch (c) {
-                case '\n':
-                    inputChar = "<enter>";
-                    break;
-                case '\t':
-                    inputChar = "<tab>";
-                    break;
-                case ' ':
-                    inputChar = "<space>";
-                    break;
-            }
-            UsabilityStudyLogUtils.getInstance().write(LogGroup.KEY,
-                    inputChar + "\t" + x + "\t" + y);
+        public void writeKeyEvent(int code, int x, int y) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(Keyboard.printableCode(code));
+            sb.append('\t'); sb.append(x);
+            sb.append('\t'); sb.append(y);
+            write(LogGroup.KEY, sb.toString());
+
+            // TODO: replace with a cleaner flush+retrieve mechanism
             LatinImeLogger.onPrintAllUsabilityStudyLogs();
         }
 
-        public void write(final LogGroup logGroup, final String log) {
+        public void writeCorrection(String subgroup, String before, String after, int position) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(subgroup);
+            sb.append('\t'); sb.append(before);
+            sb.append('\t'); sb.append(after);
+            sb.append('\t'); sb.append(position);
+            write(LogGroup.CORRECTION, sb.toString());
+        }
+
+        public void writeStateChange(String subgroup, String details) {
+            write(LogGroup.STATE_CHANGE, subgroup + "\t" + details);
+        }
+
+        private void write(final LogGroup logGroup, final String log) {
             mLoggingHandler.post(new Runnable() {
                 @Override
                 public void run() {