├── .eslintrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── ReactNativeKeyboardInput.podspec
├── Supplementals
├── example-android.gif
├── example.gif
└── example2.gif
├── android
├── app
│ ├── BUCK
│ ├── build.gradle
│ ├── build_defs.bzl
│ ├── debug.keystore
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── keyboardinput
│ │ │ ├── MainActivity.java
│ │ │ └── MainApplication.java
│ │ └── res
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── demo
├── demoKeyboards.js
├── demoRoot.android.js
├── demoScreen.js
└── res
│ ├── settings.png
│ └── star.png
├── e2e
├── init.js
├── mocha.opts
└── sanity.spec.js
├── index.android.js
├── index.ios.js
├── ios
├── KeyboardInput-tvOS
│ └── Info.plist
├── KeyboardInput-tvOSTests
│ └── Info.plist
├── KeyboardInput.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── KeyboardInput-tvOS.xcscheme
│ │ └── KeyboardInput.xcscheme
├── KeyboardInput.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── KeyboardInput
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Info.plist
│ └── main.m
├── KeyboardInputTests
│ ├── Info.plist
│ └── KeyboardInputTests.m
├── Podfile
└── Podfile.lock
├── lib
├── android
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── wix
│ │ └── reactnativekeyboardinput
│ │ ├── AppContextHolder.java
│ │ ├── CustomKeyboardLayout.java
│ │ ├── CustomKeyboardRootView.java
│ │ ├── CustomKeyboardRootViewManager.java
│ │ ├── CustomKeyboardRootViewShadow.java
│ │ ├── GlobalDefs.java
│ │ ├── KeyboardInputModule.java
│ │ ├── KeyboardInputPackage.java
│ │ ├── ReactContextHolder.java
│ │ ├── ReactScreenMonitor.java
│ │ ├── ReactSoftKeyboardMonitor.java
│ │ └── utils
│ │ ├── Logger.java
│ │ ├── PredicateFunc.java
│ │ ├── RuntimeUtils.java
│ │ └── ViewUtils.java
└── ios
│ ├── LNInterpolation
│ ├── Color+Interpolation.h
│ ├── Color+Interpolation.m
│ ├── LNAnimator.h
│ ├── LNAnimator.m
│ ├── LNInterpolable.h
│ ├── LNInterpolable.m
│ ├── LNInterpolation.h
│ ├── NSValue+Interpolation.h
│ └── NSValue+Interpolation.mm
│ ├── RCTCustomInputController.xcodeproj
│ └── project.pbxproj
│ └── RCTCustomInputController
│ ├── RCTCustomInputController.h
│ ├── RCTCustomInputController.m
│ ├── RCTCustomKeyboardViewController.h
│ └── RCTCustomKeyboardViewController.m
├── package-lock.json
├── package.json
├── src
├── CustomKeyboardView.js
├── KeyboardAccessoryView.js
├── KeyboardRegistry.spec.js
├── KeyboardsRegistry.js
├── TextInputKeyboardManagerAndroid.js
├── TextInputKeyboardMangerIOS.js
├── index.js
└── utils
│ ├── EventEmitterManager.js
│ ├── EventEmitterManager.spec.js
│ └── KeyboardUtils.js
└── wallaby.js
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "plugins": ["react-native"],
4 | "extends": [
5 | "airbnb"
6 | ],
7 | "rules": {
8 | "object-curly-spacing": "off",
9 | "react/prefer-stateless-function": "off",
10 | "react/jsx-filename-extension": "off",
11 | "react/require-default-props": "off",
12 | "no-use-before-define": "off",
13 | "react/forbid-prop-types": "off",
14 | "react/jsx-space-before-closing": "off",
15 | "react/jsx-tag-spacing": "off",
16 | "max-len": [2, 135, 4, {"ignoreUrls": true}],
17 | "class-methods-use-this": "off",
18 | "arrow-body-style": "off",
19 | "no-plusplus": "off",
20 | "no-array-index-key": "off",
21 | "no-multi-comp": "off"
22 | },
23 | "env": {
24 | "browser": true,
25 | "node": true,
26 | "jest": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 | ModuleCache/
9 |
10 | ## Various settings
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata/
20 | ios/info.plist
21 |
22 | ## Other
23 | *.moved-aside
24 | *.xcuserstate
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 | *.dSYM.zip
30 | *.dSYM
31 |
32 | # CocoaPods
33 | #
34 | # We recommend against adding the Pods directory to your .gitignore. However
35 | # you should judge for yourself, the pros and cons are mentioned at:
36 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
37 | #
38 | # Pods/
39 |
40 | # Carthage
41 | #
42 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
43 | # Carthage/Checkouts
44 |
45 | Carthage/Build
46 | /ios/Pods/
47 |
48 | # fastlane
49 | #
50 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
51 | # screenshots whenever they are needed.
52 | # For more information about the recommended setup visit:
53 | # https://docs.fastlane.tools/best-practices/source-control/
54 |
55 | fastlane/report.xml
56 | fastlane/screenshots
57 |
58 | #Code Injection
59 | #
60 | # After new code Injection tools there's a generated folder /iOSInjectionProject
61 | # https://github.com/johnno1962/injectionforxcode
62 |
63 | iOSInjectionProject/
64 |
65 | # OSX
66 | #
67 | .DS_Store
68 |
69 | # Xcode
70 | #
71 | build/
72 | *.pbxuser
73 | !default.pbxuser
74 | *.mode1v3
75 | !default.mode1v3
76 | *.mode2v3
77 | !default.mode2v3
78 | *.perspectivev3
79 | !default.perspectivev3
80 | xcuserdata
81 | *.xccheckout
82 | *.moved-aside
83 | DerivedData
84 | *.hmap
85 | *.ipa
86 | *.xcuserstate
87 | project.xcworkspace
88 |
89 | # Android/IJ
90 | #
91 | *.iml
92 | .idea
93 | .gradle
94 | local.properties
95 | android/.project
96 | android/.settings/*
97 | android/app/.project
98 | android/app/.classpath
99 | android/app/.settings/*
100 | lib/android/.project
101 | lib/android/.classpath
102 | lib/android/.settings/*
103 |
104 | # node.js
105 | #
106 | node_modules/
107 | npm-debug.log
108 |
109 | # BUCK
110 | buck-out/
111 | \.buckd/
112 | android/app/libs
113 | *.keystore
114 | !debug.keystore
115 | yarn.lock
116 |
117 | #vscoe
118 | .vscode/*
119 |
120 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | demo/
2 | ios/
3 | android/
4 | Supplementals/
5 | index.android.js
6 | index.ios.js
7 | .eslintrc
8 | *.spec.js
9 |
10 | # Xcode
11 | #
12 |
13 |
14 | ## Build generated
15 | build/
16 | DerivedData/
17 | ModuleCache/
18 |
19 | ## Various settings
20 | *.pbxuser
21 | !default.pbxuser
22 | *.mode1v3
23 | !default.mode1v3
24 | *.mode2v3
25 | !default.mode2v3
26 | *.perspectivev3
27 | !default.perspectivev3
28 | xcuserdata/
29 | ios/info.plist
30 |
31 | ## Other
32 | *.moved-aside
33 | *.xcuserstate
34 |
35 | ## Obj-C/Swift specific
36 | *.hmap
37 | *.ipa
38 | *.dSYM.zip
39 | *.dSYM
40 |
41 | # Android/IJ
42 | #
43 | *.iml
44 | .idea
45 | .gradle
46 | local.properties
47 |
48 | # OSX
49 | #
50 | .DS_Store
51 |
52 | # node.js
53 | #
54 | npm-debug.log
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Wix.com
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 | # Important: deprecation alert
2 | This library is being deprecated and the repository will not be maintaned, the components have moved to our UI library - please start migrating to [RN-UILib](https://github.com/wix/react-native-ui-lib/).
3 | If you want to try out our excelent (and constantly improving) UI compoenent library, please use:
4 | ```
5 | import {Keyboard} from 'react-native-ui-lib';
6 | const KeyboardAccessoryView = Keyboard.KeyboardAccessoryView;
7 | ```
8 | If you don't want to import the whole library, you can use only the `keyboard` package:
9 | ```
10 | import {KeyboardAccessoryView} from 'react-native-ui-lib/keyboard';
11 | ```
12 |
13 | # React Native Keyboard Input
14 |
15 | Presents a React component as an input view which replaces the system keyboard. Can be used for creating custom input views such as an image gallery, stickers, etc.
16 |
17 | Supports both iOS and Android.
18 |
19 |
20 |
21 | # Installation
22 | Install the package from npm:
23 |
24 | `yarn add react-native-keyboard-input` or `npm i --save react-native-keyboard-input`
25 |
26 | ## Android
27 |
28 | Update your dependencies in `android/app/build.gradle`:
29 |
30 | ```gradle
31 | dependencies {
32 | // Add this dependency:
33 | compile project(":reactnativekeyboardinput")
34 | }
35 | ```
36 |
37 | Update your `android/settings.gradle`:
38 |
39 | ```gradle
40 | include ':reactnativekeyboardinput'
41 | project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyboard-input/lib/android')
42 | ```
43 |
44 | In your `MainApplication.java`, add to the `getPackages()` list:
45 |
46 | ```java
47 | import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
48 |
49 | @Override
50 | protected List getPackages() {
51 | return Arrays.asList(
52 | // Add this package:
53 | new KeyboardInputPackage(this) // (this = Android application object)
54 | );
55 | }
56 | ```
57 |
58 | ### ProGuard
59 |
60 | If you have pro-guard enabled and are having trouble with your build, apply this to your project's main `proguard-rules.pro`:
61 |
62 | ```
63 | -dontwarn com.wix.reactnativekeyboardinput.**
64 | ```
65 |
66 | ## iOS
67 | In Xcode, drag both `RCTCustomInputController.xcodeproj` and `KeyboardTrackingView.xcodeproj` from your `node_modules` to the Libraries folder in the Project Navigator, then add `libRCTCustomInputController.a` and `libKeyboardTrackingView.a` to your app target "Linked Frameworks and Libraries".
68 |
69 | #### Covering the whold keyboard in predictive mode
70 | To utilize this feature you'll need to add `KeyboardTrackingView` to your projects scheme build action.
71 |
72 | From Xcode menu:
73 |
74 | 1. `product -> scheme -> manage schemes -> double-click your project`
75 | 2. Slect build at the right menu, click the + icon at the bottom of the targets list and select `KeyboardTrackingView`.
76 | 3. Drag and position `KeyboardTrackingView` to be first, above your project, and unmark "Parallelize Build" option at the top.
77 |
78 | If necessary, you can take a look at how it is set-up in the demo project.
79 |
80 |
81 | # Usage
82 |
83 | There are 2 main parts necessary for the implementation:
84 |
85 | ## 1. A keyboard component
86 | Create a component that you wish to use as a keyboard input. For example:
87 |
88 | ```js
89 | class KeyboardView extends Component {
90 | static propTypes = {
91 | title: PropTypes.string,
92 | };
93 | render() {
94 | return (
95 |
96 | HELOOOO!!!
97 | {this.props.title}
98 |
99 | );
100 | }
101 | }
102 | ```
103 |
104 | Now register with the keyboard registry so it can be used later as a keyboard:
105 |
106 | ```js
107 | import {KeyboardRegistry} from 'react-native-keyboard-input';
108 |
109 | KeyboardRegistry.registerKeyboard('MyKeyboardView', () => KeyboardView);
110 | ```
111 |
112 | When you need to notify about selecting an item in the keyboard, use:
113 |
114 | ```js
115 | KeyboardRegistry.onItemSelected(`MyKeyboardView`, params);
116 | ```
117 |
118 | ## 2. Using the keyboard component as an input view
119 | While this package provides several component and classes for low-level control over custom keyboard inputs, the easiets way would be to use `KeyboardAccessoryView`. It's the only thing you'll need to show your Keyboard component as a custom input. For example:
120 |
121 | ```js
122 |
128 | ```
129 |
130 | | Prop | Type | Description |
131 | | ---- | ---- | ----------- |
132 | | renderContent | Function | a fucntion for rendering the content of the keyboard toolbar |
133 | | kbInputRef | Object | A ref to the input component which triggers the showing of the keyboard |
134 | | kbComponent | String | The registered component name |
135 | | kbInitialProps | Object | Initial props to pass to the registered keyboard component |
136 | | onItemSelected | Function | a callback function for a selection of an item in the keyboard component |
137 |
138 | This component takes care of making your toolbar (which is rendered via `renderContent `) "float" above the keyboard (necessary for iOS), and for setting your component as the keyboard input when the `kbComponent` changes.
139 |
140 | # Demo
141 |
142 | See [demoScreen.js](https://github.com/wix/react-native-keyboard-input/blob/master/demo/demoScreen.js) for a full working example.
143 |
--------------------------------------------------------------------------------
/ReactNativeKeyboardInput.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "ReactNativeKeyboardInput"
7 | s.version = package['version']
8 | s.summary = package['description']
9 |
10 | s.authors = package['author']
11 | s.homepage = package['homepage']
12 | s.license = package['license']
13 | s.platforms = { :ios => "9.0", :tvos => "9.2" }
14 |
15 | s.module_name = 'ReactNativeKeyboardInput'
16 |
17 | s.source = { :git => "https://github.com/wix/react-native-autogrow-textinput", :tag => "#{s.version}" }
18 | s.source_files = "lib/ios/**/*.{h,m}"
19 |
20 | s.dependency 'React'
21 | s.frameworks = 'UIKit'
22 | end
23 |
--------------------------------------------------------------------------------
/Supplementals/example-android.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/Supplementals/example-android.gif
--------------------------------------------------------------------------------
/Supplementals/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/Supplementals/example.gif
--------------------------------------------------------------------------------
/Supplementals/example2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/Supplementals/example2.gif
--------------------------------------------------------------------------------
/android/app/BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.keyboardinput",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.keyboardinput",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation
19 | * entryFile: "index.android.js",
20 | *
21 | * // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
22 | * bundleCommand: "ram-bundle",
23 | *
24 | * // whether to bundle JS and assets in debug mode
25 | * bundleInDebug: false,
26 | *
27 | * // whether to bundle JS and assets in release mode
28 | * bundleInRelease: true,
29 | *
30 | * // whether to bundle JS and assets in another build variant (if configured).
31 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
32 | * // The configuration property can be in the following formats
33 | * // 'bundleIn${productFlavor}${buildType}'
34 | * // 'bundleIn${buildType}'
35 | * // bundleInFreeDebug: true,
36 | * // bundleInPaidRelease: true,
37 | * // bundleInBeta: true,
38 | *
39 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
40 | * // for example: to disable dev mode in the staging build type (if configured)
41 | * devDisabledInStaging: true,
42 | * // The configuration property can be in the following formats
43 | * // 'devDisabledIn${productFlavor}${buildType}'
44 | * // 'devDisabledIn${buildType}'
45 | *
46 | * // the root of your project, i.e. where "package.json" lives
47 | * root: "../../",
48 | *
49 | * // where to put the JS bundle asset in debug mode
50 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
51 | *
52 | * // where to put the JS bundle asset in release mode
53 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
54 | *
55 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
56 | * // require('./image.png')), in debug mode
57 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
58 | *
59 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
60 | * // require('./image.png')), in release mode
61 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
62 | *
63 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
64 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
65 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
66 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
67 | * // for example, you might want to remove it from here.
68 | * inputExcludes: ["android/**", "ios/**"],
69 | *
70 | * // override which node gets called and with what additional arguments
71 | * nodeExecutableAndArgs: ["node"],
72 | *
73 | * // supply additional arguments to the packager
74 | * extraPackagerArgs: []
75 | * ]
76 | */
77 |
78 | project.ext.react = [
79 | entryFile: "index.js",
80 | enableHermes: false, // clean and rebuild if changing
81 | ]
82 |
83 | apply from: "../../node_modules/react-native/react.gradle"
84 |
85 | /**
86 | * Set this to true to create two separate APKs instead of one:
87 | * - An APK that only works on ARM devices
88 | * - An APK that only works on x86 devices
89 | * The advantage is the size of the APK is reduced by about 4MB.
90 | * Upload all the APKs to the Play Store and people will download
91 | * the correct one based on the CPU architecture of their device.
92 | */
93 | def enableSeparateBuildPerCPUArchitecture = false
94 |
95 | /**
96 | * Run Proguard to shrink the Java bytecode in release builds.
97 | */
98 | def enableProguardInReleaseBuilds = false
99 |
100 | /**
101 | * The preferred build flavor of JavaScriptCore.
102 | *
103 | * For example, to use the international variant, you can use:
104 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
105 | *
106 | * The international variant includes ICU i18n library and necessary data
107 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
108 | * give correct results when using with locales other than en-US. Note that
109 | * this variant is about 6MiB larger per architecture than default.
110 | */
111 | def jscFlavor = 'org.webkit:android-jsc:+'
112 |
113 | /**
114 | * Whether to enable the Hermes VM.
115 | *
116 | * This should be set on project.ext.react and mirrored here. If it is not set
117 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
118 | * and the benefits of using Hermes will therefore be sharply reduced.
119 | */
120 | def enableHermes = project.ext.react.get("enableHermes", false);
121 |
122 | android {
123 | compileSdkVersion rootProject.ext.compileSdkVersion
124 |
125 | compileOptions {
126 | sourceCompatibility JavaVersion.VERSION_1_8
127 | targetCompatibility JavaVersion.VERSION_1_8
128 | }
129 |
130 | defaultConfig {
131 | applicationId "com.keyboardinput"
132 | minSdkVersion rootProject.ext.minSdkVersion
133 | targetSdkVersion rootProject.ext.targetSdkVersion
134 | missingDimensionStrategy "RNN.reactNativeVersion", "reactNative60"
135 | versionCode 1
136 | versionName "1.0"
137 | }
138 | splits {
139 | abi {
140 | reset()
141 | enable enableSeparateBuildPerCPUArchitecture
142 | universalApk false // If true, also generate a universal APK
143 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
144 | }
145 | }
146 | signingConfigs {
147 | debug {
148 | storeFile file('debug.keystore')
149 | storePassword 'android'
150 | keyAlias 'androiddebugkey'
151 | keyPassword 'android'
152 | }
153 | }
154 | buildTypes {
155 | debug {
156 | signingConfig signingConfigs.debug
157 | }
158 | release {
159 | // Caution! In production, you need to generate your own keystore file.
160 | // see https://facebook.github.io/react-native/docs/signed-apk-android.
161 | signingConfig signingConfigs.debug
162 | minifyEnabled enableProguardInReleaseBuilds
163 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
164 | }
165 | }
166 | // applicationVariants are e.g. debug, release
167 | applicationVariants.all { variant ->
168 | variant.outputs.each { output ->
169 | // For each separate APK per architecture, set a unique version code as described here:
170 | // https://developer.android.com/studio/build/configure-apk-splits.html
171 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
172 | def abi = output.getFilter(OutputFile.ABI)
173 | if (abi != null) { // null for the universal-debug, universal-release variants
174 | output.versionCodeOverride =
175 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
176 | }
177 |
178 | }
179 | }
180 | }
181 |
182 | dependencies {
183 | implementation fileTree(dir: "libs", include: ["*.jar"])
184 | implementation "com.facebook.react:react-native:+" // From node_modules
185 | implementation project(":reactnativekeyboardinput")
186 | implementation project(':react-native-navigation')
187 |
188 | if (enableHermes) {
189 | def hermesPath = "../../node_modules/hermes-engine/android/";
190 | debugImplementation files(hermesPath + "hermes-debug.aar")
191 | releaseImplementation files(hermesPath + "hermes-release.aar")
192 | } else {
193 | implementation jscFlavor
194 | }
195 | }
196 |
197 | // Run this once to be able to run the application with BUCK
198 | // puts all compile dependencies into folder libs for BUCK to use
199 | task copyDownloadableDepsToLibs(type: Copy) {
200 | from configurations.compile
201 | into 'libs'
202 | }
203 |
204 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
205 |
--------------------------------------------------------------------------------
/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/debug.keystore
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/keyboardinput/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.keyboardinput;
2 |
3 | import com.reactnativenavigation.NavigationActivity;
4 |
5 | public class MainActivity extends NavigationActivity {}
6 |
7 | //public class MainActivity extends ReactActivity {
8 | //
9 | // /**
10 | // * Returns the name of the main component registered from JavaScript. This is used to schedule
11 | // * rendering of the component.
12 | // */
13 | // @Override
14 | // protected String getMainComponentName() {
15 | // return "KeyboardInput";
16 | // }
17 | //}
18 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/keyboardinput/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.keyboardinput;
2 |
3 | import com.facebook.react.ReactNativeHost;
4 | import com.facebook.react.ReactPackage;
5 | import com.reactnativenavigation.NavigationApplication;
6 | import com.reactnativenavigation.react.NavigationReactNativeHost;
7 | import com.reactnativenavigation.react.ReactGateway;
8 | import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
9 |
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | public class MainApplication extends NavigationApplication {
14 |
15 | @Override
16 | protected ReactGateway createReactGateway() {
17 | ReactNativeHost host = new NavigationReactNativeHost(this, isDebug(), createAdditionalReactPackages()) {
18 | @Override
19 | protected String getJSMainModuleName() {
20 | return "index";
21 | }
22 | };
23 | return new ReactGateway(this, isDebug(), host);
24 | }
25 |
26 | @Override
27 | public boolean isDebug() {
28 | return BuildConfig.DEBUG;
29 | }
30 |
31 | protected List getPackages() {
32 | // Add additional packages you require here
33 | // No need to add RnnPackage and MainReactPackage
34 | return Arrays.asList(
35 | new KeyboardInputPackage(this)
36 | );
37 | }
38 |
39 | @Override
40 | public List createAdditionalReactPackages() {
41 | return getPackages();
42 | }
43 | }
44 |
45 | //public class MainApplication extends Application implements ReactApplication {
46 | //
47 | // private final ReactNativeHost mReactNativeHost =
48 | // new ReactNativeHost(this) {
49 | // @Override
50 | // public boolean getUseDeveloperSupport() {
51 | // return BuildConfig.DEBUG;
52 | // }
53 | //
54 | // @Override
55 | // protected List getPackages() {
56 | // @SuppressWarnings("UnnecessaryLocalVariable")
57 | // List packages = new PackageList(this).getPackages();
58 | // // Packages that cannot be autolinked yet can be added manually here, for example:
59 | // // packages.add(new MyReactNativePackage());
60 | // return packages;
61 | // }
62 | //
63 | // @Override
64 | // protected String getJSMainModuleName() {
65 | // return "index";
66 | // }
67 | // };
68 | //
69 | // @Override
70 | // public ReactNativeHost getReactNativeHost() {
71 | // return mReactNativeHost;
72 | // }
73 | //
74 | // @Override
75 | // public void onCreate() {
76 | // super.onCreate();
77 | // SoLoader.init(this, /* native exopackage */ false);
78 | // initializeFlipper(this); // Remove this line if you don't want Flipper enabled
79 | // }
80 | //
81 | // /**
82 | // * Loads Flipper in React Native templates.
83 | // *
84 | // * @param context
85 | // */
86 | // private static void initializeFlipper(Context context) {
87 | // if (BuildConfig.DEBUG) {
88 | // try {
89 | // /*
90 | // We use reflection here to pick up the class that initializes Flipper,
91 | // since Flipper library is not available in release mode
92 | // */
93 | // Class> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper");
94 | // aClass.getMethod("initializeFlipper", Context.class).invoke(null, context);
95 | // } catch (ClassNotFoundException e) {
96 | // e.printStackTrace();
97 | // } catch (NoSuchMethodException e) {
98 | // e.printStackTrace();
99 | // } catch (IllegalAccessException e) {
100 | // e.printStackTrace();
101 | // } catch (InvocationTargetException e) {
102 | // e.printStackTrace();
103 | // }
104 | // }
105 | // }
106 | //}
107 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | KeyboardInput
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "28.0.3"
6 | minSdkVersion = 18
7 | compileSdkVersion = 28
8 | targetSdkVersion = 28
9 | }
10 | repositories {
11 | google()
12 | jcenter()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle:3.4.2")
16 |
17 | // NOTE: Do not place your application dependencies here; they belong
18 | // in the individual module build.gradle files
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | mavenLocal()
25 | maven {
26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
27 | url("$rootDir/../node_modules/react-native/android")
28 | }
29 | maven {
30 | // Android JSC is installed from npm
31 | url("$rootDir/../node_modules/jsc-android/dist")
32 | }
33 |
34 | google()
35 | jcenter()
36 | maven { url 'https://jitpack.io' }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | android.useAndroidX=true
21 | android.enableJetifier=true
22 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin, switch paths to Windows format before running java
129 | if $cygwin ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem http://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'KeyboardInput'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 |
5 | include ':reactnativekeyboardinput'
6 | project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../lib/android')
7 |
8 | include ':react-native-navigation'
9 | project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/lib/android/app/')
10 |
--------------------------------------------------------------------------------
/demo/demoKeyboards.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import {Text, TouchableOpacity, ScrollView, StyleSheet} from 'react-native';
4 | import {KeyboardRegistry} from 'react-native-keyboard-input';
5 |
6 | class KeyboardView extends Component {
7 | static propTypes = {
8 | title: PropTypes.string,
9 | };
10 |
11 | onButtonPress() {
12 | KeyboardRegistry.onItemSelected('KeyboardView', {
13 | message: 'item selected from KeyboardView',
14 | });
15 | }
16 |
17 | render() {
18 | return (
19 |
20 | HELOOOO!!!
21 | {this.props.title}
22 | this.onButtonPress()}
26 | >
27 |
28 | Click Me!
29 |
30 |
31 |
32 | );
33 | }
34 | }
35 |
36 | class AnotherKeyboardView extends Component {
37 | static propTypes = {
38 | title: PropTypes.string,
39 | };
40 |
41 | onButtonPress() {
42 | KeyboardRegistry.toggleExpandedKeyboard('AnotherKeyboardView');
43 | }
44 |
45 | render() {
46 | return (
47 |
48 | *** ANOTHER ONE ***
49 | {this.props.title}
50 | this.onButtonPress()}
54 | >
55 |
56 | Toggle Full-Screen!
57 |
58 |
59 |
60 | );
61 | }
62 | }
63 |
64 | const styles = StyleSheet.create({
65 | keyboardContainer: {
66 | flex: 1,
67 | flexDirection: 'column',
68 | alignItems: 'center',
69 | justifyContent: 'center',
70 | },
71 | });
72 |
73 | KeyboardRegistry.registerKeyboard('KeyboardView', () => KeyboardView);
74 | KeyboardRegistry.registerKeyboard('AnotherKeyboardView', () => AnotherKeyboardView);
75 |
--------------------------------------------------------------------------------
/demo/demoRoot.android.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | Text,
4 | View,
5 | TouchableOpacity,
6 | } from 'react-native';
7 | import PropTypes from 'prop-types';
8 | import {Navigation} from 'react-native-navigation';
9 |
10 | export default class KeyboardInput extends Component {
11 | static propTypes = {
12 | componentId: PropTypes.string,
13 | };
14 |
15 | constructor(props) {
16 | super(props);
17 | this.onPressBodyMessage = this.onPressBodyMessage.bind(this);
18 | }
19 |
20 | onPressBodyMessage() {
21 | Navigation.push(this.props.componentId, {
22 | component: {
23 | name: 'screens.innerScreen',
24 | passProps: {
25 | message: 'In the secondary tab, the keyboard input is inside a pushed screen, yet it works nonetheless! :-)',
26 | },
27 | options: {
28 | topBar: {
29 | title: {
30 | text: 'Second screen',
31 | },
32 | },
33 | },
34 | },
35 | });
36 | }
37 |
38 | render() {
39 | return (
40 |
41 |
42 | Tap for more keyboard fun...
43 |
44 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/demo/demoScreen.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | ScrollView,
7 | TouchableOpacity,
8 | PixelRatio,
9 | Platform,
10 | Switch,
11 | } from 'react-native';
12 | import PropTypes from 'prop-types';
13 | import {AutoGrowingTextInput} from 'react-native-autogrow-textinput';
14 | import {KeyboardAccessoryView, KeyboardUtils} from 'react-native-keyboard-input';
15 | import {KeyboardRegistry} from 'react-native-keyboard-input';
16 | import {_} from 'lodash';
17 |
18 | import './demoKeyboards';
19 | import CustomKeyboardView from '../src/CustomKeyboardView';
20 |
21 | const IsIOS = Platform.OS === 'ios';
22 | const TrackInteractive = true;
23 |
24 | export default class KeyboardInput extends Component {
25 | static propTypes = {
26 | message: PropTypes.string,
27 | };
28 |
29 | constructor(props) {
30 | super(props);
31 | this.keyboardAccessoryViewContent = this.keyboardAccessoryViewContent.bind(this);
32 | this.onKeyboardItemSelected = this.onKeyboardItemSelected.bind(this);
33 | this.resetKeyboardView = this.resetKeyboardView.bind(this);
34 | this.onKeyboardResigned = this.onKeyboardResigned.bind(this);
35 | this.showLastKeyboard = this.showLastKeyboard.bind(this);
36 | this.isCustomKeyboardOpen = this.isCustomKeyboardOpen.bind(this);
37 |
38 | this.state = {
39 | customKeyboard: {
40 | component: undefined,
41 | initialProps: undefined,
42 | },
43 | receivedKeyboardData: undefined,
44 | useSafeArea: true,
45 | keyboardOpenState: false,
46 | };
47 | }
48 |
49 | onKeyboardItemSelected(keyboardId, params) {
50 | const receivedKeyboardData = `onItemSelected from "${keyboardId}"\nreceived params: ${JSON.stringify(params)}`;
51 | this.setState({receivedKeyboardData});
52 | }
53 |
54 | onKeyboardResigned() {
55 | this.setState({keyboardOpenState: false});
56 | this.resetKeyboardView();
57 | }
58 |
59 | getToolbarButtons() {
60 | return [
61 | {
62 | text: 'show1',
63 | testID: 'show1',
64 | onPress: () => this.showKeyboardView('KeyboardView', 'FIRST - 1 (passed prop)'),
65 | },
66 | {
67 | text: 'show2',
68 | testID: 'show2',
69 | onPress: () => this.showKeyboardView('AnotherKeyboardView', 'SECOND - 2 (passed prop)'),
70 | },
71 | {
72 | text: 'reset',
73 | testID: 'reset',
74 | onPress: () => this.resetKeyboardView(),
75 | },
76 | ];
77 | }
78 |
79 | resetKeyboardView() {
80 | this.setState({customKeyboard: {}});
81 | }
82 |
83 | showKeyboardView(component, title) {
84 | this.setState({
85 | keyboardOpenState: true,
86 | customKeyboard: {
87 | component,
88 | initialProps: {title},
89 | },
90 | });
91 | }
92 |
93 | dismissKeyboard() {
94 | KeyboardUtils.dismiss();
95 | }
96 |
97 | showLastKeyboard() {
98 | const {customKeyboard} = this.state;
99 | this.setState({customKeyboard: {}});
100 |
101 | setTimeout(() => {
102 | this.setState({
103 | keyboardOpenState: true,
104 | customKeyboard,
105 | });
106 | }, 500);
107 | }
108 |
109 | isCustomKeyboardOpen = () => {
110 | const {keyboardOpenState, customKeyboard} = this.state;
111 | return keyboardOpenState && !_.isEmpty(customKeyboard);
112 | }
113 |
114 | toggleUseSafeArea = () => {
115 | const {useSafeArea} = this.state;
116 | this.setState({useSafeArea: !useSafeArea});
117 |
118 | if (this.isCustomKeyboardOpen()) {
119 | this.dismissKeyboard();
120 | this.showLastKeyboard();
121 | }
122 | }
123 |
124 | safeAreaSwitchToggle = () => {
125 | if (!IsIOS) {
126 | return ();
127 | }
128 | const {useSafeArea} = this.state;
129 | return (
130 |
131 | Safe Area Enabled:
132 |
133 |
134 | );
135 | }
136 |
137 | keyboardAccessoryViewContent() {
138 | return (
139 |
140 |
141 |
142 | {
146 | this.textInputRef = r;
147 | }}
148 | placeholder={'Message'}
149 | underlineColorAndroid="transparent"
150 | onFocus={() => this.resetKeyboardView()}
151 | testID={'input'}
152 | />
153 | KeyboardUtils.dismiss()}>
154 | Action
155 |
156 |
157 |
158 | {
159 | this.getToolbarButtons().map((button, index) =>
160 |
166 | {button.text}
167 | )
168 | }
169 |
170 |
171 | );
172 | }
173 |
174 | render() {
175 | return (
176 |
177 |
178 |
182 | {this.props.message ? this.props.message : 'Keyboards example'}
183 | {this.state.receivedKeyboardData}
184 | { this.safeAreaSwitchToggle() }
185 |
186 |
187 | this.setState({keyboardAccessoryViewHeight: IsIOS ? height : undefined})}
190 | trackInteractive={TrackInteractive}
191 | kbInputRef={this.textInputRef}
192 | kbComponent={this.state.customKeyboard.component}
193 | kbInitialProps={this.state.customKeyboard.initialProps}
194 | onItemSelected={this.onKeyboardItemSelected}
195 | onKeyboardResigned={this.onKeyboardResigned}
196 | revealKeyboardInteractive
197 | useSafeArea={this.state.useSafeArea}
198 | />
199 |
200 | );
201 | }
202 | }
203 |
204 | const COLOR = '#F5FCFF';
205 |
206 | const styles = StyleSheet.create({
207 | container: {
208 | flex: 1,
209 | backgroundColor: COLOR,
210 | },
211 | scrollContainer: {
212 | justifyContent: 'center',
213 | padding: 15,
214 | flex: 1,
215 | },
216 | welcome: {
217 | fontSize: 20,
218 | textAlign: 'center',
219 | margin: 10,
220 | paddingTop: 50,
221 | paddingBottom: 50,
222 | },
223 | inputContainer: {
224 | flexDirection: 'row',
225 | alignItems: 'center',
226 | justifyContent: 'space-between',
227 | marginBottom: 25,
228 | },
229 | keyboardContainer: {
230 | ...Platform.select({
231 | ios: {
232 | flex: 1,
233 | backgroundColor: COLOR,
234 | },
235 | }),
236 | },
237 | textInput: {
238 | flex: 1,
239 | marginLeft: 10,
240 | marginTop: 10,
241 | marginBottom: 10,
242 | paddingLeft: 10,
243 | paddingTop: 2,
244 | paddingBottom: 5,
245 | fontSize: 16,
246 | backgroundColor: 'white',
247 | borderWidth: 0.5 / PixelRatio.get(),
248 | borderRadius: 18,
249 | },
250 | sendButton: {
251 | paddingRight: 15,
252 | paddingLeft: 15,
253 | alignSelf: 'center',
254 | },
255 | switch: {
256 | marginLeft: 15,
257 | },
258 | safeAreaSwitchContainer: {
259 | flexDirection: 'row',
260 | alignItems: 'center',
261 | justifyContent: 'center',
262 | },
263 | });
264 |
--------------------------------------------------------------------------------
/demo/res/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/demo/res/settings.png
--------------------------------------------------------------------------------
/demo/res/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-keyboard-input/acb3a58e96988026f449b48e8b49f49164684d9f/demo/res/star.png
--------------------------------------------------------------------------------
/e2e/init.js:
--------------------------------------------------------------------------------
1 | require('babel-polyfill');
2 | const detox = require('detox');
3 | const config = require('../package.json').detox;
4 |
5 | before(async () => {
6 | await detox.init(config);
7 | });
8 |
9 | after(async () => {
10 | await detox.cleanup();
11 | });
--------------------------------------------------------------------------------
/e2e/mocha.opts:
--------------------------------------------------------------------------------
1 | --recursive --timeout 120000 --bail
--------------------------------------------------------------------------------
/e2e/sanity.spec.js:
--------------------------------------------------------------------------------
1 | describe('Sanity', () => {
2 | beforeEach(async () => {
3 | await device.reloadReactNative();
4 | });
5 |
6 | it('should have example screen', async () => {
7 | await expect(element(by.label('Keyboards example'))).toBeVisible();
8 | });
9 |
10 | it('should switch between keyboards', async () => {
11 | await element(by.id('input')).tap();
12 | await element(by.id('show1')).tap();
13 | await expect(element(by.label('HELOOOO!!!'))).toBeVisible();
14 | await element(by.id('show2')).tap();
15 | await expect(element(by.label('*** ANOTHER ONE ***'))).toBeVisible();
16 | await element(by.id('reset')).tap();
17 | await expect(element(by.label('Keyboards example'))).toBeVisible();
18 | });
19 |
20 | it('should select item on the first demo keyboard', async () => {
21 | await element(by.id('input')).tap();
22 | await element(by.id('show1')).tap();
23 | await expect(element(by.label('HELOOOO!!!'))).toBeVisible();
24 | await element(by.id('click-me')).tap();
25 | await expect(element(by.id('demo-message'))).toBeVisible();
26 | });
27 |
28 | it('should expand the keyboard', async () => {
29 | await element(by.id('input')).tap();
30 | await expect(element(by.id('show2'))).toBeVisible();
31 | await element(by.id('show2')).tap();
32 | await element(by.id('toggle-fs')).tap();
33 | await expect(element(by.id('show2'))).toBeNotVisible();
34 | await element(by.id('toggle-fs')).tap();
35 | await expect(element(by.id('show2'))).toBeVisible();
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | import KeyboardAppScreen from './demo/demoScreen';
2 | import DemoRootScreen from './demo/demoRoot';
3 |
4 | import {Navigation} from 'react-native-navigation';
5 |
6 | Navigation.registerComponent('screens.star', () => KeyboardAppScreen);
7 | Navigation.registerComponent('screens.settings', () => DemoRootScreen);
8 | Navigation.registerComponent('screens.innerScreen', () => KeyboardAppScreen);
9 |
10 | Navigation.setRoot({
11 | root: {
12 | bottomTabs: {
13 | children: [{
14 | stack: {
15 | children: [{
16 | component: {
17 | name: 'screens.star',
18 | passProps: {
19 | message: 'On the main tab, the keyboard input is in the root screen!',
20 | },
21 | },
22 | }],
23 | options: {
24 | bottomTab: {
25 | text: 'Main Tab',
26 | icon: require('./demo/res/star.png'),
27 | },
28 | topBar: {
29 | title: {
30 | text: 'Main Tab',
31 | },
32 | },
33 | },
34 | },
35 | },
36 | {
37 | stack: {
38 | children: [{
39 | component: {
40 | name: 'screens.settings',
41 | passProps: {
42 | message: 'Secondary Tab',
43 | },
44 | },
45 | }],
46 | options: {
47 | bottomTab: {
48 | text: 'Second Tab',
49 | icon: require('./demo/res/settings.png'),
50 | },
51 | topBar: {
52 | title: {
53 | text: 'Second Tab',
54 | },
55 | },
56 | },
57 | },
58 | }],
59 | },
60 | },
61 | });
62 |
--------------------------------------------------------------------------------
/index.ios.js:
--------------------------------------------------------------------------------
1 | import {AppRegistry} from 'react-native';
2 | import KeyboardInput from './demo/demoScreen';
3 |
4 | AppRegistry.registerComponent('KeyboardInput', () => KeyboardInput);
5 |
--------------------------------------------------------------------------------
/ios/KeyboardInput-tvOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSExceptionDomains
28 |
29 | localhost
30 |
31 | NSExceptionAllowsInsecureHTTPLoads
32 |
33 |
34 |
35 |
36 | NSLocationWhenInUseUsageDescription
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | armv7
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/ios/KeyboardInput-tvOSTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/KeyboardInput.xcodeproj/xcshareddata/xcschemes/KeyboardInput-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
94 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
113 |
119 |
120 |
121 |
122 |
124 |
125 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/ios/KeyboardInput.xcodeproj/xcshareddata/xcschemes/KeyboardInput.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
94 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
113 |
119 |
120 |
121 |
122 |
124 |
125 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/ios/KeyboardInput.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/KeyboardInput.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/KeyboardInput/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (nonatomic, strong) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/KeyboardInput/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import "AppDelegate.h"
9 |
10 | #import
11 | #import
12 | #import
13 |
14 | @implementation AppDelegate
15 |
16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
17 | {
18 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
19 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
20 | moduleName:@"KeyboardInput"
21 | initialProperties:nil];
22 |
23 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
24 |
25 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
26 | UIViewController *rootViewController = [UIViewController new];
27 | rootViewController.view = rootView;
28 | self.window.rootViewController = rootViewController;
29 | [self.window makeKeyAndVisible];
30 | return YES;
31 | }
32 |
33 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
34 | {
35 | #if DEBUG
36 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
37 | #else
38 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
39 | #endif
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ios/KeyboardInput/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/ios/KeyboardInput/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/ios/KeyboardInput/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ios/KeyboardInput/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | KeyboardInput
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSExceptionDomains
32 |
33 | localhost
34 |
35 | NSExceptionAllowsInsecureHTTPLoads
36 |
37 |
38 |
39 |
40 | NSLocationWhenInUseUsageDescription
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 | UIViewControllerBasedStatusBarAppearance
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/ios/KeyboardInput/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 |
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ios/KeyboardInputTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/KeyboardInputTests/KeyboardInputTests.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 | #import
10 |
11 | #import
12 | #import
13 |
14 | #define TIMEOUT_SECONDS 600
15 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
16 |
17 | @interface KeyboardInputTests : XCTestCase
18 |
19 | @end
20 |
21 | @implementation KeyboardInputTests
22 |
23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
24 | {
25 | if (test(view)) {
26 | return YES;
27 | }
28 | for (UIView *subview in [view subviews]) {
29 | if ([self findSubviewInView:subview matching:test]) {
30 | return YES;
31 | }
32 | }
33 | return NO;
34 | }
35 |
36 | - (void)testRendersWelcomeScreen
37 | {
38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
40 | BOOL foundElement = NO;
41 |
42 | __block NSString *redboxError = nil;
43 | #ifdef DEBUG
44 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
45 | if (level >= RCTLogLevelError) {
46 | redboxError = message;
47 | }
48 | });
49 | #endif
50 |
51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
54 |
55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
57 | return YES;
58 | }
59 | return NO;
60 | }];
61 | }
62 |
63 | #ifdef DEBUG
64 | RCTSetLogFunction(RCTDefaultLogFunction);
65 | #endif
66 |
67 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
68 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
69 | }
70 |
71 |
72 | @end
73 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '9.0'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | target 'KeyboardInput' do
5 |
6 | pod 'ReactNativeKeyboardInput', :path => '../ReactNativeKeyboardInput.podspec'
7 |
8 | # Pods for KeyboardInput
9 | pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
10 | pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
11 | pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
12 | pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
13 | pod 'React', :path => '../node_modules/react-native/'
14 | pod 'React-Core', :path => '../node_modules/react-native/'
15 | pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
16 | pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
17 | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
18 | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
19 | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
20 | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
21 | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
22 | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
23 | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
24 | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
25 | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
26 | pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
27 |
28 | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
29 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
30 | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
31 | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
32 | pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon"
33 | pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
34 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
35 |
36 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
37 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
38 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
39 |
40 | use_native_modules!
41 | end
42 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - boost-for-react-native (1.63.0)
3 | - DoubleConversion (1.1.6)
4 | - FBLazyVector (0.61.4)
5 | - FBReactNativeSpec (0.61.4):
6 | - Folly (= 2018.10.22.00)
7 | - RCTRequired (= 0.61.4)
8 | - RCTTypeSafety (= 0.61.4)
9 | - React-Core (= 0.61.4)
10 | - React-jsi (= 0.61.4)
11 | - ReactCommon/turbomodule/core (= 0.61.4)
12 | - Folly (2018.10.22.00):
13 | - boost-for-react-native
14 | - DoubleConversion
15 | - Folly/Default (= 2018.10.22.00)
16 | - glog
17 | - Folly/Default (2018.10.22.00):
18 | - boost-for-react-native
19 | - DoubleConversion
20 | - glog
21 | - glog (0.3.5)
22 | - RCTRequired (0.61.4)
23 | - RCTTypeSafety (0.61.4):
24 | - FBLazyVector (= 0.61.4)
25 | - Folly (= 2018.10.22.00)
26 | - RCTRequired (= 0.61.4)
27 | - React-Core (= 0.61.4)
28 | - React (0.61.4):
29 | - React-Core (= 0.61.4)
30 | - React-Core/DevSupport (= 0.61.4)
31 | - React-Core/RCTWebSocket (= 0.61.4)
32 | - React-RCTActionSheet (= 0.61.4)
33 | - React-RCTAnimation (= 0.61.4)
34 | - React-RCTBlob (= 0.61.4)
35 | - React-RCTImage (= 0.61.4)
36 | - React-RCTLinking (= 0.61.4)
37 | - React-RCTNetwork (= 0.61.4)
38 | - React-RCTSettings (= 0.61.4)
39 | - React-RCTText (= 0.61.4)
40 | - React-RCTVibration (= 0.61.4)
41 | - React-Core (0.61.4):
42 | - Folly (= 2018.10.22.00)
43 | - glog
44 | - React-Core/Default (= 0.61.4)
45 | - React-cxxreact (= 0.61.4)
46 | - React-jsi (= 0.61.4)
47 | - React-jsiexecutor (= 0.61.4)
48 | - Yoga
49 | - React-Core/CoreModulesHeaders (0.61.4):
50 | - Folly (= 2018.10.22.00)
51 | - glog
52 | - React-Core/Default
53 | - React-cxxreact (= 0.61.4)
54 | - React-jsi (= 0.61.4)
55 | - React-jsiexecutor (= 0.61.4)
56 | - Yoga
57 | - React-Core/Default (0.61.4):
58 | - Folly (= 2018.10.22.00)
59 | - glog
60 | - React-cxxreact (= 0.61.4)
61 | - React-jsi (= 0.61.4)
62 | - React-jsiexecutor (= 0.61.4)
63 | - Yoga
64 | - React-Core/DevSupport (0.61.4):
65 | - Folly (= 2018.10.22.00)
66 | - glog
67 | - React-Core/Default (= 0.61.4)
68 | - React-Core/RCTWebSocket (= 0.61.4)
69 | - React-cxxreact (= 0.61.4)
70 | - React-jsi (= 0.61.4)
71 | - React-jsiexecutor (= 0.61.4)
72 | - React-jsinspector (= 0.61.4)
73 | - Yoga
74 | - React-Core/RCTActionSheetHeaders (0.61.4):
75 | - Folly (= 2018.10.22.00)
76 | - glog
77 | - React-Core/Default
78 | - React-cxxreact (= 0.61.4)
79 | - React-jsi (= 0.61.4)
80 | - React-jsiexecutor (= 0.61.4)
81 | - Yoga
82 | - React-Core/RCTAnimationHeaders (0.61.4):
83 | - Folly (= 2018.10.22.00)
84 | - glog
85 | - React-Core/Default
86 | - React-cxxreact (= 0.61.4)
87 | - React-jsi (= 0.61.4)
88 | - React-jsiexecutor (= 0.61.4)
89 | - Yoga
90 | - React-Core/RCTBlobHeaders (0.61.4):
91 | - Folly (= 2018.10.22.00)
92 | - glog
93 | - React-Core/Default
94 | - React-cxxreact (= 0.61.4)
95 | - React-jsi (= 0.61.4)
96 | - React-jsiexecutor (= 0.61.4)
97 | - Yoga
98 | - React-Core/RCTImageHeaders (0.61.4):
99 | - Folly (= 2018.10.22.00)
100 | - glog
101 | - React-Core/Default
102 | - React-cxxreact (= 0.61.4)
103 | - React-jsi (= 0.61.4)
104 | - React-jsiexecutor (= 0.61.4)
105 | - Yoga
106 | - React-Core/RCTLinkingHeaders (0.61.4):
107 | - Folly (= 2018.10.22.00)
108 | - glog
109 | - React-Core/Default
110 | - React-cxxreact (= 0.61.4)
111 | - React-jsi (= 0.61.4)
112 | - React-jsiexecutor (= 0.61.4)
113 | - Yoga
114 | - React-Core/RCTNetworkHeaders (0.61.4):
115 | - Folly (= 2018.10.22.00)
116 | - glog
117 | - React-Core/Default
118 | - React-cxxreact (= 0.61.4)
119 | - React-jsi (= 0.61.4)
120 | - React-jsiexecutor (= 0.61.4)
121 | - Yoga
122 | - React-Core/RCTSettingsHeaders (0.61.4):
123 | - Folly (= 2018.10.22.00)
124 | - glog
125 | - React-Core/Default
126 | - React-cxxreact (= 0.61.4)
127 | - React-jsi (= 0.61.4)
128 | - React-jsiexecutor (= 0.61.4)
129 | - Yoga
130 | - React-Core/RCTTextHeaders (0.61.4):
131 | - Folly (= 2018.10.22.00)
132 | - glog
133 | - React-Core/Default
134 | - React-cxxreact (= 0.61.4)
135 | - React-jsi (= 0.61.4)
136 | - React-jsiexecutor (= 0.61.4)
137 | - Yoga
138 | - React-Core/RCTVibrationHeaders (0.61.4):
139 | - Folly (= 2018.10.22.00)
140 | - glog
141 | - React-Core/Default
142 | - React-cxxreact (= 0.61.4)
143 | - React-jsi (= 0.61.4)
144 | - React-jsiexecutor (= 0.61.4)
145 | - Yoga
146 | - React-Core/RCTWebSocket (0.61.4):
147 | - Folly (= 2018.10.22.00)
148 | - glog
149 | - React-Core/Default (= 0.61.4)
150 | - React-cxxreact (= 0.61.4)
151 | - React-jsi (= 0.61.4)
152 | - React-jsiexecutor (= 0.61.4)
153 | - Yoga
154 | - React-CoreModules (0.61.4):
155 | - FBReactNativeSpec (= 0.61.4)
156 | - Folly (= 2018.10.22.00)
157 | - RCTTypeSafety (= 0.61.4)
158 | - React-Core/CoreModulesHeaders (= 0.61.4)
159 | - React-RCTImage (= 0.61.4)
160 | - ReactCommon/turbomodule/core (= 0.61.4)
161 | - React-cxxreact (0.61.4):
162 | - boost-for-react-native (= 1.63.0)
163 | - DoubleConversion
164 | - Folly (= 2018.10.22.00)
165 | - glog
166 | - React-jsinspector (= 0.61.4)
167 | - React-jsi (0.61.4):
168 | - boost-for-react-native (= 1.63.0)
169 | - DoubleConversion
170 | - Folly (= 2018.10.22.00)
171 | - glog
172 | - React-jsi/Default (= 0.61.4)
173 | - React-jsi/Default (0.61.4):
174 | - boost-for-react-native (= 1.63.0)
175 | - DoubleConversion
176 | - Folly (= 2018.10.22.00)
177 | - glog
178 | - React-jsiexecutor (0.61.4):
179 | - DoubleConversion
180 | - Folly (= 2018.10.22.00)
181 | - glog
182 | - React-cxxreact (= 0.61.4)
183 | - React-jsi (= 0.61.4)
184 | - React-jsinspector (0.61.4)
185 | - React-RCTActionSheet (0.61.4):
186 | - React-Core/RCTActionSheetHeaders (= 0.61.4)
187 | - React-RCTAnimation (0.61.4):
188 | - React-Core/RCTAnimationHeaders (= 0.61.4)
189 | - React-RCTBlob (0.61.4):
190 | - React-Core/RCTBlobHeaders (= 0.61.4)
191 | - React-Core/RCTWebSocket (= 0.61.4)
192 | - React-jsi (= 0.61.4)
193 | - React-RCTNetwork (= 0.61.4)
194 | - React-RCTImage (0.61.4):
195 | - React-Core/RCTImageHeaders (= 0.61.4)
196 | - React-RCTNetwork (= 0.61.4)
197 | - React-RCTLinking (0.61.4):
198 | - React-Core/RCTLinkingHeaders (= 0.61.4)
199 | - React-RCTNetwork (0.61.4):
200 | - React-Core/RCTNetworkHeaders (= 0.61.4)
201 | - React-RCTSettings (0.61.4):
202 | - React-Core/RCTSettingsHeaders (= 0.61.4)
203 | - React-RCTText (0.61.4):
204 | - React-Core/RCTTextHeaders (= 0.61.4)
205 | - React-RCTVibration (0.61.4):
206 | - React-Core/RCTVibrationHeaders (= 0.61.4)
207 | - ReactCommon/jscallinvoker (0.61.4):
208 | - DoubleConversion
209 | - Folly (= 2018.10.22.00)
210 | - glog
211 | - React-cxxreact (= 0.61.4)
212 | - ReactCommon/turbomodule/core (0.61.4):
213 | - DoubleConversion
214 | - Folly (= 2018.10.22.00)
215 | - glog
216 | - React-Core (= 0.61.4)
217 | - React-cxxreact (= 0.61.4)
218 | - React-jsi (= 0.61.4)
219 | - ReactCommon/jscallinvoker (= 0.61.4)
220 | - ReactNativeKeyboardInput (6.0.1):
221 | - React
222 | - ReactNativeKeyboardTrackingView (5.6.1):
223 | - React
224 | - ReactNativeNavigation (3.5.1):
225 | - React
226 | - Yoga (1.14.0)
227 |
228 | DEPENDENCIES:
229 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
230 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
231 | - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
232 | - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
233 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
234 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
235 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
236 | - React (from `../node_modules/react-native/`)
237 | - React-Core (from `../node_modules/react-native/`)
238 | - React-Core/DevSupport (from `../node_modules/react-native/`)
239 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
240 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
241 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
242 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
243 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
244 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
245 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
246 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
247 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
248 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
249 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
250 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
251 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
252 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
253 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
254 | - ReactCommon/jscallinvoker (from `../node_modules/react-native/ReactCommon`)
255 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
256 | - ReactNativeKeyboardInput (from `../ReactNativeKeyboardInput.podspec`)
257 | - ReactNativeKeyboardTrackingView (from `../node_modules/react-native-keyboard-tracking-view`)
258 | - ReactNativeNavigation (from `../node_modules/react-native-navigation`)
259 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
260 |
261 | SPEC REPOS:
262 | trunk:
263 | - boost-for-react-native
264 |
265 | EXTERNAL SOURCES:
266 | DoubleConversion:
267 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
268 | FBLazyVector:
269 | :path: "../node_modules/react-native/Libraries/FBLazyVector"
270 | FBReactNativeSpec:
271 | :path: "../node_modules/react-native/Libraries/FBReactNativeSpec"
272 | Folly:
273 | :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
274 | glog:
275 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
276 | RCTRequired:
277 | :path: "../node_modules/react-native/Libraries/RCTRequired"
278 | RCTTypeSafety:
279 | :path: "../node_modules/react-native/Libraries/TypeSafety"
280 | React:
281 | :path: "../node_modules/react-native/"
282 | React-Core:
283 | :path: "../node_modules/react-native/"
284 | React-CoreModules:
285 | :path: "../node_modules/react-native/React/CoreModules"
286 | React-cxxreact:
287 | :path: "../node_modules/react-native/ReactCommon/cxxreact"
288 | React-jsi:
289 | :path: "../node_modules/react-native/ReactCommon/jsi"
290 | React-jsiexecutor:
291 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
292 | React-jsinspector:
293 | :path: "../node_modules/react-native/ReactCommon/jsinspector"
294 | React-RCTActionSheet:
295 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
296 | React-RCTAnimation:
297 | :path: "../node_modules/react-native/Libraries/NativeAnimation"
298 | React-RCTBlob:
299 | :path: "../node_modules/react-native/Libraries/Blob"
300 | React-RCTImage:
301 | :path: "../node_modules/react-native/Libraries/Image"
302 | React-RCTLinking:
303 | :path: "../node_modules/react-native/Libraries/LinkingIOS"
304 | React-RCTNetwork:
305 | :path: "../node_modules/react-native/Libraries/Network"
306 | React-RCTSettings:
307 | :path: "../node_modules/react-native/Libraries/Settings"
308 | React-RCTText:
309 | :path: "../node_modules/react-native/Libraries/Text"
310 | React-RCTVibration:
311 | :path: "../node_modules/react-native/Libraries/Vibration"
312 | ReactCommon:
313 | :path: "../node_modules/react-native/ReactCommon"
314 | ReactNativeKeyboardInput:
315 | :path: "../ReactNativeKeyboardInput.podspec"
316 | ReactNativeKeyboardTrackingView:
317 | :path: "../node_modules/react-native-keyboard-tracking-view"
318 | ReactNativeNavigation:
319 | :path: "../node_modules/react-native-navigation"
320 | Yoga:
321 | :path: "../node_modules/react-native/ReactCommon/yoga"
322 |
323 | SPEC CHECKSUMS:
324 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
325 | DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
326 | FBLazyVector: feb35a6b7f7b50f367be07f34012f34a79282fa3
327 | FBReactNativeSpec: 51477b84b1bf7ab6f9ef307c24e3dd675391be44
328 | Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
329 | glog: 1f3da668190260b06b429bb211bfbee5cd790c28
330 | RCTRequired: f3b3fb6f4723e8e52facb229d0c75fdc76773849
331 | RCTTypeSafety: 2ec60de6abb1db050b56ecc4b60188026078fd10
332 | React: 10e0130b57e55a7cd8c3dee37c1261102ce295f4
333 | React-Core: 636212410772d05f3a1eb79d965df2962ca1c70b
334 | React-CoreModules: 6f70d5e41919289c582f88c9ad9923fe5c87400a
335 | React-cxxreact: ddecbe9157ec1743f52ea17bf8d95debc0d6e846
336 | React-jsi: ca921f4041505f9d5197139b2d09eeb020bb12e8
337 | React-jsiexecutor: 8dfb73b987afa9324e4009bdce62a18ce23d983c
338 | React-jsinspector: d15478d0a8ada19864aa4d1cc1c697b41b3fa92f
339 | React-RCTActionSheet: 7369b7c85f99b6299491333affd9f01f5a130c22
340 | React-RCTAnimation: d07be15b2bd1d06d89417eb0343f98ffd2b099a7
341 | React-RCTBlob: 8e0b23d95c9baa98f6b0e127e07666aaafd96c34
342 | React-RCTImage: 443050d14a66e8c2332e9c055f45689d23e15cc7
343 | React-RCTLinking: ce9a90ba155aec41be49e75ec721bbae2d48a47e
344 | React-RCTNetwork: 41fe54bacc67dd00e6e4c4d30dd98a13e4beabc8
345 | React-RCTSettings: 45e3e0a6470310b2dab2ccc6d1d73121ba3ea936
346 | React-RCTText: 21934e0a51d522abcd0a275407e80af45d6fd9ec
347 | React-RCTVibration: 0f76400ee3cec6edb9c125da49fed279340d145a
348 | ReactCommon: a6a294e7028ed67b926d29551aa9394fd989c24c
349 | ReactNativeKeyboardInput: 266ba27a2e9921f5bdc0b4cc30289b2a2f46b157
350 | ReactNativeKeyboardTrackingView: a240a6a0dba852bb107109a7ec7e98b884055977
351 | ReactNativeNavigation: 3fad99b3843e8840affd70577aaa30bf14b272d5
352 | Yoga: ba3d99dbee6c15ea6bbe3783d1f0cb1ffb79af0f
353 |
354 | PODFILE CHECKSUM: db72c512534d095b2d49d5dbe372d7ea041b7645
355 |
356 | COCOAPODS: 1.9.3
357 |
--------------------------------------------------------------------------------
/lib/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "28.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation 'com.facebook.react:react-native:+'
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/lib/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/AppContextHolder.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.os.Bundle;
6 |
7 | public class AppContextHolder {
8 |
9 | private static Activity sCurrentActivity;
10 |
11 | public static void setApplication(Application application) {
12 | application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
13 | @Override
14 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
15 | sCurrentActivity = activity;
16 | }
17 |
18 | @Override
19 | public void onActivityStarted(Activity activity) {
20 | sCurrentActivity = activity;
21 | }
22 |
23 | @Override
24 | public void onActivityResumed(Activity activity) {
25 | sCurrentActivity = activity;
26 | }
27 |
28 | @Override
29 | public void onActivityPaused(Activity activity) {
30 | }
31 |
32 | @Override
33 | public void onActivityStopped(Activity activity) {
34 | }
35 |
36 | @Override
37 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
38 | }
39 |
40 | @Override
41 | public void onActivityDestroyed(Activity activity) {
42 | if (sCurrentActivity == activity) {
43 | sCurrentActivity = null;
44 | }
45 | }
46 | });
47 | }
48 |
49 | public static Activity getCurrentActivity() {
50 | return sCurrentActivity;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/CustomKeyboardLayout.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.view.WindowManager;
6 | import android.view.inputmethod.InputMethodManager;
7 | import android.widget.EditText;
8 |
9 | import com.facebook.react.ReactRootView;
10 | import com.facebook.react.bridge.Promise;
11 | import com.facebook.react.bridge.ReactContext;
12 | import com.facebook.react.modules.core.DeviceEventManagerModule;
13 | import com.wix.reactnativekeyboardinput.utils.Logger;
14 |
15 | import java.lang.ref.WeakReference;
16 |
17 | import static com.wix.reactnativekeyboardinput.AppContextHolder.getCurrentActivity;
18 | import static com.wix.reactnativekeyboardinput.GlobalDefs.TAG;
19 | import static com.wix.reactnativekeyboardinput.utils.RuntimeUtils.dispatchUIUpdates;
20 | import static com.wix.reactnativekeyboardinput.utils.RuntimeUtils.runOnUIThread;
21 | import static com.wix.reactnativekeyboardinput.utils.ViewUtils.getWindow;
22 |
23 | public class CustomKeyboardLayout implements ReactSoftKeyboardMonitor.Listener, ReactScreenMonitor.Listener {
24 | private boolean mFirstKeyboardShow = true;
25 | private final InputMethodManager mInputMethodManager;
26 | private final ReactSoftKeyboardMonitor mKeyboardMonitor;
27 | private WeakReference mShadowNode = new WeakReference<>(null);
28 |
29 | public CustomKeyboardLayout(ReactContext reactContext, ReactSoftKeyboardMonitor keyboardMonitor, ReactScreenMonitor screenMonitor) {
30 | mKeyboardMonitor = keyboardMonitor;
31 | mInputMethodManager = (InputMethodManager) reactContext.getSystemService(Context.INPUT_METHOD_SERVICE);
32 |
33 | mKeyboardMonitor.setListener(this);
34 | screenMonitor.addListener(this);
35 | }
36 |
37 | @Override
38 | public void onSoftKeyboardVisible(boolean distinct) {
39 | if (distinct) {
40 | clearKeyboardOverlayMode();
41 | }
42 | hideCustomKeyboardContent();
43 | }
44 |
45 | @Override
46 | public void onSoftKeyboardHidden() {
47 | if (getShadowNodeHeight() == 0) {
48 | mFirstKeyboardShow = true;
49 | }
50 | }
51 |
52 | @Override
53 | public void onNewReactScreen(ReactRootView reactRootView) {
54 | clearKeyboardOverlayMode();
55 | if (reactRootView != null) {
56 | sendCustomKeyboardResignedEvent();
57 | }
58 | }
59 |
60 | public void setShadowNode(CustomKeyboardRootViewShadow node) {
61 | Logger.v(TAG, "New shadow node: " + node);
62 | mShadowNode = new WeakReference<>(node);
63 | }
64 |
65 | public void onKeyboardHasCustomContent() {
66 | runOnUIThread(new Runnable() {
67 | @Override
68 | public void run() {
69 | showCustomKeyboardContent();
70 | setKeyboardOverlayMode();
71 | hideSoftKeyboardIfNeeded();
72 | }
73 | });
74 | }
75 |
76 | public void forceReset(final Promise promise) {
77 | runOnUIThread(new Runnable() {
78 | @Override
79 | public void run() {
80 | final View focusedView = getCurrentActivity().getCurrentFocus();
81 | if (focusedView instanceof EditText) {
82 | showSoftKeyboard();
83 | } else {
84 | hideCustomKeyboardContent();
85 | clearKeyboardOverlayMode();
86 | }
87 | promise.resolve(null);
88 | }
89 | });
90 | }
91 |
92 | public void clearFocusedView() {
93 | runOnUIThread(new Runnable() {
94 | @Override
95 | public void run() {
96 | final View focusedView = getCurrentActivity().getCurrentFocus();
97 | if (focusedView != null) {
98 | focusedView.clearFocus();
99 | }
100 | }
101 | });
102 | }
103 |
104 | private void showCustomKeyboardContent() {
105 | setCustomKeyboardHeight(getHeightForCustomContent());
106 | }
107 |
108 | private void hideCustomKeyboardContent() {
109 | setCustomKeyboardHeight(0);
110 | runOnUIThread(new Runnable() {
111 | @Override
112 | public void run() {
113 | sendCustomKeyboardResignedEvent();
114 | }
115 | });
116 | }
117 |
118 | private void syncCustomKeyboardHeightAfterUIUpdate(final int height) {
119 | dispatchUIUpdates(new Runnable() {
120 | @Override
121 | public void run() {
122 | setShadowNodeHeight(height);
123 | }
124 | });
125 | }
126 |
127 | private void setCustomKeyboardHeight(int height) {
128 | try {
129 | if (mFirstKeyboardShow) {
130 | mFirstKeyboardShow = false;
131 | syncCustomKeyboardHeightAfterUIUpdate(height);
132 | } else {
133 | setShadowNodeHeight(height);
134 | }
135 | } catch (Exception e) {
136 | e.printStackTrace();
137 | }
138 | }
139 |
140 | private void setShadowNodeHeight(int height) {
141 | final CustomKeyboardRootViewShadow shadowNode = mShadowNode.get();
142 | if (shadowNode != null) {
143 | shadowNode.setHeight(height);
144 | }
145 | }
146 |
147 | private float getShadowNodeHeight() {
148 | float height = 0;
149 | final CustomKeyboardRootViewShadow shadowNode = mShadowNode.get();
150 | if (shadowNode != null) {
151 | height = shadowNode.getHeight();
152 | }
153 | return height;
154 | }
155 |
156 | private void showSoftKeyboard() {
157 | mInputMethodManager.showSoftInput(getCurrentActivity().getCurrentFocus(), 0);
158 | }
159 |
160 | private void hideSoftKeyboardIfNeeded() {
161 | final View focusedView = getCurrentActivity().getCurrentFocus();
162 | if (focusedView != null) {
163 | mInputMethodManager.hideSoftInputFromWindow(focusedView.getWindowToken(), 0);
164 | }
165 | }
166 |
167 | private int getHeightForCustomContent() {
168 | return mKeyboardMonitor.getKeyboardHeight();
169 | }
170 |
171 | private void setKeyboardOverlayMode() {
172 | getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
173 | }
174 |
175 | private void clearKeyboardOverlayMode() {
176 | getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
177 | }
178 |
179 | private void sendCustomKeyboardResignedEvent() {
180 | Logger.v(TAG, "Notifying the custom-keyboard-resigned event to JS");
181 | if (ReactContextHolder.getContext().hasActiveCatalystInstance()) {
182 | ReactContextHolder.getContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("kbdResigned", null);
183 | }
184 | }
185 |
186 | }
187 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/CustomKeyboardRootView.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.widget.FrameLayout;
6 |
7 | import androidx.annotation.NonNull;
8 |
9 | import com.wix.reactnativekeyboardinput.utils.Logger;
10 |
11 | import static com.wix.reactnativekeyboardinput.GlobalDefs.TAG;
12 |
13 | public class CustomKeyboardRootView extends FrameLayout {
14 |
15 | private final CustomKeyboardLayout mLayout;
16 |
17 | public CustomKeyboardRootView(@NonNull Context context, CustomKeyboardLayout layout) {
18 | super(context);
19 | mLayout = layout;
20 |
21 | setWillNotDraw(false);
22 | }
23 |
24 | @Override
25 | public void onViewAdded(View child) {
26 | if (getChildCount() == 1) {
27 | Logger.d(TAG, "New custom keyboard content");
28 | mLayout.onKeyboardHasCustomContent();
29 | }
30 | super.onViewAdded(child);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/CustomKeyboardRootViewManager.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import com.facebook.react.uimanager.LayoutShadowNode;
4 | import com.facebook.react.uimanager.ThemedReactContext;
5 | import com.facebook.react.uimanager.ViewGroupManager;
6 |
7 | public class CustomKeyboardRootViewManager extends ViewGroupManager {
8 |
9 | private final CustomKeyboardLayout mLayout;
10 |
11 | public CustomKeyboardRootViewManager(CustomKeyboardLayout layout) {
12 | mLayout = layout;
13 | }
14 |
15 | @Override
16 | public String getName() {
17 | return "CustomKeyboardViewNative";
18 | }
19 |
20 | @Override
21 | public CustomKeyboardRootView createViewInstance(ThemedReactContext reactContext) {
22 | return new CustomKeyboardRootView(reactContext, mLayout);
23 | }
24 |
25 | @Override
26 | public LayoutShadowNode createShadowNodeInstance() {
27 | return new CustomKeyboardRootViewShadow(mLayout);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/CustomKeyboardRootViewShadow.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import com.facebook.react.uimanager.LayoutShadowNode;
4 | import com.facebook.react.uimanager.NativeViewHierarchyOptimizer;
5 |
6 | public class CustomKeyboardRootViewShadow extends LayoutShadowNode {
7 |
8 | private final CustomKeyboardLayout mLayout;
9 |
10 | CustomKeyboardRootViewShadow(CustomKeyboardLayout layout) {
11 | setStyleHeight(0);
12 |
13 | mLayout = layout;
14 | mLayout.setShadowNode(this);
15 | }
16 |
17 | @Override
18 | public void onBeforeLayout(NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer) {
19 | mLayout.setShadowNode(this);
20 | }
21 |
22 | public void setHeight(int heightPx) {
23 | setStyleHeight(heightPx);
24 | }
25 |
26 | public float getHeight() {
27 | return getStyleHeight().value;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/GlobalDefs.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | public interface GlobalDefs {
4 | String TAG = "RCTKeyboardInput";
5 | }
6 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/KeyboardInputModule.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import com.facebook.react.bridge.Promise;
4 | import com.facebook.react.bridge.ReactApplicationContext;
5 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
6 | import com.facebook.react.bridge.ReactMethod;
7 |
8 | public class KeyboardInputModule extends ReactContextBaseJavaModule {
9 |
10 | private static final String REACT_CLASS = "CustomKeyboardInput";
11 |
12 | private final CustomKeyboardLayout mLayout;
13 |
14 | public KeyboardInputModule(ReactApplicationContext reactContext, CustomKeyboardLayout layout) {
15 | super(reactContext);
16 |
17 | mLayout = layout;
18 | }
19 |
20 | @Override
21 | public String getName() {
22 | return REACT_CLASS;
23 | }
24 |
25 | @ReactMethod
26 | public void reset(Promise promise) {
27 | mLayout.forceReset(promise);
28 | }
29 |
30 | @ReactMethod
31 | public void clearFocusedView() {
32 | mLayout.clearFocusedView();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/KeyboardInputPackage.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import android.app.Application;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.JavaScriptModule;
7 | import com.facebook.react.bridge.NativeModule;
8 | import com.facebook.react.bridge.ReactApplicationContext;
9 | import com.facebook.react.uimanager.ViewManager;
10 | import com.wix.reactnativekeyboardinput.utils.Logger;
11 |
12 | import java.util.Arrays;
13 | import java.util.Collections;
14 | import java.util.List;
15 |
16 | public class KeyboardInputPackage implements ReactPackage {
17 |
18 | private CustomKeyboardLayout mLayout;
19 |
20 | public KeyboardInputPackage(Application application) {
21 | AppContextHolder.setApplication(application);
22 | }
23 |
24 | public KeyboardInputPackage(Application application, boolean enableLogging) {
25 | this(application);
26 | if (enableLogging) {
27 | Logger.enable();
28 | }
29 | }
30 |
31 | @Override
32 | public List createNativeModules(ReactApplicationContext reactContext) {
33 | init(reactContext);
34 | return Arrays.asList(new KeyboardInputModule(reactContext, mLayout));
35 | }
36 |
37 | @Override
38 | public List createViewManagers(ReactApplicationContext reactContext) {
39 | init(reactContext);
40 | return Arrays.asList(new CustomKeyboardRootViewManager(mLayout));
41 | }
42 |
43 | // Deprecated in RN 0.47
44 | public List> createJSModules() {
45 | return Collections.emptyList();
46 | }
47 |
48 | private synchronized void init(ReactApplicationContext reactContext) {
49 | if (ReactContextHolder.getContext() != reactContext) {
50 | ReactContextHolder.setContext(reactContext);
51 |
52 | final ReactScreenMonitor screenMonitor = new ReactScreenMonitor(reactContext);
53 | final ReactSoftKeyboardMonitor keyboardMonitor = new ReactSoftKeyboardMonitor(screenMonitor);
54 | mLayout = new CustomKeyboardLayout(reactContext, keyboardMonitor, screenMonitor);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/ReactContextHolder.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import com.facebook.react.bridge.ReactApplicationContext;
4 |
5 | public class ReactContextHolder {
6 |
7 | private static ReactApplicationContext sContext;
8 |
9 | public static void setContext(ReactApplicationContext context) {
10 | sContext = context;
11 | }
12 |
13 | public static ReactApplicationContext getContext() {
14 | return sContext;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/ReactScreenMonitor.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import android.view.ViewTreeObserver;
4 |
5 | import com.facebook.react.ReactRootView;
6 | import com.facebook.react.bridge.LifecycleEventListener;
7 | import com.facebook.react.bridge.ReactContext;
8 | import com.wix.reactnativekeyboardinput.utils.ViewUtils;
9 |
10 | import java.util.HashSet;
11 | import java.util.Set;
12 |
13 | import static com.wix.reactnativekeyboardinput.utils.ViewUtils.getWindow;
14 |
15 | public class ReactScreenMonitor implements LifecycleEventListener {
16 |
17 | public interface Listener {
18 | void onNewReactScreen(ReactRootView reactRootView);
19 | }
20 |
21 | private final ViewTreeObserver.OnGlobalLayoutListener mWindowLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
22 | @Override
23 | public void onGlobalLayout() {
24 | final ReactRootView reactRootView = ViewUtils.getReactRootView();
25 | if (mLastReactRootView != reactRootView) {
26 | mLastReactRootView = reactRootView;
27 | notifyNewScreen();
28 | }
29 | }
30 | };
31 |
32 | private ReactRootView mLastReactRootView;
33 | private Set mExternalListeners = new HashSet<>();
34 |
35 | private boolean mHasWindowLayoutListener;
36 |
37 | public ReactScreenMonitor(ReactContext reactContext) {
38 | reactContext.addLifecycleEventListener(this);
39 | }
40 |
41 | public void addListener(Listener listener) {
42 | mExternalListeners.add(listener);
43 | }
44 |
45 | @Override
46 | public void onHostResume() {
47 | if (mHasWindowLayoutListener) {
48 | removeWindowLayoutListener();
49 | }
50 | mHasWindowLayoutListener = true;
51 | registerWindowLayoutListener();
52 | }
53 |
54 | @Override
55 | public void onHostDestroy() {
56 | removeWindowLayoutListener();
57 | mHasWindowLayoutListener = false;
58 | }
59 |
60 | @Override
61 | public void onHostPause() {
62 | }
63 |
64 | private void registerWindowLayoutListener() {
65 | getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(mWindowLayoutListener);
66 | }
67 |
68 | private void removeWindowLayoutListener() {
69 | if (getWindow() == null) {
70 | // No window => no activity => nothing to clear.
71 | return;
72 | }
73 | getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(mWindowLayoutListener);
74 | }
75 |
76 | private void notifyNewScreen() {
77 | for (Listener listener : mExternalListeners) {
78 | listener.onNewReactScreen(mLastReactRootView);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/ReactSoftKeyboardMonitor.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput;
2 |
3 | import android.graphics.Rect;
4 | import android.view.ViewTreeObserver;
5 | import android.view.Window;
6 |
7 | import androidx.annotation.Nullable;
8 |
9 | import com.facebook.react.ReactRootView;
10 | import com.wix.reactnativekeyboardinput.utils.Logger;
11 | import com.wix.reactnativekeyboardinput.utils.RuntimeUtils;
12 |
13 | import static com.wix.reactnativekeyboardinput.GlobalDefs.TAG;
14 | import static com.wix.reactnativekeyboardinput.utils.ViewUtils.getWindow;
15 |
16 | public class ReactSoftKeyboardMonitor implements ReactScreenMonitor.Listener {
17 |
18 | public interface Listener {
19 | void onSoftKeyboardVisible(boolean distinct);
20 | void onSoftKeyboardHidden();
21 | }
22 |
23 | private final ViewTreeObserver.OnGlobalLayoutListener mInnerLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
24 | @Override
25 | public void onGlobalLayout() {
26 | Integer viewportVisibleHeight = getViewportVisibleHeight();
27 | if (viewportVisibleHeight == null || viewportVisibleHeight.equals(mLastViewportVisibleHeight)) {
28 | return;
29 | }
30 |
31 | mLastViewportVisibleHeight = viewportVisibleHeight;
32 | if (mMaxViewportVisibleHeight == null) {
33 | mMaxViewportVisibleHeight = viewportVisibleHeight;
34 | Logger.d(TAG, "mMaxViewportVisibleHeight WAS NULL, now is: " + mMaxViewportVisibleHeight);
35 | } else if (viewportVisibleHeight < mMaxViewportVisibleHeight) {
36 | mExternalListener.onSoftKeyboardVisible(!mSoftKeyboardUp);
37 | refreshKeyboardHeight();
38 | mSoftKeyboardUp = true;
39 | Logger.d(TAG, "Keyboard SHOWING!");
40 | } else {
41 | mSoftKeyboardUp = false;
42 | mExternalListener.onSoftKeyboardHidden();
43 | Logger.d(TAG, "Keyboard GONE!");
44 | }
45 | }
46 | };
47 |
48 | /**
49 | * Soft-keyboard appearance (yes or no) is deduced according to view-port (window-level display-frame), as
50 | * root-view height normally remains unaffected during immediate layout. We therefore keep the maximal view-port size so we could
51 | * concurrently compare heights in each layout.
52 | */
53 | private Integer mMaxViewportVisibleHeight;
54 |
55 | private Integer mLastViewportVisibleHeight;
56 |
57 | /**
58 | * Soft-keyboard *height* (when visible) is deduced by the effect on the root react-view height. This is ineffective in trying to
59 | * monitor keyboard appearance -- only for height measuring.
60 | */
61 | private Integer mLocallyVisibleHeight;
62 |
63 | private boolean mSoftKeyboardUp;
64 | private Integer mKeyboardHeight;
65 | private Listener mExternalListener;
66 | private ReactRootView mLastReactRootView;
67 |
68 | public ReactSoftKeyboardMonitor(ReactScreenMonitor screenMonitor) {
69 | screenMonitor.addListener(this);
70 | }
71 |
72 | @Override
73 | public void onNewReactScreen(ReactRootView reactRootView) {
74 | removeReactRootViewLayoutListener();
75 | mLastReactRootView = reactRootView;
76 |
77 | if (mLastReactRootView != null) { // 'Null' is applicable when activity is going down (e.g. bundle reload in RN dev mode)
78 | registerReactRootViewLayoutListener();
79 |
80 | initViewportVisibleHeight(); // TODO: running this each time might be redundant
81 | initLocallyVisibleHeight();
82 | }
83 | }
84 |
85 | public void setListener(Listener listener) {
86 | mExternalListener = listener;
87 | }
88 |
89 | @Nullable
90 | public Integer getKeyboardHeight() {
91 | if (mKeyboardHeight != null) {
92 | return mKeyboardHeight;
93 | }
94 |
95 | if (mLocallyVisibleHeight != null) {
96 | return (int) (.5f * mLocallyVisibleHeight);
97 | }
98 |
99 | Logger.d(TAG, "getKeyboardHeight, no keyboard height");
100 | return null;
101 | }
102 |
103 | private void registerReactRootViewLayoutListener() {
104 | final ViewTreeObserver viewTreeObserver = mLastReactRootView.getViewTreeObserver();
105 | viewTreeObserver.addOnGlobalLayoutListener(mInnerLayoutListener);
106 | }
107 |
108 | private void removeReactRootViewLayoutListener() {
109 | if (mLastReactRootView != null) {
110 | final ViewTreeObserver viewTreeObserver = mLastReactRootView.getViewTreeObserver();
111 | viewTreeObserver.removeOnGlobalLayoutListener(mInnerLayoutListener);
112 | }
113 | }
114 |
115 | private void initViewportVisibleHeight() {
116 | mMaxViewportVisibleHeight = getViewportVisibleHeight();
117 | mLastViewportVisibleHeight = null;
118 | Logger.d(TAG, "Measured new max view-port height: "+mMaxViewportVisibleHeight);
119 | }
120 |
121 | private void initLocallyVisibleHeight() {
122 | mLocallyVisibleHeight = getLocallyVisibleHeight();
123 | Logger.d(TAG, "Measured locally visible height: "+mLocallyVisibleHeight);
124 | mKeyboardHeight = null; // Reset so the keyboard would be measured in the next opportunity.
125 | }
126 |
127 | private void refreshKeyboardHeight() {
128 | if (mKeyboardHeight != null) {
129 | return;
130 | }
131 |
132 | RuntimeUtils.runOnUIThread(new Runnable() {
133 | @Override
134 | public void run() {
135 | final Integer locallyVisibleHeight = getLocallyVisibleHeight();
136 | if (locallyVisibleHeight == null) {
137 | // Too late to join the party - react-view seems to be gone...
138 | return;
139 | }
140 |
141 | if (mLocallyVisibleHeight == null) {
142 | mLocallyVisibleHeight = locallyVisibleHeight;
143 | mKeyboardHeight = mLocallyVisibleHeight;
144 | Logger.d(TAG, "mLocallyVisibleHeight WAS NULL, now is: " + mLocallyVisibleHeight);
145 | } else if (mLocallyVisibleHeight > locallyVisibleHeight) {
146 | mKeyboardHeight = mLocallyVisibleHeight - locallyVisibleHeight;
147 | } else {
148 | mKeyboardHeight = locallyVisibleHeight;
149 | Logger.d(TAG, "mKeyboardHeight = " + mKeyboardHeight + " mLocallyVisibleHeight = " + mLocallyVisibleHeight + " locallyVisibleHeight = " + locallyVisibleHeight);
150 | }
151 | }
152 | });
153 | }
154 |
155 | private Integer getViewportVisibleHeight() {
156 | Integer visibleHeight = null;
157 | final Rect visibleArea = new Rect();
158 | Window window = getWindow();
159 | if (window != null) {
160 | window.getDecorView().getWindowVisibleDisplayFrame(visibleArea);
161 | visibleHeight = visibleArea.height();
162 | }
163 |
164 | return visibleHeight;
165 | }
166 |
167 | private Integer getLocallyVisibleHeight() {
168 | if (mLastReactRootView != null) {
169 | return mLastReactRootView.getHeight();
170 | }
171 | return null;
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/utils/Logger.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput.utils;
2 |
3 | import android.util.Log;
4 |
5 | public class Logger {
6 |
7 | private static boolean sIsEnabled;
8 |
9 | public static void enable() {
10 | sIsEnabled = true;
11 | }
12 |
13 | public static void disable() {
14 | sIsEnabled = false;
15 | }
16 |
17 | public static void v(String tag, String message) {
18 | if (sIsEnabled) {
19 | Log.v(tag, message);
20 | }
21 | }
22 |
23 | public static void d(String tag, String message) {
24 | if (sIsEnabled) {
25 | Log.d(tag, message);
26 | }
27 | }
28 |
29 | public static void i(String tag, String message) {
30 | if (sIsEnabled) {
31 | Log.i(tag, message);
32 | }
33 | }
34 |
35 | public static void e(String tag, String message) {
36 | if (sIsEnabled) {
37 | Log.e(tag, message);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/utils/PredicateFunc.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput.utils;
2 |
3 | public interface PredicateFunc {
4 | boolean invoke(T element);
5 | }
6 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/utils/RuntimeUtils.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput.utils;
2 |
3 | import com.facebook.react.uimanager.UIManagerModule;
4 | import com.wix.reactnativekeyboardinput.ReactContextHolder;
5 |
6 | public class RuntimeUtils {
7 |
8 | // TODO Switch to GuardedRunnable when upgrading RN's minimal ver
9 | private static final Runnable sUIUpdateClosure = new Runnable() {
10 | @Override
11 | public void run() {
12 | ReactContextHolder.getContext().getNativeModule(UIManagerModule.class).onBatchComplete();
13 | }
14 | };
15 |
16 | public static void runOnUIThread(Runnable runnable) {
17 | ReactContextHolder.getContext().runOnUiQueueThread(runnable);
18 | }
19 |
20 | public static void dispatchUIUpdates(final Runnable userRunnable) {
21 | runOnUIThread(new Runnable() {
22 | @Override
23 | public void run() {
24 | userRunnable.run();
25 | ReactContextHolder.getContext().runOnNativeModulesQueueThread(sUIUpdateClosure);
26 | }
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/reactnativekeyboardinput/utils/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.wix.reactnativekeyboardinput.utils;
2 |
3 | import android.app.Activity;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.view.Window;
7 |
8 | import androidx.annotation.Nullable;
9 |
10 | import com.facebook.react.ReactRootView;
11 |
12 | import static com.wix.reactnativekeyboardinput.AppContextHolder.getCurrentActivity;
13 | import static com.wix.reactnativekeyboardinput.GlobalDefs.TAG;
14 |
15 | public class ViewUtils {
16 |
17 | private static class VisibleViewClassMatchPredicate implements PredicateFunc {
18 | private final Class mClazz;
19 |
20 | private VisibleViewClassMatchPredicate(Class clazz) {
21 | mClazz = clazz;
22 | }
23 |
24 | @Override
25 | public boolean invoke(View view) {
26 | return mClazz.isAssignableFrom(view.getClass()) && view.isShown();
27 | }
28 | }
29 | private static final VisibleViewClassMatchPredicate sVisibleReactRootViewMatcher = new VisibleViewClassMatchPredicate(ReactRootView.class);
30 |
31 | public static Window getWindow() {
32 | final Activity activity = getCurrentActivity();
33 | return (activity == null ? null : activity.getWindow());
34 | }
35 |
36 | public static ReactRootView getReactRootView() {
37 | final Window window = getWindow();
38 | if (window == null) {
39 | return null;
40 | }
41 |
42 | final ReactRootView view = findChildByClass((ViewGroup) window.getDecorView(), sVisibleReactRootViewMatcher);
43 | Logger.v(TAG, "Visible RCT view: " + (view != null ? view.hashCode() : null));
44 | return view;
45 | }
46 |
47 | /**
48 | * Returns the first instance of clazz in root for which predicate
is evaluated as true
.
49 | */
50 | @Nullable
51 | public static T findChildByClass(ViewGroup root, PredicateFunc predicate) {
52 | for (int i = 0; i < root.getChildCount(); i++) {
53 | View view = root.getChildAt(i);
54 | if (predicate.invoke(view)) {
55 | return ((T) view);
56 | }
57 |
58 | if (view instanceof ViewGroup) {
59 | view = findChildByClass((ViewGroup) view, predicate);
60 | if (view != null) {
61 | return (T) view;
62 | }
63 | }
64 | }
65 | return null;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/Color+Interpolation.h:
--------------------------------------------------------------------------------
1 | //
2 | // Color+Interpolation.h
3 | //
4 | // Created by Leo Natan on 01/10/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #if __has_include() || __has_include()
9 |
10 | #import "LNInterpolable.h"
11 |
12 | #if __has_include()
13 | #import
14 | #else
15 | #import
16 | #endif
17 |
18 | /**
19 | Interpolate using the LAB color space for optimal quality. This constant is equal to @c LNUseDefaultInterpolationBehavior.
20 | */
21 | extern LNInterpolationBehavior const LNInterpolationBehaviorUseLABColorSpace;
22 |
23 | /**
24 | Interpolate using the RGB color space.
25 | */
26 | extern LNInterpolationBehavior const LNInterpolationBehaviorUseRGBColorSpace;
27 |
28 | /**
29 | Interpolates between colors.
30 |
31 | By default, colors are interpolated in the Lab color space for optimal quality at the expense of some performance. Use @c LNUseRGBInterpolationBehavior for better performance but suboptimal quality.
32 | */
33 | #if __has_include()
34 | @interface UIColor (LNInterpolation) @end
35 | #else
36 | @interface NSColor (LNInterpolation) @end
37 | #endif
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/Color+Interpolation.m:
--------------------------------------------------------------------------------
1 | //
2 | // Color+Interpolation.m
3 | //
4 | // Created by Leo Natan on 01/10/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #if __has_include() || __has_include()
9 |
10 | #import "Color+Interpolation.h"
11 |
12 | #if __has_include()
13 | #define Color UIColor
14 | #else
15 | #define Color NSColor
16 | #endif
17 |
18 | #define SWAP(x, y) do { __typeof(x) __ZZZZ__SWAP = x; x = y; y = __ZZZZ__SWAP; } while(0)
19 |
20 | //Same value as LNInterpolationBehaviorUseDefault
21 | LNInterpolationBehavior const LNInterpolationBehaviorUseLABColorSpace = @"LNInterpolationBehaviorUseDefault";
22 | LNInterpolationBehavior const LNInterpolationBehaviorUseRGBColorSpace = @"LNInterpolationBehaviorUseRGB";
23 |
24 | extern double LNLinearInterpolate(double from, double to, double p);
25 |
26 | static NSArray* LNRGBComponentsFromColor(Color* color)
27 | {
28 | size_t numberOfComponents = CGColorGetNumberOfComponents(color.CGColor);
29 | const CGFloat* components = CGColorGetComponents(color.CGColor);
30 |
31 | return numberOfComponents == 2 ? @[@(components[0]), @(components[0]), @(components[0]), @(components[1])] : @[@(components[0]), @(components[1]), @(components[2]), @(components[3])];
32 | }
33 |
34 | static Color* LNColorFromRGBComponents(NSArray* components)
35 | {
36 | return [Color colorWithRed:components[0].doubleValue green:components[1].doubleValue blue:components[2].doubleValue alpha:components[3].doubleValue];
37 | }
38 |
39 | static NSArray* LNLabComponentsFromColor(Color* color)
40 | {
41 | NSArray* rgbComponents = LNRGBComponentsFromColor(color);
42 | CGFloat r = rgbComponents[0].doubleValue;
43 | CGFloat g = rgbComponents[1].doubleValue;
44 | CGFloat b = rgbComponents[2].doubleValue;
45 |
46 | //RGB -> XYZ
47 | //http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
48 |
49 | r = (r > 0.04045) ? pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
50 | g = (g > 0.04045) ? pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
51 | b = (b > 0.04045) ? pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
52 |
53 | //http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html (sRGB -> XYZ)
54 | CGFloat x = r * 41.24564 + g * 35.75761 + b * 18.04375;
55 | CGFloat y = r * 21.26729 + g * 71.51522 + b * 07.21750;
56 | CGFloat z = r * 01.93339 + g * 11.91920 + b * 95.03041;
57 |
58 | //XYZ -> Lab
59 | //http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
60 |
61 | static const CGFloat eps = 216.0 / 24389.0;
62 | static const CGFloat k = 24389.0 / 27.0;
63 |
64 | x = x / 100.0;//95.047;
65 | y = y / 100.0;//100.000;
66 | z = z / 100.0;//108.883;
67 |
68 | x = x > eps ? pow(x, 1.0 / 3.0) : (k * x + 16.0) / 116.0;
69 | y = y > eps ? pow(y, 1.0 / 3.0) : (k * y + 16.0) / 116.0;
70 | z = z > eps ? pow(z, 1.0 / 3.0) : (k * z + 16.0) / 116.0;
71 |
72 | CGFloat l = 116 * y - 16;
73 | CGFloat a = 500 * (x - y);
74 | b = 200 * (y - z);
75 |
76 | return @[@(l), @(a), @(b), rgbComponents[3]];
77 | }
78 |
79 | static Color* LNColorFromLabComponents(NSArray* components)
80 | {
81 | CGFloat l = components[0].doubleValue;
82 | CGFloat a = components[1].doubleValue;
83 | CGFloat b = components[2].doubleValue;
84 |
85 | //Lab -> XYZ
86 | //http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
87 |
88 | static const CGFloat eps = 216.0 / 24389.0;
89 | static const CGFloat k = 24389.0 / 27.0;
90 |
91 | CGFloat y = (l + 16.0) / 116.0;
92 | CGFloat x = a / 500 + y;
93 | CGFloat z = y - b / 200;
94 |
95 | x = pow(x, 3.0) > eps ? pow(x, 3.0) : (116 * x - 16) / k;
96 | y = l > k * eps ? pow((l + 16) / 116, 3.0) : l / k;
97 | z = pow(z, 3.0) > eps ? pow(z, 3.0) : (116 * z - 16) / k;
98 |
99 | x = x * 1.0;//.95047;
100 | y = y * 1.0;//1.00000;
101 | z = z * 1.0;//1.08883;
102 |
103 | //XYZ -> RGB
104 |
105 | //http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html (XYZ -> sRGB)
106 | CGFloat r = x * 3.2404542 + y * -1.5371385 + z * -0.4985314;
107 | CGFloat g = x * -0.9692660 + y * 1.8760108 + z * 0.0415560;
108 | b = x * 0.0556434 + y * -0.2040259 + z * 1.0572252;
109 |
110 | r = r <= 0.0031308 ? 12.92 * r : 1.055 * pow(r, 1.0 / 2.4) - 0.055;
111 | g = g <= 0.0031308 ? 12.92 * g : 1.055 * pow(g, 1.0 / 2.4) - 0.055;
112 | b = b <= 0.0031308 ? 12.92 * b : 1.055 * pow(b, 1.0 / 2.4) - 0.055;
113 |
114 | // return Color
115 | return LNColorFromRGBComponents(@[@(r), @(g), @(b), components[3]]);
116 | }
117 |
118 | static inline Color* LNInterpolateColor(Color* fromValue, Color* toValue, CGFloat p, NSArray* (*compConverter)(Color*), Color* (*colorConverter)(NSArray*))
119 | {
120 | NSArray* arrayC1 = compConverter(fromValue);
121 | NSArray* arrayC2 = compConverter(toValue);
122 |
123 | NSMutableArray* arrayOutput = [NSMutableArray new];
124 | [arrayC1 enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
125 | arrayOutput[idx] = @(LNLinearInterpolate(obj.doubleValue, arrayC2[idx].doubleValue, p));
126 | }];
127 |
128 | return colorConverter(arrayOutput);
129 | }
130 |
131 | @implementation Color (Interpolation)
132 |
133 | - (instancetype)interpolateToValue:(Color*)toValue progress:(double)p
134 | {
135 | return [self interpolateToValue:toValue progress:p behavior:LNInterpolationBehaviorUseDefault];
136 | }
137 |
138 | - (instancetype)interpolateToValue:(id)toValue progress:(double)p behavior:(LNInterpolationBehavior)behavior
139 | {
140 | if([toValue isKindOfClass:[Color class]] == NO)
141 | {
142 | return nil;
143 | }
144 |
145 | if(p <= 0)
146 | {
147 | return self;
148 | }
149 |
150 | if(p >= 1)
151 | {
152 | return toValue;
153 | }
154 |
155 | return LNInterpolateColor(self, toValue, p, behavior == LNInterpolationBehaviorUseRGBColorSpace ? LNRGBComponentsFromColor : LNLabComponentsFromColor, behavior == LNInterpolationBehaviorUseRGBColorSpace ? LNColorFromRGBComponents : LNColorFromLabComponents);
156 | }
157 |
158 | @end
159 |
160 | #endif
161 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/LNAnimator.h:
--------------------------------------------------------------------------------
1 | //
2 | // LNFrameAnimator.h
3 | // KeyboardTransitionDemo
4 | //
5 | // Created by Leo Natan (Wix) on 11/05/2017.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 |
11 | @protocol LNAnimation
12 |
13 | @property (nonatomic) CGFloat progress;
14 |
15 | @end
16 |
17 | @interface LNViewAnimation : NSObject
18 |
19 | @property (nonatomic, strong, readonly) UIView* view;
20 | @property (nonatomic, strong, readonly) NSString* keyPath;
21 | @property (nonatomic, strong, readonly) id toValue;
22 |
23 | + (instancetype)animationWithView:(UIView*)view keyPath:(NSString*)keyPath toValue:(id)toValue;
24 |
25 | @end
26 |
27 | @interface LNAnimator : NSObject
28 |
29 | @property (nonatomic, readonly) NSTimeInterval duration;
30 | @property (nonatomic, strong, readonly) NSArray>* animations;
31 |
32 | + (instancetype)animatorWithDuration:(NSTimeInterval)duration animations:(NSArray>*)animations completionHandler:(void(^)(BOOL completed))completionHandler;
33 |
34 | - (void)start;
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/LNAnimator.m:
--------------------------------------------------------------------------------
1 | //
2 | // LNFrameAnimator.m
3 | // KeyboardTransitionDemo
4 | //
5 | // Created by Leo Natan (Wix) on 11/05/2017.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "LNAnimator.h"
10 |
11 | #import "LNInterpolation.h"
12 |
13 | @implementation LNViewAnimation
14 | {
15 | id _fromValue;
16 | }
17 |
18 | @synthesize progress = _progress;
19 |
20 | - (instancetype)init
21 | {
22 | [NSException raise:NSInvalidArgumentException format:@"Use animationWithView:keyPath:toValue: to create LNViewAnimation objects."];
23 | return nil;
24 | }
25 |
26 | - (instancetype)_init
27 | {
28 | return [super init];
29 | }
30 |
31 | + (instancetype)animationWithView:(UIView*)view keyPath:(NSString*)keyPath toValue:(id)toValue
32 | {
33 | LNViewAnimation* rv = [[LNViewAnimation alloc] _init];
34 |
35 | if(rv)
36 | {
37 | rv->_view = view;
38 | rv->_keyPath = keyPath;
39 | rv->_toValue = toValue;
40 | rv->_fromValue = [view valueForKeyPath:keyPath];
41 | }
42 |
43 | return rv;
44 | }
45 |
46 | - (void)setProgress:(CGFloat)progress
47 | {
48 | _progress = progress;
49 | [_view setValue:[_fromValue interpolateToValue:_toValue progress:progress] forKeyPath:_keyPath];
50 | [_view layoutIfNeeded];
51 | }
52 |
53 | @end
54 |
55 | @implementation LNAnimator
56 | {
57 | void (^_completionHandler)(BOOL);
58 | CADisplayLink* _displayLink;
59 | CFTimeInterval _previousFrameTimestamp;
60 | CFTimeInterval _elapsedTime;
61 | }
62 |
63 | - (instancetype)init
64 | {
65 | [NSException raise:NSInvalidArgumentException format:@"Use animationWithView:keyPath:toValue: to create LNViewAnimation objects."];
66 | return nil;
67 | }
68 |
69 | - (instancetype)_init
70 | {
71 | return [super init];
72 | }
73 |
74 | + (instancetype)animatorWithDuration:(NSTimeInterval)duration animations:(NSArray>*)animations completionHandler:(void(^)(BOOL completed))completionHandler
75 | {
76 | LNAnimator* rv = [[LNAnimator alloc] _init];
77 | if(rv)
78 | {
79 | rv->_duration = duration;
80 |
81 | NSAssert(animations.count > 0, @"At least one animation must be provided.");
82 |
83 | rv->_animations = animations;
84 | rv->_completionHandler = completionHandler;
85 | }
86 |
87 | return rv;
88 | }
89 |
90 | - (void)start
91 | {
92 | _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_displayLinkDidTick)];
93 | // _displayLink.preferredFramesPerSecond = 30;
94 | [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
95 | }
96 |
97 | - (void)_displayLinkDidTick
98 | {
99 | if(_previousFrameTimestamp != 0)
100 | {
101 | _elapsedTime += _displayLink.timestamp - _previousFrameTimestamp;
102 | }
103 | _previousFrameTimestamp = _displayLink.timestamp;
104 |
105 | [_animations enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
106 | obj.progress = MIN(_elapsedTime / _duration, 1.0);
107 | }];
108 |
109 | if(_elapsedTime / _duration >= 1.0)
110 | {
111 | [_displayLink invalidate];
112 | _displayLink = nil;
113 |
114 | if(_completionHandler)
115 | {
116 | _completionHandler(YES);
117 | }
118 | }
119 | }
120 |
121 | @end
122 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/LNInterpolable.h:
--------------------------------------------------------------------------------
1 | //
2 | // LNInterpolable.h
3 | //
4 | // Created by Leo Natan on 01/10/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | NS_SWIFT_NAME(InterpolationBehavior)
13 | typedef const NSString* LNInterpolationBehavior CF_STRING_ENUM;
14 |
15 | /**
16 | Interpolate using the default behavor of each implementation.
17 | */
18 | extern LNInterpolationBehavior const LNInterpolationBehaviorUseDefault;
19 |
20 |
21 | /**
22 | Classes implementing this protocol support interpolation.
23 | */
24 | NS_SWIFT_NAME(Interpolable)
25 | @protocol LNInterpolable
26 |
27 | /**
28 | Interpolates between @c self and @c toValue accodring to @c progress using the default behavior.
29 |
30 | @param toValue The value to interpolate to
31 | @param progress The progress of the interpolation
32 | @return An object representing the interpolated value at the requested progress
33 | */
34 | - (instancetype)interpolateToValue:(id)toValue progress:(double)progress NS_SWIFT_NAME(interpolate(to:progress:));
35 |
36 | /**
37 | Interpolates between @c self and @c toValue according to @c progress using @c behavior.
38 |
39 | @param toValue The value to interpolate to
40 | @param behavior The bahvior to use for interpolation
41 | @param progress The progress of the interpolation
42 | @return An object representing the interpolated value at the requested progress
43 | */
44 | - (instancetype)interpolateToValue:(id)toValue progress:(double)progress behavior:(LNInterpolationBehavior)behavior NS_SWIFT_NAME(interpolate(to:progress:behavior:));
45 |
46 | NS_ASSUME_NONNULL_END
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/LNInterpolable.m:
--------------------------------------------------------------------------------
1 | //
2 | // LNInterpolable.c
3 | //
4 | // Created by Leo Natan on 04/10/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import "LNInterpolable.h"
9 |
10 | LNInterpolationBehavior const LNInterpolationBehaviorUseDefault = @"LNInterpolationBehaviorUseDefault";
11 |
12 | double BackEaseOut(double p)
13 | {
14 | double f = (1 - p);
15 | return 1 - (f * f * f - 0.4*f * sin(f * M_PI));
16 | }
17 |
18 | double QuarticEaseOut(double p)
19 | {
20 | double f = (p - 1);
21 | return f * f * f * (1 - p) + 1;
22 | }
23 |
24 | double LNLinearInterpolate(double from, double to, double p)
25 | {
26 | return from + QuarticEaseOut(p) * (to - from);
27 | }
28 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/LNInterpolation.h:
--------------------------------------------------------------------------------
1 | //
2 | // LNInterpolation.h
3 | //
4 | // Created by Leo Natan on 05/10/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import "LNInterpolable.h"
9 | #import "NSValue+Interpolation.h"
10 | #import "Color+Interpolation.h"
11 |
12 | //! Project version number for LNInterpolationFramework.
13 | FOUNDATION_EXPORT double LNInterpolationFrameworkVersionNumber;
14 |
15 | //! Project version string for LNInterpolationFramework.
16 | FOUNDATION_EXPORT const unsigned char LNInterpolationFrameworkVersionString[];
17 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/NSValue+Interpolation.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSValue+Interpolation.h
3 | //
4 | // Created by Leo Natan on 01/10/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import
9 | #import "LNInterpolable.h"
10 |
11 | /**
12 | Interpolates between @c NSValue objects.
13 | Currently, the following value types are supported:
14 | Numbers (@c NSNumber), decimal numbers (@c NSDecimalNumber)
15 | Core Graphics: CGPoint, CGSize, CGVector, CGRect, CGAffineTransform (@c NSValue)
16 | UIKit: UIOffset, UIEdgeInsets (@c NSValue)
17 | AppKit: NSEdgeInsets (@c NSValue)
18 | */
19 | @interface NSValue (Interpolation) @end
20 |
--------------------------------------------------------------------------------
/lib/ios/LNInterpolation/NSValue+Interpolation.mm:
--------------------------------------------------------------------------------
1 | //
2 | // NSValue+Interpolation.mm
3 | //
4 | // Created by Leo Natan on 01/10/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import "NSValue+Interpolation.h"
9 |
10 | #if __has_include()
11 | #import
12 | #endif
13 |
14 | #if __has_include()
15 | #import
16 | #endif
17 |
18 | #if __has_include()
19 | #import
20 |
21 | #define CGPointValue pointValue
22 | #define CGRectValue rectValue
23 | #define valueWithCGPoint valueWithPoint
24 | #define UIEdgeInsets NSEdgeInsets
25 | #define valueWithCGRect valueWithRect
26 |
27 | #endif
28 |
29 | extern "C" double LNLinearInterpolate(double from, double to, double p);
30 |
31 | #if __has_include()
32 | static inline CGAffineTransform _LNInterpolateCGAffineTransform(const CGAffineTransform& fromTransform, const CGAffineTransform& toTransform, CGFloat p)
33 | {
34 | CGAffineTransform rv;
35 | rv.a = LNLinearInterpolate(fromTransform.a, toTransform.a, p);
36 | rv.b = LNLinearInterpolate(fromTransform.b, toTransform.b, p);
37 | rv.c = LNLinearInterpolate(fromTransform.c, toTransform.c, p);
38 | rv.d = LNLinearInterpolate(fromTransform.d, toTransform.d, p);
39 | rv.tx = LNLinearInterpolate(fromTransform.tx, toTransform.tx, p);
40 | rv.ty = LNLinearInterpolate(fromTransform.ty, toTransform.ty, p);
41 |
42 | return rv;
43 | }
44 | #endif
45 |
46 | @implementation NSValue (Interpolation)
47 |
48 | - (instancetype)interpolateToValue:(id)toValue progress:(double)p
49 | {
50 | return [self interpolateToValue:toValue progress:p behavior:LNInterpolationBehaviorUseDefault];
51 | }
52 |
53 | - (instancetype)interpolateToValue:(id)toValue progress:(double)p behavior:(LNInterpolationBehavior)behavior
54 | {
55 | if(p <= 0)
56 | {
57 | return self;
58 | }
59 |
60 | if(p >= 1)
61 | {
62 | return toValue;
63 | }
64 |
65 | if([self isKindOfClass:[NSNumber class]])
66 | {
67 | if([self isKindOfClass:[NSDecimalNumber class]])
68 | {
69 | //Special case for decimal numbers.
70 | NSDecimalNumber* from = (id)self;
71 | NSDecimalNumber* to = (id)toValue;
72 |
73 | return [[[to decimalNumberBySubtracting:from] decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithDouble:p]] decimalNumberByAdding:from];
74 | }
75 |
76 | double f = [(NSNumber*)self doubleValue];
77 | double f2 = [(NSNumber*)toValue doubleValue];
78 |
79 | return [NSNumber numberWithDouble:LNLinearInterpolate(f, f2, p)];
80 | }
81 |
82 | #if __has_include() || __has_include()
83 | if((strcmp(self.objCType, @encode(CGPoint)) == 0) || (strcmp(self.objCType, @encode(CGSize)) == 0) || (strcmp(self.objCType, @encode(CGVector)) == 0)
84 | #if __has_include()
85 | || (strcmp(self.objCType, @encode(UIOffset)) == 0)
86 | #endif
87 | )
88 | {
89 | CGPoint v = [self CGPointValue];
90 | CGPoint v2 = [self CGPointValue];
91 | v.x = LNLinearInterpolate(v.x, v2.y, p);
92 | v.y = LNLinearInterpolate(v.x, v2.y, p);
93 |
94 | return [NSValue valueWithCGPoint:v];
95 | }
96 |
97 | if((strcmp(self.objCType, @encode(CGRect)) == 0)
98 | || (strcmp(self.objCType, @encode(UIEdgeInsets)) == 0))
99 | {
100 | CGRect v = [self CGRectValue];
101 | CGRect v2 = [toValue CGRectValue];
102 | v.origin.x = LNLinearInterpolate(v.origin.x, v2.origin.x, p);
103 | v.origin.y = LNLinearInterpolate(v.origin.y, v2.origin.y, p);
104 | v.size.width = LNLinearInterpolate(v.size.width, v2.size.width, p);
105 | v.size.height = LNLinearInterpolate(v.size.height, v2.size.height, p);
106 |
107 | return [NSValue valueWithCGRect:v];
108 | }
109 |
110 | #if __has_include()
111 | if(strcmp(self.objCType, @encode(CGAffineTransform)) == 0)
112 | {
113 | return [NSValue valueWithCGAffineTransform:_LNInterpolateCGAffineTransform([self CGAffineTransformValue], [toValue CGAffineTransformValue], p)];
114 | }
115 | #endif
116 | #endif
117 |
118 | //Unsupported value type.
119 |
120 | return self;
121 | }
122 |
123 | @end
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/lib/ios/RCTCustomInputController/RCTCustomInputController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RCTCustomInputController.h
3 | //
4 | // Created by Leo Natan (Wix) on 13/12/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #if __has_include()
9 | #import
10 | #else
11 | #import "RCTEventEmitter.h"
12 | #endif
13 |
14 | @interface RCTCustomInputController : RCTEventEmitter
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/lib/ios/RCTCustomInputController/RCTCustomInputController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTCustomInputController.m
3 | //
4 | // Created by Leo Natan (Wix) on 13/12/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import "RCTCustomInputController.h"
9 | #import "RCTCustomKeyboardViewController.h"
10 |
11 | #import
12 | #import
13 |
14 | #import "LNAnimator.h"
15 |
16 | #define kHlperViewTag 0x1f1f1f
17 |
18 | NSString *const RCTCustomInputControllerKeyboardResigendEvent = @"kbdResigned";
19 |
20 | @protocol _WXInputHelperViewDelegate
21 | -(void)_WXInputHelperViewResignFirstResponder:(UIView*)wxInputHelperView;
22 | @end
23 |
24 | @interface _WXInputHelperView : UIView
25 |
26 | @property (nullable, nonatomic, readwrite, strong) UIInputViewController *inputViewController;
27 | @property (nonatomic, weak) id<_WXInputHelperViewDelegate> delegate;
28 | @property (nullable, readwrite, strong) UIView *inputAccessoryView;
29 | @property (nonatomic) BOOL keepInSuperviewOnResign;
30 |
31 | @end
32 |
33 | @implementation _WXInputHelperView
34 |
35 | - (BOOL)canBecomeFirstResponder
36 | {
37 | return YES;
38 | }
39 |
40 | - (BOOL)resignFirstResponder
41 | {
42 | BOOL rv = [super resignFirstResponder];
43 |
44 | if (!_keepInSuperviewOnResign)
45 | {
46 | [self removeFromSuperview];
47 |
48 | if(self.delegate && [self.delegate respondsToSelector:@selector(_WXInputHelperViewResignFirstResponder:)])
49 | {
50 | [self.delegate _WXInputHelperViewResignFirstResponder:self];
51 | }
52 |
53 | }
54 |
55 | return rv;
56 | }
57 |
58 | @end
59 |
60 |
61 | @interface RCTCustomInputController () <_WXInputHelperViewDelegate> {
62 | UIWindow *_fullScreenWindow;
63 | BOOL _performingExpandTransition;
64 | }
65 |
66 | @property (nonatomic) BOOL customInputComponentPresented;
67 | @end
68 |
69 | @implementation RCTCustomInputController
70 |
71 | + (BOOL)requiresMainQueueSetup
72 | {
73 | return YES;
74 | }
75 |
76 | - (dispatch_queue_t)methodQueue
77 | {
78 | return dispatch_get_main_queue();
79 | }
80 |
81 | - (NSArray *)supportedEvents
82 | {
83 | return @[RCTCustomInputControllerKeyboardResigendEvent];
84 | }
85 |
86 | RCT_EXPORT_MODULE(CustomInputController)
87 |
88 | - (instancetype)init
89 | {
90 | self = [super init];
91 | if (self)
92 | {
93 | self.customInputComponentPresented = NO;
94 | }
95 | return self;
96 | }
97 |
98 | -(UIView*)getFirstResponder:(UIView*)view
99 | {
100 | if (view == nil || [view isFirstResponder])
101 | {
102 | return view;
103 | }
104 |
105 | for (UIView *subview in view.subviews)
106 | {
107 | UIView *firstResponder = [self getFirstResponder:subview];
108 | if(firstResponder != nil)
109 | {
110 | return firstResponder;
111 | }
112 | }
113 | return nil;
114 | }
115 |
116 | - (BOOL)shouldUseSafeAreaFrom:(NSDictionary *)params {
117 | return [params[@"useSafeArea"] isEqual:@(1)];
118 | }
119 |
120 | RCT_EXPORT_METHOD(presentCustomInputComponent:(nonnull NSNumber*)inputFieldTag params:(nonnull NSDictionary*)params)
121 | {
122 | RCTBridge* bridge = [self.bridge valueForKey:@"parentBridge"];
123 | if(bridge == nil)
124 | {
125 | return;
126 | }
127 |
128 | UIView* inputField = [self.bridge.uiManager viewForReactTag:inputFieldTag];
129 | NSDictionary *initialProps = params[@"initialProps"];
130 | RCTRootView* rv = [[RCTRootView alloc] initWithBridge:bridge moduleName:params[@"component"] initialProperties:initialProps];
131 | if(initialProps != nil && initialProps[@"backgroundColor"] != nil)
132 | {
133 | UIColor *backgroundColor = [RCTConvert UIColor:initialProps[@"backgroundColor"]];
134 | if(backgroundColor != nil)
135 | {
136 | rv.backgroundColor = backgroundColor;
137 | }
138 | }
139 |
140 | self.customInputComponentPresented = NO;
141 |
142 | BOOL useSafeArea = [self shouldUseSafeAreaFrom:params];
143 | RCTCustomKeyboardViewController* customKeyboardController = [[RCTCustomKeyboardViewController alloc] initWithUsingSafeArea:useSafeArea];
144 | customKeyboardController.rootView = rv;
145 |
146 | _WXInputHelperView* helperView = [[_WXInputHelperView alloc] initWithFrame:CGRectZero];
147 | helperView.tag = kHlperViewTag;
148 | helperView.delegate = self;
149 |
150 | if ([inputField isKindOfClass:NSClassFromString(@"RCTTextView")])
151 | {
152 | UITextView *textView = nil;
153 | Ivar backedTextInputIvar = class_getInstanceVariable([inputField class], "_backedTextInput");
154 | if (backedTextInputIvar != NULL)
155 | {
156 | textView = [inputField valueForKey:@"_backedTextInput"];
157 | }
158 | else if([inputField isKindOfClass:[UITextView class]])
159 | {
160 | textView = (UITextView*)inputField;
161 | }
162 |
163 | if (textView != nil)
164 | {
165 | helperView.inputAccessoryView = textView.inputAccessoryView;
166 | }
167 | }
168 | else if ([inputField isKindOfClass:NSClassFromString(@"RCTUITextView")] && [inputField isKindOfClass:[UITextView class]])
169 | {
170 | UITextView *textView = (UITextView*)inputField;
171 | helperView.inputAccessoryView = textView.inputAccessoryView;
172 | }
173 | else if([inputField isKindOfClass:NSClassFromString(@"RCTMultilineTextInputView")])
174 | {
175 | Ivar backedTextInputIvar = class_getInstanceVariable([inputField class], "_backedTextInputView");
176 | if (backedTextInputIvar != NULL)
177 | {
178 | id textViewObj = [inputField valueForKey:@"_backedTextInputView"];
179 | if ([textViewObj isKindOfClass:[UITextView class]])
180 | {
181 | UITextView *textView = (UITextView*)textViewObj;
182 | helperView.inputAccessoryView = textView.inputAccessoryView;
183 | }
184 | }
185 | }
186 | else
187 | {
188 | UIView *firstResponder = [self getFirstResponder:inputField];
189 | helperView.inputAccessoryView = firstResponder.inputAccessoryView;
190 | }
191 |
192 | [helperView reloadInputViews];
193 |
194 | helperView.backgroundColor = [UIColor clearColor];
195 | [inputField.superview addSubview:helperView];
196 | [inputField.superview sendSubviewToBack:helperView];
197 |
198 | helperView.inputViewController = customKeyboardController;
199 | [helperView reloadInputViews];
200 | [helperView becomeFirstResponder];
201 |
202 | self.customInputComponentPresented = YES;
203 | }
204 |
205 | RCT_EXPORT_METHOD(resetInput:(nonnull NSNumber*)inputFieldTag)
206 | {
207 | self.customInputComponentPresented = NO;
208 |
209 | UIView* inputField = [self.bridge.uiManager viewForReactTag:inputFieldTag];
210 | if(inputField != nil)
211 | {
212 | _WXInputHelperView* helperView = [inputField.superview viewWithTag:kHlperViewTag];
213 | if(helperView != nil && [helperView isFirstResponder])
214 | {//restore the first responder only if it was already the first responder to prevent the keyboard from opening again if not necessary
215 | [inputField reactFocus];
216 | }
217 | }
218 | }
219 |
220 | RCT_EXPORT_METHOD(dismissKeyboard)
221 | {
222 | UIView *firstResponder = [self getFirstResponder:[UIApplication sharedApplication].delegate.window];
223 | if(firstResponder != nil)
224 | {
225 | [firstResponder resignFirstResponder];
226 | }
227 | }
228 |
229 | -(void)changeKeyboardHeightForInput:(nonnull NSNumber*)inputFieldTag newHeight:(CGFloat)newHeight
230 | {
231 | UIView* inputField = [self.bridge.uiManager viewForReactTag:inputFieldTag];
232 | if(inputField != nil)
233 | {
234 | _WXInputHelperView* helperView = [inputField.superview viewWithTag:kHlperViewTag];
235 | if(helperView != nil)
236 | {
237 | [((RCTCustomKeyboardViewController*)helperView.inputViewController) setAllowsSelfSizing:YES];
238 | ((RCTCustomKeyboardViewController*)helperView.inputViewController).heightConstraint.constant = newHeight;
239 |
240 | UIInputView *inputView = helperView.inputViewController.inputView;
241 | [inputView setNeedsUpdateConstraints];
242 | [UIView animateWithDuration:0.55
243 | delay:0
244 | usingSpringWithDamping:500.0
245 | initialSpringVelocity:0
246 | options:UIViewAnimationOptionCurveEaseOut
247 | animations:^{ [inputView layoutIfNeeded]; }
248 | completion:nil];
249 | }
250 | }
251 | }
252 |
253 | -(UIColor*)reactViewAvgColor:(RCTRootView*)rootView
254 | {
255 | if (rootView.frame.size.width == 0 || rootView.frame.size.height == 0)
256 | {
257 | return [UIColor clearColor];
258 | }
259 |
260 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
261 | CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, -(rootView.frame.size.height - 1));
262 | [rootView.layer renderInContext:UIGraphicsGetCurrentContext()];
263 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
264 | UIGraphicsEndImageContext();
265 |
266 | CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
267 | const UInt8* data = CFDataGetBytePtr(pixelData);
268 | CFRelease(pixelData);
269 |
270 | //after scale defaults to bgr
271 | CGFloat red = data[2] / 255.0f,
272 | green = data[1] / 255.0f,
273 | blue = data[0] / 255.0f,
274 | alpha = data[3] / 255.0f;
275 |
276 | UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
277 | return color;
278 | }
279 |
280 | RCT_EXPORT_METHOD(expandFullScreenForInput:(nonnull NSNumber*)inputFieldTag)
281 | {
282 | if (_fullScreenWindow != nil || _performingExpandTransition)
283 | {
284 | return;
285 | }
286 |
287 | UIView* inputField = [self.bridge.uiManager viewForReactTag:inputFieldTag];
288 | if(inputField != nil)
289 | {
290 | _WXInputHelperView* helperView = [inputField.superview viewWithTag:kHlperViewTag];
291 | if(helperView != nil)
292 | {
293 | _performingExpandTransition = YES;
294 |
295 | helperView.keepInSuperviewOnResign = YES;
296 |
297 | RCTCustomKeyboardViewController *customKeyboardViewController = (RCTCustomKeyboardViewController*)helperView.inputViewController;
298 | RCTRootView *rv = customKeyboardViewController.rootView;
299 | UIInputView *inputView = helperView.inputViewController.inputView;
300 |
301 | _fullScreenWindow = [[UIWindow alloc] initWithFrame:[inputView.window convertRect:inputView.bounds fromView:inputView]];
302 | UIColor *originalBackgroundColor = rv.backgroundColor;
303 | rv.backgroundColor = [self reactViewAvgColor:rv];
304 |
305 | customKeyboardViewController.rootView = nil;
306 |
307 | UIViewController *vc = [UIViewController new];
308 | vc.view = rv;
309 |
310 | inputView.window.hidden = YES;
311 |
312 | [UIView performWithoutAnimation:^{
313 | _fullScreenWindow.hidden = NO;
314 | _fullScreenWindow.rootViewController = vc;
315 |
316 | [_fullScreenWindow layoutIfNeeded];
317 | }];
318 |
319 | [[LNAnimator animatorWithDuration:0.5
320 | animations:@[[LNViewAnimation animationWithView:_fullScreenWindow keyPath:@"frame" toValue:[NSValue valueWithCGRect:[UIScreen mainScreen].bounds]]]
321 | completionHandler:^(BOOL completed)
322 | {
323 | [UIView performWithoutAnimation:^{
324 | inputView.window.hidden = NO;
325 | [helperView resignFirstResponder];
326 | [_fullScreenWindow makeKeyAndVisible];
327 |
328 | rv.backgroundColor = originalBackgroundColor;
329 | }];
330 | _performingExpandTransition = NO;
331 | }] start];
332 | }
333 | }
334 | }
335 |
336 | RCT_EXPORT_METHOD(resetSizeForInput:(nonnull NSNumber*)inputFieldTag)
337 | {
338 | if (_fullScreenWindow == nil || _performingExpandTransition)
339 | {
340 | return;
341 | }
342 |
343 | UIView* inputField = [self.bridge.uiManager viewForReactTag:inputFieldTag];
344 | if(inputField != nil)
345 | {
346 | _WXInputHelperView* helperView = [inputField.superview viewWithTag:kHlperViewTag];
347 | if(helperView != nil)
348 | {
349 | _performingExpandTransition = YES;
350 |
351 | __block CGRect keyboardTargetFrame;
352 | UIInputView *inputView = helperView.inputViewController.inputView;
353 |
354 | [UIView performWithoutAnimation:^{
355 | [helperView.window makeKeyWindow];
356 | [helperView becomeFirstResponder];
357 | [helperView layoutIfNeeded];
358 |
359 | keyboardTargetFrame = [inputView.window convertRect:inputView.bounds fromView:inputView];
360 | }];
361 |
362 | _fullScreenWindow.windowLevel = inputView.window.windowLevel + 1;
363 |
364 | [_fullScreenWindow layoutIfNeeded];
365 | [_fullScreenWindow endEditing:YES];
366 |
367 | [[LNAnimator animatorWithDuration:0.5
368 | animations:@[[LNViewAnimation animationWithView:_fullScreenWindow keyPath:@"frame" toValue:[NSValue valueWithCGRect:keyboardTargetFrame]]]
369 | completionHandler:^(BOOL completed)
370 | {
371 | RCTCustomKeyboardViewController *customKeyboardViewController = (RCTCustomKeyboardViewController*)helperView.inputViewController;
372 | RCTRootView *rv = (RCTRootView*)_fullScreenWindow.rootViewController.view;
373 |
374 | [UIView performWithoutAnimation:^{
375 |
376 | _fullScreenWindow.rootViewController.view = [UIView new];
377 | customKeyboardViewController.rootView = rv;
378 |
379 | _fullScreenWindow.hidden = YES;
380 | _fullScreenWindow = nil;
381 | }];
382 |
383 | helperView.keepInSuperviewOnResign = NO;
384 | _performingExpandTransition = NO;
385 | }] start];
386 | }
387 | }
388 | }
389 |
390 | #pragma mark - _WXInputHelperViewDelegate methods
391 |
392 | -(void)_WXInputHelperViewResignFirstResponder:(UIView*)wxInputHelperView
393 | {
394 | if(self.customInputComponentPresented)
395 | {
396 | [self sendEventWithName:RCTCustomInputControllerKeyboardResigendEvent body:nil];
397 | }
398 | self.customInputComponentPresented = NO;
399 | }
400 |
401 | @end
402 |
--------------------------------------------------------------------------------
/lib/ios/RCTCustomInputController/RCTCustomKeyboardViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RCTCustomKeyboardViewController.h
3 | //
4 | // Created by Leo Natan (Wix) on 12/12/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import
9 |
10 | #if __has_include()
11 | #import
12 | #else
13 | #import "RCTRootView.h"
14 | #endif
15 |
16 | @interface RCTCustomKeyboardViewController : UIInputViewController
17 |
18 | - (instancetype)initWithUsingSafeArea:(BOOL)useSafeArea;
19 | - (void) setAllowsSelfSizing:(BOOL)allowsSelfSizing;
20 |
21 | @property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
22 | @property (nonatomic, strong) RCTRootView *rootView;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/lib/ios/RCTCustomInputController/RCTCustomKeyboardViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTCustomKeyboardViewController.m
3 | //
4 | // Created by Leo Natan (Wix) on 12/12/2016.
5 | // Copyright © 2016 Leo Natan. All rights reserved.
6 | //
7 |
8 | #import "RCTCustomKeyboardViewController.h"
9 |
10 | #if __has_include()
11 | #import
12 | #define ObservingInputAccessoryView_IsAvailable true
13 | #endif
14 |
15 | @interface RCTCustomKeyboardViewController ()
16 | @property (nonatomic, assign, getter=isUsingSafeArea) BOOL useSafeArea;
17 | @end
18 |
19 | @implementation RCTCustomKeyboardViewController
20 |
21 | - (instancetype)initWithUsingSafeArea:(BOOL)useSafeArea
22 | {
23 | self = [super init];
24 |
25 | if(self)
26 | {
27 | self.inputView = [[UIInputView alloc] initWithFrame:CGRectZero inputViewStyle:UIInputViewStyleKeyboard];
28 |
29 | self.heightConstraint = [self.inputView.heightAnchor constraintEqualToConstant:0];
30 | self.useSafeArea = useSafeArea;
31 |
32 | #ifdef ObservingInputAccessoryView_IsAvailable
33 | ObservingInputAccessoryView *activeObservingInputAccessoryView = [ObservingInputAccessoryViewManager sharedInstance].activeObservingInputAccessoryView;
34 | if (activeObservingInputAccessoryView != nil)
35 | {
36 | CGFloat keyboardHeight = activeObservingInputAccessoryView.keyboardHeight;
37 | if (keyboardHeight > 0)
38 | {
39 | self.heightConstraint.constant = keyboardHeight;
40 | [self setAllowsSelfSizing:YES];
41 | }
42 | }
43 | #endif
44 | //!!!
45 | self.view.translatesAutoresizingMaskIntoConstraints = NO;
46 | }
47 |
48 | return self;
49 | }
50 |
51 | - (void) setAllowsSelfSizing:(BOOL)allowsSelfSizing
52 | {
53 | if(self.inputView.allowsSelfSizing != allowsSelfSizing)
54 | {
55 | self.inputView.allowsSelfSizing = allowsSelfSizing;
56 | self.heightConstraint.active = allowsSelfSizing;
57 | }
58 | }
59 |
60 | -(void)setRootView:(RCTRootView*)rootView
61 | {
62 | if(_rootView != nil)
63 | {
64 | [_rootView removeFromSuperview];
65 | }
66 |
67 | _rootView = rootView;
68 | _rootView.translatesAutoresizingMaskIntoConstraints = NO;
69 | [self.inputView addSubview:_rootView];
70 |
71 | [self updateRootViewConstraints];
72 | [self.inputView setNeedsLayout];
73 | }
74 |
75 | - (void)updateRootViewConstraints {
76 | [_rootView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
77 | [_rootView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
78 | [_rootView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
79 |
80 | NSLayoutYAxisAnchor *yAxisAnchor = [self bottomLayoutAnchorUsingSafeArea:self.isUsingSafeArea];
81 | [_rootView.bottomAnchor constraintEqualToAnchor:yAxisAnchor].active = YES;
82 | }
83 |
84 | - (NSLayoutYAxisAnchor *)bottomLayoutAnchorUsingSafeArea:(BOOL)useSafeArea {
85 | NSLayoutYAxisAnchor *yAxisAnchor = self.view.bottomAnchor;
86 |
87 | if (!useSafeArea) {
88 | return yAxisAnchor;
89 | }
90 |
91 | #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3
92 | if (@available(iOS 11.0, *)) {
93 | yAxisAnchor = self.view.safeAreaLayoutGuide.bottomAnchor;
94 | }
95 | #endif
96 | return yAxisAnchor;
97 | }
98 | @end
99 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-keyboard-input",
3 | "description": "React Native Custom Input Controller",
4 | "version": "6.0.2",
5 | "main": "src/index.js",
6 | "publishConfig": {
7 | "registry": "https://registry.npmjs.org/"
8 | },
9 | "homepage": "https://github.com/wix/react-native-keyboard-input",
10 | "author": "Leo Natan ",
11 | "license": "MIT",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/wix/react-native-keyboard-input.git"
15 | },
16 | "nativePackage": true,
17 | "keywords": [
18 | "react-native",
19 | "react",
20 | "react-component",
21 | "custom-input",
22 | "input"
23 | ],
24 | "bugs": {
25 | "url": "https://github.com/wix/react-native-keyboard-input/issues"
26 | },
27 | "scripts": {
28 | "android": "react-native run-android",
29 | "ios": "react-native run-ios",
30 | "start": "react-native start",
31 | "test": "jest",
32 | "lint": "eslint src -c .eslintrc",
33 | "e2e": "detox test"
34 | },
35 | "dependencies": {
36 | "lodash": "^4.17.4",
37 | "react-native-keyboard-tracking-view": "^5.5.0"
38 | },
39 | "peerDependencies": {
40 | "react": "16.9.0",
41 | "react-native": "0.61.4"
42 | },
43 | "devDependencies": {
44 | "@babel/core": "7.7.2",
45 | "@babel/runtime": "7.7.2",
46 | "babel-eslint": "^7.2.1",
47 | "babel-jest": "24.9.0",
48 | "babel-preset-react-native": "1.9.1",
49 | "detox": "latest",
50 | "eslint": "6.6.0",
51 | "eslint-config-airbnb": "^14.0.0",
52 | "eslint-plugin-import": "^2.2.0",
53 | "eslint-plugin-jsx-a11y": "4.0.0",
54 | "eslint-plugin-react": "^6.9.0",
55 | "eslint-plugin-react-native": "^2.0.0",
56 | "jest": "24.9.0",
57 | "mocha": "^3.3.0",
58 | "react": "16.9.0",
59 | "react-native": "0.61.4",
60 | "react-test-renderer": "16.9.0",
61 | "react-native-autogrow-textinput": "^5.0.0",
62 | "react-native-navigation": "^3.4.0",
63 | "metro-react-native-babel-preset": "0.56.3",
64 | "prop-types": "^15.7.2"
65 | },
66 | "jest": {
67 | "preset": "react-native",
68 | "testPathIgnorePatterns": [
69 | "/e2e/",
70 | "/ios/",
71 | "/android/",
72 | "/node_modules/"
73 | ],
74 | "testRegex": "(/__tests__/.*|(\\.|/)(spec|test))\\.js?$"
75 | },
76 | "detox": {
77 | "configurations": {
78 | "ios.sim.debug": {
79 | "binaryPath": "ios/DerivedData/KeyboardInput/Build/Products/Debug-iphonesimulator/KeyboardInput.app",
80 | "build": "xcodebuild -project ios/KeyboardInput.xcodeproj -scheme KeyboardInput -configuration Debug -sdk iphonesimulator -derivedDataPath ios",
81 | "type": "ios.simulator",
82 | "name": "iPhone 7"
83 | }
84 | }
85 | },
86 | "babel": {
87 | "env": {
88 | "test": {
89 | "presets": [
90 | "react-native"
91 | ],
92 | "retainLines": true
93 | }
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/CustomKeyboardView.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import {Platform, requireNativeComponent} from 'react-native';
4 | import TextInputKeyboardManagerIOS from './TextInputKeyboardMangerIOS';
5 | import TextInputKeyboardManagerAndroid from './TextInputKeyboardManagerAndroid';
6 | import KeyboardRegistry from './KeyboardsRegistry';
7 |
8 | const IsAndroid = Platform.OS === 'android';
9 | const IsIOS = Platform.OS === 'ios';
10 |
11 | const CustomKeyboardViewNativeAndroid = requireNativeComponent('CustomKeyboardViewNative');
12 |
13 | export default class CustomKeyboardView extends Component {
14 | static propTypes = {
15 | inputRef: PropTypes.object,
16 | initialProps: PropTypes.object,
17 | component: PropTypes.string,
18 | onItemSelected: PropTypes.func,
19 | useSafeArea: PropTypes.bool,
20 | };
21 | static defaultProps = {
22 | initialProps: {},
23 | useSafeArea: true,
24 | };
25 |
26 | constructor(props) {
27 | super(props);
28 |
29 | const {inputRef, component, initialProps, onItemSelected, useSafeArea} = props;
30 | if (component) {
31 | this.addOnItemSelectListener(onItemSelected, component);
32 |
33 | if (TextInputKeyboardManagerIOS && inputRef) {
34 | TextInputKeyboardManagerIOS.setInputComponent(inputRef, {component, initialProps, useSafeArea});
35 | }
36 |
37 | this.registeredRequestShowKeyboard = false;
38 | }
39 |
40 | this.keyboardExpandedToggle = {};
41 | if (IsIOS && TextInputKeyboardManagerIOS) {
42 | KeyboardRegistry.addListener('onToggleExpandedKeyboard', (args) => {
43 | if (this.props.inputRef) {
44 | if (this.keyboardExpandedToggle[args.keyboardId] === undefined) {
45 | this.keyboardExpandedToggle[args.keyboardId] = false;
46 | }
47 | this.keyboardExpandedToggle[args.keyboardId] = !this.keyboardExpandedToggle[args.keyboardId];
48 | TextInputKeyboardManagerIOS.toggleExpandKeyboard(
49 | this.props.inputRef, this.keyboardExpandedToggle[args.keyboardId], this.props.initialProps.expandWithLayoutAnimation,
50 | );
51 | }
52 | });
53 | }
54 | }
55 |
56 | shouldComponentUpdate(nextProps) {
57 | return (nextProps.component !== this.props.component);
58 | }
59 |
60 | componentWillUnmount() {
61 | KeyboardRegistry.removeListeners('onRequestShowKeyboard');
62 | KeyboardRegistry.removeListeners('onToggleExpandedKeyboard');
63 |
64 | if (this.keyboardEventListeners) {
65 | this.keyboardEventListeners.forEach(eventListener => eventListener.remove());
66 | }
67 | if (this.props.component) {
68 | KeyboardRegistry.removeListeners(`${this.props.component}.onItemSelected`);
69 | }
70 | }
71 |
72 | addOnItemSelectListener(onItemSelected, component) {
73 | if (onItemSelected) {
74 | KeyboardRegistry.addListener(`${component}.onItemSelected`, (args) => {
75 | onItemSelected(component, args);
76 | });
77 | }
78 | }
79 |
80 | async UNSAFE_componentWillReceiveProps(nextProps) { //eslint-disable-line
81 | const {inputRef, component, initialProps, onRequestShowKeyboard, useSafeArea} = nextProps;
82 |
83 | if (IsAndroid) {
84 | if (this.props.component !== component && !component) {
85 | await TextInputKeyboardManagerAndroid.reset();
86 | }
87 | }
88 |
89 | if (IsIOS && TextInputKeyboardManagerIOS && inputRef && component !== this.props.component) {
90 | if (component) {
91 | TextInputKeyboardManagerIOS.setInputComponent(inputRef, {
92 | component,
93 | initialProps,
94 | useSafeArea,
95 | });
96 | } else {
97 | TextInputKeyboardManagerIOS.removeInputComponent(inputRef);
98 | }
99 | }
100 |
101 | if (onRequestShowKeyboard && !this.registeredRequestShowKeyboard) {
102 | this.registeredRequestShowKeyboard = true;
103 | KeyboardRegistry.addListener('onRequestShowKeyboard', (args) => {
104 | onRequestShowKeyboard(args.keyboardId);
105 | });
106 | }
107 | this.registerListener(this.props, nextProps);
108 | }
109 |
110 | registerListener(props, nextProps) {
111 | const {component, onItemSelected} = nextProps;
112 | if (component && props.component !== component) {
113 | if (props.component) {
114 | KeyboardRegistry.removeListeners(`${props.component}.onItemSelected`);
115 | }
116 | KeyboardRegistry.removeListeners(`${component}.onItemSelected`);
117 | this.addOnItemSelectListener(onItemSelected, component);
118 | }
119 | }
120 |
121 | render() {
122 | if (IsAndroid) {
123 | const {component, initialProps} = this.props;
124 | const KeyboardComponent = component && KeyboardRegistry.getKeyboard(component);
125 | return (
126 |
127 | {KeyboardComponent && }
128 |
129 | );
130 | }
131 | return null;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/KeyboardAccessoryView.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import {StyleSheet, Platform, NativeModules, NativeEventEmitter, DeviceEventEmitter, processColor, BackHandler} from 'react-native';
4 | import {KeyboardTrackingView} from 'react-native-keyboard-tracking-view';
5 | import CustomKeyboardView from './CustomKeyboardView';
6 | import KeyboardUtils from './utils/KeyboardUtils';
7 |
8 | const IsIOS = Platform.OS === 'ios';
9 | const IsAndroid = Platform.OS === 'android';
10 |
11 | export default class KeyboardAccessoryView extends Component {
12 | static propTypes = {
13 | renderContent: PropTypes.func,
14 | onHeightChanged: PropTypes.func,
15 | kbInputRef: PropTypes.object,
16 | kbComponent: PropTypes.string,
17 | kbInitialProps: PropTypes.object,
18 | onItemSelected: PropTypes.func,
19 | onRequestShowKeyboard: PropTypes.func,
20 | onKeyboardResigned: PropTypes.func,
21 | iOSScrollBehavior: PropTypes.number,
22 | revealKeyboardInteractive: PropTypes.bool,
23 | manageScrollView: PropTypes.bool,
24 | requiresSameParentToManageScrollView: PropTypes.bool,
25 | addBottomView: PropTypes.bool,
26 | allowHitsOutsideBounds: PropTypes.bool,
27 | useSafeArea: PropTypes.bool,
28 | };
29 | static defaultProps = {
30 | iOSScrollBehavior: -1,
31 | revealKeyboardInteractive: false,
32 | manageScrollView: true,
33 | requiresSameParentToManageScrollView: false,
34 | addBottomView: false,
35 | allowHitsOutsideBounds: false,
36 | };
37 |
38 | constructor(props) {
39 | super(props);
40 |
41 | this.onContainerComponentHeightChanged = this.onContainerComponentHeightChanged.bind(this);
42 | this.processInitialProps = this.processInitialProps.bind(this);
43 | this.registerForKeyboardResignedEvent = this.registerForKeyboardResignedEvent.bind(this);
44 | this.registerAndroidBackHandler = this.registerAndroidBackHandler.bind(this);
45 | this.onAndroidBackPressed = this.onAndroidBackPressed.bind(this);
46 |
47 | this.registerForKeyboardResignedEvent();
48 | this.registerAndroidBackHandler();
49 | }
50 |
51 | componentWillUnmount() {
52 | if (this.customInputControllerEventsSubscriber) {
53 | this.customInputControllerEventsSubscriber.remove();
54 | }
55 | if (IsAndroid) {
56 | BackHandler.removeEventListener('hardwareBackPress', this.onAndroidBackPressed);
57 | }
58 | }
59 |
60 | onContainerComponentHeightChanged(event) {
61 | if (this.props.onHeightChanged) {
62 | this.props.onHeightChanged(event.nativeEvent.layout.height);
63 | }
64 | }
65 |
66 | onAndroidBackPressed() {
67 | if (this.props.kbComponent) {
68 | KeyboardUtils.dismiss();
69 | return true;
70 | }
71 | return false;
72 | }
73 |
74 | getIOSTrackingScrollBehavior() {
75 | let scrollBehavior = this.props.iOSScrollBehavior;
76 | if (IsIOS && NativeModules.KeyboardTrackingViewManager && scrollBehavior === -1) {
77 | scrollBehavior = NativeModules.KeyboardTrackingViewManager.KeyboardTrackingScrollBehaviorFixedOffset;
78 | }
79 | return scrollBehavior;
80 | }
81 |
82 | async getNativeProps() {
83 | if (this.trackingViewRef) {
84 | return await this.trackingViewRef.getNativeProps();
85 | }
86 | return {};
87 | }
88 |
89 | registerForKeyboardResignedEvent() {
90 | let eventEmitter = null;
91 | if (IsIOS) {
92 | if (NativeModules.CustomInputController) {
93 | eventEmitter = new NativeEventEmitter(NativeModules.CustomInputController);
94 | }
95 | } else {
96 | eventEmitter = DeviceEventEmitter;
97 | }
98 |
99 | if (eventEmitter !== null) {
100 | this.customInputControllerEventsSubscriber = eventEmitter.addListener('kbdResigned', () => {
101 | if (this.props.onKeyboardResigned) {
102 | this.props.onKeyboardResigned();
103 | }
104 | });
105 | }
106 | }
107 |
108 | registerAndroidBackHandler() {
109 | if (IsAndroid) {
110 | BackHandler.addEventListener('hardwareBackPress', this.onAndroidBackPressed);
111 | }
112 | }
113 |
114 | processInitialProps() {
115 | if (IsIOS && this.props.kbInitialProps && this.props.kbInitialProps.backgroundColor) {
116 | const processedProps = Object.assign({}, this.props.kbInitialProps);
117 | processedProps.backgroundColor = processColor(processedProps.backgroundColor);
118 | return processedProps;
119 | }
120 | return this.props.kbInitialProps;
121 | }
122 |
123 | scrollToStart() {
124 | if (this.trackingViewRef) {
125 | this.trackingViewRef.scrollToStart();
126 | }
127 | }
128 |
129 | render() {
130 | return (
131 | this.trackingViewRef = r}
133 | style={styles.trackingToolbarContainer}
134 | onLayout={this.onContainerComponentHeightChanged}
135 | scrollBehavior={this.getIOSTrackingScrollBehavior()}
136 | revealKeyboardInteractive={this.props.revealKeyboardInteractive}
137 | manageScrollView={this.props.manageScrollView}
138 | requiresSameParentToManageScrollView={this.props.requiresSameParentToManageScrollView}
139 | addBottomView={this.props.addBottomView}
140 | allowHitsOutsideBounds={this.props.allowHitsOutsideBounds}
141 | >
142 | {this.props.renderContent && this.props.renderContent()}
143 |
151 |
152 | );
153 | }
154 | }
155 |
156 | const styles = StyleSheet.create({
157 | trackingToolbarContainer: {
158 | ...Platform.select({
159 | ios: {
160 | position: 'absolute',
161 | bottom: 0,
162 | left: 0,
163 | right: 0,
164 | },
165 | }),
166 | },
167 | });
168 |
--------------------------------------------------------------------------------
/src/KeyboardRegistry.spec.js:
--------------------------------------------------------------------------------
1 | import {AppRegistry, View} from 'react-native';
2 | import React from 'react';
3 | import KeyboardRegistry from './KeyboardsRegistry';
4 |
5 | describe('KeyboardRegistry - components', () => {
6 | const mockComponent = 'test_component';
7 | const anotherMockComponent = 'test_component2';
8 | const MockElement = React.createElement(View, [], ['Hello world']);
9 | const AnotherMockElement = React.createElement(View, [], ['Hello world again!']);
10 | const mockGenerator = () => MockElement;
11 | const anotherMockGenerator = () => AnotherMockElement;
12 |
13 | beforeEach(() => {
14 | AppRegistry.registerComponent = jest.fn(AppRegistry.registerComponent);
15 | console.error = jest.fn(); //eslint-disable-line
16 | });
17 |
18 | it('should register the component in the keyboard registry', () => {
19 | KeyboardRegistry.registerKeyboard(mockComponent, mockGenerator);
20 | expect(KeyboardRegistry.getKeyboard(mockComponent)).toEqual(MockElement);
21 | });
22 |
23 | it('should register the component in the App Registry as well', () => {
24 | KeyboardRegistry.registerKeyboard(mockComponent, mockGenerator);
25 |
26 | expect(AppRegistry.registerComponent).toHaveBeenCalledTimes(1);
27 | expect(AppRegistry.registerComponent.mock.calls[0][0]).toEqual(mockComponent);
28 | expect(AppRegistry.registerComponent.mock.calls[0][1]).toEqual(mockGenerator);
29 | });
30 |
31 | it('should fail if generator is not provided and produce an error', () => {
32 | KeyboardRegistry.registerKeyboard(mockComponent);
33 | expect(AppRegistry.registerComponent).not.toHaveBeenCalled();
34 | expect(console.error).toHaveBeenCalledTimes(1); //eslint-disable-line
35 | });
36 |
37 | it('should only allow to register a generator function and produce an error', () => {
38 | KeyboardRegistry.registerKeyboard(mockComponent, MockElement);
39 | expect(AppRegistry.registerComponent).not.toHaveBeenCalled();
40 | expect(console.error).toHaveBeenCalledTimes(1); //eslint-disable-line
41 | });
42 |
43 | it('should produce an error if component was not and return undefined', () => {
44 | const res = KeyboardRegistry.getKeyboard('not_existing_component');
45 | expect(console.error).toHaveBeenCalledTimes(1); //eslint-disable-line
46 | expect(res).toBe(undefined);
47 | });
48 |
49 | it('should get all keyboards, id of the keyboard included as a param', () => {
50 | const mockParams = {icon: 5, title: 'mock title'};
51 | KeyboardRegistry.registerKeyboard(mockComponent, mockGenerator, mockParams);
52 | const keyboards = KeyboardRegistry.getAllKeyboards();
53 | expect(keyboards).toEqual([{id: mockComponent, ...mockParams}]);
54 | });
55 |
56 | it('should get specific keyboards by demand', () => {
57 | const mockParams1 = {icon: 5, title: 'mock title'};
58 | const mockParams2 = {icon: 6, title: 'mock title2'};
59 | KeyboardRegistry.registerKeyboard(mockComponent, mockGenerator, mockParams1);
60 | KeyboardRegistry.registerKeyboard(anotherMockComponent, anotherMockGenerator, mockParams2);
61 |
62 | let keyboards = KeyboardRegistry.getKeyboards([mockComponent]);
63 | expect(keyboards).toEqual([{id: mockComponent, ...mockParams1}]);
64 |
65 | keyboards = KeyboardRegistry.getKeyboards([anotherMockComponent]);
66 | expect(keyboards).toEqual([{id: anotherMockComponent, ...mockParams2}]);
67 |
68 | keyboards = KeyboardRegistry.getKeyboards([mockComponent, anotherMockComponent]);
69 | expect(keyboards).toEqual([{id: mockComponent, ...mockParams1}, {id: anotherMockComponent, ...mockParams2}]);
70 |
71 | keyboards = KeyboardRegistry.getKeyboards(['not_existing']);
72 | expect(keyboards).toEqual([]);
73 |
74 | keyboards = KeyboardRegistry.getKeyboards(['not_existing', mockComponent]);
75 | expect(keyboards).toEqual([{id: mockComponent, ...mockParams1}]);
76 |
77 | keyboards = KeyboardRegistry.getKeyboards([]);
78 | expect(keyboards).toEqual([]);
79 |
80 | keyboards = KeyboardRegistry.getKeyboards();
81 | expect(keyboards).toEqual([]);
82 | });
83 |
84 | it('should get specific keyboards by order', () => {
85 | KeyboardRegistry.registerKeyboard(mockComponent, mockGenerator);
86 | KeyboardRegistry.registerKeyboard(anotherMockComponent, anotherMockGenerator);
87 |
88 | let keyboards = KeyboardRegistry.getKeyboards([anotherMockComponent, mockComponent]);
89 | expect(keyboards).toEqual([{id: anotherMockComponent}, {id: mockComponent}]);
90 | });
91 | });
92 |
93 | describe('KeyboardRegistry - listeners', () => {
94 | const mockId = 'a_listener';
95 | const mockCallback = () => {};
96 | const mockArgs = {param1: '1', param2: '2'};
97 |
98 | beforeEach(() => {
99 | KeyboardRegistry.eventEmitter = {
100 | listenOn: jest.fn(),
101 | emitEvent: jest.fn(),
102 | removeListeners: jest.fn(),
103 | };
104 | });
105 |
106 | it('should listen', () => {
107 | KeyboardRegistry.addListener(mockId, mockCallback);
108 | expect(KeyboardRegistry.eventEmitter.listenOn).toHaveBeenCalledTimes(1);
109 | expect(KeyboardRegistry.eventEmitter.listenOn.mock.calls[0][0]).toEqual(mockId);
110 | expect(KeyboardRegistry.eventEmitter.listenOn.mock.calls[0][1]).toEqual(mockCallback);
111 | });
112 |
113 | it('should notify', () => {
114 | KeyboardRegistry.notifyListeners(mockId, mockArgs);
115 | expect(KeyboardRegistry.eventEmitter.emitEvent).toHaveBeenCalledTimes(1);
116 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][0]).toEqual(mockId);
117 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][1]).toEqual(mockArgs);
118 | });
119 |
120 | it('should remove', () => {
121 | KeyboardRegistry.removeListeners(mockId);
122 | expect(KeyboardRegistry.eventEmitter.removeListeners.mock.calls[0][0]).toEqual(mockId);
123 | });
124 |
125 | it('should notify when calling onItemSelected with dedicated onItemSelected id', () => {
126 | KeyboardRegistry.onItemSelected(mockId, mockArgs);
127 | expect(KeyboardRegistry.eventEmitter.emitEvent).toHaveBeenCalledTimes(1);
128 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][0]).toEqual(`${mockId}.onItemSelected`);
129 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][1]).toEqual(mockArgs);
130 | });
131 |
132 | it('should notify when calling requestShowKeyboard with the keyboard id', () => {
133 | KeyboardRegistry.requestShowKeyboard(mockId, mockArgs);
134 | expect(KeyboardRegistry.eventEmitter.emitEvent).toHaveBeenCalledTimes(1);
135 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][0]).toEqual('onRequestShowKeyboard');
136 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][1]).toEqual({keyboardId: mockId});
137 | });
138 |
139 | it('should notify when calling requestShowKeyboard with the keyboard id', () => {
140 | KeyboardRegistry.toggleExpandedKeyboard(mockId, mockArgs);
141 | expect(KeyboardRegistry.eventEmitter.emitEvent).toHaveBeenCalledTimes(1);
142 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][0]).toEqual('onToggleExpandedKeyboard');
143 | expect(KeyboardRegistry.eventEmitter.emitEvent.mock.calls[0][1]).toEqual({keyboardId: mockId});
144 | });
145 | });
146 |
--------------------------------------------------------------------------------
/src/KeyboardsRegistry.js:
--------------------------------------------------------------------------------
1 | import {AppRegistry} from 'react-native';
2 | import _ from 'lodash';
3 | import EventEmitterManager from './utils/EventEmitterManager';
4 |
5 | /*
6 | * Tech debt: how to deal with multiple registries in the app?
7 | */
8 |
9 | const getKeyboardsWithIDs = (keyboardIDs) => {
10 | return keyboardIDs.map((keyboardId) => {
11 | return {
12 | id: keyboardId,
13 | ...KeyboardRegistry.registeredKeyboards[keyboardId].params,
14 | };
15 | });
16 | };
17 |
18 | export default class KeyboardRegistry {
19 | static registeredKeyboards = {};
20 | static eventEmitter = new EventEmitterManager();
21 |
22 | static registerKeyboard = (componentID, generator, params = {}) => {
23 | if (!_.isFunction(generator)) {
24 | console.error(`KeyboardRegistry.registerKeyboard: ${componentID} you must register a generator function`);//eslint-disable-line
25 | return;
26 | }
27 | KeyboardRegistry.registeredKeyboards[componentID] = {generator, params, componentID};
28 | AppRegistry.registerComponent(componentID, generator);
29 | };
30 |
31 | static getKeyboard = (componentID) => {
32 | const res = KeyboardRegistry.registeredKeyboards[componentID];
33 | if (!res || !res.generator) {
34 | console.error(`KeyboardRegistry.getKeyboard: ${componentID} used but not yet registered`);//eslint-disable-line
35 | return undefined;
36 | }
37 | return res.generator();
38 | };
39 |
40 | static getKeyboards = (componentIDs = []) => {
41 | const validKeyboardIDs = _.intersection(componentIDs, Object.keys(KeyboardRegistry.registeredKeyboards));
42 | return getKeyboardsWithIDs(validKeyboardIDs);
43 | };
44 |
45 | static getAllKeyboards = () => {
46 | return getKeyboardsWithIDs(Object.keys(KeyboardRegistry.registeredKeyboards));
47 | };
48 |
49 | static addListener = (globalID, callback) => {
50 | KeyboardRegistry.eventEmitter.listenOn(globalID, callback);
51 | };
52 |
53 | static notifyListeners = (globalID, args) => {
54 | KeyboardRegistry.eventEmitter.emitEvent(globalID, args);
55 | };
56 |
57 | static removeListeners = (globalID) => {
58 | KeyboardRegistry.eventEmitter.removeListeners(globalID);
59 | };
60 |
61 | static onItemSelected = (globalID, args) => {
62 | KeyboardRegistry.notifyListeners(`${globalID}.onItemSelected`, args);
63 | };
64 |
65 | static requestShowKeyboard = (globalID) => {
66 | KeyboardRegistry.notifyListeners('onRequestShowKeyboard', {keyboardId: globalID});
67 | };
68 |
69 | static toggleExpandedKeyboard = (globalID) => {
70 | KeyboardRegistry.notifyListeners('onToggleExpandedKeyboard', {keyboardId: globalID});
71 | };
72 | }
73 |
--------------------------------------------------------------------------------
/src/TextInputKeyboardManagerAndroid.js:
--------------------------------------------------------------------------------
1 | import {NativeModules} from 'react-native';
2 |
3 | const CustomKeyboardInput = NativeModules.CustomKeyboardInput;
4 |
5 | export default class TextInputKeyboardManagerAndroid {
6 | static reset = () => CustomKeyboardInput.reset();
7 |
8 | static dismissKeyboard = async () => {
9 | CustomKeyboardInput.clearFocusedView();
10 | await TextInputKeyboardManagerAndroid.reset();
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/src/TextInputKeyboardMangerIOS.js:
--------------------------------------------------------------------------------
1 | import ReactNative, {NativeModules, LayoutAnimation} from 'react-native';
2 |
3 | const CustomInputController = NativeModules.CustomInputController;
4 |
5 | export default class TextInputKeyboardManagerIOS {
6 |
7 | static setInputComponent = (textInputRef, {component, initialProps, useSafeArea}) => {
8 | if (!textInputRef || !CustomInputController) {
9 | return;
10 | }
11 | const reactTag = findNodeHandle(textInputRef);
12 | if (reactTag) {
13 | CustomInputController.presentCustomInputComponent(reactTag, {component, initialProps, useSafeArea});
14 | }
15 | };
16 |
17 | static removeInputComponent = (textInputRef) => {
18 | if (!textInputRef || !CustomInputController) {
19 | return;
20 | }
21 | const reactTag = findNodeHandle(textInputRef);
22 | if (reactTag) {
23 | CustomInputController.resetInput(reactTag);
24 | }
25 | };
26 |
27 | static dismissKeyboard = () => {
28 | CustomInputController.dismissKeyboard();
29 | };
30 |
31 | static toggleExpandKeyboard = (textInputRef, expand, performLayoutAnimation = false) => {
32 | if (textInputRef) {
33 | if (performLayoutAnimation) {
34 | LayoutAnimation.configureNext(springAnimation);
35 | }
36 | const reactTag = findNodeHandle(textInputRef);
37 | if (expand) {
38 | CustomInputController.expandFullScreenForInput(reactTag);
39 | } else {
40 | CustomInputController.resetSizeForInput(reactTag);
41 | }
42 | }
43 | };
44 | }
45 |
46 | function findNodeHandle(ref) {
47 | return ReactNative.findNodeHandle(ref.current || ref);
48 | }
49 |
50 | const springAnimation = {
51 | duration: 400,
52 | create: {
53 | type: LayoutAnimation.Types.linear,
54 | property: LayoutAnimation.Properties.opacity,
55 | },
56 | update: {
57 | type: LayoutAnimation.Types.spring,
58 | springDamping: 1.0,
59 | },
60 | delete: {
61 | type: LayoutAnimation.Types.linear,
62 | property: LayoutAnimation.Properties.opacity,
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import TextInputKeyboardMangerIOS from './TextInputKeyboardMangerIOS';
2 | import CustomKeyboardView from './CustomKeyboardView';
3 | import KeyboardAccessoryView from './KeyboardAccessoryView';
4 | import KeyboardRegistry from './KeyboardsRegistry';
5 | import KeyboardUtils from './utils/KeyboardUtils';
6 |
7 | export {TextInputKeyboardMangerIOS, CustomKeyboardView, KeyboardRegistry, KeyboardAccessoryView, KeyboardUtils};
8 |
--------------------------------------------------------------------------------
/src/utils/EventEmitterManager.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export default class EventEmitterManager {
4 | constructor() {
5 | this.handlerCallbacks = {};
6 | }
7 |
8 | listenOn(eventName, handlerCallback) {
9 | if (!this.handlerCallbacks[eventName]) {
10 | this.handlerCallbacks[eventName] = [];
11 | }
12 | if (_.indexOf(this.handlerCallbacks[eventName], handlerCallback) === -1) {
13 | this.handlerCallbacks[eventName].push(handlerCallback);
14 | }
15 | }
16 |
17 | emitEvent(eventName, params = {}) {
18 | if (this.handlerCallbacks[eventName]) {
19 | (this.handlerCallbacks[eventName]).forEach(callback => callback(params));
20 | }
21 | }
22 |
23 | removeListeners(eventName) {
24 | delete this.handlerCallbacks[eventName];
25 | }
26 |
27 | removeListener(eventName, listener) {
28 | const handlers = this.handlerCallbacks[eventName];
29 |
30 | if (handlers) {
31 | (handlers).forEach((handler, index) => {
32 | if (handler === listener) {
33 | handlers.splice(index, 1);
34 | }
35 | });
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/utils/EventEmitterManager.spec.js:
--------------------------------------------------------------------------------
1 | const EventEmitterManager = require('./EventEmitterManager').default;
2 |
3 | let EventEmitter;
4 |
5 | describe('EventEmitterManager tests', () => {
6 | let mockCallback;
7 | let otherMockCallback;
8 |
9 | beforeEach(() => {
10 | mockCallback = jest.fn();
11 | otherMockCallback = jest.fn();
12 | EventEmitter = new EventEmitterManager();
13 | });
14 |
15 | it('should have a EventEmitterManager default instance', () => {
16 | expect(EventEmitterManager).toBeDefined();
17 | expect(EventEmitterManager).not.toBeNull();
18 | });
19 |
20 | it('should register a listener and receive a callback', () => {
21 | EventEmitter.listenOn('MyEvent', mockCallback);
22 |
23 | expect(mockCallback).not.toHaveBeenCalled();
24 |
25 | EventEmitter.emitEvent('MyEvent');
26 |
27 | expect(mockCallback).toHaveBeenCalledTimes(1);
28 | expect(mockCallback).toHaveBeenCalledWith({});
29 | });
30 |
31 | it('should register a listener and receive a callback with params', () => {
32 | EventEmitter.listenOn('MyEvent', mockCallback);
33 |
34 | expect(mockCallback).not.toHaveBeenCalled();
35 |
36 | EventEmitter.emitEvent('MyEvent', {param1: 'param1', param2: 'param2'});
37 |
38 | expect(mockCallback).toHaveBeenCalledTimes(1);
39 | expect(mockCallback).toHaveBeenCalledWith({param1: 'param1', param2: 'param2'});
40 | });
41 |
42 | it('should receive only registered events callbacks', () => {
43 | EventEmitter.listenOn('MyEvent', mockCallback);
44 |
45 | expect(mockCallback).not.toHaveBeenCalled();
46 |
47 | EventEmitter.emitEvent('MyEvent2', {param1: 'param1', param2: 'param2'});
48 |
49 | expect(mockCallback).not.toHaveBeenCalled();
50 | });
51 |
52 | it('should receive multiple registered events with the same callback', () => {
53 | EventEmitter.listenOn('MyEvent', mockCallback);
54 | EventEmitter.listenOn('MyEvent2', mockCallback);
55 |
56 | expect(mockCallback).not.toHaveBeenCalled();
57 |
58 | EventEmitter.emitEvent('MyEvent', {param1: 'param1', param2: 'param2'});
59 | EventEmitter.emitEvent('MyEvent2', {param1: 'param1', param2: 'param2'});
60 |
61 | expect(mockCallback).toHaveBeenCalledTimes(2);
62 | });
63 |
64 | it('should receive multiple registered events with different callbacks', () => {
65 | EventEmitter.listenOn('MyEvent', mockCallback);
66 | EventEmitter.listenOn('MyEvent2', otherMockCallback);
67 |
68 | expect(mockCallback).not.toHaveBeenCalled();
69 | expect(otherMockCallback).not.toHaveBeenCalled();
70 |
71 | EventEmitter.emitEvent('MyEvent', {param1: 'param1', param2: 'param2'});
72 | EventEmitter.emitEvent('MyEvent2', {param1: 'param1', param2: 'param2'});
73 |
74 | expect(mockCallback).toHaveBeenCalledTimes(1);
75 | expect(otherMockCallback).toHaveBeenCalledTimes(1);
76 | });
77 |
78 | it('should receive multiple callbacks for the same event', () => {
79 | EventEmitter.listenOn('MyEvent', mockCallback);
80 | EventEmitter.listenOn('MyEvent', otherMockCallback);
81 |
82 | expect(mockCallback).not.toHaveBeenCalled();
83 | expect(otherMockCallback).not.toHaveBeenCalled();
84 |
85 | EventEmitter.emitEvent('MyEvent', {param1: 'param1', param2: 'param2'});
86 |
87 | expect(mockCallback).toHaveBeenCalledTimes(1);
88 | expect(otherMockCallback).toHaveBeenCalledTimes(1);
89 | });
90 |
91 | it('should remove listeners', () => {
92 | EventEmitter.listenOn('MyEvent', mockCallback);
93 | EventEmitter.removeListeners('MyEvent');
94 |
95 | EventEmitter.emitEvent('MyEvent', {param1: 'param1', param2: 'param2'});
96 |
97 | expect(mockCallback).not.toHaveBeenCalled();
98 | });
99 |
100 | it('should do nothing when trying to emit an undefined or null even name', () => {
101 | EventEmitter.listenOn('MyEvent', mockCallback);
102 |
103 | EventEmitter.emitEvent(undefined, {param1: 'param1', param2: 'param2'});
104 | EventEmitter.emitEvent(null, {param1: 'param1', param2: 'param2'});
105 |
106 | expect(mockCallback).not.toHaveBeenCalled();
107 | });
108 |
109 | it('should ignore removing listeners without an event name', () => {
110 | EventEmitter.listenOn('MyEvent', mockCallback);
111 | EventEmitter.removeListeners();
112 |
113 | EventEmitter.emitEvent('MyEvent', {param1: 'param1', param2: 'param2'});
114 |
115 | expect(mockCallback).toHaveBeenCalled();
116 | });
117 |
118 | it('should not register the same callback for the same event', () => {
119 | EventEmitter.listenOn('MyEvent', mockCallback);
120 | EventEmitter.listenOn('MyEvent', mockCallback);
121 | expect(EventEmitter.handlerCallbacks.MyEvent.length).toBe(1);
122 | });
123 | });
124 |
--------------------------------------------------------------------------------
/src/utils/KeyboardUtils.js:
--------------------------------------------------------------------------------
1 | import {Keyboard, Platform} from 'react-native';
2 | import TextInputKeyboardMangerIOS from '../TextInputKeyboardMangerIOS';
3 | import TextInputKeyboardManagerAndroid from '../TextInputKeyboardManagerAndroid';
4 |
5 | const IsIOS = Platform.OS === 'ios';
6 |
7 | export default class KeyboardUtils {
8 | static dismiss = () => {
9 | Keyboard.dismiss();
10 | if (IsIOS) {
11 | TextInputKeyboardMangerIOS.dismissKeyboard();
12 | } else {
13 | TextInputKeyboardManagerAndroid.dismissKeyboard();
14 | }
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/wallaby.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable*/
2 | 'use strict';
3 | process.env.BABEL_ENV = 'test';
4 | const babelOptions = require('./package.json').babel.env.test;
5 | module.exports = function(wallaby) {
6 | return {
7 | env: {
8 | type: 'node',
9 | runner: 'node'
10 | },
11 |
12 | testFramework: 'jest',
13 |
14 | files: [
15 | 'package.json',
16 | 'src/**/*.js',
17 | '!src/**/*.spec.js'
18 | ],
19 |
20 | tests: [
21 | 'src/**/*.spec.js'
22 | ],
23 |
24 | compilers: {
25 | '**/*.js': wallaby.compilers.babel(babelOptions)
26 | },
27 |
28 | setup: function(w) {
29 | require('babel-polyfill');
30 | w.testFramework.configure(require('./package.json').jest);
31 | }
32 | };
33 | };
34 |
--------------------------------------------------------------------------------