2011-12-22 06:09:41 -08:00
|
|
|
#filter substitution
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Firefox Mobile Test Framework.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Mozilla.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Trevor Fairey <tnfairey@gmail.com>
|
|
|
|
* David Burns <dburns@mozilla.com>
|
|
|
|
* Joel Maher <joel.maher@gmail.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
package @ANDROID_PACKAGE_NAME@;
|
|
|
|
|
|
|
|
import java.lang.Class;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.lang.reflect.Proxy;
|
|
|
|
import java.lang.reflect.InvocationHandler;
|
|
|
|
import java.lang.Long;
|
|
|
|
|
|
|
|
import android.app.Activity;
|
|
|
|
import android.app.Instrumentation;
|
2012-02-02 13:07:00 -08:00
|
|
|
import android.os.SystemClock;
|
2011-12-22 06:09:41 -08:00
|
|
|
import android.view.View;
|
|
|
|
import android.view.KeyEvent;
|
|
|
|
|
|
|
|
import java.util.concurrent.SynchronousQueue;
|
|
|
|
|
|
|
|
import org.json.*;
|
|
|
|
|
|
|
|
import com.jayway.android.robotium.solo.Solo;
|
|
|
|
|
|
|
|
public class FennecNativeActions implements Actions {
|
|
|
|
private Solo solo;
|
|
|
|
private Instrumentation instr;
|
2012-01-30 19:46:13 -08:00
|
|
|
private Activity geckoApp;
|
2011-12-22 06:09:41 -08:00
|
|
|
|
|
|
|
// Objects for reflexive access of fennec classes.
|
|
|
|
private ClassLoader classLoader;
|
|
|
|
private Class gel;
|
|
|
|
private Class ge;
|
|
|
|
private Class gas;
|
2012-01-30 19:46:13 -08:00
|
|
|
private Class drawListener;
|
2011-12-22 06:09:41 -08:00
|
|
|
private Method registerGEL;
|
|
|
|
private Method unregisterGEL;
|
|
|
|
private Method sendGE;
|
2012-01-30 19:46:13 -08:00
|
|
|
private Method getLayerClient;
|
|
|
|
private Method setDrawListener;
|
2011-12-22 06:09:41 -08:00
|
|
|
|
|
|
|
public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
|
|
|
|
this.solo = robocop;
|
|
|
|
this.instr = instrumentation;
|
2012-01-30 19:46:13 -08:00
|
|
|
this.geckoApp = activity;
|
2011-12-22 06:09:41 -08:00
|
|
|
// Set up reflexive access of java classes and methods.
|
|
|
|
try {
|
|
|
|
classLoader = activity.getClassLoader();
|
|
|
|
gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
|
|
|
|
ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
|
|
|
|
gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
|
|
|
|
Class [] parameters = new Class[2];
|
|
|
|
parameters[0] = String.class;
|
|
|
|
parameters[1] = gel;
|
|
|
|
registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
|
|
|
|
unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
|
|
|
|
parameters = new Class[1];
|
|
|
|
parameters[0] = ge;
|
|
|
|
sendGE = gas.getMethod("sendEventToGecko", parameters);
|
2012-01-30 19:46:13 -08:00
|
|
|
|
|
|
|
getLayerClient = activity.getClass().getMethod("getSoftwareLayerClient");
|
|
|
|
Class gslc = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
|
|
|
|
drawListener = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
|
|
|
|
setDrawListener = gslc.getDeclaredMethod("setDrawListener", drawListener);
|
2011-12-22 06:09:41 -08:00
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (SecurityException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (NoSuchMethodException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class wakeInvocationHandler implements InvocationHandler {
|
2012-01-05 18:36:17 -08:00
|
|
|
private final GeckoEventExpecter mEventExpecter;
|
|
|
|
|
|
|
|
public wakeInvocationHandler(GeckoEventExpecter expecter) {
|
|
|
|
mEventExpecter = expecter;
|
|
|
|
}
|
|
|
|
|
2011-12-22 06:09:41 -08:00
|
|
|
public Object invoke(Object proxy, Method method, Object[] args) {
|
|
|
|
String methodName = method.getName();
|
|
|
|
//Depending on the method, return a completely different type.
|
|
|
|
if(methodName.equals("toString")) {
|
|
|
|
return "wakeInvocationHandler";
|
|
|
|
}
|
|
|
|
if(methodName.equals("equals")) {
|
|
|
|
return this == args[0];
|
|
|
|
}
|
|
|
|
if(methodName.equals("clone")) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
if(methodName.equals("hashCode")) {
|
|
|
|
return 314;
|
|
|
|
}
|
2012-02-08 06:32:42 -08:00
|
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
|
|
|
|
"Waking up on "+methodName);
|
2012-01-05 18:36:17 -08:00
|
|
|
mEventExpecter.notifyOfEvent();
|
2011-12-22 06:09:41 -08:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2012-01-05 18:36:17 -08:00
|
|
|
|
|
|
|
class GeckoEventExpecter implements EventExpecter {
|
|
|
|
private final String mGeckoEvent;
|
|
|
|
private final Object[] mRegistrationParams;
|
|
|
|
private boolean mEventReceived;
|
|
|
|
|
|
|
|
GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
|
|
|
|
mGeckoEvent = geckoEvent;
|
|
|
|
mRegistrationParams = registrationParams;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized void blockForEvent() {
|
|
|
|
while (! mEventReceived) {
|
|
|
|
try {
|
|
|
|
this.wait();
|
|
|
|
} catch (InterruptedException ie) {
|
|
|
|
ie.printStackTrace();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-02-08 06:32:42 -08:00
|
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
|
|
|
|
"unblocked on expecter for " + mGeckoEvent);
|
2012-01-05 18:36:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized boolean eventReceived() {
|
|
|
|
return mEventReceived;
|
|
|
|
}
|
|
|
|
|
|
|
|
void notifyOfEvent() {
|
|
|
|
try {
|
|
|
|
unregisterGEL.invoke(null, mRegistrationParams);
|
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (InvocationTargetException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2012-02-08 06:32:42 -08:00
|
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
|
|
|
|
"received event " + mGeckoEvent);
|
2012-01-05 18:36:17 -08:00
|
|
|
synchronized (this) {
|
|
|
|
mEventReceived = true;
|
|
|
|
this.notifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-12-22 06:09:41 -08:00
|
|
|
|
2012-01-05 18:36:17 -08:00
|
|
|
public EventExpecter expectGeckoEvent(String geckoEvent) {
|
2012-02-08 06:32:42 -08:00
|
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
|
|
|
|
"waiting for "+geckoEvent);
|
2011-12-22 06:09:41 -08:00
|
|
|
try {
|
|
|
|
Class [] interfaces = new Class[1];
|
|
|
|
interfaces[0] = gel;
|
|
|
|
Object[] finalParams = new Object[2];
|
|
|
|
finalParams[0] = geckoEvent;
|
|
|
|
|
2012-01-05 18:36:17 -08:00
|
|
|
GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
|
|
|
|
wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
|
2011-12-22 06:09:41 -08:00
|
|
|
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH);
|
|
|
|
finalParams[1] = proxy;
|
|
|
|
registerGEL.invoke(null, finalParams);
|
|
|
|
|
2012-01-05 18:36:17 -08:00
|
|
|
return expecter;
|
2011-12-22 06:09:41 -08:00
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (InvocationTargetException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2012-01-05 18:36:17 -08:00
|
|
|
return null;
|
2011-12-22 06:09:41 -08:00
|
|
|
}
|
|
|
|
|
2012-01-30 19:46:13 -08:00
|
|
|
class DrawListenerProxy implements InvocationHandler {
|
|
|
|
private final PaintExpecter mPaintExpecter;
|
|
|
|
|
|
|
|
DrawListenerProxy(PaintExpecter paintExpecter) {
|
|
|
|
mPaintExpecter = paintExpecter;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Object invoke(Object proxy, Method method, Object[] args) {
|
|
|
|
String methodName = method.getName();
|
|
|
|
if ("drawFinished".equals(methodName)) {
|
2012-02-08 06:32:42 -08:00
|
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
|
|
|
|
"Received drawFinished notification");
|
2012-01-30 19:46:13 -08:00
|
|
|
mPaintExpecter.notifyOfEvent();
|
|
|
|
} else if ("toString".equals(methodName)) {
|
|
|
|
return "DrawListenerProxy";
|
|
|
|
} else if ("equals".equals(methodName)) {
|
|
|
|
return false;
|
|
|
|
} else if ("hashCode".equals(methodName)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-02 13:07:00 -08:00
|
|
|
class PaintExpecter implements RepeatedEventExpecter {
|
2012-01-30 19:46:13 -08:00
|
|
|
private Object mLayerClient;
|
|
|
|
private boolean mPaintDone;
|
|
|
|
|
|
|
|
PaintExpecter() throws IllegalAccessException, InvocationTargetException {
|
|
|
|
mLayerClient = getLayerClient.invoke(geckoApp);
|
|
|
|
setDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(classLoader, new Class[] { drawListener }, new DrawListenerProxy(this)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void notifyOfEvent() {
|
|
|
|
synchronized (this) {
|
|
|
|
mPaintDone = true;
|
|
|
|
this.notifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized void blockForEvent() {
|
|
|
|
while (! mPaintDone) {
|
|
|
|
try {
|
|
|
|
this.wait();
|
|
|
|
} catch (InterruptedException ie) {
|
|
|
|
ie.printStackTrace();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-02-02 13:07:00 -08:00
|
|
|
try {
|
|
|
|
setDrawListener.invoke(mLayerClient, (Object)null);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2012-01-30 19:46:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized boolean eventReceived() {
|
|
|
|
return mPaintDone;
|
|
|
|
}
|
2012-02-02 13:07:00 -08:00
|
|
|
|
|
|
|
public synchronized void blockUntilClear(long millis) {
|
|
|
|
if (millis <= 0) {
|
|
|
|
throw new IllegalArgumentException("millis must be > 0");
|
|
|
|
}
|
|
|
|
// wait for at least one event
|
|
|
|
while (! mPaintDone) {
|
|
|
|
try {
|
|
|
|
this.wait();
|
|
|
|
} catch (InterruptedException ie) {
|
|
|
|
ie.printStackTrace();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// now wait for a period of millis where we don't get an event
|
|
|
|
long startTime = SystemClock.uptimeMillis();
|
|
|
|
while (true) {
|
|
|
|
try {
|
|
|
|
this.wait(millis);
|
|
|
|
} catch (InterruptedException ie) {
|
|
|
|
ie.printStackTrace();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
long endTime = SystemClock.uptimeMillis();
|
|
|
|
if (endTime - startTime >= millis) {
|
|
|
|
// success
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// we got a notify() before we could wait long enough, so we need to start over
|
|
|
|
startTime = endTime;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
setDrawListener.invoke(mLayerClient, (Object)null);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2012-01-30 19:46:13 -08:00
|
|
|
}
|
|
|
|
|
2012-02-02 13:07:00 -08:00
|
|
|
public RepeatedEventExpecter expectPaint() {
|
2012-01-30 19:46:13 -08:00
|
|
|
try {
|
|
|
|
return new PaintExpecter();
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-22 06:09:41 -08:00
|
|
|
public void sendSpecialKey(SpecialKey button) {
|
|
|
|
switch( button) {
|
|
|
|
case DOWN:
|
|
|
|
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
|
|
|
|
break;
|
|
|
|
case UP:
|
|
|
|
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
|
|
|
|
break;
|
|
|
|
case LEFT:
|
|
|
|
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT);
|
|
|
|
break;
|
|
|
|
case RIGHT:
|
|
|
|
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT);
|
|
|
|
break;
|
|
|
|
case ENTER:
|
|
|
|
instr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
|
|
|
|
break;
|
2012-02-07 11:12:23 -08:00
|
|
|
case MENU:
|
|
|
|
instr.sendCharacterSync(KeyEvent.KEYCODE_MENU);
|
|
|
|
break;
|
|
|
|
case BACK:
|
|
|
|
instr.sendCharacterSync(KeyEvent.KEYCODE_BACK);
|
|
|
|
break;
|
2011-12-22 06:09:41 -08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void sendKeys(String input) {
|
|
|
|
instr.sendStringSync(input);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void drag(int startingX, int endingX, int startingY, int endingY) {
|
|
|
|
solo.drag(startingX, endingX, startingY, endingY, 10);
|
|
|
|
}
|
|
|
|
}
|