gecko/mobile/android/base/tests/MotionEventReplayer.java
Brian Nicholson b7b1568a70 Bug 905703 - Remove preprocessing from robocop harness and tests. r=nalexander
--HG--
rename : build/mobile/robocop/Actions.java.in => build/mobile/robocop/Actions.java
rename : build/mobile/robocop/Assert.java.in => build/mobile/robocop/Assert.java
rename : build/mobile/robocop/Driver.java.in => build/mobile/robocop/Driver.java
rename : build/mobile/robocop/Element.java.in => build/mobile/robocop/Element.java
rename : build/mobile/robocop/FennecInstrumentationTestRunner.java.in => build/mobile/robocop/FennecInstrumentationTestRunner.java
rename : build/mobile/robocop/FennecMochitestAssert.java.in => build/mobile/robocop/FennecMochitestAssert.java
rename : build/mobile/robocop/FennecNativeActions.java.in => build/mobile/robocop/FennecNativeActions.java
rename : build/mobile/robocop/FennecNativeDriver.java.in => build/mobile/robocop/FennecNativeDriver.java
rename : build/mobile/robocop/FennecNativeElement.java.in => build/mobile/robocop/FennecNativeElement.java
rename : build/mobile/robocop/FennecTalosAssert.java.in => build/mobile/robocop/FennecTalosAssert.java
rename : build/mobile/robocop/PaintedSurface.java.in => build/mobile/robocop/PaintedSurface.java
rename : build/mobile/robocop/RoboCopException.java.in => build/mobile/robocop/RoboCopException.java
rename : build/mobile/robocop/RobocopUtils.java.in => build/mobile/robocop/RobocopUtils.java
rename : mobile/android/base/tests/AboutHomeTest.java.in => mobile/android/base/tests/AboutHomeTest.java
rename : mobile/android/base/tests/BaseTest.java.in => mobile/android/base/tests/BaseTest.java
rename : mobile/android/base/tests/ContentContextMenuTest.java.in => mobile/android/base/tests/ContentContextMenuTest.java
rename : mobile/android/base/tests/ContentProviderTest.java.in => mobile/android/base/tests/ContentProviderTest.java
rename : mobile/android/base/tests/DatabaseHelper.java.in => mobile/android/base/tests/DatabaseHelper.java
rename : mobile/android/base/tests/JavascriptTest.java.in => mobile/android/base/tests/JavascriptTest.java
rename : mobile/android/base/tests/MotionEventHelper.java.in => mobile/android/base/tests/MotionEventHelper.java
rename : mobile/android/base/tests/MotionEventReplayer.java.in => mobile/android/base/tests/MotionEventReplayer.java
rename : mobile/android/base/tests/PixelTest.java.in => mobile/android/base/tests/PixelTest.java
rename : mobile/android/base/tests/StringHelper.java.in => mobile/android/base/tests/StringHelper.java
rename : mobile/android/base/tests/testAboutPage.java.in => mobile/android/base/tests/testAboutPage.java
rename : mobile/android/base/tests/testAddSearchEngine.java.in => mobile/android/base/tests/testAddSearchEngine.java
rename : mobile/android/base/tests/testAddonManager.java.in => mobile/android/base/tests/testAddonManager.java
rename : mobile/android/base/tests/testAwesomebar.java.in => mobile/android/base/tests/testAwesomebar.java
rename : mobile/android/base/tests/testAwesomebarSwipes.java.in => mobile/android/base/tests/testAwesomebarSwipes.java
rename : mobile/android/base/tests/testAxisLocking.java.in => mobile/android/base/tests/testAxisLocking.java
rename : mobile/android/base/tests/testBookmark.java.in => mobile/android/base/tests/testBookmark.java
rename : mobile/android/base/tests/testBookmarkFolders.java.in => mobile/android/base/tests/testBookmarkFolders.java
rename : mobile/android/base/tests/testBookmarkKeyword.java.in => mobile/android/base/tests/testBookmarkKeyword.java
rename : mobile/android/base/tests/testBookmarklets.java.in => mobile/android/base/tests/testBookmarklets.java
rename : mobile/android/base/tests/testBookmarksPage.java.in => mobile/android/base/tests/testBookmarksPage.java
rename : mobile/android/base/tests/testBrowserProvider.java.in => mobile/android/base/tests/testBrowserProvider.java
rename : mobile/android/base/tests/testBrowserProviderPerf.java.in => mobile/android/base/tests/testBrowserProviderPerf.java
rename : mobile/android/base/tests/testBrowserSearchVisibility.java.in => mobile/android/base/tests/testBrowserSearchVisibility.java
rename : mobile/android/base/tests/testCheck.java.in => mobile/android/base/tests/testCheck.java
rename : mobile/android/base/tests/testCheck2.java.in => mobile/android/base/tests/testCheck2.java
rename : mobile/android/base/tests/testClearPrivateData.java.in => mobile/android/base/tests/testClearPrivateData.java
rename : mobile/android/base/tests/testDeviceSearchEngine.java.in => mobile/android/base/tests/testDeviceSearchEngine.java
rename : mobile/android/base/tests/testDistribution.java.in => mobile/android/base/tests/testDistribution.java
rename : mobile/android/base/tests/testDoorHanger.java.in => mobile/android/base/tests/testDoorHanger.java
rename : mobile/android/base/tests/testFindInPage.java.in => mobile/android/base/tests/testFindInPage.java
rename : mobile/android/base/tests/testFlingCorrectness.java.in => mobile/android/base/tests/testFlingCorrectness.java
rename : mobile/android/base/tests/testFormHistory.java.in => mobile/android/base/tests/testFormHistory.java
rename : mobile/android/base/tests/testGetUserMedia.java.in => mobile/android/base/tests/testGetUserMedia.java
rename : mobile/android/base/tests/testHistory.java.in => mobile/android/base/tests/testHistory.java
rename : mobile/android/base/tests/testHomeBanner.java.in => mobile/android/base/tests/testHomeBanner.java
rename : mobile/android/base/tests/testImportFromAndroid.java.in => mobile/android/base/tests/testImportFromAndroid.java
rename : mobile/android/base/tests/testInputUrlBar.java.in => mobile/android/base/tests/testInputUrlBar.java
rename : mobile/android/base/tests/testJNI.java.in => mobile/android/base/tests/testJNI.java
rename : mobile/android/base/tests/testJarReader.java.in => mobile/android/base/tests/testJarReader.java
rename : mobile/android/base/tests/testLinkContextMenu.java.in => mobile/android/base/tests/testLinkContextMenu.java
rename : mobile/android/base/tests/testLoad.java.in => mobile/android/base/tests/testLoad.java
rename : mobile/android/base/tests/testMailToContextMenu.java.in => mobile/android/base/tests/testMailToContextMenu.java
rename : mobile/android/base/tests/testMasterPassword.java.in => mobile/android/base/tests/testMasterPassword.java
rename : mobile/android/base/tests/testNewTab.java.in => mobile/android/base/tests/testNewTab.java
rename : mobile/android/base/tests/testOrderedBroadcast.java.in => mobile/android/base/tests/testOrderedBroadcast.java
rename : mobile/android/base/tests/testOverscroll.java.in => mobile/android/base/tests/testOverscroll.java
rename : mobile/android/base/tests/testPan.java.in => mobile/android/base/tests/testPan.java
rename : mobile/android/base/tests/testPanCorrectness.java.in => mobile/android/base/tests/testPanCorrectness.java
rename : mobile/android/base/tests/testPasswordEncrypt.java.in => mobile/android/base/tests/testPasswordEncrypt.java
rename : mobile/android/base/tests/testPasswordProvider.java.in => mobile/android/base/tests/testPasswordProvider.java
rename : mobile/android/base/tests/testPermissions.java.in => mobile/android/base/tests/testPermissions.java
rename : mobile/android/base/tests/testPictureLinkContextMenu.java.in => mobile/android/base/tests/testPictureLinkContextMenu.java
rename : mobile/android/base/tests/testPrefsObserver.java.in => mobile/android/base/tests/testPrefsObserver.java
rename : mobile/android/base/tests/testPrivateBrowsing.java.in => mobile/android/base/tests/testPrivateBrowsing.java
rename : mobile/android/base/tests/testPromptGridInput.java.in => mobile/android/base/tests/testPromptGridInput.java
rename : mobile/android/base/tests/testReaderMode.java.in => mobile/android/base/tests/testReaderMode.java
rename : mobile/android/base/tests/testSearchSuggestions.java.in => mobile/android/base/tests/testSearchSuggestions.java
rename : mobile/android/base/tests/testSettingsMenuItems.java.in => mobile/android/base/tests/testSettingsMenuItems.java
rename : mobile/android/base/tests/testShareLink.java.in => mobile/android/base/tests/testShareLink.java
rename : mobile/android/base/tests/testSharedPreferences.java.in => mobile/android/base/tests/testSharedPreferences.java
rename : mobile/android/base/tests/testSystemPages.java.in => mobile/android/base/tests/testSystemPages.java
rename : mobile/android/base/tests/testTabHistory.java.in => mobile/android/base/tests/testTabHistory.java
rename : mobile/android/base/tests/testThumbnails.java.in => mobile/android/base/tests/testThumbnails.java
rename : mobile/android/base/tests/testVkbOverlap.java.in => mobile/android/base/tests/testVkbOverlap.java
rename : mobile/android/base/tests/test_bug720538.java.in => mobile/android/base/tests/test_bug720538.java
extra : rebase_source : c8d715aa110b07c6010054a674879e143a5f04e7
extra : amend_source : fb5714b78e055e336b5e0c6a0da8eb8c931ff09e
extra : histedit_source : ec57d8ba94000092438a2ba0bf4fb66091e98513%2C114ade72bb9b72b96557da74f1179734fa347e5c
2013-11-07 11:18:51 -05:00

