| /* |
| * Copyright (C) 2010 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; |
| |
| import com.google.common.base.Functions; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.Collections2; |
| |
| import com.android.monkeyrunner.doc.MonkeyRunnerExported; |
| |
| import org.python.core.ArgParser; |
| import org.python.core.Py; |
| import org.python.core.PyDictionary; |
| import org.python.core.PyException; |
| import org.python.core.PyObject; |
| import org.python.core.PyTuple; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Map; |
| |
| import javax.annotation.Nullable; |
| |
| /* |
| * Abstract base class that represents a single connected Android |
| * Device and provides MonkeyRunner API methods for interacting with |
| * that device. Each backend will need to create a concrete |
| * implementation of this class. |
| */ |
| public abstract class MonkeyDevice { |
| /** |
| * 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(); |
| |
| @MonkeyRunnerExported(doc = "Fetch the screenbuffer from the device and return it.", |
| returns = "The captured snapshot.") |
| public abstract MonkeyImage takeSnapshot(); |
| |
| @MonkeyRunnerExported(doc = "Get a MonkeyRunner property (like build.fingerprint)", |
| args = {"key"}, |
| argDocs = {"The key of the property to return"}, |
| returns = "The value of the property") |
| public String getProperty(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| return getProperty(ap.getString(0)); |
| } |
| |
| @MonkeyRunnerExported(doc = "Get a system property (returns the same value as getprop).", |
| args = {"key"}, |
| argDocs = {"The key of the property to return"}, |
| returns = "The value of the property") |
| public String getSystemProperty(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| return getSystemProperty(ap.getString(0)); |
| } |
| |
| @MonkeyRunnerExported(doc = "Enumeration of possible touch and press event types. This gets " + |
| "passed into a press or touch call to specify the event type.", |
| argDocs = {"Indicates the down part of a touch/press event", |
| "Indicates the up part of a touch/press event.", |
| "Indicates that the monkey should send a down event immediately " + |
| "followed by an up event"}) |
| public enum TouchPressType { |
| DOWN, UP, DOWN_AND_UP |
| } |
| |
| @MonkeyRunnerExported(doc = "Send a touch event at the specified location", |
| args = { "x", "y", "type" }, |
| argDocs = { "x coordinate", "y coordinate", "the type of touch event to send"}) |
| public void touch(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| int x = ap.getInt(0); |
| int y = ap.getInt(1); |
| |
| // Default |
| MonkeyDevice.TouchPressType type = MonkeyDevice.TouchPressType.DOWN_AND_UP; |
| try { |
| PyObject pyObject = ap.getPyObject(2); |
| type = (TouchPressType) pyObject.__tojava__(MonkeyDevice.TouchPressType.class); |
| } catch (PyException e) { |
| // bad stuff was passed in, just use the already specified default value |
| type = MonkeyDevice.TouchPressType.DOWN_AND_UP; |
| } |
| touch(x, y, type); |
| } |
| |
| @MonkeyRunnerExported(doc = "Simulate a drag on the screen.", |
| args = { "start", "end", "duration", "steps"}, |
| argDocs = { "The starting point for the drag (a tuple of x,y)", |
| "The end point for the drag (a tuple of x,y)", |
| "How long (in seconds) should the drag take (default is 1.0 seconds)", |
| "The number of steps to take when interpolating points. (default is 10)"}) |
| public void drag(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| PyObject startObject = ap.getPyObject(0); |
| if (!(startObject instanceof PyTuple)) { |
| throw Py.TypeError("Agrument 0 is not a tuple"); |
| } |
| PyObject endObject = ap.getPyObject(1); |
| if (!(endObject instanceof PyTuple)) { |
| throw Py.TypeError("Agrument 1 is not a tuple"); |
| } |
| |
| PyTuple start = (PyTuple) startObject; |
| PyTuple end = (PyTuple) endObject; |
| |
| int startx = (Integer) start.__getitem__(0).__tojava__(Integer.class); |
| int starty = (Integer) start.__getitem__(1).__tojava__(Integer.class); |
| int endx = (Integer) end.__getitem__(0).__tojava__(Integer.class); |
| int endy = (Integer) end.__getitem__(1).__tojava__(Integer.class); |
| |
| double seconds = JythonUtils.getFloat(ap, 2, 1.0); |
| long ms = (long) (seconds * 1000.0); |
| |
| int steps = ap.getInt(3, 10); |
| |
| drag(startx, starty, endx, endy, steps, ms); |
| } |
| |
| @MonkeyRunnerExported(doc = "Send a key press event to the specified button", |
| args = { "name", "type" }, |
| argDocs = { "the name of the key to press", "the type of touch event to send"}) |
| public void press(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String name = ap.getString(0); |
| |
| // Default |
| MonkeyDevice.TouchPressType type = MonkeyDevice.TouchPressType.DOWN_AND_UP; |
| try { |
| PyObject pyObject = ap.getPyObject(1); |
| type = (TouchPressType) pyObject.__tojava__(MonkeyDevice.TouchPressType.class); |
| } catch (PyException e) { |
| // bad stuff was passed in, just use the already specified default value |
| type = MonkeyDevice.TouchPressType.DOWN_AND_UP; |
| } |
| press(name, type); |
| } |
| |
| @MonkeyRunnerExported(doc = "Type the specified string on the keyboard.", |
| args = { "message" }, |
| argDocs = { "the message to type." }) |
| public void type(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String message = ap.getString(0); |
| type(message); |
| } |
| |
| @MonkeyRunnerExported(doc = "Execute the given command on the shell.", |
| args = { "cmd"}, |
| argDocs = { "The command to execute" }, |
| returns = "The output of the command") |
| public String shell(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String cmd = ap.getString(0); |
| return shell(cmd); |
| } |
| |
| @MonkeyRunnerExported(doc = "Reboot the specified device", |
| args = { "into" }, |
| argDocs = { "the bootloader to reboot into (bootloader, recovery, or None)"}) |
| public void reboot(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String into = ap.getString(0, null); |
| |
| reboot(into); |
| } |
| |
| @MonkeyRunnerExported(doc = "Install the specified apk onto the device.", |
| args = { "path" }, |
| argDocs = { "The path on the host filesystem to the APK to install." }, |
| returns = "True if install succeeded") |
| public boolean installPackage(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String path = ap.getString(0); |
| return installPackage(path); |
| } |
| |
| @MonkeyRunnerExported(doc = "Remove the specified package from the device.", |
| args = { "package"}, |
| argDocs = { "The name of the package to uninstall"}, |
| returns = "'True if remove succeeded") |
| public boolean removePackage(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String packageName = ap.getString(0); |
| return removePackage(packageName); |
| } |
| |
| @MonkeyRunnerExported(doc = "Start the Activity specified by the intent.", |
| args = { "uri", "action", "data", "mimetype", "categories", "extras", |
| "component", "flags" }, |
| argDocs = { "The URI for the intent", |
| "The action for the intent", |
| "The data URI for the intent", |
| "The mime type for the intent", |
| "The list of category names for the intent", |
| "A dictionary of extras to add to the intent. Types of these extras " + |
| "are inferred from the python types of the values", |
| "The component of the intent", |
| "A list of flags for the intent" }) |
| public void startActivity(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String uri = ap.getString(0, null); |
| String action = ap.getString(1, null); |
| String data = ap.getString(2, null); |
| String mimetype = ap.getString(3, null); |
| Collection<String> categories = Collections2.transform(JythonUtils.getList(ap, 4), |
| Functions.toStringFunction()); |
| Map<String, Object> extras = JythonUtils.getMap(ap, 5); |
| String component = ap.getString(6, null); |
| int flags = ap.getInt(7, 0); |
| |
| startActivity(uri, action, data, mimetype, categories, extras, component, flags); |
| } |
| |
| @MonkeyRunnerExported(doc = "Start the specified broadcast intent on the device.", |
| args = { "uri", "action", "data", "mimetype", "categories", "extras", |
| "component", "flags" }, |
| argDocs = { "The URI for the intent", |
| "The action for the intent", |
| "The data URI for the intent", |
| "The mime type for the intent", |
| "The list of category names for the intent", |
| "A dictionary of extras to add to the intent. Types of these extras " + |
| "are inferred from the python types of the values", |
| "The component of the intent", |
| "A list of flags for the intent" }) |
| public void broadcastIntent(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String uri = ap.getString(0, null); |
| String action = ap.getString(1, null); |
| String data = ap.getString(2, null); |
| String mimetype = ap.getString(3, null); |
| Collection<String> categories = Collections2.transform(JythonUtils.getList(ap, 4), |
| Functions.toStringFunction()); |
| Map<String, Object> extras = JythonUtils.getMap(ap, 5); |
| String component = ap.getString(6, null); |
| int flags = ap.getInt(7, 0); |
| |
| broadcastIntent(uri, action, data, mimetype, categories, extras, component, flags); |
| } |
| |
| @MonkeyRunnerExported(doc = "Instrument the specified package and return the results from it.", |
| args = { "className", "args" }, |
| argDocs = { "The class name to instrument (like com.android.test/.TestInstrument)", |
| "A Map of String to Objects for the aruments to pass to this " + |
| "instrumentation (default value is None)" }, |
| returns = "A map of string to objects for the results this instrumentation returned") |
| public PyDictionary instrument(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| String packageName = ap.getString(0); |
| Map<String, Object> instrumentArgs = JythonUtils.getMap(ap, 1); |
| if (instrumentArgs == null) { |
| instrumentArgs = Collections.emptyMap(); |
| } |
| |
| Map<String, Object> result = instrument(packageName, instrumentArgs); |
| return JythonUtils.convertMapToDict(result); |
| } |
| |
| @MonkeyRunnerExported(doc = "Wake up the screen on the device") |
| public void wake(PyObject[] args, String[] kws) { |
| ArgParser ap = JythonUtils.createArgParser(args, kws); |
| Preconditions.checkNotNull(ap); |
| |
| wake(); |
| } |
| |
| /** |
| * Reboot the device. |
| * |
| * @param into which bootloader to boot into. Null means default reboot. |
| */ |
| protected abstract void reboot(@Nullable String into); |
| |
| protected abstract String getProperty(String key); |
| protected abstract String getSystemProperty(String key); |
| protected abstract void touch(int x, int y, TouchPressType type); |
| protected abstract void press(String keyName, TouchPressType type); |
| protected abstract void drag(int startx, int starty, int endx, int endy, int steps, long ms); |
| protected abstract void type(String string); |
| protected abstract String shell(String cmd); |
| protected abstract boolean installPackage(String path); |
| protected abstract boolean removePackage(String packageName); |
| protected 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); |
| protected 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); |
| protected abstract Map<String, Object> instrument(String packageName, |
| Map<String, Object> args); |
| protected abstract void wake(); |
| } |