diff --git a/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardAlternateCharacterMap.java b/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardAlternateCharacterMap.java new file mode 100644 index 00000000000..5fe5c708aaf --- /dev/null +++ b/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardAlternateCharacterMap.java @@ -0,0 +1,23 @@ +package expo.modules.hardwarekeyboard; + +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowKeyCharacterMap; + +@Implements(KeyCharacterMap.class) +public class HardwareKeyboardAlternateCharacterMap extends ShadowKeyCharacterMap { + @Implementation + protected static char nativeGetCharacter(long pointer, int keyCode, int metaState) { + if (keyCode != KeyEvent.KEYCODE_Q) return '\u0000'; + boolean shifted = (metaState & KeyEvent.META_SHIFT_ON) != 0; + if ((metaState & KeyEvent.META_ALT_ON) != 0) return shifted ? '#' : '@'; + return shifted ? 'Q' : 'q'; + } + + @Implementation + protected static char nativeGetDisplayLabel(long pointer, int keyCode) { + return keyCode == KeyEvent.KEYCODE_Q ? 'Q' : '\u0000'; + } +} diff --git a/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardAlternateLayoutTest.kt b/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardAlternateLayoutTest.kt new file mode 100644 index 00000000000..e109b1346ed --- /dev/null +++ b/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardAlternateLayoutTest.kt @@ -0,0 +1,32 @@ +package expo.modules.hardwarekeyboard + +import android.view.KeyEvent +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28], manifest = Config.NONE, shadows = [HardwareKeyboardAlternateCharacterMap::class]) +class HardwareKeyboardAlternateLayoutTest { + @Test fun synthesizedAltGrKeepsNativeTextAndNeverClaimsARelease() { + for (shift in listOf(0, KeyEvent.META_SHIFT_ON)) { + val fixture = HardwareKeyboardNativeBoundaryTest.Fixture() + val meta = KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON or shift + assertFalse(fixture.key(KeyEvent.KEYCODE_Q, meta = meta)) + assertEquals("first", fixture.input.text.toString()) + assertTrue(fixture.context.events.isEmpty()) + assertFalse(fixture.key(KeyEvent.KEYCODE_Q, KeyEvent.ACTION_UP, meta = meta)) + } + } + + @Test fun ordinaryCtrlStillCapturesTheLogicalKeyAndFieldBoundary() { + val fixture = HardwareKeyboardNativeBoundaryTest.Fixture() + assertTrue(fixture.key(KeyEvent.KEYCODE_Q, meta = KeyEvent.META_CTRL_ON)) + assertEquals("q", fixture.lastKey()["key"]) + assertEquals("first", (fixture.lastKey()["fieldBoundary"] as Map<*, *>)["text"]) + assertEquals("", fixture.input.text.toString()) + assertTrue(fixture.key(KeyEvent.KEYCODE_Q, KeyEvent.ACTION_UP)) + } +} diff --git a/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardNativeBoundaryTest.kt b/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardNativeBoundaryTest.kt index 10e3f85261c..4231e094779 100644 --- a/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardNativeBoundaryTest.kt +++ b/mobile/packages/expo-hardware-keyboard/android/src/test/java/expo/modules/hardwarekeyboard/HardwareKeyboardNativeBoundaryTest.kt @@ -19,7 +19,7 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [28], manifest = Config.NONE) class HardwareKeyboardNativeBoundaryTest { - private class Fixture { + internal class Fixture { val context = HardwareKeyboardRecordingContext(RuntimeEnvironment.getApplication()).also { DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(it) }