tags:

views:

1356

answers:

5

Hello,

I've discovered the Robot class today and wanted to use it to do some funny scripts...

I want to convert a String into KeyEvent to do something like this :

writeKeyboard(myBot,"abcd");

public void writeKeyboard(Robot bot, String st){
     char[] arr = arr.toCharArray();
     int i = arr.length();
     int j = 0;
     int keycode;
     while (j<i) {
         keycode = arr[j].something;
         bot.keyPress(keycode);
         bot.keyRelease(keycode);
         j++;
     }
}

Thank you

+2  A: 

It's a bit of a Kludge, but you can use the Command pattern to encapsulate the keystrokes for each character in the String, then get the Command for each character in turn and invoke the method. The advantage of this is that you only have to set up the map once. The disadvantage is it still involves a bunch of bolierplate:

public interface Command {
    void pressKey(Robot bot);
}

//add a command to the map for each keystroke
commandMap.put("A", new Command() {
    void pressKey(Robot bot) {
        pressWithShift(KeyEvent.VK_A);
    }
});
commandMap.put("a", new Command() {
    void pressKey(Robot bot) {
        press(KeyEvent.VK_A);
    }
});
commandMap.put("B", new Command() {
    void pressKey(Robot bot) {
        pressWithShift(KeyEvent.VK_B);
    }
});
...
//loads more definitions here

//helper methods
private void pressWithShift(Robot bot, KeyEvent event) P
    bot.keyPress(KeyEvent.VK_SHIFT);
    press(bot, event);
    bot.keyRelase(KeyEvent.VK_SHIFT);
}

private void press(Robot bot, KeyEvent event) P
    bot.keyPress(event);
    bot.keyRelease(event);
}

Then to use the Map:

for (int i = 0; i < st.length(); i++) {
    String subString = st.substring(i, i + 1);

    commandMap.get(subString).pressKey(bot);
}
Rich Seller
+1 this is the right approach but I think it needs too much boilderplate code
dfa
@dfa I do too, really this should all be done by some RobotHelper in my opinion. What would you do to reduce the boilerplate?
Rich Seller
please check my answer
dfa
+2  A: 

I'm basically using the Command pattern as Rich Seller does in his answer, with two minor modifications for the brevity's sake:

  • use of the Decoration pattern for reusing instances of a-z command
  • use of reflection to remove KeyEvent.VK_???

Command interface:

interface Command {

    void pressKey(Robot robot);
}

