diff options
Diffstat (limited to 'monkeyrunner/src')
29 files changed, 1175 insertions, 290 deletions
diff --git a/monkeyrunner/src/Android.mk b/monkeyrunner/src/Android.mk index 38be272..8cca0bc 100644 --- a/monkeyrunner/src/Android.mk +++ b/monkeyrunner/src/Android.mk @@ -24,7 +24,9 @@ LOCAL_JAVA_LIBRARIES := \ jython \ guavalib \ jsilver \ - sdklib + sdklib \ + hierarchyviewerlib \ + swt LOCAL_JAVA_RESOURCE_DIRS := resources diff --git a/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java b/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java index 864441e..badddff 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java +++ b/monkeyrunner/src/com/android/monkeyrunner/JythonUtils.java @@ -23,15 +23,18 @@ import java.text.BreakIterator; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.python.core.ArgParser; import org.python.core.ClassDictInit; import org.python.core.Py; +import org.python.core.PyBoolean; import org.python.core.PyDictionary; import org.python.core.PyFloat; import org.python.core.PyInteger; @@ -73,6 +76,7 @@ public final class JythonUtils { // What python calls float, most people call double builder.put(PyFloat.class, Double.class); builder.put(PyInteger.class, Integer.class); + builder.put(PyBoolean.class, Boolean.class); PYOBJECT_TO_JAVA_OBJECT_MAP = builder.build(); } @@ -228,6 +232,8 @@ public final class JythonUtils { } else if (o instanceof Float) { float f = (Float) o; return new PyFloat(f); + } else if (o instanceof Boolean) { + return new PyBoolean((Boolean) o); } return Py.None; } @@ -479,4 +485,20 @@ public final class JythonUtils { lines.add(currentLine.toString()); return lines; } + + /** + * Obtain the set of method names available from Python. + * + * @param clazz Class to inspect. + * @return set of method names annotated with {@code MonkeyRunnerExported}. + */ + public static Set<String> getMethodNames(Class<?> clazz) { + HashSet<String> methodNames = new HashSet<String>(); + for (Method m: clazz.getMethods()) { + if (m.isAnnotationPresent(MonkeyRunnerExported.class)) { + methodNames.add(m.getName()); + } + } + return methodNames; + } } diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java index 0f4362a..649e33c 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java +++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java @@ -20,8 +20,6 @@ import java.util.Collections; import java.util.Map; import java.util.Set; -import javax.annotation.Nullable; - import org.python.core.ArgParser; import org.python.core.ClassDictInit; import org.python.core.Py; @@ -30,7 +28,11 @@ import org.python.core.PyException; import org.python.core.PyObject; import org.python.core.PyTuple; +import com.android.monkeyrunner.core.IMonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyImage; import com.android.monkeyrunner.doc.MonkeyRunnerExported; +import com.android.monkeyrunner.easy.HierarchyViewer; + import com.google.common.base.Functions; import com.google.common.base.Preconditions; import com.google.common.collect.Collections2; @@ -43,7 +45,7 @@ import com.google.common.collect.ImmutableMap; * implementation of this class. */ @MonkeyRunnerExported(doc = "Represents a device attached to the system.") -public abstract class MonkeyDevice extends PyObject implements ClassDictInit { +public class MonkeyDevice extends PyObject implements ClassDictInit { public static void classDictInit(PyObject dict) { JythonUtils.convertDocAnnotationsForClass(MonkeyDevice.class, dict); } @@ -55,37 +57,41 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { @MonkeyRunnerExported(doc = "Sends a DOWN event, immediately followed by an UP event when used with touch() or press()") public static final String DOWN_AND_UP = "downAndUp"; + // TODO: This may not be accessible from jython; if so, remove it. public enum TouchPressType { DOWN, UP, DOWN_AND_UP, } - public static final Map<String, TouchPressType> TOUCH_NAME_TO_ENUM = - ImmutableMap.of(MonkeyDevice.DOWN, TouchPressType.DOWN, - MonkeyDevice.UP, TouchPressType.UP, - MonkeyDevice.DOWN_AND_UP, TouchPressType.DOWN_AND_UP); + public static final Map<String, IMonkeyDevice.TouchPressType> TOUCH_NAME_TO_ENUM = + ImmutableMap.of(DOWN, IMonkeyDevice.TouchPressType.DOWN, + UP, IMonkeyDevice.TouchPressType.UP, + DOWN_AND_UP, IMonkeyDevice.TouchPressType.DOWN_AND_UP); private static final Set<String> VALID_DOWN_UP_TYPES = TOUCH_NAME_TO_ENUM.keySet(); - /** - * Create a MonkeyMananger for talking to this device. - * - * NOTE: This is not part of the jython API. - * - * @return the MonkeyManager - */ - public abstract MonkeyManager getManager(); - - /** - * Dispose of any native resoureces this device may have taken hold of. - * - * NOTE: This is not part of the jython API. - */ - public abstract void dispose(); + private IMonkeyDevice impl; + + public MonkeyDevice(IMonkeyDevice impl) { + this.impl = impl; + } + + public IMonkeyDevice getImpl() { + return impl; + } + + @MonkeyRunnerExported(doc = "Get the HierarchyViewer object for the device.", + returns = "A HierarchyViewer object") + public HierarchyViewer getHierarchyViewer(PyObject[] args, String[] kws) { + return impl.getHierarchyViewer(); + } @MonkeyRunnerExported(doc = "Gets the device's screen buffer, yielding a screen capture of the entire display.", returns = "A MonkeyImage object (a bitmap wrapper)") - public abstract MonkeyImage takeSnapshot(); + public MonkeyImage takeSnapshot() { + IMonkeyImage image = impl.takeSnapshot(); + return new MonkeyImage(image); + } @MonkeyRunnerExported(doc = "Given the name of a variable on the device, " + "returns the variable's value", @@ -97,7 +103,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { ArgParser ap = JythonUtils.createArgParser(args, kws); Preconditions.checkNotNull(ap); - return getProperty(ap.getString(0)); + return impl.getProperty(ap.getString(0)); } @MonkeyRunnerExported(doc = "Synonym for getProperty()", @@ -107,7 +113,8 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { public String getSystemProperty(PyObject[] args, String[] kws) { ArgParser ap = JythonUtils.createArgParser(args, kws); Preconditions.checkNotNull(ap); - return getSystemProperty(ap.getString(0)); + + return impl.getSystemProperty(ap.getString(0)); } @MonkeyRunnerExported(doc = "Sends a touch event at the specified location", @@ -136,7 +143,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { // bad stuff was passed in, just use the already specified default value type = MonkeyDevice.DOWN_AND_UP; } - touch(x, y, TOUCH_NAME_TO_ENUM.get(type)); + impl.touch(x, y, TOUCH_NAME_TO_ENUM.get(type)); } @MonkeyRunnerExported(doc = "Simulates dragging (touch, hold, and move) on the device screen.", @@ -171,7 +178,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { int steps = ap.getInt(3, 10); - drag(startx, starty, endx, endy, steps, ms); + impl.drag(startx, starty, endx, endy, steps, ms); } @MonkeyRunnerExported(doc = "Send a key event to the specified key", @@ -199,7 +206,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { // bad stuff was passed in, just use the already specified default value type = MonkeyDevice.DOWN_AND_UP; } - press(name, TOUCH_NAME_TO_ENUM.get(type)); + impl.press(name, TOUCH_NAME_TO_ENUM.get(type)); } @MonkeyRunnerExported(doc = "Types the specified string on the keyboard. This is " + @@ -211,7 +218,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { Preconditions.checkNotNull(ap); String message = ap.getString(0); - type(message); + impl.type(message); } @MonkeyRunnerExported(doc = "Executes an adb shell command and returns the result, if any.", @@ -223,7 +230,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { Preconditions.checkNotNull(ap); String cmd = ap.getString(0); - return shell(cmd); + return impl.shell(cmd); } @MonkeyRunnerExported(doc = "Reboots the specified device into a specified bootloader.", @@ -235,7 +242,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { String into = ap.getString(0, null); - reboot(into); + impl.reboot(into); } @MonkeyRunnerExported(doc = "Installs the specified Android package (.apk file) " + @@ -248,7 +255,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { Preconditions.checkNotNull(ap); String path = ap.getString(0); - return installPackage(path); + return impl.installPackage(path); } @MonkeyRunnerExported(doc = "Deletes the specified package from the device, including its " + @@ -261,7 +268,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { Preconditions.checkNotNull(ap); String packageName = ap.getString(0); - return removePackage(packageName); + return impl.removePackage(packageName); } @MonkeyRunnerExported(doc = "Starts an Activity on the device by sending an Intent " + @@ -294,7 +301,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { String component = ap.getString(6, null); int flags = ap.getInt(7, 0); - startActivity(uri, action, data, mimetype, categories, extras, component, flags); + impl.startActivity(uri, action, data, mimetype, categories, extras, component, flags); } @MonkeyRunnerExported(doc = "Sends a broadcast intent to the device.", @@ -326,7 +333,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { String component = ap.getString(6, null); int flags = ap.getInt(7, 0); - broadcastIntent(uri, action, data, mimetype, categories, extras, component, flags); + impl.broadcastIntent(uri, action, data, mimetype, categories, extras, component, flags); } @MonkeyRunnerExported(doc = "Run the specified package with instrumentation and return " + @@ -354,7 +361,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { instrumentArgs = Collections.emptyMap(); } - Map<String, Object> result = instrument(packageName, instrumentArgs); + Map<String, Object> result = impl.instrument(packageName, instrumentArgs); return JythonUtils.convertMapToDict(result); } @@ -363,34 +370,6 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit { ArgParser ap = JythonUtils.createArgParser(args, kws); Preconditions.checkNotNull(ap); - wake(); + impl.wake(); } - - /** - * Reboot the device. - * - * @param into which bootloader to boot into. Null means default reboot. - */ - public abstract void reboot(@Nullable String into); - - public abstract String getProperty(String key); - public abstract String getSystemProperty(String key); - public abstract void touch(int x, int y, TouchPressType type); - public abstract void press(String keyName, TouchPressType type); - public abstract void drag(int startx, int starty, int endx, int endy, int steps, long ms); - public abstract void type(String string); - public abstract String shell(String cmd); - public abstract boolean installPackage(String path); - public abstract boolean removePackage(String packageName); - public abstract void startActivity(@Nullable String uri, @Nullable String action, - @Nullable String data, @Nullable String mimetype, - Collection<String> categories, Map<String, Object> extras, @Nullable String component, - int flags); - public abstract void broadcastIntent(@Nullable String uri, @Nullable String action, - @Nullable String data, @Nullable String mimetype, - Collection<String> categories, Map<String, Object> extras, @Nullable String component, - int flags); - public abstract Map<String, Object> instrument(String packageName, - Map<String, Object> args); - public abstract void wake(); } diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java index d506613..b55b4f3 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java +++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyImage.java @@ -17,6 +17,7 @@ package com.android.monkeyrunner; import com.google.common.base.Preconditions; +import com.android.monkeyrunner.core.IMonkeyImage; import com.android.monkeyrunner.doc.MonkeyRunnerExported; import org.python.core.ArgParser; @@ -25,57 +26,31 @@ import org.python.core.PyInteger; import org.python.core.PyObject; import org.python.core.PyTuple; -import java.awt.Graphics; import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.Iterator; - -import javax.imageio.ImageIO; -import javax.imageio.ImageWriter; -import javax.imageio.stream.ImageOutputStream; +import java.util.logging.Logger; /** * Jython object to encapsulate images that have been taken. */ @MonkeyRunnerExported(doc = "An image") -public abstract class MonkeyImage extends PyObject implements ClassDictInit { +public class MonkeyImage extends PyObject implements ClassDictInit { + private static Logger LOG = Logger.getLogger(MonkeyImage.class.getCanonicalName()); + public static void classDictInit(PyObject dict) { JythonUtils.convertDocAnnotationsForClass(MonkeyImage.class, dict); } - /** - * Convert the MonkeyImage into a BufferedImage. - * - * @return a BufferedImage for this MonkeyImage. - */ - public abstract BufferedImage createBufferedImage(); + private IMonkeyImage impl; - // Cache the BufferedImage so we don't have to generate it every time. - private WeakReference<BufferedImage> cachedBufferedImage = null; - - /** - * Utility method to handle getting the BufferedImage and managing the cache. - * - * @return the BufferedImage for this image. - */ - private BufferedImage getBufferedImage() { - // Check the cache first - if (cachedBufferedImage != null) { - BufferedImage img = cachedBufferedImage.get(); - if (img != null) { - return img; - } - } + public MonkeyImage(IMonkeyImage impl) { + this.impl = impl; + } - // Not in the cache, so create it and cache it. - BufferedImage img = createBufferedImage(); - cachedBufferedImage = new WeakReference<BufferedImage>(img); - return img; + public IMonkeyImage getImpl() { + return impl; } + @MonkeyRunnerExported(doc = "Converts the MonkeyImage into a particular format and returns " + "the result as a String. Use this to get access to the raw" + "pixels in a particular format. String output is for better " + @@ -89,16 +64,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit { Preconditions.checkNotNull(ap); String format = ap.getString(0, "png"); - - BufferedImage argb = convertSnapshot(); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - try { - ImageIO.write(argb, format, os); - } catch (IOException e) { - return new byte[0]; - } - return os.toByteArray(); + return impl.convertToBytes(format); } @MonkeyRunnerExported(doc = "Write the MonkeyImage to a file. If no " + @@ -116,38 +82,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit { String path = ap.getString(0); String format = ap.getString(1, null); - - if (format != null) { - return writeToFile(path, format); - } - int offset = path.lastIndexOf('.'); - if (offset < 0) { - return writeToFile(path, "png"); - } - String ext = path.substring(offset + 1); - Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix(ext); - if (!writers.hasNext()) { - return writeToFile(path, "png"); - } - ImageWriter writer = writers.next(); - BufferedImage image = getBufferedImage(); - try { - File f = new File(path); - f.delete(); - - ImageOutputStream outputStream = ImageIO.createImageOutputStream(f); - writer.setOutput(outputStream); - - try { - writer.write(image); - } finally { - writer.dispose(); - outputStream.flush(); - } - } catch (IOException e) { - return false; - } - return true; + return impl.writeToFile(path, format); } @MonkeyRunnerExported(doc = "Get a single ARGB (alpha, red, green, blue) pixel at location " + @@ -163,7 +98,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit { int x = ap.getInt(0); int y = ap.getInt(1); - int pixel = getPixel(x, y); + int pixel = impl.getPixel(x, y); PyInteger a = new PyInteger((pixel & 0xFF000000) >> 24); PyInteger r = new PyInteger((pixel & 0x00FF0000) >> 16); PyInteger g = new PyInteger((pixel & 0x0000FF00) >> 8); @@ -184,35 +119,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit { int x = ap.getInt(0); int y = ap.getInt(1); - return getPixel(x, y); - } - - private int getPixel(int x, int y) { - BufferedImage image = getBufferedImage(); - return image.getRGB(x, y); - } - - private BufferedImage convertSnapshot() { - BufferedImage image = getBufferedImage(); - - // Convert the image to ARGB so ImageIO writes it out nicely - BufferedImage argb = new BufferedImage(image.getWidth(), image.getHeight(), - BufferedImage.TYPE_INT_ARGB); - Graphics g = argb.createGraphics(); - g.drawImage(image, 0, 0, null); - g.dispose(); - return argb; - } - - public boolean writeToFile(String path, String format) { - BufferedImage argb = convertSnapshot(); - - try { - ImageIO.write(argb, format, new File(path)); - } catch (IOException e) { - return false; - } - return true; + return impl.getPixel(x, y); } @MonkeyRunnerExported(doc = "Compare this MonkeyImage object to aother MonkeyImage object.", @@ -227,53 +134,13 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit { Preconditions.checkNotNull(ap); PyObject otherObject = ap.getPyObject(0); - MonkeyImage other = (MonkeyImage) otherObject.__tojava__(MonkeyImage.class); + // TODO: check if this conversion wortks + IMonkeyImage other = (IMonkeyImage) otherObject.__tojava__( + IMonkeyImage.class); double percent = JythonUtils.getFloat(ap, 1, 1.0); - BufferedImage otherImage = other.getBufferedImage(); - BufferedImage myImage = getBufferedImage(); - - // Easy size check - if (otherImage.getWidth() != myImage.getWidth()) { - return false; - } - if (otherImage.getHeight() != myImage.getHeight()) { - return false; - } - - int[] otherPixel = new int[1]; - int[] myPixel = new int[1]; - - int width = myImage.getWidth(); - int height = myImage.getHeight(); - - int numDiffPixels = 0; - // Now, go through pixel-by-pixel and check that the images are the same; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - if (myImage.getRGB(x, y) != otherImage.getRGB(x, y)) { - numDiffPixels++; - } - } - } - double numberPixels = (height * width); - double diffPercent = numDiffPixels / numberPixels; - return percent <= 1.0 - diffPercent; - } - - private static class BufferedImageMonkeyImage extends MonkeyImage { - private final BufferedImage image; - - public BufferedImageMonkeyImage(BufferedImage image) { - this.image = image; - } - - @Override - public BufferedImage createBufferedImage() { - return image; - } - + return impl.sameAs(other, percent); } @MonkeyRunnerExported(doc = "Copy a rectangular region of the image.", @@ -292,7 +159,7 @@ public abstract class MonkeyImage extends PyObject implements ClassDictInit { int w = rect.__getitem__(2).asInt(); int h = rect.__getitem__(3).asInt(); - BufferedImage image = getBufferedImage(); - return new BufferedImageMonkeyImage(image.getSubimage(x, y, w, h)); + IMonkeyImage image = impl.getSubImage(x, y, w, h); + return new MonkeyImage(image); } -} +}
\ No newline at end of file diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java index 8480223..5529802 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java +++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java @@ -19,6 +19,10 @@ import com.google.common.base.Functions; import com.google.common.base.Preconditions; import com.google.common.collect.Collections2; +import com.android.monkeyrunner.core.IMonkeyBackend; +import com.android.monkeyrunner.core.IMonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyImage; +import com.android.monkeyrunner.core.MonkeyImageBase; import com.android.monkeyrunner.doc.MonkeyRunnerExported; import org.python.core.ArgParser; @@ -38,7 +42,7 @@ import javax.swing.JOptionPane; @MonkeyRunnerExported(doc = "Main entry point for MonkeyRunner") public class MonkeyRunner extends PyObject implements ClassDictInit { private static final Logger LOG = Logger.getLogger(MonkeyRunner.class.getCanonicalName()); - private static MonkeyRunnerBackend backend; + private static IMonkeyBackend backend; public static void classDictInit(PyObject dict) { JythonUtils.convertDocAnnotationsForClass(MonkeyRunner.class, dict); @@ -49,7 +53,7 @@ public class MonkeyRunner extends PyObject implements ClassDictInit { * * @param backend the backend to use. */ - /* package */ static void setBackend(MonkeyRunnerBackend backend) { + /* package */ static void setBackend(IMonkeyBackend backend) { MonkeyRunner.backend = backend; } @@ -71,8 +75,10 @@ public class MonkeyRunner extends PyObject implements ClassDictInit { timeoutMs = Long.MAX_VALUE; } - return backend.waitForConnection(timeoutMs, + IMonkeyDevice device = backend.waitForConnection(timeoutMs, ap.getString(1, ".*")); + MonkeyDevice monkeyDevice = new MonkeyDevice(device); + return monkeyDevice; } @MonkeyRunnerExported(doc = "Pause the currently running program for the specified " + @@ -174,6 +180,21 @@ public class MonkeyRunner extends PyObject implements ClassDictInit { return choice(message, title, choices); } + @MonkeyRunnerExported(doc = "Loads a MonkeyImage from a file.", + args = { "path" }, + argDocs = { + "The path to the file to load. This file path is in terms of the computer running " + + "MonkeyRunner and not a path on the Android Device. " }, + returns = "A new MonkeyImage representing the specified file") + public static MonkeyImage loadImageFromFile(PyObject[] args, String kws[]) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + String path = ap.getString(0); + IMonkeyImage image = MonkeyImageBase.loadImageFromFile(path); + return new MonkeyImage(image); + } + /** * Display an alert dialog. * diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java index 90fce6f..8c9942c 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java +++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerStarter.java @@ -20,6 +20,7 @@ import com.google.common.base.Predicates; import com.google.common.collect.ImmutableMap; import com.android.monkeyrunner.adb.AdbBackend; +import com.android.monkeyrunner.core.IMonkeyBackend; import com.android.monkeyrunner.stub.StubBackend; import org.python.util.PythonInterpreter; @@ -50,7 +51,7 @@ public class MonkeyRunnerStarter { private static final Logger LOG = Logger.getLogger(MonkeyRunnerStarter.class.getName()); private static final String MONKEY_RUNNER_MAIN_MANIFEST_NAME = "MonkeyRunnerStartupRunner"; - private final MonkeyRunnerBackend backend; + private final IMonkeyBackend backend; private final MonkeyRunnerOptions options; public MonkeyRunnerStarter(MonkeyRunnerOptions options) { @@ -68,7 +69,7 @@ public class MonkeyRunnerStarter { * @param backendName the name of the backend to create * @return the new backend, or null if none were found. */ - public static MonkeyRunnerBackend createBackendByName(String backendName) { + public static IMonkeyBackend createBackendByName(String backendName) { if ("adb".equals(backendName)) { return new AdbBackend(); } else if ("stub".equals(backendName)) { @@ -191,13 +192,12 @@ public class MonkeyRunnerStarter { public static void main(String[] args) { MonkeyRunnerOptions options = MonkeyRunnerOptions.processOptions(args); - // logging property files are difficult - replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE, options.getLogLevel()); - if (options == null) { return; } + // logging property files are difficult + replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE, options.getLogLevel()); MonkeyRunnerStarter runner = new MonkeyRunnerStarter(options); int error = runner.run(); diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java index 455d131..49cac08 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java +++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbBackend.java @@ -19,8 +19,8 @@ import com.google.common.collect.Lists; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.IDevice; -import com.android.monkeyrunner.MonkeyDevice; -import com.android.monkeyrunner.MonkeyRunnerBackend; +import com.android.monkeyrunner.core.IMonkeyBackend; +import com.android.monkeyrunner.core.IMonkeyDevice; import com.android.sdklib.SdkConstants; import java.io.File; @@ -32,12 +32,11 @@ import java.util.regex.Pattern; /** * Backend implementation that works over ADB to talk to the device. */ -public class AdbBackend implements MonkeyRunnerBackend { +public class AdbBackend implements IMonkeyBackend { private static Logger LOG = Logger.getLogger(AdbBackend.class.getCanonicalName()); // How long to wait each time we check for the device to be connected. private static final int CONNECTION_ITERATION_TIMEOUT_MS = 200; - private final List<AdbMonkeyDevice> devices = Lists.newArrayList(); - + private final List<IMonkeyDevice> devices = Lists.newArrayList(); private final AndroidDebugBridge bridge; public AdbBackend() { @@ -87,18 +86,20 @@ public class AdbBackend implements MonkeyRunnerBackend { return null; } - public MonkeyDevice waitForConnection() { + @Override + public IMonkeyDevice waitForConnection() { return waitForConnection(Integer.MAX_VALUE, ".*"); } - public MonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex) { + @Override + public IMonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex) { do { IDevice device = findAttacedDevice(deviceIdRegex); // Only return the device when it is online if (device != null && device.getState() == IDevice.DeviceState.ONLINE) { - AdbMonkeyDevice amd = new AdbMonkeyDevice(device); - devices.add(amd); - return amd; + IMonkeyDevice monkeyDevice = new AdbMonkeyDevice(device); + devices.add(monkeyDevice); + return monkeyDevice; } try { @@ -113,8 +114,9 @@ public class AdbBackend implements MonkeyRunnerBackend { return null; } + @Override public void shutdown() { - for (AdbMonkeyDevice device : devices) { + for (IMonkeyDevice device : devices) { device.dispose(); } AndroidDebugBridge.terminate(); diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java index e7e2e1c..60eaba9 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java +++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java @@ -25,10 +25,11 @@ import com.android.ddmlib.IDevice; import com.android.ddmlib.InstallException; import com.android.ddmlib.ShellCommandUnresponsiveException; import com.android.ddmlib.TimeoutException; -import com.android.monkeyrunner.MonkeyDevice; -import com.android.monkeyrunner.MonkeyImage; import com.android.monkeyrunner.MonkeyManager; import com.android.monkeyrunner.adb.LinearInterpolator.Point; +import com.android.monkeyrunner.core.IMonkeyImage; +import com.android.monkeyrunner.core.IMonkeyDevice; +import com.android.monkeyrunner.easy.HierarchyViewer; import java.io.IOException; import java.net.InetAddress; @@ -47,7 +48,7 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; -public class AdbMonkeyDevice extends MonkeyDevice { +public class AdbMonkeyDevice implements IMonkeyDevice { private static final Logger LOG = Logger.getLogger(AdbMonkeyDevice.class.getName()); private static final String[] ZERO_LENGTH_STRING_ARRAY = new String[0]; @@ -81,9 +82,15 @@ public class AdbMonkeyDevice extends MonkeyDevice { manager = null; } + @Override + public HierarchyViewer getHierarchyViewer() { + return new HierarchyViewer(device); + } + private void executeAsyncCommand(final String command, final LoggingOutputReceiver logger) { executor.submit(new Runnable() { + @Override public void run() { try { device.executeShellCommand(command, logger); @@ -184,7 +191,7 @@ public class AdbMonkeyDevice extends MonkeyDevice { } @Override - public MonkeyImage takeSnapshot() { + public IMonkeyImage takeSnapshot() { try { return new AdbMonkeyImage(device.getScreenshot()); } catch (TimeoutException e) { @@ -419,9 +426,10 @@ public class AdbMonkeyDevice extends MonkeyDevice { } else { // treat is as a string. valueString = value.toString(); - arg = "--esmake"; + arg = "--es"; } parts.add(arg); + parts.add(entry.getKey()); parts.add(valueString); } @@ -498,6 +506,7 @@ public class AdbMonkeyDevice extends MonkeyDevice { LinearInterpolator.Point start = new LinearInterpolator.Point(startx, starty); LinearInterpolator.Point end = new LinearInterpolator.Point(endx, endy); lerp.interpolate(start, end, new LinearInterpolator.Callback() { + @Override public void step(Point point) { try { manager.touchMove(point.getX(), point.getY()); @@ -512,9 +521,11 @@ public class AdbMonkeyDevice extends MonkeyDevice { } } + @Override public void start(Point point) { try { manager.touchDown(point.getX(), point.getY()); + manager.touchMove(point.getX(), point.getY()); } catch (IOException e) { LOG.log(Level.SEVERE, "Error sending drag start event", e); } @@ -526,8 +537,10 @@ public class AdbMonkeyDevice extends MonkeyDevice { } } + @Override public void end(Point point) { try { + manager.touchMove(point.getX(), point.getY()); manager.touchUp(point.getX(), point.getY()); } catch (IOException e) { LOG.log(Level.SEVERE, "Error sending drag end event", e); diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java index fc32600..e2bd86e 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java +++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyImage.java @@ -16,15 +16,15 @@ package com.android.monkeyrunner.adb; import com.android.ddmlib.RawImage; -import com.android.monkeyrunner.MonkeyImage; import com.android.monkeyrunner.adb.image.ImageUtils; +import com.android.monkeyrunner.core.MonkeyImageBase; import java.awt.image.BufferedImage; /** * ADB implementation of the MonkeyImage class. */ -public class AdbMonkeyImage extends MonkeyImage { +public class AdbMonkeyImage extends MonkeyImageBase { private final RawImage image; /** diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java b/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java index 7e31ea5..5a317f1 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java +++ b/monkeyrunner/src/com/android/monkeyrunner/adb/image/CaptureRawAndConvertedImage.java @@ -16,9 +16,11 @@ package com.android.monkeyrunner.adb.image; import com.android.ddmlib.RawImage; -import com.android.monkeyrunner.MonkeyDevice; import com.android.monkeyrunner.adb.AdbBackend; import com.android.monkeyrunner.adb.AdbMonkeyImage; +import com.android.monkeyrunner.core.IMonkeyBackend; +import com.android.monkeyrunner.core.IMonkeyImage; +import com.android.monkeyrunner.core.IMonkeyDevice; import java.io.FileOutputStream; import java.io.IOException; @@ -94,13 +96,13 @@ public class CaptureRawAndConvertedImage { } public static void main(String[] args) throws IOException { - AdbBackend backend = new AdbBackend(); - MonkeyDevice device = backend.waitForConnection(); - AdbMonkeyImage snapshot = (AdbMonkeyImage) device.takeSnapshot(); + IMonkeyBackend backend = new AdbBackend(); + IMonkeyDevice device = backend.waitForConnection(); + IMonkeyImage snapshot = (IMonkeyImage) device.takeSnapshot(); // write out to a file snapshot.writeToFile("output.png", "png"); - writeOutImage(snapshot.getRawImage(), "output.raw"); + writeOutImage(((AdbMonkeyImage)snapshot).getRawImage(), "output.raw"); System.exit(0); } } diff --git a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java index e199a75..ca3195c 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java +++ b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyController.java @@ -15,8 +15,9 @@ */ package com.android.monkeyrunner.controller; -import com.android.monkeyrunner.MonkeyDevice; import com.android.monkeyrunner.adb.AdbBackend; +import com.android.monkeyrunner.core.IMonkeyBackend; +import com.android.monkeyrunner.core.IMonkeyDevice; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -36,9 +37,10 @@ public class MonkeyController extends JFrame { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { - AdbBackend adb = new AdbBackend(); - final MonkeyDevice device = adb.waitForConnection(); + IMonkeyBackend adb = new AdbBackend(); + final IMonkeyDevice device = adb.waitForConnection(); MonkeyControllerFrame mf = new MonkeyControllerFrame(device); mf.setVisible(true); mf.addWindowListener(new WindowAdapter() { diff --git a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java index 7f5a7d8..7750936 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java +++ b/monkeyrunner/src/com/android/monkeyrunner/controller/MonkeyControllerFrame.java @@ -15,10 +15,10 @@ */ package com.android.monkeyrunner.controller; -import com.android.monkeyrunner.MonkeyDevice; -import com.android.monkeyrunner.MonkeyImage; import com.android.monkeyrunner.MonkeyManager; import com.android.monkeyrunner.PhysicalButton; +import com.android.monkeyrunner.core.IMonkeyImage; +import com.android.monkeyrunner.core.IMonkeyDevice; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; @@ -61,7 +61,7 @@ public class MonkeyControllerFrame extends JFrame { } }); - private final MonkeyDevice device; + private final IMonkeyDevice device; private class PressAction extends AbstractAction { private final PhysicalButton button; @@ -85,7 +85,7 @@ public class MonkeyControllerFrame extends JFrame { return button; } - public MonkeyControllerFrame(MonkeyDevice device) { + public MonkeyControllerFrame(IMonkeyDevice device) { super("MonkeyController"); this.device = device; @@ -155,7 +155,7 @@ public class MonkeyControllerFrame extends JFrame { } private void updateScreen() { - MonkeyImage snapshot = device.takeSnapshot(); + IMonkeyImage snapshot = device.takeSnapshot(); currentImage = snapshot.createBufferedImage(); imageLabel.setIcon(new ImageIcon(currentImage)); diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerBackend.java b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyBackend.java index 216d214..3c1b943 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunnerBackend.java +++ b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyBackend.java @@ -13,13 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.monkeyrunner; +package com.android.monkeyrunner.core; + +import com.android.monkeyrunner.MonkeyDevice; /** * Interface between MonkeyRunner common code and the MonkeyRunner backend. The backend is * responsible for communicating between the host and the device. */ -public interface MonkeyRunnerBackend { +public interface IMonkeyBackend { + /** + * Wait for a default device to connect to the backend. + * + * @return the connected device (or null if timeout); + */ + IMonkeyDevice waitForConnection(); + /** * Wait for a device to connect to the backend. * @@ -27,7 +36,7 @@ public interface MonkeyRunnerBackend { * @param deviceIdRegex the regular expression to specify which device to wait for. * @return the connected device (or null if timeout); */ - MonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex); + IMonkeyDevice waitForConnection(long timeoutMs, String deviceIdRegex); /** * Shutdown the backend and cleanup any resources it was using. diff --git a/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyDevice.java new file mode 100644 index 0000000..c081a56 --- /dev/null +++ b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyDevice.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.monkeyrunner.core; + +import com.android.monkeyrunner.MonkeyManager; +import com.android.monkeyrunner.easy.HierarchyViewer; + +import java.util.Collection; +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * MonkeyDevice interface. + */ +public interface IMonkeyDevice { + enum TouchPressType { + DOWN, UP, DOWN_AND_UP, + } + + /** + * Create a MonkeyMananger for talking to this device. + * + * @return the MonkeyManager + */ + MonkeyManager getManager(); + + /** + * Dispose of any native resources this device may have taken hold of. + */ + void dispose(); + + /** + * @return hierarchy viewer implementation for querying state of the view + * hierarchy. + */ + HierarchyViewer getHierarchyViewer(); + + /** + * Take the current screen's snapshot. + * @return the snapshot image + */ + IMonkeyImage takeSnapshot(); + + /** + * Reboot the device. + * + * @param into which bootloader to boot into. Null means default reboot. + */ + void reboot(@Nullable String into); + + /** + * Get device's property. + * + * @param key the property name + * @return the property value + */ + String getProperty(String key); + + /** + * Get system property. + * + * @param key the name of the system property + * @return the property value + */ + String getSystemProperty(String key); + + /** + * Perform a touch of the given type at (x,y). + * + * @param x the x coordinate + * @param y the y coordinate + * @param type the touch type + */ + void touch(int x, int y, TouchPressType type); + + /** + * Perform a press of a given type using a given key. + * + * TODO: define standard key names in a separate class or enum + * + * @param keyName the name of the key to use + * @param type the type of press to perform + */ + void press(String keyName, TouchPressType type); + + /** + * Perform a drag from one one location to another + * + * @param startx the x coordinate of the drag's starting point + * @param starty the y coordinate of the drag's starting point + * @param endx the x coordinate of the drag's end point + * @param endy the y coordinate of the drag's end point + * @param steps the number of steps to take when interpolating points + * @param ms the duration of the drag + */ + void drag(int startx, int starty, int endx, int endy, int steps, long ms); + + /** + * Type a given string. + * + * @param string the string to type + */ + void type(String string); + + /** + * Execute a shell command. + * + * @param cmd the command to execute + * @return the output of the command + */ + String shell(String cmd); + + /** + * Install a given package. + * + * @param path the path to the installation package + * @return true if success + */ + boolean installPackage(String path); + + /** + * Uninstall a given package. + * + * @param packageName the name of the package + * @return true if success + */ + boolean removePackage(String packageName); + + /** + * Start an activity. + * + * @param uri the URI for the Intent + * @param action the action for the Intent + * @param data the data URI for the Intent + * @param mimeType the mime type for the Intent + * @param categories the category names for the Intent + * @param extras the extras to add to the Intent + * @param component the component of the Intent + * @param flags the flags for the Intent + */ + void startActivity(@Nullable String uri, @Nullable String action, + @Nullable String data, @Nullable String mimeType, + Collection<String> categories, Map<String, Object> extras, @Nullable String component, + int flags); + + /** + * Send a broadcast intent to the device. + * + * @param uri the URI for the Intent + * @param action the action for the Intent + * @param data the data URI for the Intent + * @param mimeType the mime type for the Intent + * @param categories the category names for the Intent + * @param extras the extras to add to the Intent + * @param component the component of the Intent + * @param flags the flags for the Intent + */ + void broadcastIntent(@Nullable String uri, @Nullable String action, + @Nullable String data, @Nullable String mimeType, + Collection<String> categories, Map<String, Object> extras, @Nullable String component, + int flags); + + /** + * Run the specified package with instrumentation and return the output it + * generates. + * + * Use this to run a test package using InstrumentationTestRunner. + * + * @param packageName The class to run with instrumentation. The format is + * packageName/className. Use packageName to specify the Android package to + * run, and className to specify the class to run within that package. For + * test packages, this is usually testPackageName/InstrumentationTestRunner + * @param args a map of strings to objects containing the arguments to pass + * to this instrumentation. + * @return A map of strings to objects for the output from the package. + * For a test package, contains a single key-value pair: the key is 'stream' + * and the value is a string containing the test output. + */ + Map<String, Object> instrument(String packageName, + Map<String, Object> args); + + /** + * Wake up the screen on the device. + */ + void wake(); +} diff --git a/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyImage.java b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyImage.java new file mode 100644 index 0000000..5a24fa7 --- /dev/null +++ b/monkeyrunner/src/com/android/monkeyrunner/core/IMonkeyImage.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.monkeyrunner.core; + +import java.awt.image.BufferedImage; + +/** + * MonkeyImage interface. + * + * This interface defines an image representing a screen snapshot. + */ +public interface IMonkeyImage { + // TODO: add java docs + BufferedImage createBufferedImage(); + BufferedImage getBufferedImage(); + + IMonkeyImage getSubImage(int x, int y, int w, int h); + + byte[] convertToBytes(String format); + boolean writeToFile(String path, String format); + int getPixel(int x, int y); + boolean sameAs(IMonkeyImage other, double percent); +} diff --git a/monkeyrunner/src/com/android/monkeyrunner/core/MonkeyImageBase.java b/monkeyrunner/src/com/android/monkeyrunner/core/MonkeyImageBase.java new file mode 100644 index 0000000..04ccb93 --- /dev/null +++ b/monkeyrunner/src/com/android/monkeyrunner/core/MonkeyImageBase.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.monkeyrunner.core; + +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.imageio.ImageIO; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; + +/** + * Base class with basic functionality for MonkeyImage implementations. + */ +public abstract class MonkeyImageBase implements IMonkeyImage { + private static Logger LOG = Logger.getLogger(MonkeyImageBase.class.getCanonicalName()); + + /** + * Convert the MonkeyImage to a BufferedImage. + * + * @return a BufferedImage for this MonkeyImage. + */ + @Override + public abstract BufferedImage createBufferedImage(); + + // Cache the BufferedImage so we don't have to generate it every time. + private WeakReference<BufferedImage> cachedBufferedImage = null; + + /** + * Utility method to handle getting the BufferedImage and managing the cache. + * + * @return the BufferedImage for this image. + */ + @Override + public BufferedImage getBufferedImage() { + // Check the cache first + if (cachedBufferedImage != null) { + BufferedImage img = cachedBufferedImage.get(); + if (img != null) { + return img; + } + } + + // Not in the cache, so create it and cache it. + BufferedImage img = createBufferedImage(); + cachedBufferedImage = new WeakReference<BufferedImage>(img); + return img; + } + + @Override + public byte[] convertToBytes(String format) { + BufferedImage argb = convertSnapshot(); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + ImageIO.write(argb, format, os); + } catch (IOException e) { + return new byte[0]; + } + return os.toByteArray(); + } + + @Override + public boolean writeToFile(String path, String format) { + if (format != null) { + return writeToFileHelper(path, format); + } + int offset = path.lastIndexOf('.'); + if (offset < 0) { + return writeToFileHelper(path, "png"); + } + String ext = path.substring(offset + 1); + Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix(ext); + if (!writers.hasNext()) { + return writeToFileHelper(path, "png"); + } + ImageWriter writer = writers.next(); + BufferedImage image = convertSnapshot(); + try { + File f = new File(path); + f.delete(); + + ImageOutputStream outputStream = ImageIO.createImageOutputStream(f); + writer.setOutput(outputStream); + + try { + writer.write(image); + } finally { + writer.dispose(); + outputStream.flush(); + } + } catch (IOException e) { + return false; + } + return true; + } + + @Override + public int getPixel(int x, int y) { + BufferedImage image = getBufferedImage(); + return image.getRGB(x, y); + } + + private BufferedImage convertSnapshot() { + BufferedImage image = getBufferedImage(); + + // Convert the image to ARGB so ImageIO writes it out nicely + BufferedImage argb = new BufferedImage(image.getWidth(), image.getHeight(), + BufferedImage.TYPE_INT_ARGB); + Graphics g = argb.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return argb; + } + + private boolean writeToFileHelper(String path, String format) { + BufferedImage argb = convertSnapshot(); + + try { + ImageIO.write(argb, format, new File(path)); + } catch (IOException e) { + return false; + } + return true; + } + + @Override + public boolean sameAs(IMonkeyImage other, double percent) { + BufferedImage otherImage = other.getBufferedImage(); + BufferedImage myImage = getBufferedImage(); + + // Easy size check + if (otherImage.getWidth() != myImage.getWidth()) { + return false; + } + if (otherImage.getHeight() != myImage.getHeight()) { + return false; + } + + int[] otherPixel = new int[1]; + int[] myPixel = new int[1]; + + int width = myImage.getWidth(); + int height = myImage.getHeight(); + + int numDiffPixels = 0; + // Now, go through pixel-by-pixel and check that the images are the same; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (myImage.getRGB(x, y) != otherImage.getRGB(x, y)) { + numDiffPixels++; + } + } + } + double numberPixels = (height * width); + double diffPercent = numDiffPixels / numberPixels; + return percent <= 1.0 - diffPercent; + } + + // TODO: figure out the location of this class and is superclasses + private static class BufferedImageMonkeyImage extends MonkeyImageBase { + private final BufferedImage image; + + public BufferedImageMonkeyImage(BufferedImage image) { + this.image = image; + } + + @Override + public BufferedImage createBufferedImage() { + return image; + } + } + + public static IMonkeyImage loadImageFromFile(String path) { + File f = new File(path); + if (f.exists() && f.canRead()) { + try { + BufferedImage bufferedImage = ImageIO.read(new File(path)); + if (bufferedImage == null) { + LOG.log(Level.WARNING, "Cannot decode file %s", path); + return null; + } + return new BufferedImageMonkeyImage(bufferedImage); + } catch (IOException e) { + LOG.log(Level.WARNING, "Exception trying to decode image", e); + return null; + } + } else { + LOG.log(Level.WARNING, "Cannot read file %s", path); + return null; + } + } + + @Override + public IMonkeyImage getSubImage(int x, int y, int w, int h) { + BufferedImage image = getBufferedImage(); + return new BufferedImageMonkeyImage(image.getSubimage(x, y, w, h)); + } +} diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/By.java b/monkeyrunner/src/com/android/monkeyrunner/easy/By.java new file mode 100644 index 0000000..1ed1c6f --- /dev/null +++ b/monkeyrunner/src/com/android/monkeyrunner/easy/By.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.monkeyrunner.easy; + +import com.google.common.base.Preconditions; + +import com.android.hierarchyviewerlib.device.ViewNode; +import com.android.monkeyrunner.JythonUtils; +import com.android.monkeyrunner.doc.MonkeyRunnerExported; + +import org.python.core.ArgParser; +import org.python.core.ClassDictInit; +import org.python.core.PyObject; + +/** + * Select a view object based on some criteria. + * + * Currently supports the By.id criteria to search for an element by id. + * In the future it will support other criteria such as: + * By.classid - search by class. + * By.hash - search by hashcode + * and recursively searching under an already selected object. + * + * WARNING: This API is under development, expect the interface to change + * without notice. + * + * TODO: Implement other selectors, like classid, hash, and so on. + * TODO: separate java-only core from jython wrapper + */ +public class By extends PyObject implements ClassDictInit { + public static void classDictInit(PyObject dict) { + JythonUtils.convertDocAnnotationsForClass(By.class, dict); + } + + private String id; + + By(String id) { + this.id = id; + } + + @MonkeyRunnerExported(doc = "Select an object by id.", + args = { "id" }, + argDocs = { "The identifier of the object." }) + public static By id(PyObject[] args, String[] kws) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + String id = ap.getString(0); + return new By(id); + } + + public static By id(String id) { + return new By(id); + } + + /** + * Find the selected view from the root view node. + */ + ViewNode find(ViewNode rootNode) { + if (rootNode.id.equals(id)) { + return rootNode; + } + for (ViewNode child : rootNode.children) { + ViewNode found = find(child); + if (found != null) { + return found; + } + } + return null; + } +} diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/EasyMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/easy/EasyMonkeyDevice.java new file mode 100644 index 0000000..e72e462 --- /dev/null +++ b/monkeyrunner/src/com/android/monkeyrunner/easy/EasyMonkeyDevice.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.monkeyrunner.easy; + +import com.google.common.base.Preconditions; + +import com.android.hierarchyviewerlib.device.ViewNode; +import com.android.hierarchyviewerlib.device.ViewNode.Property; +import com.android.monkeyrunner.JythonUtils; +import com.android.monkeyrunner.MonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyDevice.TouchPressType; +import com.android.monkeyrunner.doc.MonkeyRunnerExported; + +import org.eclipse.swt.graphics.Point; +import org.python.core.ArgParser; +import org.python.core.ClassDictInit; +import org.python.core.Py; +import org.python.core.PyException; +import org.python.core.PyInteger; +import org.python.core.PyObject; +import org.python.core.PyString; +import org.python.core.PyTuple; + +import java.util.Set; + +/** + * Extends {@link MonkeyDevice} to support looking up views using a 'selector'. + * Currently, only identifiers can be used as a selector. All methods on + * MonkeyDevice can be used on this class in Python. + * + * WARNING: This API is under development, expect the interface to change + * without notice. + */ +@MonkeyRunnerExported(doc = "MonkeyDevice with easier methods to refer to objects.") +public class EasyMonkeyDevice extends PyObject implements ClassDictInit { + public static void classDictInit(PyObject dict) { + JythonUtils.convertDocAnnotationsForClass(EasyMonkeyDevice.class, dict); + } + + private MonkeyDevice mDevice; + private HierarchyViewer mHierarchyViewer; + + private static final Set<String> EXPORTED_METHODS = + JythonUtils.getMethodNames(EasyMonkeyDevice.class); + + @MonkeyRunnerExported(doc = "Creates EasyMonkeyDevice with an underlying MonkeyDevice.", + args = { "device" }, + argDocs = { "MonkeyDevice to extend." }) + public EasyMonkeyDevice(MonkeyDevice device) { + this.mDevice = device; + this.mHierarchyViewer = device.getImpl().getHierarchyViewer(); + } + + @MonkeyRunnerExported(doc = "Sends a touch event to the selected object.", + args = { "selector", "type" }, + argDocs = { + "The selector identifying the object.", + "The event type as returned by TouchPressType()." }) + public void touch(PyObject[] args, String[] kws) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + By selector = getSelector(ap, 0); + String tmpType = ap.getString(1); + TouchPressType type = MonkeyDevice.TOUCH_NAME_TO_ENUM.get(tmpType); + if (type == null) type = TouchPressType.DOWN_AND_UP; + // TODO: try catch rethrow PyExc + touch(selector, type); + } + + public void touch(By selector, TouchPressType type) { + Point p = getElementCenter(selector); + mDevice.getImpl().touch(p.x, p.y, type); + } + + @MonkeyRunnerExported(doc = "Types a string into the specified object.", + args = { "selector", "text" }, + argDocs = { + "The selector identifying the object.", + "The text to type into the object." }) + public void type(PyObject[] args, String[] kws) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + By selector = getSelector(ap, 0); + String text = ap.getString(1); + type(selector, text); + } + + public void type(By selector, String text) { + Point p = getElementCenter(selector); + mDevice.getImpl().touch(p.x, p.y, TouchPressType.DOWN_AND_UP); + mDevice.getImpl().type(text); + } + + @MonkeyRunnerExported(doc = "Locates the coordinates of the selected object.", + args = { "selector" }, + argDocs = { "The selector identifying the object." }, + returns = "Tuple containing (x,y,w,h) location and size.") + public PyTuple locate(PyObject[] args, String[] kws) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + By selector = getSelector(ap, 0); + + ViewNode node = mHierarchyViewer.findView(selector); + Point p = HierarchyViewer.getAbsolutePositionOfView(node); + PyTuple tuple = new PyTuple( + new PyInteger(p.x), + new PyInteger(p.y), + new PyInteger(node.width), + new PyInteger(node.height)); + return tuple; + } + + @MonkeyRunnerExported(doc = "Checks if the specified object exists.", + args = { "selector" }, + returns = "True if the object exists.", + argDocs = { "The selector identifying the object." }) + public boolean exists(PyObject[] args, String[] kws) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + By selector = getSelector(ap, 0); + return exists(selector); + } + + public boolean exists(By selector) { + ViewNode node = mHierarchyViewer.findView(selector); + return node != null; + } + + @MonkeyRunnerExported(doc = "Checks if the specified object is visible.", + args = { "selector" }, + returns = "True if the object is visible.", + argDocs = { "The selector identifying the object." }) + public boolean visible(PyObject[] args, String[] kws) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + By selector = getSelector(ap, 0); + return visible(selector); + } + + public boolean visible(By selector) { + return mHierarchyViewer.visible(selector); + } + + @MonkeyRunnerExported(doc = "Obtain the text in the selected input box.", + args = { "selector" }, + argDocs = { "The selector identifying the object." }, + returns = "Text in the selected input box.") + public String getText(PyObject[] args, String[] kws) { + ArgParser ap = JythonUtils.createArgParser(args, kws); + Preconditions.checkNotNull(ap); + + By selector = getSelector(ap, 0); + return getText(selector); + } + + public String getText(By selector) { + return mHierarchyViewer.getText(selector); + } + + @MonkeyRunnerExported(doc = "Gets the id of the focused window.", + returns = "The symbolic id of the focused window or None.") + public String getFocusedWindowId(PyObject[] args, String[] kws) { + return getFocusedWindowId(); + } + + public String getFocusedWindowId() { + return mHierarchyViewer.getFocusedWindowName(); + } + + /** + * Forwards unknown methods to the original MonkeyDevice object. + */ + @Override + public PyObject __findattr_ex__(String name) { + if (!EXPORTED_METHODS.contains(name)) { + return mDevice.__findattr_ex__(name); + } + return super.__findattr_ex__(name); + } + + /** + * Get the selector object from the argument parser. + * + * @param ap argument parser to get it from. + * @param i argument index. + * @return selector object. + */ + private By getSelector(ArgParser ap, int i) { + return (By)ap.getPyObject(i).__tojava__(By.class); + } + + /** + * Get the coordinates of the element's center. + * + * @param selector the element selector + * @return the (x,y) coordinates of the center + */ + private Point getElementCenter(By selector) { + ViewNode node = mHierarchyViewer.findView(selector); + if (node == null) { + throw new PyException(Py.ValueError, + String.format("View not found: %s", selector)); + } + + Point p = HierarchyViewer.getAbsoluteCenterOfView(node); + return p; + } + +}
\ No newline at end of file diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/HierarchyViewer.java b/monkeyrunner/src/com/android/monkeyrunner/easy/HierarchyViewer.java new file mode 100644 index 0000000..450571c --- /dev/null +++ b/monkeyrunner/src/com/android/monkeyrunner/easy/HierarchyViewer.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.monkeyrunner.easy; + +import com.android.ddmlib.IDevice; +import com.android.ddmlib.Log; +import com.android.hierarchyviewerlib.device.DeviceBridge; +import com.android.hierarchyviewerlib.device.ViewNode; +import com.android.hierarchyviewerlib.device.Window; + +import org.eclipse.swt.graphics.Point; + +/** + * Class for querying the view hierarchy of the device. + */ +public class HierarchyViewer { + public static final String TAG = "hierarchyviewer"; + + private IDevice mDevice; + + /** + * Constructs the hierarchy viewer for the specified device. + * + * @param device The Android device to connect to. + */ + public HierarchyViewer(IDevice device) { + this.mDevice = device; + setupViewServer(); + } + + private void setupViewServer() { + DeviceBridge.setupDeviceForward(mDevice); + if (!DeviceBridge.isViewServerRunning(mDevice)) { + if (!DeviceBridge.startViewServer(mDevice)) { + // TODO: Get rid of this delay. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + } + if (!DeviceBridge.startViewServer(mDevice)) { + Log.e(TAG, "Unable to debug device " + mDevice); + throw new RuntimeException("Could not connect to the view server"); + } + return; + } + } + DeviceBridge.loadViewServerInfo(mDevice); + } + + /** + * Finds a view using a selector. Currently only supports selectors which + * specify an id. + * + * @param selector selector for the view. + * @return view with the specified ID, or {@code null} if no view found. + */ + public ViewNode findView(By selector) { + ViewNode rootNode = DeviceBridge.loadWindowData( + new Window(mDevice, "", 0xffffffff)); + if (rootNode == null) { + throw new RuntimeException("Could not dump view"); + } + return selector.find(rootNode); + } + + /** + * Gets the window that currently receives the focus. + * + * @return name of the window that currently receives the focus. + */ + public String getFocusedWindowName() { + int id = DeviceBridge.getFocusedWindow(mDevice); + Window[] windows = DeviceBridge.loadWindows(mDevice); + for (Window w : windows) { + if (w.getHashCode() == id) + return w.getTitle(); + } + return null; + } + + /** + * Gets the absolute x/y position of the view node. + * + * @param node view node to find position of. + * @return point specifying the x/y position of the node. + */ + public static Point getAbsolutePositionOfView(ViewNode node) { + int x = node.left; + int y = node.top; + ViewNode p = node.parent; + while (p != null) { + x += p.left - p.scrollX; + y += p.top - p.scrollY; + p = p.parent; + } + return new Point(x, y); + } + + /** + * Gets the absolute x/y center of the specified view node. + * + * @param node view node to find position of. + * @return absolute x/y center of the specified view node. + */ + public static Point getAbsoluteCenterOfView(ViewNode node) { + Point point = getAbsolutePositionOfView(node); + return new Point( + point.x + (node.width / 2), point.y + (node.height / 2)); + } + + /** + * Gets the visibility of a given element. + * + * @param selector selector for the view. + * @return True if the element is visible. + */ + public boolean visible(By selector) { + ViewNode node = findView(selector); + boolean ret = (node != null) + && node.namedProperties.containsKey("getVisibility()") + && "VISIBLE".equalsIgnoreCase( + node.namedProperties.get("getVisibility()").value); + return ret; + + } + + /** + * Gets the text of a given element. + * + * @param selector selector for the view. + * @return the text of the given element. + */ + public String getText(By selector) { + ViewNode node = findView(selector); + if (node == null) { + throw new RuntimeException("Node not found"); + } + ViewNode.Property textProperty = node.namedProperties.get("text:mText"); + if (textProperty == null) { + throw new RuntimeException("No text property on node"); + } + return textProperty.value; + } +} diff --git a/monkeyrunner/src/com/android/monkeyrunner/easy/README b/monkeyrunner/src/com/android/monkeyrunner/easy/README new file mode 100644 index 0000000..239bedd --- /dev/null +++ b/monkeyrunner/src/com/android/monkeyrunner/easy/README @@ -0,0 +1,27 @@ +com.android.monkeyrunner.easy contains classes intended to make it easier +to interact with applications using the MonkeyRunner framework. Instead of +referencing a button or input box by x,y coordinate, they can be referenced +by identifier, as in the following Python example: + +############################################################################## + +from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice +from com.android.monkeyrunner.easy import EasyMonkeyDevice +from com.android.monkeyrunner.easy import By + +# Connect to the current device. +device = MonkeyRunner.waitForConnection() + +# Use the EasyMonkey API, all methods on device are available in easy_device. +easy_device = EasyMonkeyDevice(device) + +if not easy_device.visible(By.id('id/all_apps_button')): + raise Error('Could not find the "all apps" button') + +print "Location of element:", easy_device.locate(By.id('id/all_apps_button')) + +easy_device.touch(By.id('id/all_apps_button'), 'DOWN_AND_UP') + +############################################################################## + +WARNING: This API is under development and may change without notice. diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java index c1a8f7f..914a5b9 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java @@ -15,8 +15,9 @@ */ package com.android.monkeyrunner.recorder; -import com.android.monkeyrunner.MonkeyDevice; import com.android.monkeyrunner.adb.AdbBackend; +import com.android.monkeyrunner.core.IMonkeyBackend; +import com.android.monkeyrunner.core.IMonkeyDevice; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -44,7 +45,7 @@ public class MonkeyRecorder { * * @param device */ - public static void start(final MonkeyDevice device) { + public static void start(final IMonkeyDevice device) { MonkeyRecorderFrame frame = new MonkeyRecorderFrame(device); // TODO: this is a hack until the window listener works. frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); @@ -69,7 +70,7 @@ public class MonkeyRecorder { } public static void main(String[] args) { - AdbBackend adb = new AdbBackend(); + IMonkeyBackend adb = new AdbBackend(); MonkeyRecorder.start(adb.waitForConnection()); } } diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java index b6c1f78..88c1e16 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java @@ -16,7 +16,8 @@ package com.android.monkeyrunner.recorder; import com.android.monkeyrunner.MonkeyDevice; -import com.android.monkeyrunner.MonkeyImage; +import com.android.monkeyrunner.core.IMonkeyImage; +import com.android.monkeyrunner.core.IMonkeyDevice; import com.android.monkeyrunner.recorder.actions.Action; import com.android.monkeyrunner.recorder.actions.DragAction; import com.android.monkeyrunner.recorder.actions.DragAction.Direction; @@ -60,7 +61,7 @@ public class MonkeyRecorderFrame extends JFrame { private static final Logger LOG = Logger.getLogger(MonkeyRecorderFrame.class.getName()); - private final MonkeyDevice device; + private final IMonkeyDevice device; private static final long serialVersionUID = 1L; private JPanel jContentPane = null; @@ -83,6 +84,7 @@ public class MonkeyRecorderFrame extends JFrame { private ActionListModel actionListModel; private final Timer refreshTimer = new Timer(1000, new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { refreshDisplay(); // @jve:decl-index=0: } @@ -91,7 +93,7 @@ public class MonkeyRecorderFrame extends JFrame { /** * This is the default constructor */ - public MonkeyRecorderFrame(MonkeyDevice device) { + public MonkeyRecorderFrame(IMonkeyDevice device) { this.device = device; initialize(); } @@ -110,7 +112,7 @@ public class MonkeyRecorderFrame extends JFrame { } private void refreshDisplay() { - MonkeyImage snapshot = device.takeSnapshot(); + IMonkeyImage snapshot = device.takeSnapshot(); currentImage = snapshot.createBufferedImage(); Graphics2D g = scaledImage.createGraphics(); @@ -200,6 +202,7 @@ public class MonkeyRecorderFrame extends JFrame { waitButton = new JButton(); waitButton.setText("Wait"); waitButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { String howLongStr = JOptionPane.showInputDialog("How many seconds to wait?"); if (howLongStr != null) { @@ -222,6 +225,7 @@ public class MonkeyRecorderFrame extends JFrame { pressButton = new JButton(); pressButton.setText("Press a Button"); pressButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { JPanel panel = new JPanel(); JLabel text = new JLabel("What button to press?"); @@ -255,6 +259,7 @@ public class MonkeyRecorderFrame extends JFrame { typeButton = new JButton(); typeButton.setText("Type Something"); typeButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { String whatToType = JOptionPane.showInputDialog("What to type?"); if (whatToType != null) { @@ -276,6 +281,7 @@ public class MonkeyRecorderFrame extends JFrame { flingButton = new JButton(); flingButton.setText("Fling"); flingButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); @@ -358,6 +364,7 @@ public class MonkeyRecorderFrame extends JFrame { exportActionButton = new JButton(); exportActionButton.setText("Export Actions"); exportActionButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent ev) { JFileChooser fc = new JFileChooser(); if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { @@ -383,6 +390,7 @@ public class MonkeyRecorderFrame extends JFrame { refreshButton = new JButton(); refreshButton.setText("Refresh Display"); refreshButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { refreshDisplay(); } diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java index d582aa4..6fa91ab 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java @@ -15,7 +15,7 @@ */ package com.android.monkeyrunner.recorder.actions; -import com.android.monkeyrunner.MonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyDevice; /** * All actions that can be recorded must implement this interface. @@ -41,5 +41,5 @@ public interface Action { * * @param device the device to execute the action on. */ - void execute(MonkeyDevice device) throws Exception; + void execute(IMonkeyDevice device) throws Exception; } diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java index 082bfe4..2461c0d 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java @@ -15,7 +15,7 @@ */ package com.android.monkeyrunner.recorder.actions; -import com.android.monkeyrunner.MonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyDevice; /** * Action to drag the "finger" across the device. @@ -77,7 +77,7 @@ public class DragAction implements Action { } @Override - public void execute(MonkeyDevice device) { + public void execute(IMonkeyDevice device) { device.drag(startx, starty, endx, endy, steps, timeMs); } } diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java index a0d9e0e..66a933a 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java @@ -19,6 +19,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; import com.android.monkeyrunner.MonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyDevice; /** * Action to press a certain button. @@ -60,7 +61,7 @@ public class PressAction implements Action { } @Override - public void execute(MonkeyDevice device) { + public void execute(IMonkeyDevice device) { device.press(key, MonkeyDevice.TOUCH_NAME_TO_ENUM.get(downUpFlag)); } diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java index 4633edb..4e0ae2d 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java @@ -19,6 +19,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; import com.android.monkeyrunner.MonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyDevice; /** * Action to touch the touchscreen at a certain location. @@ -46,7 +47,7 @@ public class TouchAction implements Action { } @Override - public void execute(MonkeyDevice device) throws Exception { + public void execute(IMonkeyDevice device) throws Exception { device.touch(x, y, MonkeyDevice.TOUCH_NAME_TO_ENUM.get(direction)); } diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java index 1bfb9e9..78e90b0 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java @@ -15,7 +15,7 @@ */ package com.android.monkeyrunner.recorder.actions; -import com.android.monkeyrunner.MonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyDevice; /** * Action to type in a string on the device. @@ -34,12 +34,13 @@ public class TypeAction implements Action { @Override public String serialize() { - String pydict = PyDictUtilBuilder.newBuilder().add("message", whatToType).build(); + String pydict = PyDictUtilBuilder.newBuilder() + .add("message", whatToType).build(); return "TYPE|" + pydict; } @Override - public void execute(MonkeyDevice device) { + public void execute(IMonkeyDevice device) { device.type(whatToType); } } diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java index 9115f9a..bd2d421 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java +++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java @@ -15,7 +15,7 @@ */ package com.android.monkeyrunner.recorder.actions; -import com.android.monkeyrunner.MonkeyDevice; +import com.android.monkeyrunner.core.IMonkeyDevice; /** * Action that specifies to wait for a certain amount of time. @@ -27,19 +27,16 @@ public class WaitAction implements Action { this.howLongSeconds = howLongSeconds; } - @Override public String getDisplayName() { return String.format("Wait for %g seconds", this.howLongSeconds); } - @Override public String serialize() { String pydict = PyDictUtilBuilder.newBuilder().add("seconds", howLongSeconds).build(); return "WAIT|" + pydict; } - @Override - public void execute(MonkeyDevice device) throws Exception { + public void execute(IMonkeyDevice device) throws Exception { long ms = (long) (1000.0f * howLongSeconds); Thread.sleep(ms); } diff --git a/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java b/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java index c2fa5f7..b868bf1 100644 --- a/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java +++ b/monkeyrunner/src/com/android/monkeyrunner/stub/StubBackend.java @@ -15,18 +15,22 @@ */ package com.android.monkeyrunner.stub; -import com.android.monkeyrunner.MonkeyDevice; import com.android.monkeyrunner.MonkeyManager; -import com.android.monkeyrunner.MonkeyRunnerBackend; - -public class StubBackend implements MonkeyRunnerBackend { +import com.android.monkeyrunner.core.IMonkeyBackend; +import com.android.monkeyrunner.core.IMonkeyDevice; +public class StubBackend implements IMonkeyBackend { public MonkeyManager createManager(String address, int port) { // TODO Auto-generated method stub return null; } - public MonkeyDevice waitForConnection(long timeout, String deviceId) { + public IMonkeyDevice waitForConnection() { + // TODO Auto-generated method stub + return null; + } + + public IMonkeyDevice waitForConnection(long timeout, String deviceId) { // TODO Auto-generated method stub return null; } |