diff options
author | 2013-01-31 09:59:16 -0800 | |
---|---|---|
committer | 2013-02-04 06:13:51 -0800 | |
commit | 7708bcf6fb80f42f62f34b57aece4a2baa5b3320 (patch) | |
tree | 735d863920422c6ca3324a8eeada2067d6a2a457 /java/src/com/android/inputmethod/research/MotionEventReader.java | |
parent | ce9e7f667d49735bbae344ea6c64e3ae39f7368a (diff) | |
download | latinime-7708bcf6fb80f42f62f34b57aece4a2baa5b3320.tar.gz latinime-7708bcf6fb80f42f62f34b57aece4a2baa5b3320.tar.xz latinime-7708bcf6fb80f42f62f34b57aece4a2baa5b3320.zip |
[Rlog48c] Replay historical motion data
Change-Id: Ib398ea61ff048b1a4ac3b7f7b4a772e173a7b294
Diffstat (limited to 'java/src/com/android/inputmethod/research/MotionEventReader.java')
-rw-r--r-- | java/src/com/android/inputmethod/research/MotionEventReader.java | 253 |
1 files changed, 236 insertions, 17 deletions
diff --git a/java/src/com/android/inputmethod/research/MotionEventReader.java b/java/src/com/android/inputmethod/research/MotionEventReader.java index 26a1d7f55..e59adfa19 100644 --- a/java/src/com/android/inputmethod/research/MotionEventReader.java +++ b/java/src/com/android/inputmethod/research/MotionEventReader.java @@ -19,6 +19,8 @@ package com.android.inputmethod.research; import android.util.JsonReader; import android.util.Log; import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; +import android.view.MotionEvent.PointerProperties; import com.android.inputmethod.latin.define.ProductionFlag; @@ -33,6 +35,14 @@ import java.util.ArrayList; public class MotionEventReader { private static final String TAG = MotionEventReader.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; + // Assumes that MotionEvent.ACTION_MASK does not have all bits set.` + private static final int UNINITIALIZED_ACTION = ~MotionEvent.ACTION_MASK; + // No legitimate int is negative + private static final int UNINITIALIZED_INT = -1; + // No legitimate long is negative + private static final long UNINITIALIZED_LONG = -1L; + // No legitimate float is negative + private static final float UNINITIALIZED_FLOAT = -1.0f; public ReplayData readMotionEventData(final File file) { final ReplayData replayData = new ReplayData(); @@ -55,19 +65,82 @@ public class MotionEventReader { static class ReplayData { final ArrayList<Integer> mActions = new ArrayList<Integer>(); - final ArrayList<Integer> mXCoords = new ArrayList<Integer>(); - final ArrayList<Integer> mYCoords = new ArrayList<Integer>(); + final ArrayList<PointerProperties[]> mPointerPropertiesArrays + = new ArrayList<PointerProperties[]>(); + final ArrayList<PointerCoords[]> mPointerCoordsArrays = new ArrayList<PointerCoords[]>(); final ArrayList<Long> mTimes = new ArrayList<Long>(); } - private void readLogStatement(final JsonReader jsonReader, final ReplayData replayData) - throws IOException { + /** + * Read motion data from a logStatement and store it in {@code replayData}. + * + * Two kinds of logStatements can be read. In the first variant, the MotionEvent data is + * represented as attributes at the top level like so: + * + * <pre> + * { + * "_ct": 1359590400000, + * "_ut": 4381933, + * "_ty": "MotionEvent", + * "action": "UP", + * "isLoggingRelated": false, + * "x": 100, + * "y": 200 + * } + * </pre> + * + * In the second variant, there is a separate attribute for the MotionEvent that includes + * historical data if present: + * + * <pre> + * { + * "_ct": 135959040000, + * "_ut": 4382702, + * "_ty": "MotionEvent", + * "action": "MOVE", + * "isLoggingRelated": false, + * "motionEvent": { + * "pointerIds": [ + * 0 + * ], + * "xyt": [ + * { + * "t": 4382551, + * "d": [ + * { + * "x": 141.25, + * "y": 151.8485107421875, + * "toma": 101.82337188720703, + * "tomi": 101.82337188720703, + * "o": 0.0 + * } + * ] + * }, + * { + * "t": 4382559, + * "d": [ + * { + * "x": 140.7266082763672, + * "y": 151.8485107421875, + * "toma": 101.82337188720703, + * "tomi": 101.82337188720703, + * "o": 0.0 + * } + * ] + * } + * ] + * } + * }, + * </pre> + */ + /* package for test */ void readLogStatement(final JsonReader jsonReader, + final ReplayData replayData) throws IOException { String logStatementType = null; - Integer actionType = null; - Integer x = null; - Integer y = null; - Long time = null; - boolean loggingRelated = false; + int actionType = UNINITIALIZED_ACTION; + int x = UNINITIALIZED_INT; + int y = UNINITIALIZED_INT; + long time = UNINITIALIZED_LONG; + boolean isLoggingRelated = false; jsonReader.beginObject(); while (jsonReader.hasNext()) { @@ -90,7 +163,18 @@ public class MotionEventReader { actionType = MotionEvent.ACTION_MOVE; } } else if (key.equals("loggingRelated")) { - loggingRelated = jsonReader.nextBoolean(); + isLoggingRelated = jsonReader.nextBoolean(); + } else if (logStatementType != null && logStatementType.equals("MotionEvent") + && key.equals("motionEvent")) { + if (actionType == UNINITIALIZED_ACTION) { + Log.e(TAG, "no actionType assigned in MotionEvent json"); + } + // Second variant of LogStatement. + if (isLoggingRelated) { + jsonReader.skipValue(); + } else { + readEmbeddedMotionEvent(jsonReader, replayData, actionType); + } } else { if (DEBUG) { Log.w(TAG, "Unknown JSON key in LogStatement: " + key); @@ -100,14 +184,149 @@ public class MotionEventReader { } jsonReader.endObject(); - if (logStatementType != null && time != null && x != null && y != null && actionType != null - && logStatementType.equals("MotionEvent") - && !loggingRelated) { - replayData.mActions.add(actionType); - replayData.mXCoords.add(x); - replayData.mYCoords.add(y); - replayData.mTimes.add(time); + if (logStatementType != null && time != UNINITIALIZED_LONG && x != UNINITIALIZED_INT + && y != UNINITIALIZED_INT && actionType != UNINITIALIZED_ACTION + && logStatementType.equals("MotionEvent") && !isLoggingRelated) { + // First variant of LogStatement. + final PointerProperties pointerProperties = new PointerProperties(); + pointerProperties.id = 0; + pointerProperties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN; + final PointerProperties[] pointerPropertiesArray = { + pointerProperties + }; + final PointerCoords pointerCoords = new PointerCoords(); + pointerCoords.x = x; + pointerCoords.y = y; + pointerCoords.pressure = 1.0f; + pointerCoords.size = 1.0f; + final PointerCoords[] pointerCoordsArray = { + pointerCoords + }; + addMotionEventData(replayData, actionType, time, pointerPropertiesArray, + pointerCoordsArray); + } + } + + private void readEmbeddedMotionEvent(final JsonReader jsonReader, final ReplayData replayData, + final int actionType) throws IOException { + jsonReader.beginObject(); + PointerProperties[] pointerPropertiesArray = null; + while (jsonReader.hasNext()) { // pointerIds/xyt + final String name = jsonReader.nextName(); + if (name.equals("pointerIds")) { + pointerPropertiesArray = readPointerProperties(jsonReader); + } else if (name.equals("xyt")) { + readPointerData(jsonReader, replayData, actionType, pointerPropertiesArray); + } + } + jsonReader.endObject(); + } + + private PointerProperties[] readPointerProperties(final JsonReader jsonReader) + throws IOException { + final ArrayList<PointerProperties> pointerPropertiesArrayList = + new ArrayList<PointerProperties>(); + jsonReader.beginArray(); + while (jsonReader.hasNext()) { + final PointerProperties pointerProperties = new PointerProperties(); + pointerProperties.id = jsonReader.nextInt(); + pointerProperties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN; + pointerPropertiesArrayList.add(pointerProperties); + } + jsonReader.endArray(); + return pointerPropertiesArrayList.toArray( + new PointerProperties[pointerPropertiesArrayList.size()]); + } + + private void readPointerData(final JsonReader jsonReader, final ReplayData replayData, + final int actionType, final PointerProperties[] pointerPropertiesArray) + throws IOException { + if (pointerPropertiesArray == null) { + Log.e(TAG, "PointerIDs must be given before xyt data in json for MotionEvent"); + jsonReader.skipValue(); + return; + } + long time = UNINITIALIZED_LONG; + jsonReader.beginArray(); + while (jsonReader.hasNext()) { // Array of historical data + jsonReader.beginObject(); + final ArrayList<PointerCoords> pointerCoordsArrayList = new ArrayList<PointerCoords>(); + while (jsonReader.hasNext()) { // Time/data object + final String name = jsonReader.nextName(); + if (name.equals("t")) { + time = jsonReader.nextLong(); + } else if (name.equals("d")) { + jsonReader.beginArray(); + while (jsonReader.hasNext()) { // array of data per pointer + final PointerCoords pointerCoords = readPointerCoords(jsonReader); + if (pointerCoords != null) { + pointerCoordsArrayList.add(pointerCoords); + } + } + jsonReader.endArray(); + } else { + jsonReader.skipValue(); + } + } + jsonReader.endObject(); + // Data was recorded as historical events, but must be split apart into + // separate MotionEvents for replaying + if (time != UNINITIALIZED_LONG) { + addMotionEventData(replayData, actionType, time, pointerPropertiesArray, + pointerCoordsArrayList.toArray( + new PointerCoords[pointerCoordsArrayList.size()])); + } else { + Log.e(TAG, "Time not assigned in json for MotionEvent"); + } + } + jsonReader.endArray(); + } + + private PointerCoords readPointerCoords(final JsonReader jsonReader) throws IOException { + jsonReader.beginObject(); + float x = UNINITIALIZED_FLOAT; + float y = UNINITIALIZED_FLOAT; + while (jsonReader.hasNext()) { // x,y + final String name = jsonReader.nextName(); + if (name.equals("x")) { + x = (float) jsonReader.nextDouble(); + } else if (name.equals("y")) { + y = (float) jsonReader.nextDouble(); + } else { + jsonReader.skipValue(); + } + } + jsonReader.endObject(); + + if (Float.compare(x, UNINITIALIZED_FLOAT) == 0 + || Float.compare(y, UNINITIALIZED_FLOAT) == 0) { + Log.w(TAG, "missing x or y value in MotionEvent json"); + return null; } + final PointerCoords pointerCoords = new PointerCoords(); + pointerCoords.x = x; + pointerCoords.y = y; + pointerCoords.pressure = 1.0f; + pointerCoords.size = 1.0f; + return pointerCoords; + } + + /** + * Tests that {@code x} is uninitialized. + * + * Assumes that {@code x} will never be given a valid value less than 0, and that + * UNINITIALIZED_FLOAT is less than 0.0f. + */ + private boolean isUninitializedFloat(final float x) { + return x < 0.0f; } + private void addMotionEventData(final ReplayData replayData, final int actionType, + final long time, final PointerProperties[] pointerProperties, + final PointerCoords[] pointerCoords) { + replayData.mActions.add(actionType); + replayData.mTimes.add(time); + replayData.mPointerPropertiesArrays.add(pointerProperties); + replayData.mPointerCoordsArrays.add(pointerCoords); + } } |