react-native-universal-keyboard-aware-scrollview 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/android/app/build.gradle +182 -0
- package/android/app/debug.keystore +0 -0
- package/android/app/proguard-rules.pro +14 -0
- package/android/app/src/debug/AndroidManifest.xml +7 -0
- package/android/app/src/debugOptimized/AndroidManifest.xml +7 -0
- package/android/app/src/main/AndroidManifest.xml +25 -0
- package/android/app/src/main/java/com/anonymous/reactnativeuniversalkeyboardawarescrollview/MainActivity.kt +61 -0
- package/android/app/src/main/java/com/anonymous/reactnativeuniversalkeyboardawarescrollview/MainApplication.kt +56 -0
- package/android/app/src/main/res/drawable/ic_launcher_background.xml +6 -0
- package/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/values/colors.xml +6 -0
- package/android/app/src/main/res/values/strings.xml +5 -0
- package/android/app/src/main/res/values/styles.xml +11 -0
- package/android/app/src/main/res/values-night/colors.xml +1 -0
- package/android/build.gradle +89 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/android/gradle.properties +65 -0
- package/android/gradlew +251 -0
- package/android/gradlew.bat +94 -0
- package/android/settings.gradle +39 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/universalkeyboard/UniversalKeyboardModule.kt +349 -0
- package/android/src/main/java/com/universalkeyboard/UniversalKeyboardPackage.kt +21 -0
- package/ios/.xcode.env +11 -0
- package/ios/Podfile +60 -0
- package/ios/Podfile.lock +2001 -0
- package/ios/Podfile.properties.json +5 -0
- package/ios/UniversalKeyboard.h +24 -0
- package/ios/UniversalKeyboard.m +413 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/AppDelegate.swift +70 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/AppIcon.appiconset/Contents.json +14 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/Contents.json +6 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenBackground.colorset/Contents.json +20 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +23 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image@2x.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image@3x.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Info.plist +76 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/PrivacyInfo.xcprivacy +48 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/SplashScreen.storyboard +48 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Supporting/Expo.plist +12 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/reactnativeuniversalkeyboardawarescrollview-Bridging-Header.h +3 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/reactnativeuniversalkeyboardawarescrollview.entitlements +5 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview.xcodeproj/project.pbxproj +540 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview.xcodeproj/xcshareddata/xcschemes/reactnativeuniversalkeyboardawarescrollview.xcscheme +88 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview.xcworkspace/contents.xcworkspacedata +10 -0
- package/package.json +61 -0
- package/react-native-universal-keyboard-aware-scrollview.podspec +32 -0
- package/react-native.config.js +18 -0
- package/src/NativeModule.ts +61 -0
- package/src/components/KeyboardAwareScrollView.tsx +388 -0
- package/src/components/index.ts +5 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useKeyboard.ts +360 -0
- package/src/index.ts +27 -0
- package/src/types.ts +87 -0
- package/src/utils/KeyboardController.ts +112 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
package com.universalkeyboard
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.graphics.Rect
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import android.util.DisplayMetrics
|
|
7
|
+
import android.view.View
|
|
8
|
+
import android.view.ViewTreeObserver
|
|
9
|
+
import android.view.WindowInsets
|
|
10
|
+
import android.view.WindowManager
|
|
11
|
+
import com.facebook.react.bridge.*
|
|
12
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
13
|
+
import java.lang.ref.WeakReference
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* UniversalKeyboardModule - Native Android module for detecting keyboard height changes.
|
|
17
|
+
*
|
|
18
|
+
* This module provides reliable keyboard detection that works in:
|
|
19
|
+
* - Normal React Native screens
|
|
20
|
+
* - React Native Modal components
|
|
21
|
+
* - BottomSheet components
|
|
22
|
+
* - Any overlay/dialog scenarios
|
|
23
|
+
*
|
|
24
|
+
* It uses the GlobalLayoutListener approach which is the most reliable method
|
|
25
|
+
* for detecting keyboard visibility on Android, as it measures the actual
|
|
26
|
+
* visible window frame.
|
|
27
|
+
*/
|
|
28
|
+
class UniversalKeyboardModule(private val reactContext: ReactApplicationContext) :
|
|
29
|
+
ReactContextBaseJavaModule(reactContext), LifecycleEventListener {
|
|
30
|
+
|
|
31
|
+
companion object {
|
|
32
|
+
const val MODULE_NAME = "UniversalKeyboard"
|
|
33
|
+
const val EVENT_KEYBOARD_DID_SHOW = "keyboardDidShow"
|
|
34
|
+
const val EVENT_KEYBOARD_DID_HIDE = "keyboardDidHide"
|
|
35
|
+
const val EVENT_KEYBOARD_HEIGHT_CHANGED = "keyboardHeightChanged"
|
|
36
|
+
|
|
37
|
+
// Threshold for keyboard detection (200dp is a reasonable minimum keyboard height)
|
|
38
|
+
private const val KEYBOARD_MIN_HEIGHT_DP = 100
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
|
|
42
|
+
private var rootView: WeakReference<View>? = null
|
|
43
|
+
private var lastKeyboardHeight = 0
|
|
44
|
+
private var isKeyboardVisible = false
|
|
45
|
+
private var hasListeners = false
|
|
46
|
+
private var screenHeight = 0
|
|
47
|
+
|
|
48
|
+
override fun getName(): String = MODULE_NAME
|
|
49
|
+
|
|
50
|
+
override fun initialize() {
|
|
51
|
+
super.initialize()
|
|
52
|
+
reactContext.addLifecycleEventListener(this)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override fun invalidate() {
|
|
56
|
+
removeKeyboardListener()
|
|
57
|
+
reactContext.removeLifecycleEventListener(this)
|
|
58
|
+
super.invalidate()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Lifecycle events
|
|
62
|
+
override fun onHostResume() {
|
|
63
|
+
if (hasListeners) {
|
|
64
|
+
setupKeyboardListener()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override fun onHostPause() {
|
|
69
|
+
// Keep listener active during pause for modals
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override fun onHostDestroy() {
|
|
73
|
+
removeKeyboardListener()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Start listening for keyboard events.
|
|
78
|
+
* This method MUST be called from JS before keyboard events will be emitted.
|
|
79
|
+
*/
|
|
80
|
+
@ReactMethod
|
|
81
|
+
fun startListening(promise: Promise) {
|
|
82
|
+
hasListeners = true
|
|
83
|
+
|
|
84
|
+
UiThreadUtil.runOnUiThread {
|
|
85
|
+
try {
|
|
86
|
+
setupKeyboardListener()
|
|
87
|
+
promise.resolve(true)
|
|
88
|
+
} catch (e: Exception) {
|
|
89
|
+
promise.reject("ERROR", "Failed to start keyboard listener: ${e.message}")
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Stop listening for keyboard events.
|
|
96
|
+
* Call this when the component unmounts to prevent memory leaks.
|
|
97
|
+
*/
|
|
98
|
+
@ReactMethod
|
|
99
|
+
fun stopListening(promise: Promise) {
|
|
100
|
+
hasListeners = false
|
|
101
|
+
|
|
102
|
+
UiThreadUtil.runOnUiThread {
|
|
103
|
+
try {
|
|
104
|
+
removeKeyboardListener()
|
|
105
|
+
promise.resolve(true)
|
|
106
|
+
} catch (e: Exception) {
|
|
107
|
+
promise.reject("ERROR", "Failed to stop keyboard listener: ${e.message}")
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get the current keyboard height synchronously.
|
|
114
|
+
*/
|
|
115
|
+
@ReactMethod
|
|
116
|
+
fun getKeyboardHeight(promise: Promise) {
|
|
117
|
+
promise.resolve(lastKeyboardHeight.toDouble() / getScreenDensity())
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if the keyboard is currently visible.
|
|
122
|
+
*/
|
|
123
|
+
@ReactMethod
|
|
124
|
+
fun isKeyboardVisible(promise: Promise) {
|
|
125
|
+
promise.resolve(isKeyboardVisible)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Force dismiss the keyboard.
|
|
130
|
+
*/
|
|
131
|
+
@ReactMethod
|
|
132
|
+
fun dismissKeyboard(promise: Promise) {
|
|
133
|
+
UiThreadUtil.runOnUiThread {
|
|
134
|
+
try {
|
|
135
|
+
val activity = currentActivity
|
|
136
|
+
if (activity != null) {
|
|
137
|
+
val view = activity.currentFocus
|
|
138
|
+
if (view != null) {
|
|
139
|
+
val imm = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as android.view.inputmethod.InputMethodManager
|
|
140
|
+
imm.hideSoftInputFromWindow(view.windowToken, 0)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
promise.resolve(true)
|
|
144
|
+
} catch (e: Exception) {
|
|
145
|
+
promise.reject("ERROR", "Failed to dismiss keyboard: ${e.message}")
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get the screen density for converting pixels to density-independent pixels.
|
|
152
|
+
*/
|
|
153
|
+
private fun getScreenDensity(): Float {
|
|
154
|
+
return reactContext.resources.displayMetrics.density
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get the minimum keyboard height in pixels based on screen density.
|
|
159
|
+
*/
|
|
160
|
+
private fun getMinKeyboardHeightPx(): Int {
|
|
161
|
+
return (KEYBOARD_MIN_HEIGHT_DP * getScreenDensity()).toInt()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Sets up the global layout listener to detect keyboard visibility changes.
|
|
166
|
+
* This is the core mechanism for reliable keyboard detection on Android.
|
|
167
|
+
*/
|
|
168
|
+
private fun setupKeyboardListener() {
|
|
169
|
+
val activity = currentActivity ?: return
|
|
170
|
+
|
|
171
|
+
// Get the root decorView
|
|
172
|
+
val decorView = activity.window.decorView
|
|
173
|
+
val contentView = decorView.findViewById<View>(android.R.id.content) ?: return
|
|
174
|
+
|
|
175
|
+
rootView = WeakReference(contentView)
|
|
176
|
+
|
|
177
|
+
// Store the full screen height
|
|
178
|
+
val displayMetrics = DisplayMetrics()
|
|
179
|
+
@Suppress("DEPRECATION")
|
|
180
|
+
activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
|
|
181
|
+
screenHeight = displayMetrics.heightPixels
|
|
182
|
+
|
|
183
|
+
// Remove any existing listener
|
|
184
|
+
removeKeyboardListener()
|
|
185
|
+
|
|
186
|
+
globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
|
|
187
|
+
measureKeyboardHeight()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
contentView.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Measures the keyboard height by comparing the visible window frame
|
|
195
|
+
* with the root view's height.
|
|
196
|
+
*/
|
|
197
|
+
private fun measureKeyboardHeight() {
|
|
198
|
+
val view = rootView?.get() ?: return
|
|
199
|
+
val activity = currentActivity ?: return
|
|
200
|
+
|
|
201
|
+
val visibleFrame = Rect()
|
|
202
|
+
view.getWindowVisibleDisplayFrame(visibleFrame)
|
|
203
|
+
|
|
204
|
+
val keyboardHeight: Int
|
|
205
|
+
|
|
206
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
207
|
+
// Android 11+ uses WindowInsets for more accurate keyboard detection
|
|
208
|
+
val rootWindowInsets = view.rootWindowInsets
|
|
209
|
+
if (rootWindowInsets != null) {
|
|
210
|
+
val imeInsets = rootWindowInsets.getInsets(WindowInsets.Type.ime())
|
|
211
|
+
val navigationBarInsets = rootWindowInsets.getInsets(WindowInsets.Type.navigationBars())
|
|
212
|
+
|
|
213
|
+
// The keyboard height is the IME inset minus the navigation bar
|
|
214
|
+
// because IME insets include the navigation bar
|
|
215
|
+
keyboardHeight = imeInsets.bottom - if (imeInsets.bottom > 0) 0 else navigationBarInsets.bottom
|
|
216
|
+
} else {
|
|
217
|
+
keyboardHeight = calculateKeyboardHeightLegacy(view, visibleFrame)
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
keyboardHeight = calculateKeyboardHeightLegacy(view, visibleFrame)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
handleKeyboardHeightChange(keyboardHeight)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Legacy method for calculating keyboard height on Android < 11.
|
|
228
|
+
*/
|
|
229
|
+
private fun calculateKeyboardHeightLegacy(view: View, visibleFrame: Rect): Int {
|
|
230
|
+
val activity = currentActivity ?: return 0
|
|
231
|
+
|
|
232
|
+
// Get the screen height including system bars
|
|
233
|
+
val displayMetrics = DisplayMetrics()
|
|
234
|
+
@Suppress("DEPRECATION")
|
|
235
|
+
activity.windowManager.defaultDisplay.getRealMetrics(displayMetrics)
|
|
236
|
+
val screenHeight = displayMetrics.heightPixels
|
|
237
|
+
|
|
238
|
+
// The keyboard height is the difference between screen height and visible frame bottom
|
|
239
|
+
// We need to account for the navigation bar as well
|
|
240
|
+
val navBarHeight = getNavigationBarHeight()
|
|
241
|
+
|
|
242
|
+
// Calculate content height difference
|
|
243
|
+
val heightDiff = screenHeight - visibleFrame.bottom
|
|
244
|
+
|
|
245
|
+
// If the difference is greater than our threshold, keyboard is visible
|
|
246
|
+
return if (heightDiff > getMinKeyboardHeightPx()) {
|
|
247
|
+
heightDiff - navBarHeight
|
|
248
|
+
} else {
|
|
249
|
+
0
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Get the navigation bar height for proper keyboard height calculation.
|
|
255
|
+
*/
|
|
256
|
+
private fun getNavigationBarHeight(): Int {
|
|
257
|
+
val activity = currentActivity ?: return 0
|
|
258
|
+
|
|
259
|
+
val resourceId = reactContext.resources.getIdentifier(
|
|
260
|
+
"navigation_bar_height",
|
|
261
|
+
"dimen",
|
|
262
|
+
"android"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return if (resourceId > 0) {
|
|
266
|
+
reactContext.resources.getDimensionPixelSize(resourceId)
|
|
267
|
+
} else {
|
|
268
|
+
0
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Handles keyboard height changes and emits appropriate events.
|
|
274
|
+
*/
|
|
275
|
+
private fun handleKeyboardHeightChange(keyboardHeightPx: Int) {
|
|
276
|
+
val density = getScreenDensity()
|
|
277
|
+
val keyboardHeightDp = (keyboardHeightPx / density).toInt()
|
|
278
|
+
|
|
279
|
+
val minHeight = KEYBOARD_MIN_HEIGHT_DP
|
|
280
|
+
val keyboardNowVisible = keyboardHeightDp >= minHeight
|
|
281
|
+
|
|
282
|
+
// Always emit height change if it's different
|
|
283
|
+
if (keyboardHeightPx != lastKeyboardHeight) {
|
|
284
|
+
lastKeyboardHeight = keyboardHeightPx
|
|
285
|
+
sendEvent(EVENT_KEYBOARD_HEIGHT_CHANGED, createKeyboardEventData(keyboardHeightDp))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Emit show/hide events
|
|
289
|
+
if (keyboardNowVisible && !isKeyboardVisible) {
|
|
290
|
+
isKeyboardVisible = true
|
|
291
|
+
sendEvent(EVENT_KEYBOARD_DID_SHOW, createKeyboardEventData(keyboardHeightDp))
|
|
292
|
+
} else if (!keyboardNowVisible && isKeyboardVisible) {
|
|
293
|
+
isKeyboardVisible = false
|
|
294
|
+
lastKeyboardHeight = 0
|
|
295
|
+
sendEvent(EVENT_KEYBOARD_DID_HIDE, createKeyboardEventData(0))
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Creates the event data payload for keyboard events.
|
|
301
|
+
*/
|
|
302
|
+
private fun createKeyboardEventData(keyboardHeight: Int): WritableMap {
|
|
303
|
+
val params = Arguments.createMap()
|
|
304
|
+
params.putDouble("height", keyboardHeight.toDouble())
|
|
305
|
+
params.putBoolean("isVisible", keyboardHeight > 0)
|
|
306
|
+
params.putDouble("screenHeight", screenHeight / getScreenDensity().toDouble())
|
|
307
|
+
params.putDouble("timestamp", System.currentTimeMillis().toDouble())
|
|
308
|
+
return params
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Sends an event to JavaScript.
|
|
313
|
+
*/
|
|
314
|
+
private fun sendEvent(eventName: String, params: WritableMap) {
|
|
315
|
+
if (!hasListeners) return
|
|
316
|
+
|
|
317
|
+
reactContext
|
|
318
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
319
|
+
?.emit(eventName, params)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Removes the global layout listener to prevent memory leaks.
|
|
324
|
+
*/
|
|
325
|
+
private fun removeKeyboardListener() {
|
|
326
|
+
globalLayoutListener?.let { listener ->
|
|
327
|
+
rootView?.get()?.viewTreeObserver?.let { observer ->
|
|
328
|
+
if (observer.isAlive) {
|
|
329
|
+
observer.removeOnGlobalLayoutListener(listener)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
globalLayoutListener = null
|
|
334
|
+
rootView = null
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Required for TurboModule support - indicates this module requires main queue setup.
|
|
339
|
+
*/
|
|
340
|
+
@ReactMethod
|
|
341
|
+
fun addListener(eventName: String) {
|
|
342
|
+
// Required for RN built-in event emitter
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
@ReactMethod
|
|
346
|
+
fun removeListeners(count: Int) {
|
|
347
|
+
// Required for RN built-in event emitter
|
|
348
|
+
}
|
|
349
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package com.universalkeyboard
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* React Native Package that registers the UniversalKeyboard native module.
|
|
10
|
+
* This package is automatically linked by React Native's autolinking feature.
|
|
11
|
+
*/
|
|
12
|
+
class UniversalKeyboardPackage : ReactPackage {
|
|
13
|
+
|
|
14
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
15
|
+
return listOf(UniversalKeyboardModule(reactContext))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
19
|
+
return emptyList()
|
|
20
|
+
}
|
|
21
|
+
}
|
package/ios/.xcode.env
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# This `.xcode.env` file is versioned and is used to source the environment
|
|
2
|
+
# used when running script phases inside Xcode.
|
|
3
|
+
# To customize your local environment, you can create an `.xcode.env.local`
|
|
4
|
+
# file that is not versioned.
|
|
5
|
+
|
|
6
|
+
# NODE_BINARY variable contains the PATH to the node executable.
|
|
7
|
+
#
|
|
8
|
+
# Customize the NODE_BINARY variable here.
|
|
9
|
+
# For example, to use nvm with brew, add the following line
|
|
10
|
+
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
|
11
|
+
export NODE_BINARY=$(command -v node)
|
package/ios/Podfile
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
|
|
2
|
+
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
|
3
|
+
|
|
4
|
+
require 'json'
|
|
5
|
+
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
|
|
6
|
+
|
|
7
|
+
def ccache_enabled?(podfile_properties)
|
|
8
|
+
# Environment variable takes precedence
|
|
9
|
+
return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE']
|
|
10
|
+
|
|
11
|
+
# Fall back to Podfile properties
|
|
12
|
+
podfile_properties['apple.ccacheEnabled'] == 'true'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false'
|
|
16
|
+
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
|
|
17
|
+
ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
|
|
18
|
+
ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
|
|
19
|
+
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
|
|
20
|
+
|
|
21
|
+
prepare_react_native_project!
|
|
22
|
+
|
|
23
|
+
target 'reactnativeuniversalkeyboardawarescrollview' do
|
|
24
|
+
use_expo_modules!
|
|
25
|
+
|
|
26
|
+
if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
|
|
27
|
+
config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
|
|
28
|
+
else
|
|
29
|
+
config_command = [
|
|
30
|
+
'npx',
|
|
31
|
+
'expo-modules-autolinking',
|
|
32
|
+
'react-native-config',
|
|
33
|
+
'--json',
|
|
34
|
+
'--platform',
|
|
35
|
+
'ios'
|
|
36
|
+
]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
config = use_native_modules!(config_command)
|
|
40
|
+
|
|
41
|
+
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
|
|
42
|
+
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
|
|
43
|
+
|
|
44
|
+
use_react_native!(
|
|
45
|
+
:path => config[:reactNativePath],
|
|
46
|
+
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
|
|
47
|
+
# An absolute path to your application root.
|
|
48
|
+
:app_path => "#{Pod::Config.instance.installation_root}/..",
|
|
49
|
+
:privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
post_install do |installer|
|
|
53
|
+
react_native_post_install(
|
|
54
|
+
installer,
|
|
55
|
+
config[:reactNativePath],
|
|
56
|
+
:mac_catalyst_enabled => false,
|
|
57
|
+
:ccache_enabled => ccache_enabled?(podfile_properties),
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
end
|