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
package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt
CHANGED
|
@@ -14,14 +14,21 @@ fun ThemedReactContext.setupWindowDimensionsListener() {
|
|
|
14
14
|
WindowDimensionListener(this)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
fun ThemedReactContext?.dispatchEvent(
|
|
17
|
+
fun ThemedReactContext?.dispatchEvent(
|
|
18
|
+
viewId: Int,
|
|
19
|
+
event: Event<*>,
|
|
20
|
+
) {
|
|
18
21
|
val eventDispatcher: EventDispatcher? =
|
|
19
22
|
UIManagerHelper.getEventDispatcherForReactTag(this as ReactContext, viewId)
|
|
20
23
|
eventDispatcher?.dispatchEvent(event)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
fun ThemedReactContext?.emitEvent(
|
|
24
|
-
|
|
26
|
+
fun ThemedReactContext?.emitEvent(
|
|
27
|
+
event: String,
|
|
28
|
+
params: WritableMap,
|
|
29
|
+
) {
|
|
30
|
+
this
|
|
31
|
+
?.reactApplicationContext
|
|
25
32
|
?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
26
33
|
?.emit(event, params)
|
|
27
34
|
|
|
@@ -18,16 +18,18 @@ fun View.requestApplyInsetsWhenAttached() {
|
|
|
18
18
|
requestApplyInsets()
|
|
19
19
|
} else {
|
|
20
20
|
// We're not attached to the hierarchy, add a listener to request when we are
|
|
21
|
-
addOnAttachStateChangeListener(
|
|
22
|
-
|
|
23
|
-
v
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
addOnAttachStateChangeListener(
|
|
22
|
+
object : View.OnAttachStateChangeListener {
|
|
23
|
+
override fun onViewAttachedToWindow(v: View) {
|
|
24
|
+
v.removeOnAttachStateChangeListener(this)
|
|
25
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
|
|
26
|
+
v.requestApplyInsets()
|
|
27
|
+
}
|
|
26
28
|
}
|
|
27
|
-
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
override fun onViewDetachedFromWindow(v: View) = Unit
|
|
31
|
+
},
|
|
32
|
+
)
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -73,7 +73,8 @@ internal class KeyboardAnimationController {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// Keep track of the IME insets, and the IME visibility, at the start of the request
|
|
76
|
-
isImeShownAtStart = ViewCompat
|
|
76
|
+
isImeShownAtStart = ViewCompat
|
|
77
|
+
.getRootWindowInsets(view)
|
|
77
78
|
?.isVisible(WindowInsetsCompat.Type.ime()) == true
|
|
78
79
|
|
|
79
80
|
// Create a cancellation signal, which we pass to controlWindowInsetsAnimation() below
|
|
@@ -114,7 +115,10 @@ internal class KeyboardAnimationController {
|
|
|
114
115
|
* @param view The view which is triggering this request
|
|
115
116
|
* @param velocityY the velocity of the touch gesture which caused this call
|
|
116
117
|
*/
|
|
117
|
-
fun startAndFling(
|
|
118
|
+
fun startAndFling(
|
|
119
|
+
view: View,
|
|
120
|
+
velocityY: Float,
|
|
121
|
+
) = startControlRequest(view) {
|
|
118
122
|
animateToFinish(velocityY)
|
|
119
123
|
}
|
|
120
124
|
|
|
@@ -127,11 +131,12 @@ internal class KeyboardAnimationController {
|
|
|
127
131
|
* @return the amount of [dy] consumed by the inset animation, in pixels
|
|
128
132
|
*/
|
|
129
133
|
fun insetBy(dy: Int): Int {
|
|
130
|
-
val controller =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
|
|
134
|
+
val controller =
|
|
135
|
+
insetsAnimationController
|
|
136
|
+
?: throw IllegalStateException(
|
|
137
|
+
"Current WindowInsetsAnimationController is null." +
|
|
138
|
+
"This should only be called if isAnimationInProgress() returns true",
|
|
139
|
+
)
|
|
135
140
|
|
|
136
141
|
InteractiveKeyboardProvider.isInteractive = true
|
|
137
142
|
// Call updateInsetTo() with the new inset value
|
|
@@ -147,11 +152,12 @@ internal class KeyboardAnimationController {
|
|
|
147
152
|
* @return the distance moved by the inset animation, in pixels
|
|
148
153
|
*/
|
|
149
154
|
fun insetTo(inset: Int): Int {
|
|
150
|
-
val controller =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"
|
|
154
|
-
|
|
155
|
+
val controller =
|
|
156
|
+
insetsAnimationController
|
|
157
|
+
?: throw IllegalStateException(
|
|
158
|
+
"Current WindowInsetsAnimationController is null." +
|
|
159
|
+
"This should only be called if isAnimationInProgress() returns true",
|
|
160
|
+
)
|
|
155
161
|
|
|
156
162
|
val hiddenBottom = controller.hiddenStateInsets.bottom
|
|
157
163
|
val shownBottom = controller.shownStateInsets.bottom
|
|
@@ -182,23 +188,17 @@ internal class KeyboardAnimationController {
|
|
|
182
188
|
/**
|
|
183
189
|
* Return `true` if an inset animation is in progress.
|
|
184
190
|
*/
|
|
185
|
-
fun isInsetAnimationInProgress(): Boolean
|
|
186
|
-
return insetsAnimationController != null
|
|
187
|
-
}
|
|
191
|
+
fun isInsetAnimationInProgress(): Boolean = insetsAnimationController != null
|
|
188
192
|
|
|
189
193
|
/**
|
|
190
194
|
* Return `true` if an inset animation is currently finishing.
|
|
191
195
|
*/
|
|
192
|
-
fun isInsetAnimationFinishing(): Boolean
|
|
193
|
-
return currentSpringAnimation != null
|
|
194
|
-
}
|
|
196
|
+
fun isInsetAnimationFinishing(): Boolean = currentSpringAnimation != null
|
|
195
197
|
|
|
196
198
|
/**
|
|
197
199
|
* Return `true` if a request to control an inset animation is in progress.
|
|
198
200
|
*/
|
|
199
|
-
fun isInsetAnimationRequestPending(): Boolean
|
|
200
|
-
return pendingRequestCancellationSignal != null
|
|
201
|
-
}
|
|
201
|
+
fun isInsetAnimationRequestPending(): Boolean = pendingRequestCancellationSignal != null
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
204
|
* Cancel the current [WindowInsetsAnimationControllerCompat]. We immediately finish
|
|
@@ -280,10 +280,11 @@ internal class KeyboardAnimationController {
|
|
|
280
280
|
when {
|
|
281
281
|
// If we have a velocity, we can use it's direction to determine
|
|
282
282
|
// the visibility. Upwards == visible
|
|
283
|
-
velocityY != null ->
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
283
|
+
velocityY != null ->
|
|
284
|
+
animateImeToVisibility(
|
|
285
|
+
visible = velocityY < 0,
|
|
286
|
+
velocityY = velocityY,
|
|
287
|
+
)
|
|
287
288
|
// The current inset matches either the shown/hidden inset, finish() immediately
|
|
288
289
|
current == shown -> {
|
|
289
290
|
InteractiveKeyboardProvider.shown = true
|
|
@@ -307,11 +308,12 @@ internal class KeyboardAnimationController {
|
|
|
307
308
|
}
|
|
308
309
|
|
|
309
310
|
fun getCurrentKeyboardHeight(): Int {
|
|
310
|
-
val controller =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
"
|
|
314
|
-
|
|
311
|
+
val controller =
|
|
312
|
+
insetsAnimationController
|
|
313
|
+
?: throw IllegalStateException(
|
|
314
|
+
"Current WindowInsetsAnimationController is null." +
|
|
315
|
+
"This should only be called if isAnimationInProgress() returns true",
|
|
316
|
+
)
|
|
315
317
|
|
|
316
318
|
return controller.currentInsets.bottom
|
|
317
319
|
}
|
|
@@ -356,37 +358,40 @@ internal class KeyboardAnimationController {
|
|
|
356
358
|
velocityY: Float? = null,
|
|
357
359
|
) {
|
|
358
360
|
@Suppress("detekt:UseCheckOrError")
|
|
359
|
-
val controller =
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
currentSpringAnimation = null
|
|
361
|
+
val controller =
|
|
362
|
+
insetsAnimationController
|
|
363
|
+
?: throw IllegalStateException("Controller should not be null")
|
|
364
|
+
|
|
365
|
+
currentSpringAnimation =
|
|
366
|
+
springAnimationOf(
|
|
367
|
+
setter = {
|
|
368
|
+
insetTo(it.roundToInt())
|
|
369
|
+
},
|
|
370
|
+
getter = { controller.currentInsets.bottom.toFloat() },
|
|
371
|
+
finalPosition =
|
|
372
|
+
when {
|
|
373
|
+
visible -> controller.shownStateInsets.bottom.toFloat()
|
|
374
|
+
else -> controller.hiddenStateInsets.bottom.toFloat()
|
|
375
|
+
},
|
|
376
|
+
).withSpringForceProperties {
|
|
377
|
+
// Tweak the damping value, to remove any bounciness.
|
|
378
|
+
dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
|
|
379
|
+
// The stiffness value controls the strength of the spring animation, which
|
|
380
|
+
// controls the speed. Medium (the default) is a good value, but feel free to
|
|
381
|
+
// play around with this value.
|
|
382
|
+
stiffness = SpringForce.STIFFNESS_MEDIUM
|
|
383
|
+
}.apply {
|
|
384
|
+
if (velocityY != null) {
|
|
385
|
+
setStartVelocity(velocityY)
|
|
385
386
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
387
|
+
addEndListener { anim, _, _, _ ->
|
|
388
|
+
if (anim == currentSpringAnimation) {
|
|
389
|
+
currentSpringAnimation = null
|
|
390
|
+
}
|
|
391
|
+
// Once the animation has ended, finish the controller
|
|
392
|
+
finish()
|
|
393
|
+
}
|
|
394
|
+
}.also { it.start() }
|
|
390
395
|
}
|
|
391
396
|
}
|
|
392
397
|
|
|
@@ -11,5 +11,10 @@ interface Interpolator {
|
|
|
11
11
|
* @param offset extra space to the keyboard to activate a gesture
|
|
12
12
|
* @return the distance the keyboard should be moved from its current location.
|
|
13
13
|
*/
|
|
14
|
-
fun interpolate(
|
|
14
|
+
fun interpolate(
|
|
15
|
+
dy: Int,
|
|
16
|
+
absoluteFingerPosition: Int,
|
|
17
|
+
keyboardPosition: Int,
|
|
18
|
+
offset: Int,
|
|
19
|
+
): Int
|
|
15
20
|
}
|
|
@@ -8,8 +8,10 @@ class IosInterpolator : Interpolator {
|
|
|
8
8
|
offset: Int,
|
|
9
9
|
): Int {
|
|
10
10
|
if (
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// user over scrolled keyboard
|
|
12
|
+
absoluteFingerPosition <= keyboardPosition + offset ||
|
|
13
|
+
// user scrolls up
|
|
14
|
+
dy <= 0
|
|
13
15
|
) {
|
|
14
16
|
return dy
|
|
15
17
|
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
CHANGED
|
@@ -25,16 +25,17 @@ import com.reactnativekeyboardcontroller.extensions.screenLocation
|
|
|
25
25
|
import com.reactnativekeyboardcontroller.traversal.FocusedInputHolder
|
|
26
26
|
import com.reactnativekeyboardcontroller.traversal.ViewHierarchyNavigator
|
|
27
27
|
|
|
28
|
-
val noFocusedInputEvent =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
val noFocusedInputEvent =
|
|
29
|
+
FocusedInputLayoutChangedEventData(
|
|
30
|
+
x = 0.0,
|
|
31
|
+
y = 0.0,
|
|
32
|
+
width = 0.0,
|
|
33
|
+
height = 0.0,
|
|
34
|
+
absoluteX = 0.0,
|
|
35
|
+
absoluteY = 0.0,
|
|
36
|
+
target = -1,
|
|
37
|
+
parentScrollViewTarget = -1,
|
|
38
|
+
)
|
|
38
39
|
|
|
39
40
|
class FocusedInputObserver(
|
|
40
41
|
val view: View,
|
|
@@ -82,50 +83,52 @@ class FocusedInputObserver(
|
|
|
82
83
|
FocusedInputSelectionChangedEvent(
|
|
83
84
|
surfaceId,
|
|
84
85
|
eventPropagationView.id,
|
|
85
|
-
event =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
event =
|
|
87
|
+
FocusedInputSelectionChangedEventData(
|
|
88
|
+
target = input.id,
|
|
89
|
+
start = start,
|
|
90
|
+
end = end,
|
|
91
|
+
startX = startX,
|
|
92
|
+
startY = startY,
|
|
93
|
+
endX = endX,
|
|
94
|
+
endY = endY,
|
|
95
|
+
),
|
|
94
96
|
),
|
|
95
97
|
)
|
|
96
98
|
}
|
|
97
|
-
private val focusListener =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
99
|
+
private val focusListener =
|
|
100
|
+
OnGlobalFocusChangeListener { oldFocus, newFocus ->
|
|
101
|
+
// unfocused or focus was changed
|
|
102
|
+
if (newFocus == null || oldFocus != null) {
|
|
103
|
+
lastFocusedInput?.removeOnLayoutChangeListener(layoutListener)
|
|
104
|
+
lastFocusedInput?.removeTextChangedListener(textWatcher)
|
|
105
|
+
selectionSubscription?.invoke()
|
|
106
|
+
lastFocusedInput = null
|
|
107
|
+
}
|
|
108
|
+
if (newFocus is ReactEditText) {
|
|
109
|
+
lastFocusedInput = newFocus
|
|
110
|
+
newFocus.addOnLayoutChangeListener(layoutListener)
|
|
111
|
+
this.syncUpLayout()
|
|
112
|
+
textWatcher = newFocus.addOnTextChangedListener(textListener)
|
|
113
|
+
selectionSubscription = newFocus.addOnSelectionChangedListener(selectionListener)
|
|
114
|
+
FocusedInputHolder.set(newFocus)
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
val allInputFields = ViewHierarchyNavigator.getAllInputFields(context?.rootView)
|
|
117
|
+
val currentIndex = allInputFields.indexOf(newFocus)
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
context.emitEvent(
|
|
120
|
+
"KeyboardController::focusDidSet",
|
|
121
|
+
Arguments.createMap().apply {
|
|
122
|
+
putInt("current", currentIndex)
|
|
123
|
+
putInt("count", allInputFields.size)
|
|
124
|
+
},
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
// unfocused
|
|
128
|
+
if (newFocus == null) {
|
|
129
|
+
dispatchEventToJS(noFocusedInputEvent)
|
|
130
|
+
}
|
|
123
131
|
}
|
|
124
|
-
// unfocused
|
|
125
|
-
if (newFocus == null) {
|
|
126
|
-
dispatchEventToJS(noFocusedInputEvent)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
132
|
|
|
130
133
|
init {
|
|
131
134
|
view.viewTreeObserver.addOnGlobalFocusChangeListener(focusListener)
|
|
@@ -135,16 +138,17 @@ class FocusedInputObserver(
|
|
|
135
138
|
val input = lastFocusedInput ?: return
|
|
136
139
|
|
|
137
140
|
val (x, y) = input.screenLocation
|
|
138
|
-
val event =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
val event =
|
|
142
|
+
FocusedInputLayoutChangedEventData(
|
|
143
|
+
x = input.x.dp,
|
|
144
|
+
y = input.y.dp,
|
|
145
|
+
width = input.width.toFloat().dp,
|
|
146
|
+
height = input.height.toFloat().dp,
|
|
147
|
+
absoluteX = x.toFloat().dp,
|
|
148
|
+
absoluteY = y.toFloat().dp,
|
|
149
|
+
target = input.id,
|
|
150
|
+
parentScrollViewTarget = input.parentScrollViewTarget,
|
|
151
|
+
)
|
|
148
152
|
|
|
149
153
|
dispatchEventToJS(event)
|
|
150
154
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller.listeners
|
|
2
2
|
|
|
3
|
-
import android.os.Build
|
|
4
3
|
import android.view.View
|
|
5
4
|
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener
|
|
6
5
|
import androidx.core.graphics.Insets
|
|
@@ -14,6 +13,7 @@ import com.facebook.react.uimanager.ThemedReactContext
|
|
|
14
13
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
15
14
|
import com.facebook.react.views.textinput.ReactEditText
|
|
16
15
|
import com.facebook.react.views.view.ReactViewGroup
|
|
16
|
+
import com.reactnativekeyboardcontroller.constants.Keyboard
|
|
17
17
|
import com.reactnativekeyboardcontroller.events.KeyboardTransitionEvent
|
|
18
18
|
import com.reactnativekeyboardcontroller.extensions.dispatchEvent
|
|
19
19
|
import com.reactnativekeyboardcontroller.extensions.dp
|
|
@@ -24,7 +24,7 @@ import com.reactnativekeyboardcontroller.log.Logger
|
|
|
24
24
|
import kotlin.math.abs
|
|
25
25
|
|
|
26
26
|
private val TAG = KeyboardAnimationCallback::class.qualifiedName
|
|
27
|
-
private val isResizeHandledInCallbackMethods =
|
|
27
|
+
private val isResizeHandledInCallbackMethods = Keyboard.IS_ANIMATION_EMULATED
|
|
28
28
|
|
|
29
29
|
data class KeyboardAnimationCallbackConfig(
|
|
30
30
|
val persistentInsetTypes: Int,
|
|
@@ -33,12 +33,22 @@ data class KeyboardAnimationCallbackConfig(
|
|
|
33
33
|
val hasTranslucentNavigationBar: Boolean = false,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
+
interface Suspendable {
|
|
37
|
+
var isSuspended: Boolean
|
|
38
|
+
|
|
39
|
+
fun suspend(suspended: Boolean) {
|
|
40
|
+
isSuspended = suspended
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
class KeyboardAnimationCallback(
|
|
37
45
|
val eventPropagationView: ReactViewGroup,
|
|
38
46
|
val view: View,
|
|
39
47
|
val context: ThemedReactContext?,
|
|
40
48
|
private val config: KeyboardAnimationCallbackConfig,
|
|
41
|
-
) : WindowInsetsAnimationCompat.Callback(config.dispatchMode),
|
|
49
|
+
) : WindowInsetsAnimationCompat.Callback(config.dispatchMode),
|
|
50
|
+
OnApplyWindowInsetsListener,
|
|
51
|
+
Suspendable {
|
|
42
52
|
private val surfaceId = UIManagerHelper.getSurfaceId(eventPropagationView)
|
|
43
53
|
|
|
44
54
|
// state variables
|
|
@@ -49,48 +59,50 @@ class KeyboardAnimationCallback(
|
|
|
49
59
|
private var duration = 0
|
|
50
60
|
private var viewTagFocused = -1
|
|
51
61
|
private var animationsToSkip = hashSetOf<WindowInsetsAnimationCompat>()
|
|
62
|
+
override var isSuspended: Boolean = false
|
|
52
63
|
|
|
53
64
|
// listeners
|
|
54
|
-
private val focusListener =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
KeyboardTransitionEvent(
|
|
68
|
-
surfaceId,
|
|
65
|
+
private val focusListener =
|
|
66
|
+
OnGlobalFocusChangeListener { oldFocus, newFocus ->
|
|
67
|
+
if (newFocus is ReactEditText) {
|
|
68
|
+
viewTagFocused = newFocus.id
|
|
69
|
+
|
|
70
|
+
// keyboard is visible and focus has been changed
|
|
71
|
+
if (this.isKeyboardVisible && oldFocus !== null) {
|
|
72
|
+
// imitate iOS behavior and send two instant start/end events containing an info about new tag
|
|
73
|
+
// 1. onStart/onMove/onEnd can be still dispatched after, if keyboard change size (numeric -> alphabetic type)
|
|
74
|
+
// 2. event should be send only when keyboard is visible, since this event arrives earlier -> `tag` will be
|
|
75
|
+
// 100% included in onStart/onMove/onEnd life cycles, but triggering onStart/onEnd several time
|
|
76
|
+
// can bring breaking changes
|
|
77
|
+
context.dispatchEvent(
|
|
69
78
|
eventPropagationView.id,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
KeyboardTransitionEvent(
|
|
80
|
+
surfaceId,
|
|
81
|
+
eventPropagationView.id,
|
|
82
|
+
"topKeyboardMoveStart",
|
|
83
|
+
this.persistentKeyboardHeight,
|
|
84
|
+
1.0,
|
|
85
|
+
0,
|
|
86
|
+
viewTagFocused,
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
context.dispatchEvent(
|
|
81
90
|
eventPropagationView.id,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
KeyboardTransitionEvent(
|
|
92
|
+
surfaceId,
|
|
93
|
+
eventPropagationView.id,
|
|
94
|
+
"topKeyboardMoveEnd",
|
|
95
|
+
this.persistentKeyboardHeight,
|
|
96
|
+
1.0,
|
|
97
|
+
0,
|
|
98
|
+
viewTagFocused,
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
context.emitEvent("KeyboardController::keyboardWillShow", getEventParams(this.persistentKeyboardHeight))
|
|
102
|
+
context.emitEvent("KeyboardController::keyboardDidShow", getEventParams(this.persistentKeyboardHeight))
|
|
103
|
+
}
|
|
91
104
|
}
|
|
92
105
|
}
|
|
93
|
-
}
|
|
94
106
|
private var layoutObserver: FocusedInputObserver? = null
|
|
95
107
|
|
|
96
108
|
init {
|
|
@@ -113,7 +125,10 @@ class KeyboardAnimationCallback(
|
|
|
113
125
|
* have to implement `onApplyWindowInsets` listener and simulate onStart/onProgress/onEnd
|
|
114
126
|
* events when keyboard changes its size.
|
|
115
127
|
*/
|
|
116
|
-
override fun onApplyWindowInsets(
|
|
128
|
+
override fun onApplyWindowInsets(
|
|
129
|
+
v: View,
|
|
130
|
+
insets: WindowInsetsCompat,
|
|
131
|
+
): WindowInsetsCompat {
|
|
117
132
|
val keyboardHeight = getCurrentKeyboardHeight()
|
|
118
133
|
// when keyboard appears values will be (false && true)
|
|
119
134
|
// when keyboard disappears values will be (true && false)
|
|
@@ -142,7 +157,7 @@ class KeyboardAnimationCallback(
|
|
|
142
157
|
this.onKeyboardResized(keyboardHeight)
|
|
143
158
|
}
|
|
144
159
|
|
|
145
|
-
return
|
|
160
|
+
return insets
|
|
146
161
|
}
|
|
147
162
|
|
|
148
163
|
@Suppress("detekt:ReturnCount")
|
|
@@ -150,7 +165,7 @@ class KeyboardAnimationCallback(
|
|
|
150
165
|
animation: WindowInsetsAnimationCompat,
|
|
151
166
|
bounds: WindowInsetsAnimationCompat.BoundsCompat,
|
|
152
167
|
): WindowInsetsAnimationCompat.BoundsCompat {
|
|
153
|
-
if (!animation.isKeyboardAnimation) {
|
|
168
|
+
if (!animation.isKeyboardAnimation || isSuspended) {
|
|
154
169
|
return bounds
|
|
155
170
|
}
|
|
156
171
|
|
|
@@ -206,7 +221,13 @@ class KeyboardAnimationCallback(
|
|
|
206
221
|
// onProgress() is called when any of the running animations progress...
|
|
207
222
|
|
|
208
223
|
// ignore non-keyboard animation or animation that we intentionally want to skip
|
|
209
|
-
|
|
224
|
+
val shouldSkipAnimation =
|
|
225
|
+
runningAnimations.find {
|
|
226
|
+
it.isKeyboardAnimation && !animationsToSkip.contains(it)
|
|
227
|
+
} == null
|
|
228
|
+
if (isSuspended || shouldSkipAnimation) {
|
|
229
|
+
return insets
|
|
230
|
+
}
|
|
210
231
|
|
|
211
232
|
// First we get the insets which are potentially deferred
|
|
212
233
|
val typesInset = insets.getInsets(config.deferredInsetTypes)
|
|
@@ -218,9 +239,10 @@ class KeyboardAnimationCallback(
|
|
|
218
239
|
if (config.hasTranslucentNavigationBar) {
|
|
219
240
|
otherInset = Insets.NONE
|
|
220
241
|
}
|
|
221
|
-
val diff =
|
|
222
|
-
Insets.
|
|
223
|
-
|
|
242
|
+
val diff =
|
|
243
|
+
Insets.subtract(typesInset, otherInset).let {
|
|
244
|
+
Insets.max(it, Insets.NONE)
|
|
245
|
+
}
|
|
224
246
|
val diffY = (diff.bottom - diff.top).toFloat()
|
|
225
247
|
val height = diffY.dp
|
|
226
248
|
|
|
@@ -256,7 +278,7 @@ class KeyboardAnimationCallback(
|
|
|
256
278
|
override fun onEnd(animation: WindowInsetsAnimationCompat) {
|
|
257
279
|
super.onEnd(animation)
|
|
258
280
|
|
|
259
|
-
if (!animation.isKeyboardAnimation) {
|
|
281
|
+
if (!animation.isKeyboardAnimation || isSuspended) {
|
|
260
282
|
return
|
|
261
283
|
}
|
|
262
284
|
|
|
@@ -305,10 +327,13 @@ class KeyboardAnimationCallback(
|
|
|
305
327
|
duration = 0
|
|
306
328
|
}
|
|
307
329
|
|
|
308
|
-
fun syncKeyboardPosition(
|
|
309
|
-
|
|
330
|
+
fun syncKeyboardPosition(
|
|
331
|
+
height: Double? = null,
|
|
332
|
+
isVisible: Boolean? = null,
|
|
333
|
+
) {
|
|
334
|
+
val keyboardHeight = height ?: getCurrentKeyboardHeight()
|
|
310
335
|
// update internal state
|
|
311
|
-
isKeyboardVisible = isKeyboardVisible()
|
|
336
|
+
isKeyboardVisible = isVisible ?: isKeyboardVisible()
|
|
312
337
|
prevKeyboardHeight = keyboardHeight
|
|
313
338
|
isTransitioning = false
|
|
314
339
|
duration = 0
|