react-native-keyboard-controller 1.5.8 → 1.6.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/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 +128 -18
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationController.kt +2 -0
- 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 +119 -96
- 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/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 +58 -11
- package/lib/commonjs/animated.js +5 -4
- 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 +18 -7
- 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 +4 -3
- 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 +17 -7
- 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/replicas.d.ts +2 -7
- package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +3 -1
- package/lib/typescript/types.d.ts +15 -1
- package/package.json +5 -5
- package/react-native-keyboard-controller.podspec +9 -1
- package/src/animated.tsx +4 -7
- 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 +21 -3
- 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(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
177
|
+
this.emitEvent(
|
|
178
|
+
"KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow",
|
|
179
|
+
getEventParams(keyboardHeight),
|
|
180
|
+
)
|
|
181
|
+
|
|
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,6 +232,7 @@ class KeyboardAnimationCallback(
|
|
|
145
232
|
super.onEnd(animation)
|
|
146
233
|
|
|
147
234
|
isTransitioning = false
|
|
235
|
+
duration = animation.durationMillis.toInt()
|
|
148
236
|
|
|
149
237
|
var keyboardHeight = this.persistentKeyboardHeight
|
|
150
238
|
// if keyboard becomes shown after interactive animation completion
|
|
@@ -160,8 +248,23 @@ class KeyboardAnimationCallback(
|
|
|
160
248
|
}
|
|
161
249
|
isKeyboardVisible = isKeyboardVisible || isKeyboardShown
|
|
162
250
|
|
|
163
|
-
this.emitEvent(
|
|
164
|
-
|
|
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
|
|
165
268
|
}
|
|
166
269
|
|
|
167
270
|
private fun isKeyboardVisible(): Boolean {
|
|
@@ -194,7 +297,14 @@ class KeyboardAnimationCallback(
|
|
|
194
297
|
private fun getEventParams(height: Double): WritableMap {
|
|
195
298
|
val params: WritableMap = Arguments.createMap()
|
|
196
299
|
params.putDouble("height", height)
|
|
300
|
+
params.putInt("duration", duration)
|
|
301
|
+
params.putDouble("timestamp", System.currentTimeMillis().toDouble())
|
|
302
|
+
params.putInt("target", viewTagFocused)
|
|
197
303
|
|
|
198
304
|
return params
|
|
199
305
|
}
|
|
306
|
+
|
|
307
|
+
companion object {
|
|
308
|
+
private const val DEFAULT_ANIMATION_TIME = 250L
|
|
309
|
+
}
|
|
200
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
|
|
@@ -354,6 +355,7 @@ internal class KeyboardAnimationController {
|
|
|
354
355
|
visible: Boolean,
|
|
355
356
|
velocityY: Float? = null,
|
|
356
357
|
) {
|
|
358
|
+
@Suppress("detekt:UseCheckOrError")
|
|
357
359
|
val controller = insetsAnimationController
|
|
358
360
|
?: throw IllegalStateException("Controller should not be null")
|
|
359
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,
|