├── .gitignore ├── LICENSE.md ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── github │ └── kevinejohn │ └── keyevent │ ├── KeyEventModule.java │ └── KeyEventPackage.java ├── index.d.ts ├── index.js ├── ios ├── RNKeyEvent.h ├── RNKeyEvent.m ├── RNKeyEvent.xcodeproj │ └── project.pbxproj └── RNKeyEvent.xcworkspace │ └── contents.xcworkspacedata ├── package-lock.json ├── package.json ├── react-native-keyevent.podspec └── react-native.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | *.iml 28 | .idea 29 | .gradle 30 | local.properties 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | 37 | # BUCK 38 | buck-out/ 39 | \.buckd/ 40 | android/app/libs 41 | android/keystores/debug.keystore 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Kevin Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native KeyEvent 2 | 3 | [![npm version](https://badge.fury.io/js/react-native-keyevent.svg)](http://badge.fury.io/js/react-native-keyevent) 4 | 5 | Capture external keyboard keys or remote control button events 6 | 7 | [Learn about Android KeyEvent here](https://developer.android.com/reference/android/view/KeyEvent.html). 8 | 9 | 10 | ### Installation 11 | 12 | #### via npm 13 | 14 | Run `npm install react-native-keyevent --save` 15 | 16 | #### via yarn 17 | 18 | Run `yarn add react-native-keyevent` 19 | 20 | ### Linking 21 | 22 | #### Android: 23 | 24 | `react-native link react-native-keyevent` (for React Native <= 0.59 only) 25 | 26 | #### iOS: 27 | 28 | `pod install` inside your `ios` folder. 29 | 30 | > Note: You still must manually register module in MainActivity! (Instructions below under Configuration Android) 31 | 32 | 33 | #### Linking Manually 34 | 35 | **Android** 36 | 37 | 1. In `android/setting.gradle` 38 | 39 | ``` 40 | ... 41 | include ':react-native-keyevent' 42 | project(':react-native-keyevent').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyevent/android') 43 | ``` 44 | 45 | 2. In `android/app/build.gradle` 46 | 47 | ``` 48 | ... 49 | dependencies { 50 | ... 51 | compile project(':react-native-keyevent') 52 | } 53 | ``` 54 | 55 | 3. Register module (in MainApplication.java) 56 | 57 | ``` 58 | import com.github.kevinejohn.keyevent.KeyEventPackage; // <--- import 59 | 60 | public class MainApplication extends Application implements ReactApplication { 61 | ...... 62 | 63 | @Override 64 | protected List getPackages() { 65 | return Arrays.asList( 66 | new MainReactPackage(), 67 | new KeyEventPackage() <------- Add this 68 | ); 69 | } 70 | 71 | ...... 72 | 73 | } 74 | ``` 75 | 76 | **iOS** 77 | 78 | Follow the instrutions listed here: [Manually Linking iOS](https://facebook.github.io/react-native/docs/linking-libraries-ios#manual-linking). 79 | 80 | ### Configuration 81 | 82 | #### Android 83 | 84 | Implement onConfigurationChanged method in MainActivity.java 85 | 86 | ``` 87 | import android.view.KeyEvent; // <--- import 88 | import com.github.kevinejohn.keyevent.KeyEventModule; // <--- import 89 | 90 | 91 | public class MainActivity extends ReactActivity { 92 | ...... 93 | @Override // <--- Add this method if you want to react to keyDown 94 | public boolean onKeyDown(int keyCode, KeyEvent event) { 95 | 96 | // A. Prevent multiple events on long button press 97 | // In the default behavior multiple events are fired if a button 98 | // is pressed for a while. You can prevent this behavior if you 99 | // forward only the first event: 100 | // if (event.getRepeatCount() == 0) { 101 | // KeyEventModule.getInstance().onKeyDownEvent(keyCode, event); 102 | // } 103 | // 104 | // B. If multiple Events shall be fired when the button is pressed 105 | // for a while use this code: 106 | // KeyEventModule.getInstance().onKeyDownEvent(keyCode, event); 107 | // 108 | // Using B. 109 | KeyEventModule.getInstance().onKeyDownEvent(keyCode, event); 110 | 111 | // There are 2 ways this can be done: 112 | // 1. Override the default keyboard event behavior 113 | // super.onKeyDown(keyCode, event); 114 | // return true; 115 | 116 | // 2. Keep default keyboard event behavior 117 | // return super.onKeyDown(keyCode, event); 118 | 119 | // Using method #1 without blocking multiple 120 | super.onKeyDown(keyCode, event); 121 | return true; 122 | } 123 | 124 | @Override // <--- Add this method if you want to react to keyUp 125 | public boolean onKeyUp(int keyCode, KeyEvent event) { 126 | KeyEventModule.getInstance().onKeyUpEvent(keyCode, event); 127 | 128 | // There are 2 ways this can be done: 129 | // 1. Override the default keyboard event behavior 130 | // super.onKeyUp(keyCode, event); 131 | // return true; 132 | 133 | // 2. Keep default keyboard event behavior 134 | // return super.onKeyUp(keyCode, event); 135 | 136 | // Using method #1 137 | super.onKeyUp(keyCode, event); 138 | return true; 139 | } 140 | 141 | @Override 142 | public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { 143 | KeyEventModule.getInstance().onKeyMultipleEvent(keyCode, repeatCount, event); 144 | return super.onKeyMultiple(keyCode, repeatCount, event); 145 | } 146 | ...... 147 | 148 | } 149 | ``` 150 | 151 | #### iOS 152 | 153 | This will need to be added in your `AppDelegate.m` file: 154 | 155 | ```objc 156 | // react-native-keyevent 157 | #import // import our package after linking 158 | 159 | @implementation AppDelegate 160 | 161 | //.... 162 | 163 | /*! 164 | * react-native-keyevent support 165 | */ 166 | RNKeyEvent *keyEvent = nil; 167 | 168 | - (NSMutableArray *)keyCommands { 169 | NSMutableArray *keys = [NSMutableArray new]; 170 | 171 | if (keyEvent == nil) { 172 | keyEvent = [[RNKeyEvent alloc] init]; 173 | } 174 | 175 | if ([keyEvent isListening]) { 176 | NSArray *namesArray = [[keyEvent getKeys] componentsSeparatedByString:@","]; 177 | 178 | NSCharacterSet *validChars = [NSCharacterSet characterSetWithCharactersInString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZ"]; 179 | 180 | for (NSString* names in namesArray) { 181 | NSRange range = [names rangeOfCharacterFromSet:validChars]; 182 | 183 | if (NSNotFound != range.location) { 184 | [keys addObject: [UIKeyCommand keyCommandWithInput:names modifierFlags:UIKeyModifierShift action:@selector(keyInput:)]]; 185 | } else { 186 | [keys addObject: [UIKeyCommand keyCommandWithInput:names modifierFlags:0 action:@selector(keyInput:)]]; 187 | } 188 | } 189 | } 190 | 191 | return keys; 192 | } 193 | 194 | - (void)keyInput:(UIKeyCommand *)sender { 195 | NSString *selected = sender.input; 196 | [keyEvent sendKeyEvent:selected]; 197 | } 198 | 199 | @end 200 | ``` 201 | 202 | ## Usage 203 | 204 | Whenever you want to use it within React Native code now you can: 205 | 206 | `import KeyEvent from 'react-native-keyevent';` 207 | 208 | ```javascript 209 | componentDidMount() { 210 | // if you want to react to keyDown 211 | KeyEvent.onKeyDownListener((keyEvent) => { 212 | console.log(`onKeyDown keyCode: ${keyEvent.keyCode}`); 213 | console.log(`Action: ${keyEvent.action}`); 214 | console.log(`Key: ${keyEvent.pressedKey}`); 215 | }); 216 | 217 | // if you want to react to keyUp 218 | KeyEvent.onKeyUpListener((keyEvent) => { 219 | console.log(`onKeyUp keyCode: ${keyEvent.keyCode}`); 220 | console.log(`Action: ${keyEvent.action}`); 221 | console.log(`Key: ${keyEvent.pressedKey}`); 222 | }); 223 | 224 | // if you want to react to keyMultiple 225 | KeyEvent.onKeyMultipleListener((keyEvent) => { 226 | console.log(`onKeyMultiple keyCode: ${keyEvent.keyCode}`); 227 | console.log(`Action: ${keyEvent.action}`); 228 | console.log(`Characters: ${keyEvent.characters}`); 229 | }); 230 | } 231 | 232 | componentWillUnmount() { 233 | // if you are listening to keyDown 234 | KeyEvent.removeKeyDownListener(); 235 | 236 | // if you are listening to keyUp 237 | KeyEvent.removeKeyUpListener(); 238 | 239 | // if you are listening to keyMultiple 240 | KeyEvent.removeKeyMultipleListener(); 241 | } 242 | ``` 243 | 244 | ## TODOS 245 | 246 | - [x] iOS Support 247 | - [ ] Add iOS Support for keyDown and multipleKeys 248 | - [ ] Implement `keyCode` and `action` on iOS 249 | - [x] Support for TypeScript projects 250 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 31 5 | 6 | defaultConfig { 7 | minSdkVersion 21 8 | targetSdkVersion 31 9 | versionCode 1 10 | versionName "1.0" 11 | ndk { 12 | abiFilters "armeabi-v7a", "x86" 13 | } 14 | } 15 | } 16 | 17 | dependencies { 18 | implementation 'com.facebook.react:react-native:+' 19 | } 20 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/kevinejohn/keyevent/KeyEventModule.java: -------------------------------------------------------------------------------- 1 | package com.github.kevinejohn.keyevent; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.bridge.ReactContext; 5 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 6 | import com.facebook.react.modules.core.DeviceEventManagerModule; 7 | import com.facebook.react.bridge.WritableNativeMap; 8 | import com.facebook.react.bridge.WritableMap; 9 | 10 | import android.view.KeyEvent; 11 | 12 | /** 13 | * Created by Kevin Johnson on 8/15/16. 14 | */ 15 | public class KeyEventModule extends ReactContextBaseJavaModule { 16 | private ReactContext mReactContext; 17 | private DeviceEventManagerModule.RCTDeviceEventEmitter mJSModule = null; 18 | 19 | private static KeyEventModule instance = null; 20 | 21 | public static KeyEventModule initKeyEventModule(ReactApplicationContext reactContext) { 22 | instance = new KeyEventModule(reactContext); 23 | return instance; 24 | } 25 | 26 | public static KeyEventModule getInstance() { 27 | return instance; 28 | } 29 | 30 | public void onKeyDownEvent(int keyCode, KeyEvent keyEvent) { 31 | if (!mReactContext.hasActiveCatalystInstance()) { 32 | return; 33 | } 34 | 35 | if (mJSModule == null) { 36 | mJSModule = mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); 37 | } 38 | mJSModule.emit("onKeyDown", getJsEventParams(keyCode, keyEvent, null)); 39 | }; 40 | 41 | public void onKeyUpEvent(int keyCode, KeyEvent keyEvent) { 42 | if (!mReactContext.hasActiveCatalystInstance()) { 43 | return; 44 | } 45 | 46 | if (mJSModule == null) { 47 | mJSModule = mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); 48 | } 49 | mJSModule.emit("onKeyUp", getJsEventParams(keyCode, keyEvent, null)); 50 | }; 51 | 52 | public void onKeyMultipleEvent(int keyCode, int repeatCount, KeyEvent keyEvent) { 53 | if (!mReactContext.hasActiveCatalystInstance()) { 54 | return; 55 | } 56 | 57 | if (mJSModule == null) { 58 | mJSModule = mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); 59 | } 60 | 61 | 62 | mJSModule.emit("onKeyMultiple", getJsEventParams(keyCode, keyEvent, repeatCount)); 63 | }; 64 | 65 | protected KeyEventModule(ReactApplicationContext reactContext) { 66 | super(reactContext); 67 | mReactContext = reactContext; 68 | } 69 | 70 | private WritableMap getJsEventParams(int keyCode, KeyEvent keyEvent, Integer repeatCount) { 71 | WritableMap params = new WritableNativeMap(); 72 | int action = keyEvent.getAction(); 73 | char pressedKey = (char) keyEvent.getUnicodeChar(); 74 | 75 | if (keyEvent.getAction() == KeyEvent.ACTION_MULTIPLE && keyCode == KeyEvent.KEYCODE_UNKNOWN) { 76 | String chars = keyEvent.getCharacters(); 77 | if (chars != null) { 78 | params.putString("characters", chars); 79 | } 80 | } 81 | 82 | if (repeatCount != null) { 83 | params.putInt("repeatcount", repeatCount); 84 | } 85 | 86 | params.putInt("keyCode", keyCode); 87 | params.putInt("action", action); 88 | params.putString("pressedKey", String.valueOf(pressedKey)); 89 | 90 | return params; 91 | } 92 | 93 | @Override 94 | public String getName() { 95 | return "KeyEventModule"; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/kevinejohn/keyevent/KeyEventPackage.java: -------------------------------------------------------------------------------- 1 | package com.github.kevinejohn.keyevent; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by Kevin Johnson on 8/15/16. 15 | */ 16 | public class KeyEventPackage implements ReactPackage { 17 | public KeyEventPackage() { } 18 | 19 | @Override 20 | public List createNativeModules(ReactApplicationContext reactContext) { 21 | return Arrays.asList( 22 | KeyEventModule.initKeyEventModule(reactContext) 23 | ); 24 | } 25 | 26 | // Deprecated from RN 0.47 27 | public List> createJSModules() { 28 | return Collections.emptyList(); 29 | } 30 | 31 | @Override 32 | public List createViewManagers(ReactApplicationContext reactContext) { 33 | return Collections.emptyList(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-native-keyevent'; 2 | 3 | export type KeyEventProps = { action: number; keyCode: number; pressedKey: string; characters: string }; 4 | 5 | export function onKeyDownListener(keyEvent: any): void; 6 | export function onKeyUpListener(keyEvent: any): void; 7 | export function onKeyMultipleListener(keyEvent: any): void; 8 | 9 | export function removeKeyDownListener(): void; 10 | export function removeKeyUpListener(): void; 11 | export function removeKeyMultipleListener(): void; 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { DeviceEventEmitter, NativeEventEmitter, NativeModules, Platform } from 'react-native'; 2 | 3 | class KeyEvent { 4 | onKeyDownListener(cb) { 5 | this.removeKeyDownListener(); 6 | if (Platform.OS === "ios") { 7 | let keyEvent = new NativeEventEmitter(NativeModules.RNKeyEvent); 8 | this.listenerKeyDown = keyEvent.addListener('onKeyDown', cb); 9 | } else { 10 | this.listenerKeyDown = DeviceEventEmitter.addListener('onKeyDown', cb); 11 | } 12 | } 13 | 14 | removeKeyDownListener() { 15 | if (this.listenerKeyDown) { 16 | this.listenerKeyDown.remove(); 17 | this.listenerKeyDown = null; 18 | } 19 | } 20 | 21 | onKeyUpListener(cb) { 22 | this.removeKeyUpListener(); 23 | if (Platform.OS === "ios") { 24 | let keyEvent = new NativeEventEmitter(NativeModules.RNKeyEvent); 25 | this.listenerKeyUp = keyEvent.addListener('onKeyUp', cb); 26 | } else { 27 | this.listenerKeyUp = DeviceEventEmitter.addListener('onKeyUp', cb); 28 | } 29 | } 30 | 31 | removeKeyUpListener() { 32 | if (this.listenerKeyUp) { 33 | this.listenerKeyUp.remove(); 34 | this.listenerKeyUp = null; 35 | } 36 | } 37 | 38 | onKeyMultipleListener(cb) { 39 | this.removeKeyMultipleListener(); 40 | if (Platform.OS === "ios") { 41 | let keyEvent = new NativeEventEmitter(NativeModules.RNKeyEvent); 42 | this.listenerKeyMultiple = keyEvent.addListener('onKeyMultiple', cb); 43 | } else { 44 | this.listenerKeyMultiple = DeviceEventEmitter.addListener('onKeyMultiple', cb); 45 | } 46 | } 47 | 48 | removeKeyMultipleListener() { 49 | if (this.listenerKeyMultiple) { 50 | this.listenerKeyMultiple.remove(); 51 | this.listenerKeyMultiple = null; 52 | } 53 | } 54 | } 55 | 56 | export default new KeyEvent(); 57 | -------------------------------------------------------------------------------- /ios/RNKeyEvent.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RNKeyEvent_h 3 | #define RNKeyEvent_h 4 | 5 | #import 6 | #import 7 | #import 8 | 9 | @interface RNKeyEvent : RCTEventEmitter 10 | 11 | @property (nonatomic) BOOL hasListeners; 12 | 13 | + (id)allocWithZone:(NSZone *)zone; 14 | 15 | + (id)getSingletonInstance; 16 | 17 | - (BOOL)isListening; 18 | 19 | - (NSString *)getKeys; 20 | 21 | - (void)sendKeyEvent:(NSString *)keyString; 22 | 23 | @end 24 | 25 | #endif /* RNKeyEvent_h */ 26 | -------------------------------------------------------------------------------- /ios/RNKeyEvent.m: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import "RNKeyEvent.h" 4 | 5 | NSString* const onKeyUpEvent = @"onKeyUp"; 6 | 7 | @implementation RNKeyEvent 8 | 9 | #pragma mark - RNKeyEvent implementation 10 | 11 | RCT_EXPORT_MODULE(); 12 | 13 | + (id)allocWithZone:(NSZone *)zone { 14 | static RNKeyEvent *sharedInstance = nil; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | sharedInstance = [super allocWithZone:zone]; 18 | }); 19 | return sharedInstance; 20 | } 21 | 22 | + (id)getSingletonInstance { 23 | static RNKeyEvent *sharedRNKeyEvent = nil; 24 | if (sharedRNKeyEvent == nil) { 25 | sharedRNKeyEvent = [[self alloc] init]; 26 | } 27 | return sharedRNKeyEvent; 28 | } 29 | 30 | - (BOOL)isListening { 31 | return self.hasListeners; 32 | } 33 | 34 | - (NSString *)getKeys { 35 | if (@available(iOS 15.0, *)) { 36 | return [NSString stringWithFormat:@"!,1,2,3,4,5,6,7,8,9,0,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,.,~,-,\r, \n,%@,%@,%@,%@,%@,%@",UIKeyInputLeftArrow, UIKeyInputRightArrow, UIKeyInputUpArrow, UIKeyInputDownArrow, UIKeyInputEscape, UIKeyInputDelete]; 37 | } else { 38 | return [NSString stringWithFormat:@"!,1,2,3,4,5,6,7,8,9,0,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,.,~,-,\r,%@,%@,%@,%@",UIKeyInputLeftArrow, UIKeyInputRightArrow, UIKeyInputUpArrow, UIKeyInputDownArrow]; 39 | } 40 | } 41 | 42 | - (void)sendKeyEvent:(NSString *)keyString { 43 | if (self.hasListeners && self.bridge) { 44 | [super sendEventWithName:onKeyUpEvent body:@{@"pressedKey": keyString}]; 45 | } 46 | } 47 | 48 | #pragma mark - Exported to JavaScript methods 49 | 50 | #pragma mark - RCTEventEmitter implementation 51 | 52 | - (NSArray *)supportedEvents { 53 | return @[onKeyUpEvent]; 54 | } 55 | 56 | // Note: startObserving will be called when this module's first listener is added. 57 | - (void)startObserving { 58 | self.hasListeners = YES; 59 | // Set up any upstream listeners or background tasks as necessary 60 | } 61 | 62 | // Note: stopObserving will be called when this module's last listener is removed, or on dealloc. 63 | - (void)stopObserving { 64 | self.hasListeners = NO; 65 | // Remove upstream listeners, stop unnecessary background tasks 66 | } 67 | 68 | @end 69 | 70 | -------------------------------------------------------------------------------- /ios/RNKeyEvent.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B3E7B58A1CC2AC0600A0062D /* RNKeyEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNKeyEvent.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = "include/$(PRODUCT_NAME)"; 18 | dstSubfolderSpec = 16; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 0; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 134814201AA4EA6300B7C361 /* libRNKeyEvent.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKeyEvent.a; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | B3E7B5881CC2AC0600A0062D /* RNKeyEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKeyEvent.h; sourceTree = ""; }; 28 | B3E7B5891CC2AC0600A0062D /* RNKeyEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNKeyEvent.m; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 134814211AA4EA7D00B7C361 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | 134814201AA4EA6300B7C361 /* libRNKeyEvent.a */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | 58B511D21A9E6C8500147676 = { 51 | isa = PBXGroup; 52 | children = ( 53 | B3E7B5881CC2AC0600A0062D /* RNKeyEvent.h */, 54 | B3E7B5891CC2AC0600A0062D /* RNKeyEvent.m */, 55 | 134814211AA4EA7D00B7C361 /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | /* End PBXGroup section */ 60 | 61 | /* Begin PBXNativeTarget section */ 62 | 58B511DA1A9E6C8500147676 /* RNKeyEvent */ = { 63 | isa = PBXNativeTarget; 64 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNKeyEvent" */; 65 | buildPhases = ( 66 | 58B511D71A9E6C8500147676 /* Sources */, 67 | 58B511D81A9E6C8500147676 /* Frameworks */, 68 | 58B511D91A9E6C8500147676 /* CopyFiles */, 69 | ); 70 | buildRules = ( 71 | ); 72 | dependencies = ( 73 | ); 74 | name = RNKeyEvent; 75 | productName = RCTDataManager; 76 | productReference = 134814201AA4EA6300B7C361 /* libRNKeyEvent.a */; 77 | productType = "com.apple.product-type.library.static"; 78 | }; 79 | /* End PBXNativeTarget section */ 80 | 81 | /* Begin PBXProject section */ 82 | 58B511D31A9E6C8500147676 /* Project object */ = { 83 | isa = PBXProject; 84 | attributes = { 85 | LastUpgradeCheck = 0830; 86 | ORGANIZATIONNAME = Facebook; 87 | TargetAttributes = { 88 | 58B511DA1A9E6C8500147676 = { 89 | CreatedOnToolsVersion = 6.1.1; 90 | }; 91 | }; 92 | }; 93 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNKeyEvent" */; 94 | compatibilityVersion = "Xcode 3.2"; 95 | developmentRegion = English; 96 | hasScannedForEncodings = 0; 97 | knownRegions = ( 98 | en, 99 | ); 100 | mainGroup = 58B511D21A9E6C8500147676; 101 | productRefGroup = 58B511D21A9E6C8500147676; 102 | projectDirPath = ""; 103 | projectRoot = ""; 104 | targets = ( 105 | 58B511DA1A9E6C8500147676 /* RNKeyEvent */, 106 | ); 107 | }; 108 | /* End PBXProject section */ 109 | 110 | /* Begin PBXSourcesBuildPhase section */ 111 | 58B511D71A9E6C8500147676 /* Sources */ = { 112 | isa = PBXSourcesBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | B3E7B58A1CC2AC0600A0062D /* RNKeyEvent.m in Sources */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | /* End PBXSourcesBuildPhase section */ 120 | 121 | /* Begin XCBuildConfiguration section */ 122 | 58B511ED1A9E6C8500147676 /* Debug */ = { 123 | isa = XCBuildConfiguration; 124 | buildSettings = { 125 | ALWAYS_SEARCH_USER_PATHS = NO; 126 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 127 | CLANG_CXX_LIBRARY = "libc++"; 128 | CLANG_ENABLE_MODULES = YES; 129 | CLANG_ENABLE_OBJC_ARC = YES; 130 | CLANG_WARN_BOOL_CONVERSION = YES; 131 | CLANG_WARN_CONSTANT_CONVERSION = YES; 132 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 133 | CLANG_WARN_EMPTY_BODY = YES; 134 | CLANG_WARN_ENUM_CONVERSION = YES; 135 | CLANG_WARN_INFINITE_RECURSION = YES; 136 | CLANG_WARN_INT_CONVERSION = YES; 137 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 138 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 139 | CLANG_WARN_UNREACHABLE_CODE = YES; 140 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 141 | COPY_PHASE_STRIP = NO; 142 | ENABLE_STRICT_OBJC_MSGSEND = YES; 143 | ENABLE_TESTABILITY = YES; 144 | GCC_C_LANGUAGE_STANDARD = gnu99; 145 | GCC_DYNAMIC_NO_PIC = NO; 146 | GCC_NO_COMMON_BLOCKS = YES; 147 | GCC_OPTIMIZATION_LEVEL = 0; 148 | GCC_PREPROCESSOR_DEFINITIONS = ( 149 | "DEBUG=1", 150 | "$(inherited)", 151 | ); 152 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 153 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 154 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 155 | GCC_WARN_UNDECLARED_SELECTOR = YES; 156 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 157 | GCC_WARN_UNUSED_FUNCTION = YES; 158 | GCC_WARN_UNUSED_VARIABLE = YES; 159 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 160 | MTL_ENABLE_DEBUG_INFO = YES; 161 | ONLY_ACTIVE_ARCH = YES; 162 | SDKROOT = iphoneos; 163 | }; 164 | name = Debug; 165 | }; 166 | 58B511EE1A9E6C8500147676 /* Release */ = { 167 | isa = XCBuildConfiguration; 168 | buildSettings = { 169 | ALWAYS_SEARCH_USER_PATHS = NO; 170 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 171 | CLANG_CXX_LIBRARY = "libc++"; 172 | CLANG_ENABLE_MODULES = YES; 173 | CLANG_ENABLE_OBJC_ARC = YES; 174 | CLANG_WARN_BOOL_CONVERSION = YES; 175 | CLANG_WARN_CONSTANT_CONVERSION = YES; 176 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 177 | CLANG_WARN_EMPTY_BODY = YES; 178 | CLANG_WARN_ENUM_CONVERSION = YES; 179 | CLANG_WARN_INFINITE_RECURSION = YES; 180 | CLANG_WARN_INT_CONVERSION = YES; 181 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 182 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 183 | CLANG_WARN_UNREACHABLE_CODE = YES; 184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 185 | COPY_PHASE_STRIP = YES; 186 | ENABLE_NS_ASSERTIONS = NO; 187 | ENABLE_STRICT_OBJC_MSGSEND = YES; 188 | GCC_C_LANGUAGE_STANDARD = gnu99; 189 | GCC_NO_COMMON_BLOCKS = YES; 190 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 191 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 192 | GCC_WARN_UNDECLARED_SELECTOR = YES; 193 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 194 | GCC_WARN_UNUSED_FUNCTION = YES; 195 | GCC_WARN_UNUSED_VARIABLE = YES; 196 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 197 | MTL_ENABLE_DEBUG_INFO = NO; 198 | SDKROOT = iphoneos; 199 | VALIDATE_PRODUCT = YES; 200 | }; 201 | name = Release; 202 | }; 203 | 58B511F01A9E6C8500147676 /* Debug */ = { 204 | isa = XCBuildConfiguration; 205 | buildSettings = { 206 | HEADER_SEARCH_PATHS = ( 207 | "$(inherited)", 208 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 209 | "$(SRCROOT)/../../../React/**", 210 | "$(SRCROOT)/../../react-native/React/**", 211 | ); 212 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 213 | OTHER_LDFLAGS = "-ObjC"; 214 | PRODUCT_NAME = RNKeyEvent; 215 | SKIP_INSTALL = YES; 216 | }; 217 | name = Debug; 218 | }; 219 | 58B511F11A9E6C8500147676 /* Release */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | HEADER_SEARCH_PATHS = ( 223 | "$(inherited)", 224 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 225 | "$(SRCROOT)/../../../React/**", 226 | "$(SRCROOT)/../../react-native/React/**", 227 | ); 228 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 229 | OTHER_LDFLAGS = "-ObjC"; 230 | PRODUCT_NAME = RNKeyEvent; 231 | SKIP_INSTALL = YES; 232 | }; 233 | name = Release; 234 | }; 235 | /* End XCBuildConfiguration section */ 236 | 237 | /* Begin XCConfigurationList section */ 238 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNKeyEvent" */ = { 239 | isa = XCConfigurationList; 240 | buildConfigurations = ( 241 | 58B511ED1A9E6C8500147676 /* Debug */, 242 | 58B511EE1A9E6C8500147676 /* Release */, 243 | ); 244 | defaultConfigurationIsVisible = 0; 245 | defaultConfigurationName = Release; 246 | }; 247 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNKeyEvent" */ = { 248 | isa = XCConfigurationList; 249 | buildConfigurations = ( 250 | 58B511F01A9E6C8500147676 /* Debug */, 251 | 58B511F11A9E6C8500147676 /* Release */, 252 | ); 253 | defaultConfigurationIsVisible = 0; 254 | defaultConfigurationName = Release; 255 | }; 256 | /* End XCConfigurationList section */ 257 | }; 258 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 259 | } 260 | -------------------------------------------------------------------------------- /ios/RNKeyEvent.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-keyevent", 3 | "version": "0.3.2", 4 | "description": "Capture external keyboard keys or remote control button events", 5 | "types": "index.d.ts", 6 | "keywords": [ 7 | "react-native", 8 | "keyevent", 9 | "keyboard", 10 | "external", 11 | "android", 12 | "module", 13 | "remote" 14 | ], 15 | "author": "Kevin Johnson (github.com/kevinejohn)", 16 | "license": "ISC", 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/kevinejohn/react-native-keyevent.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/kevinejohn/react-native-keyevent/issues" 23 | }, 24 | "peerDependencies": { 25 | "react-native": ">=0.30" 26 | }, 27 | "rnpm": { 28 | "android": { 29 | "packageInstance": "new KeyEventPackage()" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /react-native-keyevent.podspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'json' 4 | 5 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 6 | 7 | Pod::Spec.new do |s| 8 | s.name = 'react-native-keyevent' 9 | s.version = package['version'] 10 | s.summary = 'Capture external keyboard keys or remote control button events' 11 | s.author = 'Kevin Johnson' 12 | 13 | s.homepage = 'https://github.com/kevinejohn/react-native-keyevent' 14 | 15 | s.license = 'MIT' 16 | s.ios.deployment_target = '7.0' 17 | s.tvos.deployment_target = '9.0' 18 | 19 | s.source = { git: 'https://github.com/kevinejohn/react-native-keyevent.git', tag: s.version.to_s } 20 | 21 | s.source_files = 'ios/**/*.{h,m}' 22 | s.requires_arc = true 23 | 24 | s.dependency 'React' 25 | end 26 | -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | project: { 3 | android: { 4 | "packageInstance": "new KeyEventPackage()" 5 | } 6 | } 7 | } 8 | --------------------------------------------------------------------------------