Home | History | Annotate | Download | only in recorder
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.monkeyrunner.recorder;
     17 
     18 import com.android.monkeyrunner.MonkeyDevice;
     19 import com.android.chimpchat.core.IChimpImage;
     20 import com.android.chimpchat.core.IChimpDevice;
     21 import com.android.monkeyrunner.recorder.actions.Action;
     22 import com.android.monkeyrunner.recorder.actions.DragAction;
     23 import com.android.monkeyrunner.recorder.actions.DragAction.Direction;
     24 import com.android.monkeyrunner.recorder.actions.PressAction;
     25 import com.android.monkeyrunner.recorder.actions.TouchAction;
     26 import com.android.monkeyrunner.recorder.actions.TypeAction;
     27 import com.android.monkeyrunner.recorder.actions.WaitAction;
     28 
     29 import java.awt.BorderLayout;
     30 import java.awt.Dimension;
     31 import java.awt.Graphics2D;
     32 import java.awt.event.ActionEvent;
     33 import java.awt.event.ActionListener;
     34 import java.awt.event.MouseAdapter;
     35 import java.awt.event.MouseEvent;
     36 import java.awt.image.BufferedImage;
     37 import java.io.FileNotFoundException;
     38 import java.util.Map;
     39 import java.util.logging.Level;
     40 import java.util.logging.Logger;
     41 
     42 import javax.swing.BoxLayout;
     43 import javax.swing.ImageIcon;
     44 import javax.swing.JButton;
     45 import javax.swing.JComboBox;
     46 import javax.swing.JFileChooser;
     47 import javax.swing.JFrame;
     48 import javax.swing.JLabel;
     49 import javax.swing.JList;
     50 import javax.swing.JOptionPane;
     51 import javax.swing.JPanel;
     52 import javax.swing.JScrollPane;
     53 import javax.swing.JTextField;
     54 import javax.swing.SwingUtilities;
     55 import javax.swing.Timer;
     56 
     57 /**
     58  * MainFrame for MonkeyRecorder.
     59  */
     60 public class MonkeyRecorderFrame extends JFrame {
     61     private static final Logger LOG =
     62         Logger.getLogger(MonkeyRecorderFrame.class.getName());
     63 
     64     private final IChimpDevice device;
     65 
     66     private static final long serialVersionUID = 1L;
     67     private JPanel jContentPane = null;
     68     private JLabel display = null;
     69     private JScrollPane historyPanel = null;
     70     private JPanel actionPanel = null;
     71     private JButton waitButton = null;
     72     private JButton pressButton = null;
     73     private JButton typeButton = null;
     74     private JButton flingButton = null;
     75     private JButton exportActionButton = null;
     76 
     77     private JButton refreshButton = null;
     78 
     79     private BufferedImage currentImage;  //  @jve:decl-index=0:
     80     private BufferedImage scaledImage = new BufferedImage(320, 480,
     81             BufferedImage.TYPE_INT_ARGB);  //  @jve:decl-index=0:
     82 
     83     private JList historyList;
     84     private ActionListModel actionListModel;
     85 
     86     private final Timer refreshTimer = new Timer(1000, new ActionListener() {
     87         @Override
     88         public void actionPerformed(ActionEvent e) {
     89             refreshDisplay();  //  @jve:decl-index=0:
     90         }
     91     });
     92 
     93     /**
     94      * This is the default constructor
     95      */
     96     public MonkeyRecorderFrame(IChimpDevice device) {
     97         this.device = device;
     98         initialize();
     99     }
    100 
    101     private void initialize() {
    102         this.setSize(400, 600);
    103         this.setContentPane(getJContentPane());
    104         this.setTitle("MonkeyRecorder");
    105 
    106         SwingUtilities.invokeLater(new Runnable() {
    107             @Override
    108             public void run() {
    109                 refreshDisplay();
    110             }});
    111         refreshTimer.start();
    112     }
    113 
    114     private void refreshDisplay() {
    115         IChimpImage snapshot = device.takeSnapshot();
    116         currentImage = snapshot.createBufferedImage();
    117 
    118         Graphics2D g = scaledImage.createGraphics();
    119         g.drawImage(currentImage, 0, 0,
    120                 scaledImage.getWidth(), scaledImage.getHeight(),
    121                 null);
    122         g.dispose();
    123 
    124         display.setIcon(new ImageIcon(scaledImage));
    125 
    126         pack();
    127     }
    128 
    129     /**
    130      * This method initializes jContentPane
    131      *
    132      * @return javax.swing.JPanel
    133      */
    134     private JPanel getJContentPane() {
    135         if (jContentPane == null) {
    136             display = new JLabel();
    137             jContentPane = new JPanel();
    138             jContentPane.setLayout(new BorderLayout());
    139             jContentPane.add(display, BorderLayout.CENTER);
    140             jContentPane.add(getHistoryPanel(), BorderLayout.EAST);
    141             jContentPane.add(getActionPanel(), BorderLayout.NORTH);
    142 
    143             display.setPreferredSize(new Dimension(320, 480));
    144 
    145             display.addMouseListener(new MouseAdapter() {
    146                 @Override
    147                 public void mouseClicked(MouseEvent event) {
    148                     touch(event);
    149                 }
    150             });
    151         }
    152         return jContentPane;
    153     }
    154 
    155     /**
    156      * This method initializes historyPanel
    157      *
    158      * @return javax.swing.JScrollPane
    159      */
    160     private JScrollPane getHistoryPanel() {
    161         if (historyPanel == null) {
    162             historyPanel = new JScrollPane();
    163             historyPanel.getViewport().setView(getHistoryList());
    164         }
    165         return historyPanel;
    166     }
    167 
    168     private JList getHistoryList() {
    169         if (historyList == null) {
    170             actionListModel = new ActionListModel();
    171             historyList = new JList(actionListModel);
    172         }
    173         return historyList;
    174     }
    175 
    176     /**
    177      * This method initializes actionPanel
    178      *
    179      * @return javax.swing.JPanel
    180      */
    181     private JPanel getActionPanel() {
    182         if (actionPanel == null) {
    183             actionPanel = new JPanel();
    184             actionPanel.setLayout(new BoxLayout(getActionPanel(), BoxLayout.X_AXIS));
    185             actionPanel.add(getWaitButton(), null);
    186             actionPanel.add(getPressButton(), null);
    187             actionPanel.add(getTypeButton(), null);
    188             actionPanel.add(getFlingButton(), null);
    189             actionPanel.add(getExportActionButton(), null);
    190             actionPanel.add(getRefreshButton(), null);
    191         }
    192         return actionPanel;
    193     }
    194 
    195     /**
    196      * This method initializes waitButton
    197      *
    198      * @return javax.swing.JButton
    199      */
    200     private JButton getWaitButton() {
    201         if (waitButton == null) {
    202             waitButton = new JButton();
    203             waitButton.setText("Wait");
    204             waitButton.addActionListener(new java.awt.event.ActionListener() {
    205                 @Override
    206                 public void actionPerformed(java.awt.event.ActionEvent e) {
    207                     String howLongStr = JOptionPane.showInputDialog("How many seconds to wait?");
    208                     if (howLongStr != null) {
    209                         float howLong = Float.parseFloat(howLongStr);
    210                         addAction(new WaitAction(howLong));
    211                     }
    212                 }
    213             });
    214         }
    215         return waitButton;
    216     }
    217 
    218     /**
    219      * This method initializes pressButton
    220      *
    221      * @return javax.swing.JButton
    222      */
    223     private JButton getPressButton() {
    224         if (pressButton == null) {
    225             pressButton = new JButton();
    226             pressButton.setText("Press a Button");
    227             pressButton.addActionListener(new java.awt.event.ActionListener() {
    228                 @Override
    229                 public void actionPerformed(java.awt.event.ActionEvent e) {
    230                     JPanel panel = new JPanel();
    231                     JLabel text = new JLabel("What button to press?");
    232                     JComboBox keys = new JComboBox(PressAction.KEYS);
    233                     keys.setEditable(true);
    234                     JComboBox direction = new JComboBox(PressAction.DOWNUP_FLAG_MAP.values().toArray());
    235                     panel.add(text);
    236                     panel.add(keys);
    237                     panel.add(direction);
    238 
    239                     int result = JOptionPane.showConfirmDialog(null, panel, "Input", JOptionPane.OK_CANCEL_OPTION);
    240                     if (result == JOptionPane.OK_OPTION) {
    241                         // Look up the "flag" value for the press choice
    242                         Map<String, String> lookupMap = PressAction.DOWNUP_FLAG_MAP.inverse();
    243                         String flag = lookupMap.get(direction.getSelectedItem());
    244                         addAction(new PressAction((String) keys.getSelectedItem(), flag));
    245                     }
    246                 }
    247             });
    248         }
    249         return pressButton;
    250     }
    251 
    252     /**
    253      * This method initializes typeButton
    254      *
    255      * @return javax.swing.JButton
    256      */
    257     private JButton getTypeButton() {
    258         if (typeButton == null) {
    259             typeButton = new JButton();
    260             typeButton.setText("Type Something");
    261             typeButton.addActionListener(new java.awt.event.ActionListener() {
    262                 @Override
    263                 public void actionPerformed(java.awt.event.ActionEvent e) {
    264                     String whatToType = JOptionPane.showInputDialog("What to type?");
    265                     if (whatToType != null) {
    266                         addAction(new TypeAction(whatToType));
    267                     }
    268                 }
    269             });
    270         }
    271         return typeButton;
    272     }
    273 
    274     /**
    275      * This method initializes flingButton
    276      *
    277      * @return javax.swing.JButton
    278      */
    279     private JButton getFlingButton() {
    280         if (flingButton == null) {
    281             flingButton = new JButton();
    282             flingButton.setText("Fling");
    283             flingButton.addActionListener(new java.awt.event.ActionListener() {
    284                 @Override
    285                 public void actionPerformed(java.awt.event.ActionEvent e) {
    286                     JPanel panel = new JPanel();
    287                     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    288                     panel.add(new JLabel("Which Direction to fling?"));
    289                     JComboBox directionChooser = new JComboBox(DragAction.Direction.getNames());
    290                     panel.add(directionChooser);
    291                     panel.add(new JLabel("How long to drag (in ms)?"));
    292                     JTextField ms = new JTextField();
    293                     ms.setText("1000");
    294                     panel.add(ms);
    295                     panel.add(new JLabel("How many steps to do it in?"));
    296                     JTextField steps = new JTextField();
    297                     steps.setText("10");
    298                     panel.add(steps);
    299 
    300 
    301 
    302                     int result = JOptionPane.showConfirmDialog(null, panel, "Input", JOptionPane.OK_CANCEL_OPTION);
    303                     if (result == JOptionPane.OK_OPTION) {
    304                         DragAction.Direction dir =
    305                             DragAction.Direction.valueOf((String) directionChooser.getSelectedItem());
    306                         long millis = Long.parseLong(ms.getText());
    307                         int numSteps = Integer.parseInt(steps.getText());
    308 
    309                         addAction(newFlingAction(dir, numSteps, millis));
    310                     }
    311                 }
    312             });
    313         }
    314         return flingButton;
    315     }
    316 
    317     private DragAction newFlingAction(Direction dir, int numSteps, long millis) {
    318         int width = Integer.parseInt(device.getProperty("display.width"));
    319         int height = Integer.parseInt(device.getProperty("display.height"));
    320 
    321         // Adjust the w/h to a pct of the total size, so we don't hit things on the "outside"
    322         width = (int) (width * 0.8f);
    323         height = (int) (height * 0.8f);
    324         int minW = (int) (width * 0.2f);
    325         int minH = (int) (height * 0.2f);
    326 
    327         int midWidth = width / 2;
    328         int midHeight = height / 2;
    329 
    330         int startx = minW;
    331         int starty = minH;
    332         int endx = minW;
    333         int endy = minH;
    334 
    335         switch (dir) {
    336             case NORTH:
    337                 startx = endx = midWidth;
    338                 starty = height;
    339                 break;
    340             case SOUTH:
    341                 startx = endx = midWidth;
    342                 endy = height;
    343                 break;
    344             case EAST:
    345                 starty = endy = midHeight;
    346                 endx = width;
    347                 break;
    348             case WEST:
    349                 starty = endy = midHeight;
    350                 startx = width;
    351                 break;
    352         }
    353 
    354         return new DragAction(dir, startx, starty, endx, endy, numSteps, millis);
    355     }
    356 
    357     /**
    358      * This method initializes exportActionButton
    359      *
    360      * @return javax.swing.JButton
    361      */
    362     private JButton getExportActionButton() {
    363         if (exportActionButton == null) {
    364             exportActionButton = new JButton();
    365             exportActionButton.setText("Export Actions");
    366             exportActionButton.addActionListener(new java.awt.event.ActionListener() {
    367                 @Override
    368                 public void actionPerformed(java.awt.event.ActionEvent ev) {
    369                     JFileChooser fc = new JFileChooser();
    370                     if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
    371                         try {
    372                             actionListModel.export(fc.getSelectedFile());
    373                         } catch (FileNotFoundException e) {
    374                             LOG.log(Level.SEVERE, "Unable to save file", e);
    375                         }
    376                     }
    377                 }
    378             });
    379         }
    380         return exportActionButton;
    381     }
    382 
    383     /**
    384      * This method initializes refreshButton
    385      *
    386      * @return javax.swing.JButton
    387      */
    388     private JButton getRefreshButton() {
    389         if (refreshButton == null) {
    390             refreshButton = new JButton();
    391             refreshButton.setText("Refresh Display");
    392             refreshButton.addActionListener(new java.awt.event.ActionListener() {
    393                 @Override
    394                 public void actionPerformed(java.awt.event.ActionEvent e) {
    395                     refreshDisplay();
    396                 }
    397             });
    398         }
    399         return refreshButton;
    400     }
    401 
    402     private void touch(MouseEvent event) {
    403         int x = event.getX();
    404         int y = event.getY();
    405 
    406         // Since we scaled the image down, our x/y are scaled as well.
    407         double scalex = ((double) currentImage.getWidth()) / ((double) scaledImage.getWidth());
    408         double scaley = ((double) currentImage.getHeight()) / ((double) scaledImage.getHeight());
    409 
    410         x = (int) (x * scalex);
    411         y = (int) (y * scaley);
    412 
    413         switch (event.getID()) {
    414             case MouseEvent.MOUSE_CLICKED:
    415                 addAction(new TouchAction(x, y, MonkeyDevice.DOWN_AND_UP));
    416                 break;
    417             case MouseEvent.MOUSE_PRESSED:
    418                 addAction(new TouchAction(x, y, MonkeyDevice.DOWN));
    419                 break;
    420             case MouseEvent.MOUSE_RELEASED:
    421                 addAction(new TouchAction(x, y, MonkeyDevice.UP));
    422                 break;
    423         }
    424     }
    425 
    426     public void addAction(Action a) {
    427         actionListModel.add(a);
    428         try {
    429             a.execute(device);
    430         } catch (Exception e) {
    431             LOG.log(Level.SEVERE, "Unable to execute action!", e);
    432         }
    433     }
    434 }
    435