/*
* 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);
}
}
}