From 86393fd5fe61becac793189ce82ed0dd4e9861e2 Mon Sep 17 00:00:00 2001 From: RetGal Date: Sun, 21 Feb 2021 21:01:13 +0100 Subject: [PATCH] Improved handling of special chars typed on varying locales on assistant/assisted should close #28 --- pom.xml | 7 ++ .../RobotNetworkControlMessageHandler.java | 81 +++++++++---- ...RobotNetworkControlMessageHandlerTest.java | 106 ++++++++++++++++++ 3 files changed, 172 insertions(+), 22 deletions(-) create mode 100644 src/test/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandlerTest.java diff --git a/pom.xml b/pom.xml index 7e652dcb..84916b3e 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ 0.9.1 1.9.65 5.7.1 + 3.7.7 @@ -59,6 +60,12 @@ ${junit.jupiter.version} test + + org.mockito + mockito-core + ${mockito.core.version} + test + diff --git a/src/main/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandler.java b/src/main/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandler.java index fb2753b8..8f814bb4 100644 --- a/src/main/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandler.java +++ b/src/main/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandler.java @@ -28,6 +28,10 @@ public RobotNetworkControlMessageHandler() { } } + public RobotNetworkControlMessageHandler(Robot robot) { + this.robot = robot; + } + @Override public void subscribe(Subscriber subscriber) { subscribers.add(subscriber); @@ -71,34 +75,38 @@ public void handleMessage(NetworkMouseControlMessage message) { public void handleMessage(NetworkKeyControlMessage message) { if (message.isPressed()) { try { - robot.keyPress(message.getKeyCode()); + pressKey(message); } catch (IllegalArgumentException ex) { - Log.warn(message.toString() + " contained an invalid keyCode for " + message.getKeyChar()); - if (message.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { - KeyStroke key = KeyStroke.getKeyStroke(message.getKeyChar(), 0); - // plan b - if (key.getKeyCode() != Character.MIN_VALUE) { - Log.warn("retrying with keyCode " + key.getKeyCode()); - typeUnicode(key.getKeyCode()); - return; - } - shout(message.getKeyChar()); - } + Log.error("Error while handling key press", ex); } } else if (message.isReleased()) { try { - robot.keyRelease(message.getKeyCode()); + releaseKey(message); } catch (IllegalArgumentException ex) { - Log.warn(message.toString() + " contained an invalid keyCode for " + message.getKeyChar()); + Log.error("Error while handling key release", ex); } } } - /** - * Q&D OS detection - */ - private void typeUnicode(int keyCode) - { + private void pressKey(NetworkKeyControlMessage message) { + if (message.getKeyCode() != KeyEvent.VK_UNDEFINED) { + robot.keyPress(message.getKeyCode()); + } else { + Log.warn(message.toString() + " contained an invalid keyCode for " + message.getKeyChar()); + if (message.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { + KeyStroke key = KeyStroke.getKeyStroke(message.getKeyChar(), 0); + // plan b + if (key.getKeyCode() != Character.MIN_VALUE) { + Log.warn("retrying with keyCode " + key.getKeyCode()); + typeUnicode(key.getKeyCode()); + return; + } + shout(message.getKeyChar()); + } + } + } + + private void typeUnicode(int keyCode) { if (File.separatorChar == '/') { typeLinuxUnicode(keyCode); return; @@ -106,6 +114,28 @@ private void typeUnicode(int keyCode) typeWindowsUnicode(keyCode); } + private void releaseKey(NetworkKeyControlMessage message) { + if (message.getKeyCode() != KeyEvent.VK_UNDEFINED) { + robot.keyRelease(message.getKeyCode()); + } else { + if (message.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { + KeyStroke key = KeyStroke.getKeyStroke(message.getKeyChar(), 0); + if (key.getKeyCode() != Character.MIN_VALUE) { + releaseUnicode(); + return; + } + } + } + } + + private void releaseUnicode() { + if (File.separatorChar == '/') { + releaseLinuxUnicode(); + return; + } + releaseWindowsUnicode(); + } + /** * Unicode characters are typed in decimal on Windows ä => 228 */ @@ -117,7 +147,11 @@ private void typeWindowsUnicode(int keyCode) { robot.keyPress(code); robot.keyRelease(code); } - robot.keyRelease(KeyEvent.VK_ALT); + // will be released when handling the subsequent message + } + + private void releaseWindowsUnicode() { + robot.keyRelease(KeyEvent.VK_ALT); } /** @@ -136,8 +170,11 @@ private void typeLinuxUnicode(int keyCode) { robot.keyPress(code); robot.keyRelease(code); } - robot.keyRelease(KeyEvent.VK_SHIFT); - robot.keyRelease(KeyEvent.VK_CONTROL); + // will be released when handling the subsequent message } + private void releaseLinuxUnicode() { + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_CONTROL); + } } diff --git a/src/test/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandlerTest.java b/src/test/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandlerTest.java new file mode 100644 index 00000000..b852bdb7 --- /dev/null +++ b/src/test/java/mpo/dayon/assisted/control/RobotNetworkControlMessageHandlerTest.java @@ -0,0 +1,106 @@ +package mpo.dayon.assisted.control; + +import mpo.dayon.common.network.message.NetworkKeyControlMessage; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; + +import java.awt.*; +import java.awt.event.KeyEvent; + +import static mpo.dayon.common.network.message.NetworkKeyControlMessage.KeyState.PRESSED; +import static mpo.dayon.common.network.message.NetworkKeyControlMessage.KeyState.RELEASED; +import static org.junit.jupiter.api.condition.OS.WINDOWS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class RobotNetworkControlMessageHandlerTest { + + Robot robot = mock(Robot.class); + RobotNetworkControlMessageHandler controlMessageHandler = new RobotNetworkControlMessageHandler(robot); + + @Test + void testHandleMessagePressA() { + // given + NetworkKeyControlMessage message = new NetworkKeyControlMessage(PRESSED, 65, 'A'); + // when + controlMessageHandler.handleMessage(message); + // then + verify(robot).keyPress(message.getKeyChar()); + } + + @Test + void testHandleMessageReleaseA() { + // given + NetworkKeyControlMessage message = new NetworkKeyControlMessage(RELEASED, 65, 'A'); + // when + controlMessageHandler.handleMessage(message); + // then + verify(robot).keyRelease(message.getKeyChar()); + } + + @Test + @DisabledOnOs(WINDOWS) + void testHandleMessagePressTurkishLowerSpecialSTypedOnNonTurkishAssisted() { + // given + NetworkKeyControlMessage message = new NetworkKeyControlMessage(PRESSED, 0, 'ş'); + // when + controlMessageHandler.handleMessage(message); + // then + verify(robot).keyPress(KeyEvent.VK_CONTROL); + verify(robot).keyPress(KeyEvent.VK_SHIFT); + verify(robot).keyPress(KeyEvent.VK_U); + verify(robot).keyRelease(KeyEvent.VK_U); + // 351 as hex 15F + verify(robot).keyPress(KeyEvent.VK_1); + verify(robot).keyRelease(KeyEvent.VK_1); + verify(robot).keyPress(KeyEvent.VK_5); + verify(robot).keyRelease(KeyEvent.VK_5); + verify(robot).keyPress(KeyEvent.VK_F); + verify(robot).keyRelease(KeyEvent.VK_F); + } + + @Test + @DisabledOnOs(WINDOWS) + void testHandleMessageReleaseTurkishLowerSpecialSTypedOnNonTurkishAssisted() { + // given + NetworkKeyControlMessage message = new NetworkKeyControlMessage(RELEASED, 0, 'ş'); + // when + controlMessageHandler.handleMessage(message); + // then + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_CONTROL); + } + + @Test + @DisabledOnOs(WINDOWS) + void testHandleMessagePressTurkishUpperSpecialSTypedOnNonTurkishAssisted() { + // given + NetworkKeyControlMessage message = new NetworkKeyControlMessage(PRESSED, 0, 'Ş'); + // when + controlMessageHandler.handleMessage(message); + // then + verify(robot).keyPress(KeyEvent.VK_CONTROL); + verify(robot).keyPress(KeyEvent.VK_SHIFT); + verify(robot).keyPress(KeyEvent.VK_U); + verify(robot).keyRelease(KeyEvent.VK_U); + // 350 as hex 15E + verify(robot).keyPress(KeyEvent.VK_1); + verify(robot).keyRelease(KeyEvent.VK_1); + verify(robot).keyPress(KeyEvent.VK_5); + verify(robot).keyRelease(KeyEvent.VK_5); + verify(robot).keyPress(KeyEvent.VK_E); + verify(robot).keyRelease(KeyEvent.VK_E); + } + + @Test + @DisabledOnOs(WINDOWS) + void testHandleMessageReleaseTurkishUpperSpecialSTypedOnNonTurkishAssisted() { + // given + NetworkKeyControlMessage message = new NetworkKeyControlMessage(RELEASED, 0, 'Ş'); + // when + controlMessageHandler.handleMessage(message); + // then + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_CONTROL); + } +} \ No newline at end of file