UNPKG

expo-key-event

Version:

Provides an interface for reading key events such as from external bluetooth keyboards on Android, iOS and Web.

155 lines (106 loc) 5.84 kB
# GEMINI.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview expo-key-event is an Expo module that provides a unified interface for capturing hardware keyboard events from external devices (Bluetooth keyboards, gamepads) across iOS, macOS, Android, and Web platforms. **Key Requirement:** Expo SDK >= 52 (uses `useEvent` API introduced in SDK 52) ## Development Commands ### Main Module Development ```bash npm run build # Build the module (expo-module build) npm run lint # Lint the module (expo-module lint) npm run test # Test the module (expo-module test) npm run clean # Clean build artifacts npm run prepare # Prepare module for development npm run release # Create a new release (uses release-it) ``` ### Example App Development ```bash cd example npm run ios # Run example app on iOS npm run android # Run example app on Android npm run web # Run example app on Web ``` ### Opening Native Projects ```bash npm run open:ios # Open iOS project in Xcode npm run open:android # Open Android project in Android Studio ``` ## Architecture ### Platform-Specific Native Modules The module uses **platform-specific implementations** to capture raw key events from the OS: #### iOS/macOS (Swift) - Located in: `ios/ExpoKeyEventModule.swift` - Uses a hidden `KeyboardListenerView` (UIView/NSView) that becomes first responder - iOS: Captures events via `pressesBegan(_:with:)` using `UIPress.key.keyCode` - macOS: Captures events via `keyDown(with:)` using `NSEvent.keyCode` - Returns raw key codes as strings (e.g., "4", "79") #### Android (Kotlin) - Located in: `android/src/main/java/expo/modules/keyevent/` - `ExpoKeyEventModule.kt`: Main module that manages view lifecycle - `ExpoKeyEventView.kt`: Custom `ExpoView` that captures key events - Overrides `onKeyDown()` to intercept Android `KeyEvent` - Returns Android key codes as strings (e.g., "29", "7") - Only processes initial key down (filters repeats via `event.repeatCount == 0`) #### Web (TypeScript) - Located in: `src/hooks/useKeyEvent.web.ts` - Uses browser's native `addEventListener("keydown")` API - Directly returns Web standard key codes (e.g., "KeyA", "ArrowUp") ### Key Code Unification Layer Each platform returns **different raw key codes** for the same physical key. The unification layer translates these to Web standard codes: - **Key Mapping Files:** `src/constants/KeyCodeMapping.{ios,macos,android}.ts` - Map platform-specific numeric codes to Web standard strings - Example: iOS "4" → "KeyA", Android "29" → "KeyA" - These files contain large lookup tables (100+ entries each) - **Unification Utility:** `src/utils/unifyKeyCode.ts` - Applies platform-specific mapping via `KeyCodeMapping` object - Platform-specific imports resolved at build time - Warns in dev mode if mapping not found - **Web Platform:** No mapping needed (already uses standard codes) ### React Hooks API Two hooks provide different patterns for consuming key events: #### `useKeyEvent` (State Management) - Located in: `src/hooks/useKeyEvent.ts` (native) / `.web.ts` (web) - **Manages state internally** using Expo's `useEvent` hook (native) or `useState` (web) - Returns `{ keyEvent, startListening, stopListening }` - `keyEvent` is always the **latest** key press - Best for: Simple use cases where you just need the current key #### `useKeyEventListener` (Event Handler) - Located in: `src/hooks/useKeyEventListener.ts` (native) - **No state management** - just calls your callback - Takes a callback function as parameter - Returns `{ startListening, stopListening }` - Best for: Custom state management, side effects, logging Both hooks support: - `listenOnMount` parameter (default: true) - Auto-start listening - `preventReload` parameter (default: false) - Block 'R' key reload in dev mode - Dev mode feature: Pressing 'R' triggers `DevSettings.reload()` unless prevented ### Module Entry Points - **Public API:** `src/index.ts` - Exports both hooks and types - **Native Module:** `src/ExpoKeyEventModule.ts` - Bridge to native code - Provides `startListening()` and `stopListening()` functions - Emits `onKeyPress` events to JS - Throws helpful error if native module unavailable (Expo Go detection) - **Type Definitions:** `src/ExpoKeyEvent.types.ts` - `KeyPressEvent`: `{ key: string }` (unified key code) - `ExpoKeyEventModuleEvents`: Event emitter types ## Important Development Notes ### Native Development Requirements **This module requires a development build** - it will NOT work with Expo Go: - Use `npx expo run:ios` instead of `npx expo start` - Use `npx expo run:android` instead of `npx expo start` - See troubleshooting section in README.md for details ### Testing on Simulators/Emulators - **iOS Simulator:** Ensure "Hardware → Keyboard → Connect Hardware Keyboard" is enabled - **Android Emulator:** Does not support Bluetooth/USB hardware keyboards - Use `adb shell input keyevent <code>` to simulate key events - Or use a physical device for testing ### Key Code Discovery When adding new key mappings: 1. The module logs warnings in dev mode for unmapped keys: `"No mapping found for keyCode: X"` 2. Platform key codes can be found at: - iOS: [UIKit HID Usage constants](https://developer.apple.com/documentation/uikit/uikeyboardhidusage) - Android: [KeyEvent constants](https://developer.android.com/reference/android/view/KeyEvent) - Web: [KeyboardEvent.code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code) ## Module Configuration - **expo-module.config.json:** Defines platforms and module names - **expo-module-scripts:** Used for building, testing, linting (see package.json scripts)