aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBill Napier <napier@google.com>2010-10-18 00:00:20 -0700
committerXavier Ducrohet <xav@android.com>2010-10-20 13:23:13 -0700
commit036a36246fccc1da6e523f292277d694d3645a18 (patch)
treea7b6ec29a0261f11d1325054e85b63470e8375aa
parenteb93a3b24ab6c9b96d63b87fa205678f39e9176a (diff)
downloadsdk-036a36246fccc1da6e523f292277d694d3645a18.zip
sdk-036a36246fccc1da6e523f292277d694d3645a18.tar.gz
sdk-036a36246fccc1da6e523f292277d694d3645a18.tar.bz2
Integrate 6db57208 into tools_r8. DO NOT MERGE.
Initial cut at MonkeyRecorder. MonkeyRecorder (and MonkeyPlayback) are a set of tools for using MonkeyRunner to record and playback actions. The current implementation is not very sophisticated, but it works. Please don't review yet. Needs a lot of style cleanup. Change-Id: I79ed1b548d295563a184af5fb945140f93586b17
-rw-r--r--monkeyrunner/scripts/monkey_playback.py70
-rw-r--r--monkeyrunner/scripts/monkey_recorder.py20
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java39
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java6
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java28
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/ActionListModel.java69
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java75
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java426
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java45
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java83
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java67
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PyDictUtilBuilder.java64
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java62
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java45
-rw-r--r--monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java46
15 files changed, 1108 insertions, 37 deletions
diff --git a/monkeyrunner/scripts/monkey_playback.py b/monkeyrunner/scripts/monkey_playback.py
new file mode 100644
index 0000000..45d801a
--- /dev/null
+++ b/monkeyrunner/scripts/monkey_playback.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env monkeyrunner
+# Copyright 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.
+
+import sys
+from com.android.monkeyrunner import MonkeyRunner
+
+# The format of the file we are parsing is very carfeully constructed.
+# Each line corresponds to a single command. The line is split into 2
+# parts with a | character. Text to the left of the pipe denotes
+# which command to run. The text to the right of the pipe is a python
+# dictionary (it can be evaled into existence) that specifies the
+# arguments for the command. In most cases, this directly maps to the
+# keyword argument dictionary that could be passed to the underlying
+# command.
+
+# Lookup table to map command strings to functions that implement that
+# command.
+CMD_MAP = {
+ 'TOUCH': lambda dev, arg: dev.touch(**arg),
+ 'DRAG': lambda dev, arg: dev.drag(**arg),
+ 'PRESS': lambda dev, arg: dev.press(**arg),
+ 'TYPE': lambda dev, arg: dev.type(**arg),
+ 'WAIT': lambda dev, arg: MonkeyRunner.sleep(**arg)
+ }
+
+# Process a single file for the specified device.
+def process_file(fp, device):
+ for line in fp:
+ (cmd, rest) = line.split('|')
+ try:
+ # Parse the pydict
+ rest = eval(rest)
+ except:
+ print 'unable to parse options'
+ continue
+
+ if cmd not in CMD_MAP:
+ print 'unknown command: ' + cmd
+ continue
+
+ CMD_MAP[cmd](device, rest)
+
+
+def main():
+ file = sys.argv[1]
+ fp = open(file, 'r')
+
+ device = MonkeyRunner.waitForConnection()
+
+ process_file(fp, device)
+ fp.close();
+
+
+if __name__ == '__main__':
+ main()
+
+
+
diff --git a/monkeyrunner/scripts/monkey_recorder.py b/monkeyrunner/scripts/monkey_recorder.py
new file mode 100644
index 0000000..29e6e50
--- /dev/null
+++ b/monkeyrunner/scripts/monkey_recorder.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env monkeyrunner
+# Copyright 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.
+
+from com.android.monkeyrunner import MonkeyRunner as mr
+from com.android.monkeyrunner.recorder import MonkeyRecorder as recorder
+
+device = mr.waitForConnection()
+recorder.start(device)
diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java
index 2d120f5..0f4362a 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyDevice.java
@@ -55,12 +55,11 @@ 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";
- // Visible to subclasses
- protected enum TouchPressType {
+ public enum TouchPressType {
DOWN, UP, DOWN_AND_UP,
}
- private static final Map<String, TouchPressType> TOUCH_NAME_TO_ENUM =
+ 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);
@@ -126,7 +125,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
// Default
String type = MonkeyDevice.DOWN_AND_UP;
try {
- String tmpType = ap.getString(2);
+ String tmpType = ap.getString(1);
if (VALID_DOWN_UP_TYPES.contains(tmpType)) {
type = tmpType;
} else {
@@ -189,7 +188,7 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
// Default
String type = MonkeyDevice.DOWN_AND_UP;
try {
- String tmpType = ap.getString(2);
+ String tmpType = ap.getString(1);
if (VALID_DOWN_UP_TYPES.contains(tmpType)) {
type = tmpType;
} else {
@@ -372,26 +371,26 @@ public abstract class MonkeyDevice extends PyObject implements ClassDictInit {
*
* @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,
+ 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);
- protected abstract void broadcastIntent(@Nullable String uri, @Nullable String action,
+ 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);
- protected abstract Map<String, Object> instrument(String packageName,
+ public abstract Map<String, Object> instrument(String packageName,
Map<String, Object> args);
- protected abstract void wake();
+ public abstract void wake();
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
index 648843d..8480223 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
@@ -181,7 +181,7 @@ public class MonkeyRunner extends PyObject implements ClassDictInit {
* @param title the title of the dialog box.
* @param okTitle the title of the button.
*/
- private static void alert(String message, String title, String okTitle) {
+ public static void alert(String message, String title, String okTitle) {
Object[] options = { okTitle };
JOptionPane.showOptionDialog(null, message, title, JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE, null, options, options[0]);
@@ -195,7 +195,7 @@ public class MonkeyRunner extends PyObject implements ClassDictInit {
* @param choices the list of the choices to display.
* @return the index of the selected choice, or -1 if nothing was chosen.
*/
- private static int choice(String message, String title, Collection<String> choices) {
+ public static int choice(String message, String title, Collection<String> choices) {
Object[] possibleValues = choices.toArray();
Object selectedValue = JOptionPane.showInputDialog(null, message, title,
JOptionPane.QUESTION_MESSAGE, null, possibleValues, possibleValues[0]);
@@ -217,7 +217,7 @@ public class MonkeyRunner extends PyObject implements ClassDictInit {
* @param title the title of the dialog box.
* @return the entered string, or null if cancelled
*/
- private static String input(String message, String initialValue, String title) {
+ public static String input(String message, String initialValue, String title) {
return (String) JOptionPane.showInputDialog(null, message, title,
JOptionPane.QUESTION_MESSAGE, null, null, initialValue);
}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
index b180ccd..7130019 100644
--- a/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
+++ b/monkeyrunner/src/com/android/monkeyrunner/adb/AdbMonkeyDevice.java
@@ -199,12 +199,12 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected String getSystemProperty(String key) {
+ public String getSystemProperty(String key) {
return device.getProperty(key);
}
@Override
- protected String getProperty(String key) {
+ public String getProperty(String key) {
try {
return manager.getVariable(key);
} catch (IOException e) {
@@ -214,7 +214,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void wake() {
+ public void wake() {
try {
manager.wake();
} catch (IOException e) {
@@ -231,7 +231,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected String shell(String cmd) {
+ public String shell(String cmd) {
CommandOutputCapture capture = new CommandOutputCapture();
try {
device.executeShellCommand(cmd, capture);
@@ -252,7 +252,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected boolean installPackage(String path) {
+ public boolean installPackage(String path) {
try {
String result = device.installPackage(path, true);
if (result != null) {
@@ -267,7 +267,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected boolean removePackage(String packageName) {
+ public boolean removePackage(String packageName) {
try {
String result = device.uninstallPackage(packageName);
if (result != null) {
@@ -283,7 +283,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void press(String keyName, TouchPressType type) {
+ public void press(String keyName, TouchPressType type) {
try {
switch (type) {
case DOWN_AND_UP:
@@ -302,7 +302,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void type(String string) {
+ public void type(String string) {
try {
manager.type(string);
} catch (IOException e) {
@@ -311,7 +311,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void touch(int x, int y, TouchPressType type) {
+ public void touch(int x, int y, TouchPressType type) {
try {
switch (type) {
case DOWN:
@@ -330,7 +330,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void reboot(String into) {
+ public void reboot(String into) {
try {
device.reboot(into);
} catch (TimeoutException e) {
@@ -343,7 +343,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void startActivity(String uri, String action, String data, String mimetype,
+ public void startActivity(String uri, String action, String data, String mimetype,
Collection<String> categories, Map<String, Object> extras, String component,
int flags) {
List<String> intentArgs = buildIntentArgString(uri, action, data, mimetype, categories,
@@ -353,7 +353,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void broadcastIntent(String uri, String action, String data, String mimetype,
+ public void broadcastIntent(String uri, String action, String data, String mimetype,
Collection<String> categories, Map<String, Object> extras, String component,
int flags) {
List<String> intentArgs = buildIntentArgString(uri, action, data, mimetype, categories,
@@ -442,7 +442,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected Map<String, Object> instrument(String packageName, Map<String, Object> args) {
+ public Map<String, Object> instrument(String packageName, Map<String, Object> args) {
List<String> shellCmd = Lists.newArrayList("am", "instrument", "-w", "-r", packageName);
String result = shell(shellCmd.toArray(ZERO_LENGTH_STRING_ARRAY));
return convertInstrumentResult(result);
@@ -490,7 +490,7 @@ public class AdbMonkeyDevice extends MonkeyDevice {
}
@Override
- protected void drag(int startx, int starty, int endx, int endy, int steps, long ms) {
+ public void drag(int startx, int starty, int endx, int endy, int steps, long ms) {
final long iterationTime = ms / steps;
LinearInterpolator lerp = new LinearInterpolator(steps);
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/ActionListModel.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/ActionListModel.java
new file mode 100644
index 0000000..5e0b7e7
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/ActionListModel.java
@@ -0,0 +1,69 @@
+/*
+ * 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.recorder;
+
+import com.google.common.collect.Lists;
+
+import com.android.monkeyrunner.recorder.actions.Action;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.List;
+
+import javax.swing.AbstractListModel;
+
+/**
+ * List model for managing actions.
+ */
+public class ActionListModel extends AbstractListModel {
+ private List<Action> actionList = Lists.newArrayList();
+
+ /**
+ * Add the specified action to the end of the list
+ * @param a the action to add.
+ */
+ public void add(Action a) {
+ actionList.add(a);
+ int newIndex = actionList.size() - 1;
+ this.fireIntervalAdded(this, newIndex, newIndex);
+ }
+
+ @Override
+ public Object getElementAt(int arg0) {
+ return actionList.get(arg0).getDisplayName();
+ }
+
+
+ @Override
+ public int getSize() {
+ return actionList.size();
+ }
+
+ /**
+ * Serialize all the stored actions to the specified file.
+ *
+ * @param selectedFile the file to write to
+ * @throws FileNotFoundException if the file can't be created.
+ */
+ public void export(File selectedFile) throws FileNotFoundException {
+ PrintWriter out = new PrintWriter(selectedFile);
+ for (Action a : actionList) {
+ out.println(a.serialize());
+ }
+ out.close();
+ }
+} \ No newline at end of file
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java
new file mode 100644
index 0000000..c1a8f7f
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorder.java
@@ -0,0 +1,75 @@
+/*
+ * 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.recorder;
+
+import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.adb.AdbBackend;
+
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.WindowConstants;
+
+/**
+ * Helper entry point for MonkeyRecorder.
+ */
+public class MonkeyRecorder {
+ private static final Logger LOG = Logger.getLogger(MonkeyRecorder.class.getName());
+ // This lock is used to keep the python process blocked while the frame is runing.
+ private static final Object LOCK = new Object();
+
+ /**
+ * Jython entry point for MonkeyRecorder. Meant to be called like this:
+ *
+ * <code>
+ * from com.android.monkeyrunner import MonkeyRunner as mr
+ * from com.android.monkeyrunner import MonkeyRecorder
+ * MonkeyRecorder.start(mr.waitForConnection())
+ * </code>
+ *
+ * @param device
+ */
+ public static void start(final MonkeyDevice device) {
+ MonkeyRecorderFrame frame = new MonkeyRecorderFrame(device);
+ // TODO: this is a hack until the window listener works.
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosed(WindowEvent e) {
+ device.dispose();
+ synchronized (LOCK) {
+ LOCK.notifyAll();
+ }
+ }
+ });
+
+ frame.setVisible(true);
+ synchronized (LOCK) {
+ try {
+ LOCK.wait();
+ } catch (InterruptedException e) {
+ LOG.log(Level.SEVERE, "Unexpected Exception", e);
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ AdbBackend 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
new file mode 100644
index 0000000..b6c1f78
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/MonkeyRecorderFrame.java
@@ -0,0 +1,426 @@
+/*
+ * 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.recorder;
+
+import com.android.monkeyrunner.MonkeyDevice;
+import com.android.monkeyrunner.MonkeyImage;
+import com.android.monkeyrunner.recorder.actions.Action;
+import com.android.monkeyrunner.recorder.actions.DragAction;
+import com.android.monkeyrunner.recorder.actions.DragAction.Direction;
+import com.android.monkeyrunner.recorder.actions.PressAction;
+import com.android.monkeyrunner.recorder.actions.TouchAction;
+import com.android.monkeyrunner.recorder.actions.TypeAction;
+import com.android.monkeyrunner.recorder.actions.WaitAction;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.image.BufferedImage;
+import java.io.FileNotFoundException;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+
+/**
+ * MainFrame for MonkeyRecorder.
+ */
+public class MonkeyRecorderFrame extends JFrame {
+ private static final Logger LOG =
+ Logger.getLogger(MonkeyRecorderFrame.class.getName());
+
+ private final MonkeyDevice device;
+
+ private static final long serialVersionUID = 1L;
+ private JPanel jContentPane = null;
+ private JLabel display = null;
+ private JScrollPane historyPanel = null;
+ private JPanel actionPanel = null;
+ private JButton waitButton = null;
+ private JButton pressButton = null;
+ private JButton typeButton = null;
+ private JButton flingButton = null;
+ private JButton exportActionButton = null;
+
+ private JButton refreshButton = null;
+
+ private BufferedImage currentImage; // @jve:decl-index=0:
+ private BufferedImage scaledImage = new BufferedImage(320, 480,
+ BufferedImage.TYPE_INT_ARGB); // @jve:decl-index=0:
+
+ private JList historyList;
+ private ActionListModel actionListModel;
+
+ private final Timer refreshTimer = new Timer(1000, new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ refreshDisplay(); // @jve:decl-index=0:
+ }
+ });
+
+ /**
+ * This is the default constructor
+ */
+ public MonkeyRecorderFrame(MonkeyDevice device) {
+ this.device = device;
+ initialize();
+ }
+
+ private void initialize() {
+ this.setSize(400, 600);
+ this.setContentPane(getJContentPane());
+ this.setTitle("MonkeyRecorder");
+
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ refreshDisplay();
+ }});
+ refreshTimer.start();
+ }
+
+ private void refreshDisplay() {
+ MonkeyImage snapshot = device.takeSnapshot();
+ currentImage = snapshot.createBufferedImage();
+
+ Graphics2D g = scaledImage.createGraphics();
+ g.drawImage(currentImage, 0, 0,
+ scaledImage.getWidth(), scaledImage.getHeight(),
+ null);
+ g.dispose();
+
+ display.setIcon(new ImageIcon(scaledImage));
+
+ pack();
+ }
+
+ /**
+ * This method initializes jContentPane
+ *
+ * @return javax.swing.JPanel
+ */
+ private JPanel getJContentPane() {
+ if (jContentPane == null) {
+ display = new JLabel();
+ jContentPane = new JPanel();
+ jContentPane.setLayout(new BorderLayout());
+ jContentPane.add(display, BorderLayout.CENTER);
+ jContentPane.add(getHistoryPanel(), BorderLayout.EAST);
+ jContentPane.add(getActionPanel(), BorderLayout.NORTH);
+
+ display.setPreferredSize(new Dimension(320, 480));
+
+ display.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent event) {
+ touch(event);
+ }
+ });
+ }
+ return jContentPane;
+ }
+
+ /**
+ * This method initializes historyPanel
+ *
+ * @return javax.swing.JScrollPane
+ */
+ private JScrollPane getHistoryPanel() {
+ if (historyPanel == null) {
+ historyPanel = new JScrollPane();
+ historyPanel.getViewport().setView(getHistoryList());
+ }
+ return historyPanel;
+ }
+
+ private JList getHistoryList() {
+ if (historyList == null) {
+ actionListModel = new ActionListModel();
+ historyList = new JList(actionListModel);
+ }
+ return historyList;
+ }
+
+ /**
+ * This method initializes actionPanel
+ *
+ * @return javax.swing.JPanel
+ */
+ private JPanel getActionPanel() {
+ if (actionPanel == null) {
+ actionPanel = new JPanel();
+ actionPanel.setLayout(new BoxLayout(getActionPanel(), BoxLayout.X_AXIS));
+ actionPanel.add(getWaitButton(), null);
+ actionPanel.add(getPressButton(), null);
+ actionPanel.add(getTypeButton(), null);
+ actionPanel.add(getFlingButton(), null);
+ actionPanel.add(getExportActionButton(), null);
+ actionPanel.add(getRefreshButton(), null);
+ }
+ return actionPanel;
+ }
+
+ /**
+ * This method initializes waitButton
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getWaitButton() {
+ if (waitButton == null) {
+ waitButton = new JButton();
+ waitButton.setText("Wait");
+ waitButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ String howLongStr = JOptionPane.showInputDialog("How many seconds to wait?");
+ if (howLongStr != null) {
+ float howLong = Float.parseFloat(howLongStr);
+ addAction(new WaitAction(howLong));
+ }
+ }
+ });
+ }
+ return waitButton;
+ }
+
+ /**
+ * This method initializes pressButton
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getPressButton() {
+ if (pressButton == null) {
+ pressButton = new JButton();
+ pressButton.setText("Press a Button");
+ pressButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ JPanel panel = new JPanel();
+ JLabel text = new JLabel("What button to press?");
+ JComboBox keys = new JComboBox(PressAction.KEYS);
+ keys.setEditable(true);
+ JComboBox direction = new JComboBox(PressAction.DOWNUP_FLAG_MAP.values().toArray());
+ panel.add(text);
+ panel.add(keys);
+ panel.add(direction);
+
+ int result = JOptionPane.showConfirmDialog(null, panel, "Input", JOptionPane.OK_CANCEL_OPTION);
+ if (result == JOptionPane.OK_OPTION) {
+ // Look up the "flag" value for the press choice
+ Map<String, String> lookupMap = PressAction.DOWNUP_FLAG_MAP.inverse();
+ String flag = lookupMap.get(direction.getSelectedItem());
+ addAction(new PressAction((String) keys.getSelectedItem(), flag));
+ }
+ }
+ });
+ }
+ return pressButton;
+ }
+
+ /**
+ * This method initializes typeButton
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getTypeButton() {
+ if (typeButton == null) {
+ typeButton = new JButton();
+ typeButton.setText("Type Something");
+ typeButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ String whatToType = JOptionPane.showInputDialog("What to type?");
+ if (whatToType != null) {
+ addAction(new TypeAction(whatToType));
+ }
+ }
+ });
+ }
+ return typeButton;
+ }
+
+ /**
+ * This method initializes flingButton
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getFlingButton() {
+ if (flingButton == null) {
+ flingButton = new JButton();
+ flingButton.setText("Fling");
+ flingButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.add(new JLabel("Which Direction to fling?"));
+ JComboBox directionChooser = new JComboBox(DragAction.Direction.getNames());
+ panel.add(directionChooser);
+ panel.add(new JLabel("How long to drag (in ms)?"));
+ JTextField ms = new JTextField();
+ ms.setText("1000");
+ panel.add(ms);
+ panel.add(new JLabel("How many steps to do it in?"));
+ JTextField steps = new JTextField();
+ steps.setText("10");
+ panel.add(steps);
+
+
+
+ int result = JOptionPane.showConfirmDialog(null, panel, "Input", JOptionPane.OK_CANCEL_OPTION);
+ if (result == JOptionPane.OK_OPTION) {
+ DragAction.Direction dir =
+ DragAction.Direction.valueOf((String) directionChooser.getSelectedItem());
+ long millis = Long.parseLong(ms.getText());
+ int numSteps = Integer.parseInt(steps.getText());
+
+ addAction(newFlingAction(dir, numSteps, millis));
+ }
+ }
+ });
+ }
+ return flingButton;
+ }
+
+ private DragAction newFlingAction(Direction dir, int numSteps, long millis) {
+ int width = Integer.parseInt(device.getProperty("display.width"));
+ int height = Integer.parseInt(device.getProperty("display.height"));
+
+ // Adjust the w/h to a pct of the total size, so we don't hit things on the "outside"
+ width = (int) (width * 0.8f);
+ height = (int) (height * 0.8f);
+ int minW = (int) (width * 0.2f);
+ int minH = (int) (height * 0.2f);
+
+ int midWidth = width / 2;
+ int midHeight = height / 2;
+
+ int startx = minW;
+ int starty = minH;
+ int endx = minW;
+ int endy = minH;
+
+ switch (dir) {
+ case NORTH:
+ startx = endx = midWidth;
+ starty = height;
+ break;
+ case SOUTH:
+ startx = endx = midWidth;
+ endy = height;
+ break;
+ case EAST:
+ starty = endy = midHeight;
+ endx = width;
+ break;
+ case WEST:
+ starty = endy = midHeight;
+ startx = width;
+ break;
+ }
+
+ return new DragAction(dir, startx, starty, endx, endy, numSteps, millis);
+ }
+
+ /**
+ * This method initializes exportActionButton
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getExportActionButton() {
+ if (exportActionButton == null) {
+ exportActionButton = new JButton();
+ exportActionButton.setText("Export Actions");
+ exportActionButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent ev) {
+ JFileChooser fc = new JFileChooser();
+ if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
+ try {
+ actionListModel.export(fc.getSelectedFile());
+ } catch (FileNotFoundException e) {
+ LOG.log(Level.SEVERE, "Unable to save file", e);
+ }
+ }
+ }
+ });
+ }
+ return exportActionButton;
+ }
+
+ /**
+ * This method initializes refreshButton
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getRefreshButton() {
+ if (refreshButton == null) {
+ refreshButton = new JButton();
+ refreshButton.setText("Refresh Display");
+ refreshButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ refreshDisplay();
+ }
+ });
+ }
+ return refreshButton;
+ }
+
+ private void touch(MouseEvent event) {
+ int x = event.getX();
+ int y = event.getY();
+
+ // Since we scaled the image down, our x/y are scaled as well.
+ double scalex = ((double) currentImage.getWidth()) / ((double) scaledImage.getWidth());
+ double scaley = ((double) currentImage.getHeight()) / ((double) scaledImage.getHeight());
+
+ x = (int) (x * scalex);
+ y = (int) (y * scaley);
+
+ switch (event.getID()) {
+ case MouseEvent.MOUSE_CLICKED:
+ addAction(new TouchAction(x, y, MonkeyDevice.DOWN_AND_UP));
+ break;
+ case MouseEvent.MOUSE_PRESSED:
+ addAction(new TouchAction(x, y, MonkeyDevice.DOWN));
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ addAction(new TouchAction(x, y, MonkeyDevice.UP));
+ break;
+ }
+ }
+
+ public void addAction(Action a) {
+ actionListModel.add(a);
+ try {
+ a.execute(device);
+ } catch (Exception e) {
+ LOG.log(Level.SEVERE, "Unable to execute action!", e);
+ }
+ }
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java
new file mode 100644
index 0000000..d582aa4
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/Action.java
@@ -0,0 +1,45 @@
+/*
+ * 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.recorder.actions;
+
+import com.android.monkeyrunner.MonkeyDevice;
+
+/**
+ * All actions that can be recorded must implement this interface.
+ */
+public interface Action {
+ /**
+ * Serialize this action into a string. This method is called to put the list of actions into
+ * a file.
+ *
+ * @return the serialized string
+ */
+ String serialize();
+
+ /**
+ * Get the printable name for this action. This method is used to show the Action in the UI.
+ *
+ * @return the display name
+ */
+ String getDisplayName();
+
+ /**
+ * Execute the given action.
+ *
+ * @param device the device to execute the action on.
+ */
+ void execute(MonkeyDevice 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
new file mode 100644
index 0000000..082bfe4
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/DragAction.java
@@ -0,0 +1,83 @@
+/*
+ * 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.recorder.actions;
+
+import com.android.monkeyrunner.MonkeyDevice;
+
+/**
+ * Action to drag the "finger" across the device.
+ */
+public class DragAction implements Action {
+ private final long timeMs;
+ private final int steps;
+ private final int startx;
+ private final int starty;
+ private final int endx;
+ private final int endy;
+ private final Direction dir;
+
+ public enum Direction {
+ NORTH, SOUTH, EAST, WEST;
+
+ private static String[] names;
+ static {
+ Direction[] values = Direction.values();
+ names = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ names[x] = values[x].name();
+ }
+ }
+
+ public static String[] getNames() {
+ return names;
+ }
+ }
+
+ public DragAction(Direction dir,
+ int startx, int starty, int endx, int endy,
+ int numSteps, long millis) {
+ this.dir = dir;
+ this.startx = startx;
+ this.starty = starty;
+ this.endx = endx;
+ this.endy = endy;
+ steps = numSteps;
+ timeMs = millis;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return String.format("Fling %s", dir.name().toLowerCase());
+ }
+
+ @Override
+ public String serialize() {
+ float duration = timeMs / 1000.0f;
+
+ String pydict = PyDictUtilBuilder.newBuilder().
+ addTuple("start", startx, starty).
+ addTuple("end", endx, endy).
+ add("duration", duration).
+ add("steps", steps).
+ build();
+ return "DRAG|" + pydict;
+ }
+
+ @Override
+ public void execute(MonkeyDevice 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
new file mode 100644
index 0000000..a0d9e0e
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PressAction.java
@@ -0,0 +1,67 @@
+/*
+ * 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.recorder.actions;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+
+import com.android.monkeyrunner.MonkeyDevice;
+
+/**
+ * Action to press a certain button.
+ */
+public class PressAction implements Action {
+ public static String[] KEYS = {
+ "MENU", "HOME", "SEARCH",
+ };
+
+ public static final BiMap<String, String> DOWNUP_FLAG_MAP =
+ ImmutableBiMap.of(MonkeyDevice.DOWN_AND_UP, "Press",
+ MonkeyDevice.DOWN, "Down",
+ MonkeyDevice.UP, "Up");
+
+ private final String key;
+ private final String downUpFlag;
+
+ public PressAction(String key, String downUpFlag) {
+ this.key = key;
+ this.downUpFlag = downUpFlag;
+ }
+
+ public PressAction(String key) {
+ this(key, MonkeyDevice.DOWN_AND_UP);
+ }
+
+ @Override
+ public String getDisplayName() {
+ return String.format("%s button %s",
+ DOWNUP_FLAG_MAP.get(downUpFlag), key);
+ }
+
+ @Override
+ public String serialize() {
+ String pydict = PyDictUtilBuilder.newBuilder().
+ add("name", key).
+ add("type", downUpFlag).build();
+ return "PRESS|" + pydict;
+ }
+
+ @Override
+ public void execute(MonkeyDevice device) {
+ device.press(key,
+ MonkeyDevice.TOUCH_NAME_TO_ENUM.get(downUpFlag));
+ }
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PyDictUtilBuilder.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PyDictUtilBuilder.java
new file mode 100644
index 0000000..0cfbabe
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/PyDictUtilBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * 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.recorder.actions;
+
+/**
+ * Utility class to create Python Dictionary Strings.
+ *
+ * {'key': 'value'}
+ */
+public class PyDictUtilBuilder {
+ private StringBuilder sb = new StringBuilder();
+
+ public PyDictUtilBuilder() {
+ sb.append("{");
+ }
+
+ public static PyDictUtilBuilder newBuilder() {
+ return new PyDictUtilBuilder();
+ }
+
+ private void addHelper(String key, String value) {
+ sb.append("'").append(key).append("'");
+ sb.append(":").append(value).append(",");
+ }
+
+ public PyDictUtilBuilder add(String key, int value) {
+ addHelper(key, Integer.toString(value));
+ return this;
+ }
+
+ public PyDictUtilBuilder add(String key, float value) {
+ addHelper(key, Float.toString(value));
+ return this;
+ }
+
+ public PyDictUtilBuilder add(String key, String value) {
+ addHelper(key, "'" + value + "'");
+ return this;
+ }
+
+ public String build() {
+ sb.append("}");
+ return sb.toString();
+ }
+
+ public PyDictUtilBuilder addTuple(String key, int x, int y) {
+ String valuestr = new StringBuilder().append("(").append(x).append(",").append(y).append(")").toString();
+ addHelper(key, valuestr);
+ return this;
+ }
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java
new file mode 100644
index 0000000..4633edb
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TouchAction.java
@@ -0,0 +1,62 @@
+/*
+ * 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.recorder.actions;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+
+import com.android.monkeyrunner.MonkeyDevice;
+
+/**
+ * Action to touch the touchscreen at a certain location.
+ */
+public class TouchAction implements Action {
+ public static final BiMap<String, String> DOWNUP_FLAG_MAP =
+ ImmutableBiMap.of(MonkeyDevice.DOWN_AND_UP, "Tap",
+ MonkeyDevice.DOWN, "Down",
+ MonkeyDevice.UP, "Up");
+
+ private final int x;
+ private final int y;
+ private final String direction;
+
+ public TouchAction(int x, int y, String direction) {
+ this.x = x;
+ this.y = y;
+ this.direction = direction;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return String.format("%s touchscreen at (%d, %d)",
+ DOWNUP_FLAG_MAP.get(direction), x, y);
+ }
+
+ @Override
+ public void execute(MonkeyDevice device) throws Exception {
+ device.touch(x, y,
+ MonkeyDevice.TOUCH_NAME_TO_ENUM.get(direction));
+ }
+
+ @Override
+ public String serialize() {
+ String pydict = PyDictUtilBuilder.newBuilder().
+ add("x", x).
+ add("y", y).
+ add("type", direction).build();
+ return "TOUCH|" + pydict;
+ }
+}
diff --git a/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java
new file mode 100644
index 0000000..1bfb9e9
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/TypeAction.java
@@ -0,0 +1,45 @@
+/*
+ * 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.recorder.actions;
+
+import com.android.monkeyrunner.MonkeyDevice;
+
+/**
+ * Action to type in a string on the device.
+ */
+public class TypeAction implements Action {
+ private final String whatToType;
+
+ public TypeAction(String whatToType) {
+ this.whatToType = whatToType;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return String.format("Type \"%s\"", whatToType);
+ }
+
+ @Override
+ public String serialize() {
+ String pydict = PyDictUtilBuilder.newBuilder().add("message", whatToType).build();
+ return "TYPE|" + pydict;
+ }
+
+ @Override
+ public void execute(MonkeyDevice 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
new file mode 100644
index 0000000..9115f9a
--- /dev/null
+++ b/monkeyrunner/src/com/android/monkeyrunner/recorder/actions/WaitAction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.recorder.actions;
+
+import com.android.monkeyrunner.MonkeyDevice;
+
+/**
+ * Action that specifies to wait for a certain amount of time.
+ */
+public class WaitAction implements Action {
+ private final float howLongSeconds;
+
+ public WaitAction(float howLongSeconds) {
+ 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 {
+ long ms = (long) (1000.0f * howLongSeconds);
+ Thread.sleep(ms);
+ }
+}