and the decorator (for modification #1):

class ShiftCommand implements Command {

    private final Command command;

    public ShiftCommand(Command command) {
        this.command = command;
    }

    public void pressKey(Robot robot) {
        robot.keyPress(KeyEvent.VK_SHIFT);
        command.pressKey(robot);
        robot.keyRelease(KeyEvent.VK_SHIFT);
    }

    @Override
    public String toString() {
        return "SHIFT + " + command.toString();
    }
}

using this helper (for modification #2):

public static int getKeyEvent(Character c) {
    Field f = KeyEvent.class.getField("VK_" + Character.toUpperCase(c));
    f.setAccessible(true);
    return (Integer) f.get(null);
}

BEWARE: I'm not handling exceptions here, this is left as exercise for you :))

then populating the command using a for loop:

Map<Character, Command> commandMap = new HashMap<Character, Command>();

    for (int i = 'a'; i <= 'z'; i++) {
        final Character c = Character.valueOf((char) i);
        Command pressKeyCommand = new Command() {

            public void pressKey(Robot robot) {
                int keyEventCode = getKeyEvent(c);
                robot.keyPress(c);
                robot.keyRelease(c);
            }

            @Override
            public String toString() {
                return String.format("%c", c);
            }
        };

        // 'a' .. 'z'
        commandMap.put(c, pressKeyCommand);
        // 'A' .. 'Z' by decorating pressKeyCommand
        commandMap.put(Character.toUpperCase(c), new ShiftCommand(pressKeyCommand));
    }

TestCase

String test = "aaaBBB";
for (int i = 0; i < test.length(); i++) {
     System.out.println(commandMap.get(test.charAt(i)));
}

as expected this outputs:

a
a
a
SHIFT + b
SHIFT + b
SHIFT + b
dfa
how does this handle non-alpha characters?
Rich Seller
by simply adding new commands?
dfa
wasn't trying to be critical - I like the approach, but it's probably worth pointing out that you need to handle non-alpha keys in a similar manner to my answer
Rich Seller
+4  A: 

I'm basically using a glorified switch statement. Simple and fast:

public class Keyboard {

    private Robot robot;

    public static void main(String... args) throws Exception {
     Keyboard keyboard = new Keyboard();
     keyboard.type("Hello there, how are you?");
    }

    public Keyboard() throws AWTException {
     this.robot = new Robot();
    }

    public Keyboard(Robot robot) {
     this.robot = robot;
    }

    public void type(CharSequence characters) {
     int length = characters.length();
     for (int i = 0; i < length; i++) {
      char character = characters.charAt(i);
      type(character);
     }
    }

    public void type(char character) {
     switch (character) {
     case 'a': doType(VK_A); break;
     case 'b': doType(VK_B); break;
     case 'c': doType(VK_C); break;
     case 'd': doType(VK_D); break;
     case 'e': doType(VK_E); break;
     case 'f': doType(VK_F); break;
     case 'g': doType(VK_G); break;
     case 'h': doType(VK_H); break;
     case 'i': doType(VK_I); break;
     case 'j': doType(VK_J); break;
     case 'k': doType(VK_K); break;
     case 'l': doType(VK_L); break;
     case 'm': doType(VK_M); break;
     case 'n': doType(VK_N); break;
     case 'o': doType(VK_O); break;
     case 'p': doType(VK_P); break;
     case 'q': doType(VK_Q); break;
     case 'r': doType(VK_R); break;
     case 's': doType(VK_S); break;
     case 't': doType(VK_T); break;
     case 'u': doType(VK_U); break;
     case 'v': doType(VK_V); break;
     case 'w': doType(VK_W); break;
     case 'x': doType(VK_X); break;
     case 'y': doType(VK_Y); break;
     case 'z': doType(VK_Z); break;
     case 'A': doType(VK_SHIFT, VK_A); break;
     case 'B': doType(VK_SHIFT, VK_B); break;
     case 'C': doType(VK_SHIFT, VK_C); break;
     case 'D': doType(VK_SHIFT, VK_D); break;
     case 'E': doType(VK_SHIFT, VK_E); break;
     case 'F': doType(VK_SHIFT, VK_F); break;
     case 'G': doType(VK_SHIFT, VK_G); break;
     case 'H': doType(VK_SHIFT, VK_H); break;
     case 'I': doType(VK_SHIFT, VK_I); break;
     case 'J': doType(VK_SHIFT, VK_J); break;
     case 'K': doType(VK_SHIFT, VK_K); break;
     case 'L': doType(VK_SHIFT, VK_L); break;
     case 'M': doType(VK_SHIFT, VK_M); break;
     case 'N': doType(VK_SHIFT, VK_N); break;
     case 'O': doType(VK_SHIFT, VK_O); break;
     case 'P': doType(VK_SHIFT, VK_P); break;
     case 'Q': doType(VK_SHIFT, VK_Q); break;
     case 'R': doType(VK_SHIFT, VK_R); break;
     case 'S': doType(VK_SHIFT, VK_S); break;
     case 'T': doType(VK_SHIFT, VK_T); break;
     case 'U': doType(VK_SHIFT, VK_U); break;
     case 'V': doType(VK_SHIFT, VK_V); break;
     case 'W': doType(VK_SHIFT, VK_W); break;
     case 'X': doType(VK_SHIFT, VK_X); break;
     case 'Y': doType(VK_SHIFT, VK_Y); break;
     case 'Z': doType(VK_SHIFT, VK_Z); break;
     case '`': doType(VK_BACK_QUOTE); break;
     case '0': doType(VK_0); break;
     case '1': doType(VK_1); break;
     case '2': doType(VK_2); break;
     case '3': doType(VK_3); break;
     case '4': doType(VK_4); break;
     case '5': doType(VK_5); break;
     case '6': doType(VK_6); break;
     case '7': doType(VK_7); break;
     case '8': doType(VK_8); break;
     case '9': doType(VK_9); break;
     case '-': doType(VK_MINUS); break;
     case '=': doType(VK_EQUALS); break;
     case '~': doType(VK_SHIFT, VK_BACK_QUOTE); break;
     case '!': doType(VK_EXCLAMATION_MARK); break;
     case '@': doType(VK_AT); break;
     case '#': doType(VK_NUMBER_SIGN); break;
     case '$': doType(VK_DOLLAR); break;
     case '%': doType(VK_SHIFT, VK_5); break;
     case '^': doType(VK_CIRCUMFLEX); break;
     case '&': doType(VK_AMPERSAND); break;
     case '*': doType(VK_ASTERISK); break;
     case '(': doType(VK_LEFT_PARENTHESIS); break;
     case ')': doType(VK_RIGHT_PARENTHESIS); break;
     case '_': doType(VK_UNDERSCORE); break;
     case '+': doType(VK_PLUS); break;
     case '\t': doType(VK_TAB); break;
     case '\n': doType(VK_ENTER); break;
     case '[': doType(VK_OPEN_BRACKET); break;
     case ']': doType(VK_CLOSE_BRACKET); break;
     case '\\': doType(VK_BACK_SLASH); break;
     case '{': doType(VK_SHIFT, VK_OPEN_BRACKET); break;
     case '}': doType(VK_SHIFT, VK_CLOSE_BRACKET); break;
     case '|': doType(VK_SHIFT, VK_BACK_SLASH); break;
     case ';': doType(VK_SEMICOLON); break;
     case ':': doType(VK_COLON); break;
     case '\'': doType(VK_QUOTE); break;
     case '"': doType(VK_QUOTEDBL); break;
     case ',': doType(VK_COMMA); break;
     case '<': doType(VK_LESS); break;
     case '.': doType(VK_PERIOD); break;
     case '>': doType(VK_GREATER); break;
     case '/': doType(VK_SLASH); break;
     case '?': doType(VK_SHIFT, VK_SLASH); break;
     case ' ': doType(VK_SPACE); break;
     default:
      throw new IllegalArgumentException("Cannot type character " + character);
     }
    }

    private void doType(int... keyCodes) {
     doType(keyCodes, 0, keyCodes.length);
    }

    private void doType(int[] keyCodes, int offset, int length) {
     if (length == 0) {
      return;
     }

     robot.keyPress(keyCodes[offset]);
     doType(keyCodes, offset + 1, length - 1);
     robot.keyRelease(keyCodes[offset]);
    }

}

If you want some custom key typing, you can extend the class and override the type(char) method. For example:

public class WindowUnicodeKeyboard extends Keyboard {

    private Robot robot;

    public WindowUnicodeKeyboard(Robot robot) {
     super(robot);
     this.robot = robot;
    }

    @Override
    public void type(char character) {
     try {
      super.type(character);
     } catch (IllegalArgumentException e) {
      String unicodeDigits = String.valueOf(Character.getCodePoint(character));
      robot.keyPress(VK_ALT);
      for (int i = 0; i < unicodeDigits.length(); i++) {
       typeNumPad(Integer.parseInt(unicodeDigits.substring(i, i + 1)));
      }
      robot.keyRelease(VK_ALT);
     }
    }

    private void typeNumPad(int digit) {
     switch (digit) {
     case 0: doType(VK_NUMPAD0); break;
     case 1: doType(VK_NUMPAD1); break;
     case 2: doType(VK_NUMPAD2); break;
     case 3: doType(VK_NUMPAD3); break;
     case 4: doType(VK_NUMPAD4); break;
     case 5: doType(VK_NUMPAD5); break;
     case 6: doType(VK_NUMPAD6); break;
     case 7: doType(VK_NUMPAD7); break;
     case 8: doType(VK_NUMPAD8); break;
     case 9: doType(VK_NUMPAD9); break;
     }
    }

}

There is, of course, room for improvement, but you get the idea.

Adam Paynter
this answer is great and saved my life (this morning)... thank you!
Yar
A: 

I don't know the commandmap method but looks good, I'll have a look.

Finally I discover that the keycode for a to z is 65 to 90. So

private void write(Robot bot, String st) {
 char[] arr = st.toCharArray();
 int i = arr.length;
 int j = 0;
 while (j<i) {
  int kcode = (int) arr[j] - 32;
  bot.keyPress(kcode);
  bot.keyRelease(kcode);
  j++;
 }  
}

It just works for lowercase letters (you can easily correct it with a simple test for uppercase).

For what I was looking for it works perfectly :-)

Martin Trigaux
FYI: 65 is the ASCII code for 'A' (http://www.asciitable.com/)
Adam Paynter
+1  A: 

Your code will work as long as you are using alpha numeric characters only, it will not work for characters like ":". SmartRobot class will handle this.

Demiurg