react-native-keyboard-controller 1.5.7 → 1.6.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/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +3 -1
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +2 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationCallback.kt +130 -18
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationController.kt +10 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +11 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +7 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +2 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardGestureAreaViewManagerImpl.kt +1 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +6 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +9 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +44 -27
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardGestureAreaReactViewGroup.kt +121 -85
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +6 -4
- package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +2 -1
- package/ios/Extensions.swift +31 -0
- package/ios/KeyboardController-Bridging-Header.h +1 -0
- package/ios/KeyboardControllerView.mm +25 -6
- package/ios/KeyboardControllerViewManager.swift +4 -2
- package/ios/KeyboardMoveEvent.h +3 -1
- package/ios/KeyboardMoveEvent.m +8 -0
- package/ios/KeyboardMovementObserver.swift +64 -12
- package/lib/commonjs/animated.js +4 -3
- package/lib/commonjs/animated.js.map +1 -1
- package/lib/commonjs/bindings.js +26 -0
- package/lib/commonjs/bindings.js.map +1 -0
- package/lib/commonjs/bindings.native.js +33 -0
- package/lib/commonjs/bindings.native.js.map +1 -0
- package/lib/commonjs/{native.js → constants.js} +2 -29
- package/lib/commonjs/constants.js.map +1 -0
- package/lib/commonjs/context.js +9 -1
- package/lib/commonjs/context.js.map +1 -1
- package/lib/commonjs/hooks.js +7 -6
- package/lib/commonjs/hooks.js.map +1 -1
- package/lib/commonjs/index.js +15 -4
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/internal.js +0 -30
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/monkey-patch.android.js +34 -0
- package/lib/commonjs/monkey-patch.android.js.map +1 -0
- package/lib/commonjs/monkey-patch.js +1 -35
- package/lib/commonjs/monkey-patch.js.map +1 -1
- package/lib/commonjs/reanimated.js +9 -0
- package/lib/commonjs/reanimated.js.map +1 -0
- package/lib/commonjs/reanimated.native.js +37 -0
- package/lib/commonjs/reanimated.native.js.map +1 -0
- package/lib/commonjs/replicas.js +4 -3
- package/lib/commonjs/replicas.js.map +1 -1
- package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/animated.js +3 -2
- package/lib/module/animated.js.map +1 -1
- package/lib/module/bindings.js +16 -0
- package/lib/module/bindings.js.map +1 -0
- package/lib/module/bindings.native.js +23 -0
- package/lib/module/bindings.native.js.map +1 -0
- package/lib/module/{native.js → constants.js} +1 -24
- package/lib/module/constants.js.map +1 -0
- package/lib/module/context.js +8 -1
- package/lib/module/context.js.map +1 -1
- package/lib/module/hooks.js +7 -6
- package/lib/module/hooks.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal.js +1 -30
- package/lib/module/internal.js.map +1 -1
- package/lib/module/monkey-patch.android.js +29 -0
- package/lib/module/monkey-patch.android.js.map +1 -0
- package/lib/module/monkey-patch.js +1 -31
- package/lib/module/monkey-patch.js.map +1 -1
- package/lib/module/reanimated.js +2 -0
- package/lib/module/reanimated.js.map +1 -0
- package/lib/module/reanimated.native.js +30 -0
- package/lib/module/reanimated.native.js.map +1 -0
- package/lib/module/replicas.js +2 -1
- package/lib/module/replicas.js.map +1 -1
- package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/bindings.d.ts +6 -0
- package/lib/typescript/bindings.native.d.ts +6 -0
- package/lib/typescript/constants.d.ts +16 -0
- package/lib/typescript/context.d.ts +1 -0
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/internal.d.ts +1 -7
- package/lib/typescript/monkey-patch.android.d.ts +1 -0
- package/lib/typescript/monkey-patch.d.ts +0 -1
- package/lib/typescript/reanimated.d.ts +2 -0
- package/lib/typescript/reanimated.native.d.ts +2 -0
- package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +3 -1
- package/lib/typescript/types.d.ts +15 -1
- package/package.json +1 -1
- package/react-native-keyboard-controller.podspec +9 -1
- package/src/animated.tsx +3 -6
- package/src/{native.ts → bindings.native.ts} +4 -25
- package/src/bindings.ts +22 -0
- package/src/constants.ts +17 -0
- package/src/context.ts +12 -1
- package/src/hooks.ts +12 -7
- package/src/index.ts +2 -1
- package/src/internal.ts +2 -57
- package/src/monkey-patch.android.ts +33 -0
- package/src/monkey-patch.ts +1 -37
- package/src/reanimated.native.ts +51 -0
- package/src/reanimated.ts +6 -0
- package/src/replicas.ts +2 -1
- package/src/specs/KeyboardControllerViewNativeComponent.ts +3 -0
- package/src/types.ts +27 -5
- package/lib/commonjs/native.js.map +0 -1
- package/lib/module/native.js.map +0 -1
- package/lib/typescript/native.d.ts +0 -24
package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt
CHANGED
|
@@ -11,7 +11,9 @@ import com.facebook.react.views.view.ReactViewManager
|
|
|
11
11
|
import com.reactnativekeyboardcontroller.managers.KeyboardControllerViewManagerImpl
|
|
12
12
|
import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
|
|
13
13
|
|
|
14
|
-
class KeyboardControllerViewManager(mReactContext: ReactApplicationContext) :
|
|
14
|
+
class KeyboardControllerViewManager(mReactContext: ReactApplicationContext) :
|
|
15
|
+
ReactViewManager(),
|
|
16
|
+
KeyboardControllerViewManagerInterface<ReactViewGroup> {
|
|
15
17
|
private val manager = KeyboardControllerViewManagerImpl(mReactContext)
|
|
16
18
|
private val mDelegate = KeyboardControllerViewManagerDelegate(this)
|
|
17
19
|
|
package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt
CHANGED
|
@@ -3,7 +3,8 @@ package com.reactnativekeyboardcontroller
|
|
|
3
3
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
4
|
import com.reactnativekeyboardcontroller.modules.StatusBarManagerCompatModuleImpl
|
|
5
5
|
|
|
6
|
-
class StatusBarManagerCompatModule(
|
|
6
|
+
class StatusBarManagerCompatModule(mReactContext: ReactApplicationContext) :
|
|
7
|
+
NativeStatusBarManagerCompatSpec(mReactContext) {
|
|
7
8
|
private val module = StatusBarManagerCompatModuleImpl(mReactContext)
|
|
8
9
|
|
|
9
10
|
override fun getName(): String = StatusBarManagerCompatModuleImpl.NAME
|
package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationCallback.kt
CHANGED
|
@@ -17,29 +17,69 @@ import com.facebook.react.uimanager.ThemedReactContext
|
|
|
17
17
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
18
18
|
import com.facebook.react.uimanager.events.Event
|
|
19
19
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
20
|
+
import com.facebook.react.views.textinput.ReactEditText
|
|
20
21
|
import com.facebook.react.views.view.ReactViewGroup
|
|
21
22
|
import com.reactnativekeyboardcontroller.events.KeyboardTransitionEvent
|
|
22
23
|
import com.reactnativekeyboardcontroller.extensions.dp
|
|
23
24
|
import kotlin.math.abs
|
|
24
25
|
|
|
26
|
+
private val TAG = KeyboardAnimationCallback::class.qualifiedName
|
|
27
|
+
|
|
25
28
|
class KeyboardAnimationCallback(
|
|
26
29
|
val view: ReactViewGroup,
|
|
27
30
|
val persistentInsetTypes: Int,
|
|
28
31
|
val deferredInsetTypes: Int,
|
|
29
32
|
dispatchMode: Int = DISPATCH_MODE_STOP,
|
|
30
33
|
val context: ThemedReactContext?,
|
|
31
|
-
val onApplyWindowInsetsListener: OnApplyWindowInsetsListener,
|
|
32
34
|
) : WindowInsetsAnimationCompat.Callback(dispatchMode), OnApplyWindowInsetsListener {
|
|
33
|
-
private val TAG = KeyboardAnimationCallback::class.qualifiedName
|
|
34
35
|
private var persistentKeyboardHeight = 0.0
|
|
35
36
|
private var isKeyboardVisible = false
|
|
36
37
|
private var isTransitioning = false
|
|
38
|
+
private var duration = 0
|
|
39
|
+
private var viewTagFocused = -1
|
|
37
40
|
|
|
38
41
|
init {
|
|
39
42
|
require(persistentInsetTypes and deferredInsetTypes == 0) {
|
|
40
43
|
"persistentInsetTypes and deferredInsetTypes can not contain any of " +
|
|
41
44
|
" same WindowInsetsCompat.Type values"
|
|
42
45
|
}
|
|
46
|
+
|
|
47
|
+
view.viewTreeObserver.addOnGlobalFocusChangeListener { oldFocus, newFocus ->
|
|
48
|
+
if (newFocus is ReactEditText) {
|
|
49
|
+
viewTagFocused = newFocus.id
|
|
50
|
+
|
|
51
|
+
// keyboard is visible and focus has been changed
|
|
52
|
+
if (this.isKeyboardVisible && oldFocus !== null) {
|
|
53
|
+
// imitate iOS behavior and send two instant start/end events containing an info about new tag
|
|
54
|
+
// 1. onStart/onMove/onEnd can be still dispatched after, if keyboard change size (numeric -> alphabetic type)
|
|
55
|
+
// 2. event should be send only when keyboard is visible, since this event arrives earlier -> `tag` will be
|
|
56
|
+
// 100% included in onStart/onMove/onEnd lifecycles, but triggering onStart/onEnd several time
|
|
57
|
+
// can bring breaking changes
|
|
58
|
+
this.sendEventToJS(
|
|
59
|
+
KeyboardTransitionEvent(
|
|
60
|
+
view.id,
|
|
61
|
+
"topKeyboardMoveStart",
|
|
62
|
+
this.persistentKeyboardHeight,
|
|
63
|
+
1.0,
|
|
64
|
+
0,
|
|
65
|
+
viewTagFocused,
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
this.sendEventToJS(
|
|
69
|
+
KeyboardTransitionEvent(
|
|
70
|
+
view.id,
|
|
71
|
+
"topKeyboardMoveEnd",
|
|
72
|
+
this.persistentKeyboardHeight,
|
|
73
|
+
1.0,
|
|
74
|
+
0,
|
|
75
|
+
viewTagFocused,
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(this.persistentKeyboardHeight))
|
|
79
|
+
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(this.persistentKeyboardHeight))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
43
83
|
}
|
|
44
84
|
|
|
45
85
|
/**
|
|
@@ -59,32 +99,65 @@ class KeyboardAnimationCallback(
|
|
|
59
99
|
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
|
60
100
|
// when keyboard appears values will be (false && true)
|
|
61
101
|
// when keyboard disappears values will be (true && false)
|
|
62
|
-
|
|
102
|
+
val isKeyboardShown = isKeyboardVisible && isKeyboardVisible()
|
|
103
|
+
// `isTransitioning` check is needed to avoid calls of `onApplyWindowInsets` during keyboard animation
|
|
63
104
|
// having such check allows us not to dispatch unnecessary incorrect events
|
|
64
105
|
// the condition will be executed only when keyboard is opened and changes its size
|
|
65
106
|
// (for example it happens when user changes keyboard type from 'text' to 'emoji' input
|
|
66
|
-
|
|
107
|
+
//
|
|
108
|
+
// `InteractiveKeyboardProvider.isInteractive` detect case when keyboard moves
|
|
109
|
+
// because of the gesture
|
|
110
|
+
val isMoving = isTransitioning || InteractiveKeyboardProvider.isInteractive
|
|
111
|
+
if (isKeyboardShown && !isMoving && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
67
112
|
val keyboardHeight = getCurrentKeyboardHeight()
|
|
113
|
+
val duration = DEFAULT_ANIMATION_TIME.toInt()
|
|
68
114
|
|
|
69
115
|
this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight))
|
|
70
|
-
this.sendEventToJS(
|
|
116
|
+
this.sendEventToJS(
|
|
117
|
+
KeyboardTransitionEvent(
|
|
118
|
+
view.id,
|
|
119
|
+
"topKeyboardMoveStart",
|
|
120
|
+
keyboardHeight,
|
|
121
|
+
1.0,
|
|
122
|
+
duration,
|
|
123
|
+
viewTagFocused,
|
|
124
|
+
),
|
|
125
|
+
)
|
|
71
126
|
|
|
72
127
|
val animation = ValueAnimator.ofFloat(this.persistentKeyboardHeight.toFloat(), keyboardHeight.toFloat())
|
|
73
128
|
animation.addUpdateListener { animator ->
|
|
74
129
|
val toValue = animator.animatedValue as Float
|
|
75
|
-
this.sendEventToJS(
|
|
130
|
+
this.sendEventToJS(
|
|
131
|
+
KeyboardTransitionEvent(
|
|
132
|
+
view.id,
|
|
133
|
+
"topKeyboardMove",
|
|
134
|
+
toValue.toDouble(),
|
|
135
|
+
toValue.toDouble() / keyboardHeight,
|
|
136
|
+
duration,
|
|
137
|
+
viewTagFocused,
|
|
138
|
+
),
|
|
139
|
+
)
|
|
76
140
|
}
|
|
77
141
|
animation.doOnEnd {
|
|
78
142
|
this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
|
|
79
|
-
this.sendEventToJS(
|
|
143
|
+
this.sendEventToJS(
|
|
144
|
+
KeyboardTransitionEvent(
|
|
145
|
+
view.id,
|
|
146
|
+
"topKeyboardMoveEnd",
|
|
147
|
+
keyboardHeight,
|
|
148
|
+
1.0,
|
|
149
|
+
duration,
|
|
150
|
+
viewTagFocused,
|
|
151
|
+
),
|
|
152
|
+
)
|
|
80
153
|
}
|
|
81
|
-
animation.setDuration(
|
|
154
|
+
animation.setDuration(DEFAULT_ANIMATION_TIME).startDelay = 0
|
|
82
155
|
animation.start()
|
|
83
156
|
|
|
84
157
|
this.persistentKeyboardHeight = keyboardHeight
|
|
85
158
|
}
|
|
86
159
|
|
|
87
|
-
return
|
|
160
|
+
return insets
|
|
88
161
|
}
|
|
89
162
|
|
|
90
163
|
override fun onStart(
|
|
@@ -93,6 +166,7 @@ class KeyboardAnimationCallback(
|
|
|
93
166
|
): WindowInsetsAnimationCompat.BoundsCompat {
|
|
94
167
|
isTransitioning = true
|
|
95
168
|
isKeyboardVisible = isKeyboardVisible()
|
|
169
|
+
duration = animation.durationMillis.toInt()
|
|
96
170
|
val keyboardHeight = getCurrentKeyboardHeight()
|
|
97
171
|
|
|
98
172
|
if (isKeyboardVisible) {
|
|
@@ -100,10 +174,22 @@ class KeyboardAnimationCallback(
|
|
|
100
174
|
this.persistentKeyboardHeight = keyboardHeight
|
|
101
175
|
}
|
|
102
176
|
|
|
103
|
-
this.emitEvent(
|
|
177
|
+
this.emitEvent(
|
|
178
|
+
"KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow",
|
|
179
|
+
getEventParams(keyboardHeight),
|
|
180
|
+
)
|
|
104
181
|
|
|
105
|
-
Log.i(TAG, "HEIGHT:: $keyboardHeight")
|
|
106
|
-
this.sendEventToJS(
|
|
182
|
+
Log.i(TAG, "HEIGHT:: $keyboardHeight TAG:: $viewTagFocused")
|
|
183
|
+
this.sendEventToJS(
|
|
184
|
+
KeyboardTransitionEvent(
|
|
185
|
+
view.id,
|
|
186
|
+
"topKeyboardMoveStart",
|
|
187
|
+
keyboardHeight,
|
|
188
|
+
if (!isKeyboardVisible) 0.0 else 1.0,
|
|
189
|
+
duration,
|
|
190
|
+
viewTagFocused,
|
|
191
|
+
),
|
|
192
|
+
)
|
|
107
193
|
|
|
108
194
|
return super.onStart(animation, bounds)
|
|
109
195
|
}
|
|
@@ -131,12 +217,13 @@ class KeyboardAnimationCallback(
|
|
|
131
217
|
try {
|
|
132
218
|
progress = abs((height / persistentKeyboardHeight)).let { if (it.isNaN()) 0.0 else it }
|
|
133
219
|
} catch (e: ArithmeticException) {
|
|
134
|
-
// do nothing, send progress as 0
|
|
220
|
+
// do nothing, just log an exception send progress as 0
|
|
221
|
+
Log.w(TAG, "Caught arithmetic exception during `progress` calculation: $e")
|
|
135
222
|
}
|
|
136
|
-
Log.i(TAG, "DiffY: $diffY $height $progress ${InteractiveKeyboardProvider.isInteractive}")
|
|
223
|
+
Log.i(TAG, "DiffY: $diffY $height $progress ${InteractiveKeyboardProvider.isInteractive} $viewTagFocused")
|
|
137
224
|
|
|
138
225
|
val event = if (InteractiveKeyboardProvider.isInteractive) "topKeyboardMoveInteractive" else "topKeyboardMove"
|
|
139
|
-
this.sendEventToJS(KeyboardTransitionEvent(view.id, event, height, progress))
|
|
226
|
+
this.sendEventToJS(KeyboardTransitionEvent(view.id, event, height, progress, duration, viewTagFocused))
|
|
140
227
|
|
|
141
228
|
return insets
|
|
142
229
|
}
|
|
@@ -145,12 +232,15 @@ class KeyboardAnimationCallback(
|
|
|
145
232
|
super.onEnd(animation)
|
|
146
233
|
|
|
147
234
|
isTransitioning = false
|
|
235
|
+
duration = animation.durationMillis.toInt()
|
|
236
|
+
|
|
237
|
+
var keyboardHeight = this.persistentKeyboardHeight
|
|
148
238
|
// if keyboard becomes shown after interactive animation completion
|
|
149
239
|
// getCurrentKeyboardHeight() will be `0` and isKeyboardVisible will be `false`
|
|
150
240
|
// it's not correct behavior, so we are handling it here
|
|
151
241
|
val isKeyboardShown = InteractiveKeyboardProvider.shown
|
|
152
242
|
if (!isKeyboardShown) {
|
|
153
|
-
|
|
243
|
+
keyboardHeight = getCurrentKeyboardHeight()
|
|
154
244
|
} else {
|
|
155
245
|
// if keyboard is shown after interactions and the animation has finished
|
|
156
246
|
// then we need to reset the state
|
|
@@ -158,8 +248,23 @@ class KeyboardAnimationCallback(
|
|
|
158
248
|
}
|
|
159
249
|
isKeyboardVisible = isKeyboardVisible || isKeyboardShown
|
|
160
250
|
|
|
161
|
-
this.emitEvent(
|
|
162
|
-
|
|
251
|
+
this.emitEvent(
|
|
252
|
+
"KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow",
|
|
253
|
+
getEventParams(keyboardHeight),
|
|
254
|
+
)
|
|
255
|
+
this.sendEventToJS(
|
|
256
|
+
KeyboardTransitionEvent(
|
|
257
|
+
view.id,
|
|
258
|
+
"topKeyboardMoveEnd",
|
|
259
|
+
keyboardHeight,
|
|
260
|
+
if (!isKeyboardVisible) 0.0 else 1.0,
|
|
261
|
+
duration,
|
|
262
|
+
viewTagFocused,
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
// reset to initial state
|
|
267
|
+
duration = 0
|
|
163
268
|
}
|
|
164
269
|
|
|
165
270
|
private fun isKeyboardVisible(): Boolean {
|
|
@@ -192,7 +297,14 @@ class KeyboardAnimationCallback(
|
|
|
192
297
|
private fun getEventParams(height: Double): WritableMap {
|
|
193
298
|
val params: WritableMap = Arguments.createMap()
|
|
194
299
|
params.putDouble("height", height)
|
|
300
|
+
params.putInt("duration", duration)
|
|
301
|
+
params.putDouble("timestamp", System.currentTimeMillis().toDouble())
|
|
302
|
+
params.putInt("target", viewTagFocused)
|
|
195
303
|
|
|
196
304
|
return params
|
|
197
305
|
}
|
|
306
|
+
|
|
307
|
+
companion object {
|
|
308
|
+
private const val DEFAULT_ANIMATION_TIME = 250L
|
|
309
|
+
}
|
|
198
310
|
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationController.kt
CHANGED
|
@@ -14,6 +14,7 @@ import androidx.dynamicanimation.animation.springAnimationOf
|
|
|
14
14
|
import androidx.dynamicanimation.animation.withSpringForceProperties
|
|
15
15
|
import kotlin.math.roundToInt
|
|
16
16
|
|
|
17
|
+
@Suppress("detekt:TooManyFunctions")
|
|
17
18
|
internal class KeyboardAnimationController {
|
|
18
19
|
private var insetsAnimationController: WindowInsetsAnimationControllerCompat? = null
|
|
19
20
|
private var pendingRequestCancellationSignal: CancellationSignal? = null
|
|
@@ -284,8 +285,14 @@ internal class KeyboardAnimationController {
|
|
|
284
285
|
velocityY = velocityY,
|
|
285
286
|
)
|
|
286
287
|
// The current inset matches either the shown/hidden inset, finish() immediately
|
|
287
|
-
current == shown ->
|
|
288
|
-
|
|
288
|
+
current == shown -> {
|
|
289
|
+
InteractiveKeyboardProvider.shown = true
|
|
290
|
+
controller.finish(true)
|
|
291
|
+
}
|
|
292
|
+
current == hidden -> {
|
|
293
|
+
InteractiveKeyboardProvider.shown = false
|
|
294
|
+
controller.finish(false)
|
|
295
|
+
}
|
|
289
296
|
else -> {
|
|
290
297
|
// Otherwise, we'll look at the current position...
|
|
291
298
|
if (controller.currentFraction >= SCROLL_THRESHOLD) {
|
|
@@ -348,6 +355,7 @@ internal class KeyboardAnimationController {
|
|
|
348
355
|
visible: Boolean,
|
|
349
356
|
velocityY: Float? = null,
|
|
350
357
|
) {
|
|
358
|
+
@Suppress("detekt:UseCheckOrError")
|
|
351
359
|
val controller = insetsAnimationController
|
|
352
360
|
?: throw IllegalStateException("Controller should not be null")
|
|
353
361
|
|
package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt
CHANGED
|
@@ -4,16 +4,25 @@ import com.facebook.react.bridge.Arguments
|
|
|
4
4
|
import com.facebook.react.uimanager.events.Event
|
|
5
5
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
6
6
|
|
|
7
|
-
class KeyboardTransitionEvent(
|
|
7
|
+
class KeyboardTransitionEvent(
|
|
8
|
+
viewId: Int,
|
|
9
|
+
private val event: String,
|
|
10
|
+
private val height: Double,
|
|
11
|
+
private val progress: Double,
|
|
12
|
+
private val duration: Int,
|
|
13
|
+
private val target: Int,
|
|
14
|
+
) : Event<KeyboardTransitionEvent>(viewId) {
|
|
8
15
|
override fun getEventName() = event
|
|
9
16
|
|
|
10
|
-
//
|
|
17
|
+
// All events for a given view can be coalesced?
|
|
11
18
|
override fun getCoalescingKey(): Short = 0
|
|
12
19
|
|
|
13
20
|
override fun dispatch(rctEventEmitter: RCTEventEmitter) {
|
|
14
21
|
val map = Arguments.createMap()
|
|
15
22
|
map.putDouble("progress", progress)
|
|
16
23
|
map.putDouble("height", height)
|
|
24
|
+
map.putInt("duration", duration)
|
|
25
|
+
map.putInt("target", target)
|
|
17
26
|
rctEventEmitter.receiveEvent(viewTag, eventName, map)
|
|
18
27
|
}
|
|
19
28
|
}
|
|
@@ -5,7 +5,8 @@ import com.facebook.react.common.MapBuilder
|
|
|
5
5
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
6
|
import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
@Suppress("detekt:UnusedPrivateProperty")
|
|
9
|
+
class KeyboardControllerViewManagerImpl(mReactContext: ReactApplicationContext) {
|
|
9
10
|
fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup {
|
|
10
11
|
return EdgeToEdgeReactViewGroup(reactContext)
|
|
11
12
|
}
|
|
@@ -4,6 +4,7 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
4
4
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
5
|
import com.reactnativekeyboardcontroller.views.KeyboardGestureAreaReactViewGroup
|
|
6
6
|
|
|
7
|
+
@Suppress("detekt:UnusedPrivateProperty")
|
|
7
8
|
class KeyboardGestureAreaViewManagerImpl(mReactContext: ReactApplicationContext) {
|
|
8
9
|
fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup {
|
|
9
10
|
return KeyboardGestureAreaReactViewGroup(reactContext)
|
|
@@ -5,7 +5,12 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
5
5
|
import com.facebook.react.bridge.UiThreadUtil
|
|
6
6
|
|
|
7
7
|
class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationContext) {
|
|
8
|
-
private val mDefaultMode: Int = mReactContext
|
|
8
|
+
private val mDefaultMode: Int = mReactContext
|
|
9
|
+
.currentActivity
|
|
10
|
+
?.window
|
|
11
|
+
?.attributes
|
|
12
|
+
?.softInputMode
|
|
13
|
+
?: WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
|
|
9
14
|
|
|
10
15
|
fun setInputMode(mode: Int) {
|
|
11
16
|
setSoftInputMode(mode)
|
|
@@ -10,8 +10,9 @@ import androidx.core.view.WindowInsetsControllerCompat
|
|
|
10
10
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
11
11
|
import com.facebook.react.bridge.UiThreadUtil
|
|
12
12
|
|
|
13
|
+
private val TAG = StatusBarManagerCompatModuleImpl::class.qualifiedName
|
|
14
|
+
|
|
13
15
|
class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicationContext) {
|
|
14
|
-
private val TAG = StatusBarManagerCompatModuleImpl::class.qualifiedName
|
|
15
16
|
private var controller: WindowInsetsControllerCompat? = null
|
|
16
17
|
|
|
17
18
|
fun setHidden(hidden: Boolean) {
|
|
@@ -41,7 +42,7 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
41
42
|
colorAnimation.addUpdateListener { animator ->
|
|
42
43
|
window.statusBarColor = animator.animatedValue as Int
|
|
43
44
|
}
|
|
44
|
-
colorAnimation.setDuration(
|
|
45
|
+
colorAnimation.setDuration(DEFAULT_ANIMATION_TIME).startDelay = 0
|
|
45
46
|
colorAnimation.start()
|
|
46
47
|
} else {
|
|
47
48
|
window.statusBarColor = color
|
|
@@ -49,6 +50,7 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
@Suppress("detekt:UnusedParameter")
|
|
52
54
|
fun setTranslucent(translucent: Boolean) {
|
|
53
55
|
// the status bar is translucent by default (once you wrapped App in Provider,
|
|
54
56
|
// and EdgeToEdgeReactViewGroup has been mounted and called
|
|
@@ -73,7 +75,10 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
73
75
|
if (this.controller == null) {
|
|
74
76
|
val activity = mReactContext.currentActivity
|
|
75
77
|
if (activity == null) {
|
|
76
|
-
Log.w(
|
|
78
|
+
Log.w(
|
|
79
|
+
TAG,
|
|
80
|
+
"StatusBarManagerCompatModule: can not get `WindowInsetsControllerCompat` because current activity is null.",
|
|
81
|
+
)
|
|
77
82
|
return this.controller
|
|
78
83
|
}
|
|
79
84
|
|
|
@@ -87,5 +92,6 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
87
92
|
|
|
88
93
|
companion object {
|
|
89
94
|
const val NAME = "StatusBarManagerCompat"
|
|
95
|
+
private const val DEFAULT_ANIMATION_TIME = 300L
|
|
90
96
|
}
|
|
91
97
|
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller.views
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
+
import android.os.Handler
|
|
5
|
+
import android.os.Looper
|
|
4
6
|
import android.util.Log
|
|
7
|
+
import android.widget.FrameLayout
|
|
5
8
|
import androidx.appcompat.widget.FitWindowsLinearLayout
|
|
6
9
|
import androidx.core.view.ViewCompat
|
|
7
10
|
import androidx.core.view.WindowCompat
|
|
@@ -10,12 +13,13 @@ import androidx.core.view.WindowInsetsCompat
|
|
|
10
13
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
14
|
import com.facebook.react.views.view.ReactViewGroup
|
|
12
15
|
import com.reactnativekeyboardcontroller.KeyboardAnimationCallback
|
|
13
|
-
import com.reactnativekeyboardcontroller.R
|
|
14
16
|
import com.reactnativekeyboardcontroller.extensions.requestApplyInsetsWhenAttached
|
|
17
|
+
import com.reactnativekeyboardcontroller.extensions.rootView
|
|
18
|
+
|
|
19
|
+
private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName
|
|
15
20
|
|
|
16
21
|
@SuppressLint("ViewConstructor")
|
|
17
22
|
class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
|
|
18
|
-
private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName
|
|
19
23
|
private var isStatusBarTranslucent = false
|
|
20
24
|
private var isNavigationBarTranslucent = false
|
|
21
25
|
|
|
@@ -23,7 +27,6 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
23
27
|
val activity = reactContext.currentActivity
|
|
24
28
|
|
|
25
29
|
if (activity != null) {
|
|
26
|
-
|
|
27
30
|
val callback = KeyboardAnimationCallback(
|
|
28
31
|
view = this,
|
|
29
32
|
persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
|
|
@@ -32,30 +35,6 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
32
35
|
// child views, so that step 2.5 below receives the call
|
|
33
36
|
dispatchMode = WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE,
|
|
34
37
|
context = reactContext,
|
|
35
|
-
onApplyWindowInsetsListener = { v, insets ->
|
|
36
|
-
val content =
|
|
37
|
-
reactContext.currentActivity?.window?.decorView?.rootView?.findViewById<FitWindowsLinearLayout>(
|
|
38
|
-
androidx.appcompat.R.id.action_bar_root,
|
|
39
|
-
)
|
|
40
|
-
content?.setPadding(
|
|
41
|
-
0,
|
|
42
|
-
if (this.isStatusBarTranslucent) {
|
|
43
|
-
0
|
|
44
|
-
} else {
|
|
45
|
-
insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.top
|
|
46
|
-
?: 0
|
|
47
|
-
},
|
|
48
|
-
0,
|
|
49
|
-
if (this.isNavigationBarTranslucent) {
|
|
50
|
-
0
|
|
51
|
-
} else {
|
|
52
|
-
insets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom
|
|
53
|
-
?: 0
|
|
54
|
-
},
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
insets
|
|
58
|
-
},
|
|
59
38
|
)
|
|
60
39
|
ViewCompat.setWindowInsetsAnimationCallback(this, callback)
|
|
61
40
|
ViewCompat.setOnApplyWindowInsetsListener(this, callback)
|
|
@@ -65,9 +44,47 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
65
44
|
}
|
|
66
45
|
}
|
|
67
46
|
|
|
47
|
+
private fun setupWindowInsets() {
|
|
48
|
+
val rootView = reactContext.rootView
|
|
49
|
+
if (rootView != null) {
|
|
50
|
+
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
|
|
51
|
+
val content =
|
|
52
|
+
reactContext.rootView?.findViewById<FitWindowsLinearLayout>(
|
|
53
|
+
androidx.appcompat.R.id.action_bar_root,
|
|
54
|
+
)
|
|
55
|
+
val params = FrameLayout.LayoutParams(
|
|
56
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
57
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
params.setMargins(
|
|
61
|
+
0,
|
|
62
|
+
if (this.isStatusBarTranslucent) {
|
|
63
|
+
0
|
|
64
|
+
} else {
|
|
65
|
+
insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.top
|
|
66
|
+
?: 0
|
|
67
|
+
},
|
|
68
|
+
0,
|
|
69
|
+
if (this.isNavigationBarTranslucent) {
|
|
70
|
+
0
|
|
71
|
+
} else {
|
|
72
|
+
insets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom
|
|
73
|
+
?: 0
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
content?.layoutParams = params
|
|
78
|
+
|
|
79
|
+
insets
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
68
84
|
override fun onAttachedToWindow() {
|
|
69
85
|
super.onAttachedToWindow()
|
|
70
86
|
|
|
87
|
+
Handler(Looper.getMainLooper()).post(this::setupWindowInsets)
|
|
71
88
|
reactContext.currentActivity?.let {
|
|
72
89
|
WindowCompat.setDecorFitsSystemWindows(
|
|
73
90
|
it.window,
|