react-native-keyboard-controller 1.13.4 → 1.14.1
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/README.md +1 -0
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +5 -3
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +19 -20
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +22 -12
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/OverKeyboardViewManager.kt +40 -0
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +7 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +33 -27
- package/android/src/main/java/com/reactnativekeyboardcontroller/constants/Keyboard.kt +7 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputLayoutChangedEvent.kt +16 -15
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputSelectionChangedEvent.kt +25 -24
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputTextChangedEvent.kt +4 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +7 -6
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/Context.kt +38 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt +78 -58
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt +9 -4
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +10 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +10 -8
- package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/KeyboardAnimationController.kt +65 -60
- package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/Interpolator.kt +6 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/IosInterpolator.kt +4 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/LinearInterpolator.kt +1 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt +62 -58
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +75 -50
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/WindowDimensionListener.kt +13 -7
- package/android/src/main/java/com/reactnativekeyboardcontroller/log/Logger.kt +10 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +35 -24
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardGestureAreaViewManagerImpl.kt +21 -8
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/OverKeyboardViewManagerImpl.kt +27 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +46 -16
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +8 -8
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +14 -4
- package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolder.kt +1 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigator.kt +19 -13
- package/android/src/main/java/com/reactnativekeyboardcontroller/ui/FrameScheduler.kt +11 -8
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +27 -18
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardGestureAreaReactViewGroup.kt +23 -14
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/overlay/OverKeyboardHostShadowNode.kt +30 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/overlay/OverKeyboardViewGroup.kt +185 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +3 -1
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +19 -10
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +21 -8
- package/android/src/paper/java/com/reactnativekeyboardcontroller/OverKeyboardViewManager.kt +33 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +7 -3
- package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigatorTest.kt +30 -21
- package/ios/KeyboardController-Bridging-Header.h +1 -0
- package/ios/{core → animations}/KeyboardAnimation.swift +1 -0
- package/ios/{core → animations}/SpringAnimation.swift +1 -1
- package/ios/{core → animations}/TimingAnimation.swift +21 -20
- package/ios/extensions/UIApplication.swift +16 -0
- package/ios/extensions/UIResponder.swift +0 -3
- package/ios/extensions/UIWindow.swift +48 -0
- package/ios/views/OverKeyboardViewManager.h +29 -0
- package/ios/views/OverKeyboardViewManager.mm +220 -0
- package/jest/index.js +1 -0
- package/lib/commonjs/bindings.js +2 -1
- package/lib/commonjs/bindings.js.map +1 -1
- package/lib/commonjs/bindings.native.js +2 -1
- package/lib/commonjs/bindings.native.js.map +1 -1
- package/lib/commonjs/components/KeyboardAvoidingView/hooks.js +6 -0
- package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +2 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardToolbar/index.js +5 -3
- package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/commonjs/index.js +9 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/OverKeyboardViewNativeComponent.js +10 -0
- package/lib/commonjs/specs/OverKeyboardViewNativeComponent.js.map +1 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/views/OverKeyboardView/index.js +43 -0
- package/lib/commonjs/views/OverKeyboardView/index.js.map +1 -0
- package/lib/commonjs/views/index.js +14 -0
- package/lib/commonjs/views/index.js.map +1 -0
- package/lib/module/bindings.js +1 -0
- package/lib/module/bindings.js.map +1 -1
- package/lib/module/bindings.native.js +1 -0
- package/lib/module/bindings.native.js.map +1 -1
- package/lib/module/components/KeyboardAvoidingView/hooks.js +6 -0
- package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js +2 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/module/components/KeyboardToolbar/index.js +5 -3
- package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/OverKeyboardViewNativeComponent.js +3 -0
- package/lib/module/specs/OverKeyboardViewNativeComponent.js.map +1 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/views/OverKeyboardView/index.js +35 -0
- package/lib/module/views/OverKeyboardView/index.js.map +1 -0
- package/lib/module/views/index.js +2 -0
- package/lib/module/views/index.js.map +1 -0
- package/lib/typescript/bindings.d.ts +2 -1
- package/lib/typescript/bindings.native.d.ts +2 -1
- package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +4 -0
- package/lib/typescript/components/KeyboardToolbar/index.d.ts +2 -2
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/specs/OverKeyboardViewNativeComponent.d.ts +7 -0
- package/lib/typescript/types.d.ts +4 -1
- package/lib/typescript/views/OverKeyboardView/index.d.ts +5 -0
- package/lib/typescript/views/index.d.ts +1 -0
- package/package.json +8 -3
- package/src/bindings.native.ts +3 -0
- package/src/bindings.ts +3 -0
- package/src/components/KeyboardAvoidingView/hooks.ts +6 -0
- package/src/components/KeyboardAwareScrollView/index.tsx +5 -2
- package/src/components/KeyboardToolbar/index.tsx +7 -3
- package/src/index.ts +1 -0
- package/src/specs/OverKeyboardViewNativeComponent.ts +12 -0
- package/src/types.ts +4 -0
- package/src/views/OverKeyboardView/index.tsx +43 -0
- package/src/views/index.ts +1 -0
- package/ios/.clang-format +0 -90
- package/ios/.swiftformat +0 -5
- package/ios/.swiftlint.yml +0 -14
|
@@ -20,14 +20,17 @@ import com.reactnativekeyboardcontroller.interactive.interpolators.LinearInterpo
|
|
|
20
20
|
import kotlin.math.absoluteValue
|
|
21
21
|
import kotlin.math.roundToInt
|
|
22
22
|
|
|
23
|
-
val interpolators =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
23
|
+
val interpolators =
|
|
24
|
+
mapOf(
|
|
25
|
+
"linear" to LinearInterpolator(),
|
|
26
|
+
"ios" to IosInterpolator(),
|
|
27
|
+
)
|
|
27
28
|
|
|
28
29
|
@Suppress("detekt:TooManyFunctions")
|
|
29
30
|
@SuppressLint("ViewConstructor")
|
|
30
|
-
class KeyboardGestureAreaReactViewGroup(
|
|
31
|
+
class KeyboardGestureAreaReactViewGroup(
|
|
32
|
+
private val reactContext: ThemedReactContext,
|
|
33
|
+
) : ReactViewGroup(reactContext) {
|
|
31
34
|
// internal state management
|
|
32
35
|
private var isHandling = false
|
|
33
36
|
private var lastTouchX = 0f
|
|
@@ -127,12 +130,13 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
127
130
|
}
|
|
128
131
|
// If we currently have control, we can update the IME insets to 'scroll'
|
|
129
132
|
// the IME in
|
|
130
|
-
val moveBy =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
val moveBy =
|
|
134
|
+
this.interpolator.interpolate(
|
|
135
|
+
dy.roundToInt(),
|
|
136
|
+
this.getWindowHeight() - event.rawY.toInt(),
|
|
137
|
+
controller.getCurrentKeyboardHeight(),
|
|
138
|
+
offset,
|
|
139
|
+
)
|
|
136
140
|
|
|
137
141
|
if (moveBy != 0) {
|
|
138
142
|
controller.insetBy(moveBy)
|
|
@@ -141,8 +145,10 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
141
145
|
!controller.isInsetAnimationRequestPending() &&
|
|
142
146
|
shouldStartRequest(
|
|
143
147
|
dy = dy,
|
|
144
|
-
imeVisible =
|
|
145
|
-
|
|
148
|
+
imeVisible =
|
|
149
|
+
ViewCompat
|
|
150
|
+
.getRootWindowInsets(this)
|
|
151
|
+
?.isVisible(WindowInsetsCompat.Type.ime()) == true,
|
|
146
152
|
)
|
|
147
153
|
) {
|
|
148
154
|
// If we don't currently have control (and a request isn't pending),
|
|
@@ -210,7 +216,10 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
210
216
|
* Returns true if the given [dy], [IME visibility][imeVisible], and constructor options
|
|
211
217
|
* support a IME animation request.
|
|
212
218
|
*/
|
|
213
|
-
private fun shouldStartRequest(
|
|
219
|
+
private fun shouldStartRequest(
|
|
220
|
+
dy: Float,
|
|
221
|
+
imeVisible: Boolean,
|
|
222
|
+
) = when {
|
|
214
223
|
// If the user is scroll up, return true if scrollImeOnScreenWhenNotVisible is true, and
|
|
215
224
|
// the IME is not currently visible
|
|
216
225
|
dy < 0 -> !imeVisible && scrollKeyboardOnScreenWhenNotVisible
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller.views.overlay
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.uimanager.LayoutShadowNode
|
|
4
|
+
import com.facebook.react.uimanager.ReactShadowNodeImpl
|
|
5
|
+
import com.reactnativekeyboardcontroller.extensions.getDisplaySize
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* We implement the OverKeyboardView by using an Android WindowManager. That will fill the entire window of the
|
|
9
|
+
* application. To get layout to work properly, we need to layout all the elements within the
|
|
10
|
+
* OverKeyboardView's inner content view as if they can fill the entire window. To do that, we need to
|
|
11
|
+
* explicitly set the styleWidth and styleHeight on the LayoutShadowNode of the child of this node
|
|
12
|
+
* to be the window size. This will then cause the children of the Modal to layout as if they can
|
|
13
|
+
* fill the window.
|
|
14
|
+
*/
|
|
15
|
+
internal class OverKeyboardHostShadowNode : LayoutShadowNode() {
|
|
16
|
+
/**
|
|
17
|
+
* We need to set the styleWidth and styleHeight of the one child (represented by the
|
|
18
|
+
* <View></View> within the <RCTOverKeyboardView></RCTOverKeyboardView> in OverKeyboardView.tsx.
|
|
19
|
+
* This needs to fill the entire window.
|
|
20
|
+
*/
|
|
21
|
+
override fun addChildAt(
|
|
22
|
+
child: ReactShadowNodeImpl,
|
|
23
|
+
i: Int,
|
|
24
|
+
) {
|
|
25
|
+
super.addChildAt(child, i)
|
|
26
|
+
val displaySize = themedContext.getDisplaySize()
|
|
27
|
+
child.setStyleWidth(displaySize.x.toFloat())
|
|
28
|
+
child.setStyleHeight(displaySize.y.toFloat())
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller.views.overlay
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.graphics.PixelFormat
|
|
6
|
+
import android.view.MotionEvent
|
|
7
|
+
import android.view.View
|
|
8
|
+
import android.view.WindowManager
|
|
9
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
10
|
+
import com.facebook.react.config.ReactFeatureFlags
|
|
11
|
+
import com.facebook.react.uimanager.JSPointerDispatcher
|
|
12
|
+
import com.facebook.react.uimanager.JSTouchDispatcher
|
|
13
|
+
import com.facebook.react.uimanager.RootView
|
|
14
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
17
|
+
import com.facebook.react.views.view.ReactViewGroup
|
|
18
|
+
|
|
19
|
+
@SuppressLint("ViewConstructor")
|
|
20
|
+
class OverKeyboardHostView(
|
|
21
|
+
private val reactContext: ThemedReactContext,
|
|
22
|
+
) : ReactViewGroup(reactContext) {
|
|
23
|
+
private val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, this.id)
|
|
24
|
+
private var windowManager: WindowManager = reactContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
|
25
|
+
private var hostView: OverKeyboardRootViewGroup = OverKeyboardRootViewGroup(reactContext)
|
|
26
|
+
|
|
27
|
+
init {
|
|
28
|
+
hostView.eventDispatcher = dispatcher
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// region life cycles
|
|
32
|
+
override fun onDetachedFromWindow() {
|
|
33
|
+
super.onDetachedFromWindow()
|
|
34
|
+
|
|
35
|
+
hide()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
override fun addView(
|
|
39
|
+
child: View?,
|
|
40
|
+
index: Int,
|
|
41
|
+
) {
|
|
42
|
+
UiThreadUtil.assertOnUiThread()
|
|
43
|
+
hostView.addView(child, index)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override fun getChildCount(): Int = hostView.childCount
|
|
47
|
+
|
|
48
|
+
override fun getChildAt(index: Int): View? = hostView.getChildAt(index)
|
|
49
|
+
|
|
50
|
+
override fun removeView(child: View?) {
|
|
51
|
+
UiThreadUtil.assertOnUiThread()
|
|
52
|
+
|
|
53
|
+
if (child != null) {
|
|
54
|
+
hostView.removeView(child)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override fun removeViewAt(index: Int) {
|
|
59
|
+
UiThreadUtil.assertOnUiThread()
|
|
60
|
+
val child = getChildAt(index)
|
|
61
|
+
hostView.removeView(child)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override fun onLayout(
|
|
65
|
+
changed: Boolean,
|
|
66
|
+
l: Int,
|
|
67
|
+
t: Int,
|
|
68
|
+
r: Int,
|
|
69
|
+
b: Int,
|
|
70
|
+
) {
|
|
71
|
+
// Do nothing as we are laid out by UIManager
|
|
72
|
+
}
|
|
73
|
+
// endregion
|
|
74
|
+
|
|
75
|
+
fun show() {
|
|
76
|
+
val layoutParams =
|
|
77
|
+
WindowManager.LayoutParams(
|
|
78
|
+
WindowManager.LayoutParams.MATCH_PARENT,
|
|
79
|
+
WindowManager.LayoutParams.MATCH_PARENT,
|
|
80
|
+
// This type ensures it floats over other application windows but under system windows
|
|
81
|
+
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
|
|
82
|
+
// Ensures touches outside the view pass through to other windows
|
|
83
|
+
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
|
84
|
+
PixelFormat.TRANSLUCENT,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
windowManager.addView(hostView, layoutParams)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fun hide() {
|
|
91
|
+
if (hostView.isAttached) {
|
|
92
|
+
windowManager.removeView(hostView)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@SuppressLint("ViewConstructor")
|
|
98
|
+
class OverKeyboardRootViewGroup(
|
|
99
|
+
private val reactContext: ThemedReactContext,
|
|
100
|
+
) : ReactViewGroup(reactContext),
|
|
101
|
+
RootView {
|
|
102
|
+
private val jsTouchDispatcher: JSTouchDispatcher = JSTouchDispatcher(this)
|
|
103
|
+
private var jsPointerDispatcher: JSPointerDispatcher? = null
|
|
104
|
+
internal var eventDispatcher: EventDispatcher? = null
|
|
105
|
+
internal var isAttached = false
|
|
106
|
+
|
|
107
|
+
init {
|
|
108
|
+
if (ReactFeatureFlags.dispatchPointerEvents) {
|
|
109
|
+
jsPointerDispatcher = JSPointerDispatcher(this)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// region life cycles
|
|
114
|
+
override fun onAttachedToWindow() {
|
|
115
|
+
super.onAttachedToWindow()
|
|
116
|
+
isAttached = true
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
override fun onDetachedFromWindow() {
|
|
120
|
+
super.onDetachedFromWindow()
|
|
121
|
+
isAttached = false
|
|
122
|
+
}
|
|
123
|
+
// endregion
|
|
124
|
+
|
|
125
|
+
// region Touch events handling
|
|
126
|
+
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
127
|
+
eventDispatcher?.let { eventDispatcher ->
|
|
128
|
+
jsTouchDispatcher.handleTouchEvent(event, eventDispatcher)
|
|
129
|
+
jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true)
|
|
130
|
+
}
|
|
131
|
+
return super.onInterceptTouchEvent(event)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
135
|
+
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
136
|
+
eventDispatcher?.let { eventDispatcher ->
|
|
137
|
+
jsTouchDispatcher.handleTouchEvent(event, eventDispatcher)
|
|
138
|
+
jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false)
|
|
139
|
+
}
|
|
140
|
+
super.onTouchEvent(event)
|
|
141
|
+
// In case when there is no children interested in handling touch event, we return true from
|
|
142
|
+
// the root view in order to receive subsequent events related to that gesture
|
|
143
|
+
return true
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
|
|
147
|
+
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, true) }
|
|
148
|
+
return super.onHoverEvent(event)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
override fun onHoverEvent(event: MotionEvent): Boolean {
|
|
152
|
+
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, false) }
|
|
153
|
+
return super.onHoverEvent(event)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
|
157
|
+
// No-op - override in order to still receive events to onInterceptTouchEvent
|
|
158
|
+
// even when some other view disallow that
|
|
159
|
+
}
|
|
160
|
+
// endregion
|
|
161
|
+
|
|
162
|
+
// region RootView methods
|
|
163
|
+
override fun onChildStartedNativeGesture(
|
|
164
|
+
childView: View,
|
|
165
|
+
ev: MotionEvent,
|
|
166
|
+
) {
|
|
167
|
+
eventDispatcher?.let { eventDispatcher ->
|
|
168
|
+
jsTouchDispatcher.onChildStartedNativeGesture(ev, eventDispatcher)
|
|
169
|
+
jsPointerDispatcher?.onChildStartedNativeGesture(childView, ev, eventDispatcher)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
override fun onChildEndedNativeGesture(
|
|
174
|
+
childView: View,
|
|
175
|
+
ev: MotionEvent,
|
|
176
|
+
) {
|
|
177
|
+
eventDispatcher?.let { jsTouchDispatcher.onChildEndedNativeGesture(ev, it) }
|
|
178
|
+
jsPointerDispatcher?.onChildEndedNativeGesture()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
override fun handleException(t: Throwable) {
|
|
182
|
+
reactContext.reactApplicationContext.handleException(RuntimeException(t))
|
|
183
|
+
}
|
|
184
|
+
// endregion
|
|
185
|
+
}
|
package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt
CHANGED
|
@@ -5,7 +5,9 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
|
5
5
|
import com.facebook.react.bridge.ReactMethod
|
|
6
6
|
import com.reactnativekeyboardcontroller.modules.KeyboardControllerModuleImpl
|
|
7
7
|
|
|
8
|
-
class KeyboardControllerModule(
|
|
8
|
+
class KeyboardControllerModule(
|
|
9
|
+
mReactContext: ReactApplicationContext,
|
|
10
|
+
) : ReactContextBaseJavaModule(mReactContext) {
|
|
9
11
|
private val module = KeyboardControllerModuleImpl(mReactContext)
|
|
10
12
|
|
|
11
13
|
override fun getName(): String = KeyboardControllerModuleImpl.NAME
|
package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt
CHANGED
|
@@ -7,31 +7,40 @@ import com.facebook.react.views.view.ReactViewManager
|
|
|
7
7
|
import com.reactnativekeyboardcontroller.managers.KeyboardControllerViewManagerImpl
|
|
8
8
|
import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
|
|
9
9
|
|
|
10
|
-
class KeyboardControllerViewManager(
|
|
10
|
+
class KeyboardControllerViewManager(
|
|
11
|
+
mReactContext: ReactApplicationContext,
|
|
12
|
+
) : ReactViewManager() {
|
|
11
13
|
private val manager = KeyboardControllerViewManagerImpl(mReactContext)
|
|
12
14
|
|
|
13
15
|
override fun getName(): String = KeyboardControllerViewManagerImpl.NAME
|
|
14
16
|
|
|
15
|
-
override fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup
|
|
16
|
-
|
|
17
|
-
}
|
|
17
|
+
override fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup =
|
|
18
|
+
manager.createViewInstance(reactContext)
|
|
18
19
|
|
|
19
20
|
@ReactProp(name = "enabled")
|
|
20
|
-
fun setEnabled(
|
|
21
|
+
fun setEnabled(
|
|
22
|
+
view: EdgeToEdgeReactViewGroup,
|
|
23
|
+
enabled: Boolean,
|
|
24
|
+
) {
|
|
21
25
|
manager.setEnabled(view, enabled)
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
@ReactProp(name = "statusBarTranslucent")
|
|
25
|
-
fun setStatusBarTranslucent(
|
|
29
|
+
fun setStatusBarTranslucent(
|
|
30
|
+
view: EdgeToEdgeReactViewGroup,
|
|
31
|
+
isStatusBarTranslucent: Boolean,
|
|
32
|
+
) {
|
|
26
33
|
manager.setStatusBarTranslucent(view, isStatusBarTranslucent)
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
@ReactProp(name = "navigationBarTranslucent")
|
|
30
|
-
fun setNavigationBarTranslucent(
|
|
37
|
+
fun setNavigationBarTranslucent(
|
|
38
|
+
view: EdgeToEdgeReactViewGroup,
|
|
39
|
+
isNavigationBarTranslucent: Boolean,
|
|
40
|
+
) {
|
|
31
41
|
manager.setNavigationBarTranslucent(view, isNavigationBarTranslucent)
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any>
|
|
35
|
-
|
|
36
|
-
}
|
|
44
|
+
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> =
|
|
45
|
+
manager.getExportedCustomDirectEventTypeConstants()
|
|
37
46
|
}
|
package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt
CHANGED
|
@@ -7,32 +7,45 @@ import com.facebook.react.views.view.ReactViewManager
|
|
|
7
7
|
import com.reactnativekeyboardcontroller.managers.KeyboardGestureAreaViewManagerImpl
|
|
8
8
|
import com.reactnativekeyboardcontroller.views.KeyboardGestureAreaReactViewGroup
|
|
9
9
|
|
|
10
|
-
class KeyboardGestureAreaViewManager(
|
|
10
|
+
class KeyboardGestureAreaViewManager(
|
|
11
|
+
mReactContext: ReactApplicationContext,
|
|
12
|
+
) : ReactViewManager() {
|
|
11
13
|
private val manager = KeyboardGestureAreaViewManagerImpl(mReactContext)
|
|
12
14
|
|
|
13
15
|
override fun getName(): String = KeyboardGestureAreaViewManagerImpl.NAME
|
|
14
16
|
|
|
15
|
-
override fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup
|
|
16
|
-
|
|
17
|
-
}
|
|
17
|
+
override fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup =
|
|
18
|
+
manager.createViewInstance(reactContext)
|
|
18
19
|
|
|
19
20
|
@ReactProp(name = "offset")
|
|
20
|
-
fun setInterpolator(
|
|
21
|
+
fun setInterpolator(
|
|
22
|
+
view: KeyboardGestureAreaReactViewGroup,
|
|
23
|
+
offset: Double,
|
|
24
|
+
) {
|
|
21
25
|
manager.setOffset(view, offset)
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
@ReactProp(name = "interpolator")
|
|
25
|
-
fun setInterpolator(
|
|
29
|
+
fun setInterpolator(
|
|
30
|
+
view: KeyboardGestureAreaReactViewGroup,
|
|
31
|
+
interpolator: String,
|
|
32
|
+
) {
|
|
26
33
|
manager.setInterpolator(view, interpolator)
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
@ReactProp(name = "showOnSwipeUp")
|
|
30
|
-
fun setScrollKeyboardOnScreenWhenNotVisible(
|
|
37
|
+
fun setScrollKeyboardOnScreenWhenNotVisible(
|
|
38
|
+
view: KeyboardGestureAreaReactViewGroup,
|
|
39
|
+
value: Boolean,
|
|
40
|
+
) {
|
|
31
41
|
manager.setScrollKeyboardOnScreenWhenNotVisible(view, value)
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
@ReactProp(name = "enableSwipeToDismiss")
|
|
35
|
-
fun setScrollKeyboardOffScreenWhenVisible(
|
|
45
|
+
fun setScrollKeyboardOffScreenWhenVisible(
|
|
46
|
+
view: KeyboardGestureAreaReactViewGroup,
|
|
47
|
+
value: Boolean,
|
|
48
|
+
) {
|
|
36
49
|
manager.setScrollKeyboardOffScreenWhenVisible(view, value)
|
|
37
50
|
}
|
|
38
51
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
+
import com.facebook.react.uimanager.LayoutShadowNode
|
|
5
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewGroupManager
|
|
7
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
8
|
+
import com.reactnativekeyboardcontroller.managers.OverKeyboardViewManagerImpl
|
|
9
|
+
import com.reactnativekeyboardcontroller.views.overlay.OverKeyboardHostShadowNode
|
|
10
|
+
import com.reactnativekeyboardcontroller.views.overlay.OverKeyboardHostView
|
|
11
|
+
|
|
12
|
+
class OverKeyboardViewManager(
|
|
13
|
+
mReactContext: ReactApplicationContext,
|
|
14
|
+
) : ViewGroupManager<OverKeyboardHostView>() {
|
|
15
|
+
private val manager = OverKeyboardViewManagerImpl(mReactContext)
|
|
16
|
+
|
|
17
|
+
override fun getName(): String = OverKeyboardViewManagerImpl.NAME
|
|
18
|
+
|
|
19
|
+
override fun createViewInstance(reactContext: ThemedReactContext): OverKeyboardHostView =
|
|
20
|
+
manager.createViewInstance(reactContext)
|
|
21
|
+
|
|
22
|
+
override fun createShadowNodeInstance(): LayoutShadowNode = OverKeyboardHostShadowNode()
|
|
23
|
+
|
|
24
|
+
override fun getShadowNodeClass(): Class<out LayoutShadowNode> = OverKeyboardHostShadowNode::class.java
|
|
25
|
+
|
|
26
|
+
@ReactProp(name = "visible")
|
|
27
|
+
fun setVisible(
|
|
28
|
+
view: OverKeyboardHostView,
|
|
29
|
+
value: Boolean,
|
|
30
|
+
) {
|
|
31
|
+
manager.setVisible(view, value)
|
|
32
|
+
}
|
|
33
|
+
}
|
package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt
CHANGED
|
@@ -7,8 +7,9 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
|
7
7
|
import com.facebook.react.bridge.ReactMethod
|
|
8
8
|
import com.reactnativekeyboardcontroller.modules.StatusBarManagerCompatModuleImpl
|
|
9
9
|
|
|
10
|
-
class StatusBarManagerCompatModule(
|
|
11
|
-
|
|
10
|
+
class StatusBarManagerCompatModule(
|
|
11
|
+
mReactContext: ReactApplicationContext,
|
|
12
|
+
) : ReactContextBaseJavaModule(mReactContext) {
|
|
12
13
|
private val module = StatusBarManagerCompatModuleImpl(mReactContext)
|
|
13
14
|
|
|
14
15
|
override fun getName(): String = StatusBarManagerCompatModuleImpl.NAME
|
|
@@ -20,7 +21,10 @@ class StatusBarManagerCompatModule(mReactContext: ReactApplicationContext) :
|
|
|
20
21
|
|
|
21
22
|
@ReactMethod
|
|
22
23
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
23
|
-
private fun setColor(
|
|
24
|
+
private fun setColor(
|
|
25
|
+
color: Int,
|
|
26
|
+
animated: Boolean,
|
|
27
|
+
) {
|
|
24
28
|
module.setColor(color, animated)
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -36,8 +36,16 @@ class ViewHierarchyNavigatorTest {
|
|
|
36
36
|
|
|
37
37
|
editText1 = EditText(context).apply { id = 1 }
|
|
38
38
|
editText2 = EditText(context).apply { id = 2 }
|
|
39
|
-
editText3 =
|
|
40
|
-
|
|
39
|
+
editText3 =
|
|
40
|
+
EditText(context).apply {
|
|
41
|
+
id = 3
|
|
42
|
+
isEnabled = false
|
|
43
|
+
}
|
|
44
|
+
editText4 =
|
|
45
|
+
EditText(context).apply {
|
|
46
|
+
id = 4
|
|
47
|
+
isEnabled = false
|
|
48
|
+
}
|
|
41
49
|
editText5 = EditText(context).apply { id = 5 }
|
|
42
50
|
editText6 = EditText(context).apply { id = 6 }
|
|
43
51
|
editText7 = EditText(context).apply { id = 7 }
|
|
@@ -48,25 +56,26 @@ class ViewHierarchyNavigatorTest {
|
|
|
48
56
|
editText12 = EditText(context).apply { id = 12 }
|
|
49
57
|
editText13 = EditText(context).apply { id = 13 }
|
|
50
58
|
|
|
51
|
-
layout =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
layout =
|
|
60
|
+
LinearLayout(context).apply {
|
|
61
|
+
addView(editText1)
|
|
62
|
+
addView(editText2)
|
|
63
|
+
addView(editText3)
|
|
64
|
+
addView(editText4)
|
|
65
|
+
addView(
|
|
66
|
+
LinearLayout(context).apply {
|
|
67
|
+
addView(editText5)
|
|
68
|
+
addView(editText6)
|
|
69
|
+
addView(editText7)
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
addView(editText8)
|
|
73
|
+
addView(editText9)
|
|
74
|
+
addView(editText10)
|
|
75
|
+
addView(editText11)
|
|
76
|
+
addView(editText12)
|
|
77
|
+
addView(editText13)
|
|
78
|
+
}
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
@Test
|
|
@@ -10,7 +10,7 @@ import Foundation
|
|
|
10
10
|
import QuartzCore
|
|
11
11
|
|
|
12
12
|
// swiftlint:disable identifier_name
|
|
13
|
-
public class SpringAnimation: KeyboardAnimation {
|
|
13
|
+
public final class SpringAnimation: KeyboardAnimation {
|
|
14
14
|
// internal variables
|
|
15
15
|
private let zeta: Double // Damping ratio
|
|
16
16
|
private let omega0: Double // Undamped angular frequency of the oscillator
|
|
@@ -15,13 +15,13 @@ import QuartzCore
|
|
|
15
15
|
* This class calculates the progress of animations based on Bézier curve control points.
|
|
16
16
|
* For more details on the Bézier curves, see [Desmos Graph](https://www.desmos.com/calculator/eynenh1aga?lang=en).
|
|
17
17
|
*/
|
|
18
|
-
public class TimingAnimation: KeyboardAnimation {
|
|
18
|
+
public final class TimingAnimation: KeyboardAnimation {
|
|
19
19
|
private let p1: CGPoint
|
|
20
20
|
private let p2: CGPoint
|
|
21
21
|
|
|
22
22
|
init(animation: CABasicAnimation, fromValue: Double, toValue: Double) {
|
|
23
23
|
let timingFunction = animation.timingFunction
|
|
24
|
-
var controlPoints:
|
|
24
|
+
var controlPoints: ContiguousArray<Float> = ContiguousArray([0, 0, 0, 0])
|
|
25
25
|
timingFunction?.getControlPoint(at: 1, values: &controlPoints[0])
|
|
26
26
|
timingFunction?.getControlPoint(at: 2, values: &controlPoints[2])
|
|
27
27
|
let p1 = CGPoint(x: CGFloat(controlPoints[0]), y: CGFloat(controlPoints[1]))
|
|
@@ -33,7 +33,20 @@ public class TimingAnimation: KeyboardAnimation {
|
|
|
33
33
|
super.init(fromValue: fromValue, toValue: toValue, animation: animation)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// public functions
|
|
37
|
+
override func valueAt(time: Double) -> Double {
|
|
38
|
+
let x = time * Double(speed)
|
|
39
|
+
let frames = (animation?.duration ?? 0.0) * Double(speed)
|
|
40
|
+
let fraction = min(x / frames, 1)
|
|
41
|
+
let t = findTForX(xTarget: fraction)
|
|
42
|
+
|
|
43
|
+
let progress = bezierY(t: t)
|
|
44
|
+
|
|
45
|
+
return fromValue + (toValue - fromValue) * CGFloat(progress)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// private functions
|
|
49
|
+
private func bezier(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
|
|
37
50
|
let u = 1 - t
|
|
38
51
|
let tt = t * t
|
|
39
52
|
let uu = u * u
|
|
@@ -49,27 +62,15 @@ public class TimingAnimation: KeyboardAnimation {
|
|
|
49
62
|
return term1 + term2 + term3
|
|
50
63
|
}
|
|
51
64
|
|
|
52
|
-
func bezierY(t: CGFloat) -> CGFloat {
|
|
65
|
+
private func bezierY(t: CGFloat) -> CGFloat {
|
|
53
66
|
return bezier(t: t) { $0.y }
|
|
54
67
|
}
|
|
55
68
|
|
|
56
|
-
func bezierX(t: CGFloat) -> CGFloat {
|
|
69
|
+
private func bezierX(t: CGFloat) -> CGFloat {
|
|
57
70
|
return bezier(t: t) { $0.x }
|
|
58
71
|
}
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
override func valueAt(time: Double) -> Double {
|
|
62
|
-
let x = time * Double(speed)
|
|
63
|
-
let frames = (animation?.duration ?? 0.0) * Double(speed)
|
|
64
|
-
let fraction = min(x / frames, 1)
|
|
65
|
-
let t = findTForX(xTarget: fraction)
|
|
66
|
-
|
|
67
|
-
let progress = bezierY(t: t)
|
|
68
|
-
|
|
69
|
-
return fromValue + (toValue - fromValue) * CGFloat(progress)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
func findTForX(xTarget: CGFloat, epsilon: CGFloat = 0.0001, maxIterations: Int = 100) -> CGFloat {
|
|
73
|
+
private func findTForX(xTarget: CGFloat, epsilon: CGFloat = 0.0001, maxIterations: Int = 100) -> CGFloat {
|
|
73
74
|
var t: CGFloat = 0.5 // Start with an initial guess of t = 0.5
|
|
74
75
|
for _ in 0 ..< maxIterations {
|
|
75
76
|
let currentX = bezierX(t: t) // Compute the x-coordinate at t
|
|
@@ -84,7 +85,7 @@ public class TimingAnimation: KeyboardAnimation {
|
|
|
84
85
|
return t // Return the approximation of t
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
func bezierDerivative(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
|
|
88
|
+
private func bezierDerivative(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
|
|
88
89
|
let u = 1 - t
|
|
89
90
|
let uu = u * u
|
|
90
91
|
let tt = t * t
|
|
@@ -100,7 +101,7 @@ public class TimingAnimation: KeyboardAnimation {
|
|
|
100
101
|
return term1 + term2 + term3
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
func bezierXDerivative(t: CGFloat) -> CGFloat {
|
|
104
|
+
private func bezierXDerivative(t: CGFloat) -> CGFloat {
|
|
104
105
|
return bezierDerivative(t: t) { $0.x }
|
|
105
106
|
}
|
|
106
107
|
}
|
|
@@ -9,6 +9,22 @@ import Foundation
|
|
|
9
9
|
import UIKit
|
|
10
10
|
|
|
11
11
|
public extension UIApplication {
|
|
12
|
+
var activeWindow: UIWindow? {
|
|
13
|
+
if #available(iOS 13.0, *) {
|
|
14
|
+
for scene in connectedScenes {
|
|
15
|
+
if scene.activationState == .foregroundActive,
|
|
16
|
+
let windowScene = scene as? UIWindowScene,
|
|
17
|
+
let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow })
|
|
18
|
+
{
|
|
19
|
+
return keyWindow
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return nil
|
|
23
|
+
} else {
|
|
24
|
+
return windows.last { $0.isKeyWindow }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
static func topViewController(
|
|
13
29
|
base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
|
|
14
30
|
) -> UIViewController? {
|
|
@@ -56,10 +56,7 @@ public extension Optional where Wrapped: UIResponder {
|
|
|
56
56
|
!(currentView is UITextView),
|
|
57
57
|
scrollView.frame.width >= scrollView.contentSize.width,
|
|
58
58
|
scrollView.isScrollEnabled
|
|
59
|
-
// it was fixed in swiftlint https://github.com/realm/SwiftLint/issues/3756 but a new release is not available yet
|
|
60
|
-
// swiftlint:disable all
|
|
61
59
|
{
|
|
62
|
-
// swiftlint:enable all
|
|
63
60
|
return scrollView.reactViewTag
|
|
64
61
|
}
|
|
65
62
|
|