215 lines
10 KiB
Java

package org.mozilla.gecko.tests;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.Instrumentation;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
class MotionEventReplayer {
private static final String LOGTAG = "RobocopMotionEventReplayer";
// the inner dimensions of the window on which the motion event capture was taken from
private static final int CAPTURE_WINDOW_WIDTH = 720;
private static final int CAPTURE_WINDOW_HEIGHT = 1038;
private final Instrumentation mInstrumentation;
private final int mSurfaceOffsetX;
private final int mSurfaceOffsetY;
private final int mSurfaceWidth;
private final int mSurfaceHeight;
private final Map<String, Integer> mActionTypes;
private Method mObtainNanoMethod;
public MotionEventReplayer(Instrumentation inst, int surfaceOffsetX, int surfaceOffsetY, int surfaceWidth, int surfaceHeight) {
mInstrumentation = inst;
mSurfaceOffsetX = surfaceOffsetX;
mSurfaceOffsetY = surfaceOffsetY;
mSurfaceWidth = surfaceWidth;
mSurfaceHeight = surfaceHeight;
Log.i(LOGTAG, "Initialized using offset (" + mSurfaceOffsetX + "," + mSurfaceOffsetY + ")");
mActionTypes = new HashMap<String, Integer>();
mActionTypes.put("ACTION_CANCEL", MotionEvent.ACTION_CANCEL);
mActionTypes.put("ACTION_DOWN", MotionEvent.ACTION_DOWN);
mActionTypes.put("ACTION_MOVE", MotionEvent.ACTION_MOVE);
mActionTypes.put("ACTION_POINTER_DOWN", MotionEvent.ACTION_POINTER_DOWN);
mActionTypes.put("ACTION_POINTER_UP", MotionEvent.ACTION_POINTER_UP);
mActionTypes.put("ACTION_UP", MotionEvent.ACTION_UP);
}
private int parseAction(String action) {
int index = 0;
// ACTION_POINTER_DOWN and ACTION_POINTER_UP might be followed by
// pointer index in parentheses, like ACTION_POINTER_UP(1)
int beginParen = action.indexOf("(");
if (beginParen >= 0) {
int endParen = action.indexOf(")", beginParen + 1);
index = Integer.parseInt(action.substring(beginParen + 1, endParen));
action = action.substring(0, beginParen);
}
return mActionTypes.get(action) | (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
}
private int parseInt(String value) {
if (value == null) {
return 0;
}
if (value.startsWith("0x")) {
return Integer.parseInt(value.substring(2), 16);
}
return Integer.parseInt(value);
}
private float scaleX(float value) {
return value * (float)mSurfaceWidth / (float)CAPTURE_WINDOW_WIDTH;
}
private float scaleY(float value) {
return value * (float)mSurfaceHeight / (float)CAPTURE_WINDOW_HEIGHT;
}
public void replayEvents(InputStream eventDescriptions)
throws IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
{
// As an example, a line in the input stream might look like:
//
// MotionEvent { action=ACTION_DOWN, id[0]=0, x[0]=424.41055, y[0]=825.2412,
// toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0,
// edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=21972329,
// downTime=21972329, deviceId=6, source=0x1002 }
//
// These can be generated by printing out event.toString() in LayerView's
// onTouchEvent function on a phone running Ice Cream Sandwich. Different
// Android versions have different serializations of the motion event, and this
// code could probably be modified to parse other serializations if needed.
Pattern p = Pattern.compile("MotionEvent \\{ (.*?) \\}");
Map<String, String> eventProperties = new HashMap<String, String>();
boolean firstEvent = true;
long timeDelta = 0L;
long lastEventTime = 0L;
BufferedReader br = new BufferedReader(new InputStreamReader(eventDescriptions));
try {
for (String eventStr = br.readLine(); eventStr != null; eventStr = br.readLine()) {
Matcher m = p.matcher(eventStr);
if (! m.find()) {
// this line doesn't have any MotionEvent data, skip it
continue;
}
// extract the key-value pairs from the description and store them
// in the eventProperties table
StringTokenizer keyValues = new StringTokenizer(m.group(1), ",");
while (keyValues.hasMoreTokens()) {
String keyValue = keyValues.nextToken();
String key = keyValue.substring(0, keyValue.indexOf('=')).trim();
String value = keyValue.substring(keyValue.indexOf('=') + 1).trim();
eventProperties.put(key, value);
}
// set up the values we need to build the MotionEvent
long downTime = Long.parseLong(eventProperties.get("downTime"));
long eventTime = Long.parseLong(eventProperties.get("eventTime"));
int action = parseAction(eventProperties.get("action"));
float pressure = 1.0f;
float size = 1.0f;
int metaState = parseInt(eventProperties.get("metaState"));
float xPrecision = 1.0f;
float yPrecision = 1.0f;
int deviceId = 0;
int edgeFlags = parseInt(eventProperties.get("edgeFlags"));
int source = parseInt(eventProperties.get("source"));
int flags = parseInt(eventProperties.get("flags"));
int pointerCount = parseInt(eventProperties.get("pointerCount"));
int[] pointerIds = new int[pointerCount];
Object pointerData;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount];
for (int i = 0; i < pointerCount; i++) {
pointerIds[i] = Integer.parseInt(eventProperties.get("id[" + i + "]"));
pointerCoords[i] = new MotionEvent.PointerCoords();
pointerCoords[i].x = mSurfaceOffsetX + scaleX(Float.parseFloat(eventProperties.get("x[" + i + "]")));
pointerCoords[i].y = mSurfaceOffsetY + scaleY(Float.parseFloat(eventProperties.get("y[" + i + "]")));
}
pointerData = pointerCoords;
} else {
// pre-gingerbread we have to use a hidden API to create the motion event, and we have
// to create a flattened list of floats rather than an array of PointerCoords
final int NUM_SAMPLE_DATA = 4; // MotionEvent.NUM_SAMPLE_DATA
final int SAMPLE_X = 0; // MotionEvent.SAMPLE_X
final int SAMPLE_Y = 1; // MotionEvent.SAMPLE_Y
float[] sampleData = new float[pointerCount * NUM_SAMPLE_DATA];
for (int i = 0; i < pointerCount; i++) {
pointerIds[i] = Integer.parseInt(eventProperties.get("id[" + i + "]"));
sampleData[(i * NUM_SAMPLE_DATA) + SAMPLE_X] =
mSurfaceOffsetX + scaleX(Float.parseFloat(eventProperties.get("x[" + i + "]")));
sampleData[(i * NUM_SAMPLE_DATA) + SAMPLE_Y] =
mSurfaceOffsetY + scaleY(Float.parseFloat(eventProperties.get("y[" + i + "]")));
}
pointerData = sampleData;
}
// we want to adjust the timestamps on all the generated events so that they line up with
// the time that this function is executing on-device.
long now = SystemClock.uptimeMillis();
if (firstEvent) {
timeDelta = now - eventTime;
firstEvent = false;
}
downTime += timeDelta;
eventTime += timeDelta;
// we also generate the events in "real-time" (i.e. have delays between events that
// correspond to the delays in the event timestamps).
if (now < eventTime) {
try {
Thread.sleep(eventTime - now);
} catch (InterruptedException ie) {
}
}
// and finally we dispatch the event
MotionEvent event;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
event = MotionEvent.obtain(downTime, eventTime, action, pointerCount,
pointerIds, (MotionEvent.PointerCoords[])pointerData, metaState,
xPrecision, yPrecision, deviceId, edgeFlags, source, flags);
} else {
// pre-gingerbread we have to use a hidden API to accomplish this
if (mObtainNanoMethod == null) {
mObtainNanoMethod = MotionEvent.class.getMethod("obtainNano", long.class,
long.class, long.class, int.class, int.class, pointerIds.getClass(),
pointerData.getClass(), int.class, float.class, float.class,
int.class, int.class);
}
event = (MotionEvent)mObtainNanoMethod.invoke(null, downTime, eventTime,
eventTime * 1000000, action, pointerCount, pointerIds, (float[])pointerData,
metaState, xPrecision, yPrecision, deviceId, edgeFlags);
}
Log.v(LOGTAG, "Injecting " + event.toString());
mInstrumentation.sendPointerSync(event);
eventProperties.clear();
}
} finally {
br.close();
}
}
}