react-native-keyboard-controller 1.13.4 → 1.14.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.
Files changed (105) hide show
  1. package/README.md +1 -0
  2. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +5 -3
  3. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +19 -20
  4. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +22 -12
  5. package/android/src/fabric/java/com/reactnativekeyboardcontroller/OverKeyboardViewManager.kt +40 -0
  6. package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +7 -3
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +33 -27
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputLayoutChangedEvent.kt +16 -15
  9. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputSelectionChangedEvent.kt +25 -24
  10. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputTextChangedEvent.kt +4 -3
  11. package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +7 -6
  12. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/Context.kt +38 -0
  13. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt +78 -58
  14. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt +9 -4
  15. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +10 -3
  16. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +10 -8
  17. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/KeyboardAnimationController.kt +65 -60
  18. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/Interpolator.kt +6 -1
  19. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/IosInterpolator.kt +4 -2
  20. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/LinearInterpolator.kt +1 -3
  21. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt +62 -58
  22. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +47 -41
  23. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/WindowDimensionListener.kt +13 -7
  24. package/android/src/main/java/com/reactnativekeyboardcontroller/log/Logger.kt +10 -2
  25. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +35 -24
  26. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardGestureAreaViewManagerImpl.kt +21 -8
  27. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/OverKeyboardViewManagerImpl.kt +27 -0
  28. package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +14 -12
  29. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +8 -8
  30. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +7 -2
  31. package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolder.kt +1 -3
  32. package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigator.kt +19 -13
  33. package/android/src/main/java/com/reactnativekeyboardcontroller/ui/FrameScheduler.kt +11 -8
  34. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +22 -17
  35. package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardGestureAreaReactViewGroup.kt +23 -14
  36. package/android/src/main/java/com/reactnativekeyboardcontroller/views/overlay/OverKeyboardHostShadowNode.kt +30 -0
  37. package/android/src/main/java/com/reactnativekeyboardcontroller/views/overlay/OverKeyboardViewGroup.kt +185 -0
  38. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +3 -1
  39. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +19 -10
  40. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +21 -8
  41. package/android/src/paper/java/com/reactnativekeyboardcontroller/OverKeyboardViewManager.kt +33 -0
  42. package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +7 -3
  43. package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigatorTest.kt +30 -21
  44. package/ios/KeyboardController-Bridging-Header.h +1 -0
  45. package/ios/extensions/UIResponder.swift +0 -3
  46. package/ios/extensions/UIWindow.swift +48 -0
  47. package/ios/views/OverKeyboardViewManager.h +29 -0
  48. package/ios/views/OverKeyboardViewManager.mm +220 -0
  49. package/jest/index.js +1 -0
  50. package/lib/commonjs/bindings.js +2 -1
  51. package/lib/commonjs/bindings.js.map +1 -1
  52. package/lib/commonjs/bindings.native.js +2 -1
  53. package/lib/commonjs/bindings.native.js.map +1 -1
  54. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +2 -1
  55. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  56. package/lib/commonjs/components/KeyboardToolbar/index.js +5 -3
  57. package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
  58. package/lib/commonjs/index.js +9 -1
  59. package/lib/commonjs/index.js.map +1 -1
  60. package/lib/commonjs/specs/OverKeyboardViewNativeComponent.js +10 -0
  61. package/lib/commonjs/specs/OverKeyboardViewNativeComponent.js.map +1 -0
  62. package/lib/commonjs/types.js.map +1 -1
  63. package/lib/commonjs/views/OverKeyboardView/index.js +43 -0
  64. package/lib/commonjs/views/OverKeyboardView/index.js.map +1 -0
  65. package/lib/commonjs/views/index.js +14 -0
  66. package/lib/commonjs/views/index.js.map +1 -0
  67. package/lib/module/bindings.js +1 -0
  68. package/lib/module/bindings.js.map +1 -1
  69. package/lib/module/bindings.native.js +1 -0
  70. package/lib/module/bindings.native.js.map +1 -1
  71. package/lib/module/components/KeyboardAwareScrollView/index.js +2 -1
  72. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  73. package/lib/module/components/KeyboardToolbar/index.js +5 -3
  74. package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
  75. package/lib/module/index.js +1 -0
  76. package/lib/module/index.js.map +1 -1
  77. package/lib/module/specs/OverKeyboardViewNativeComponent.js +3 -0
  78. package/lib/module/specs/OverKeyboardViewNativeComponent.js.map +1 -0
  79. package/lib/module/types.js.map +1 -1
  80. package/lib/module/views/OverKeyboardView/index.js +35 -0
  81. package/lib/module/views/OverKeyboardView/index.js.map +1 -0
  82. package/lib/module/views/index.js +2 -0
  83. package/lib/module/views/index.js.map +1 -0
  84. package/lib/typescript/bindings.d.ts +2 -1
  85. package/lib/typescript/bindings.native.d.ts +2 -1
  86. package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +4 -0
  87. package/lib/typescript/components/KeyboardToolbar/index.d.ts +2 -2
  88. package/lib/typescript/index.d.ts +1 -0
  89. package/lib/typescript/specs/OverKeyboardViewNativeComponent.d.ts +7 -0
  90. package/lib/typescript/types.d.ts +4 -1
  91. package/lib/typescript/views/OverKeyboardView/index.d.ts +5 -0
  92. package/lib/typescript/views/index.d.ts +1 -0
  93. package/package.json +8 -3
  94. package/src/bindings.native.ts +3 -0
  95. package/src/bindings.ts +3 -0
  96. package/src/components/KeyboardAwareScrollView/index.tsx +5 -2
  97. package/src/components/KeyboardToolbar/index.tsx +7 -3
  98. package/src/index.ts +1 -0
  99. package/src/specs/OverKeyboardViewNativeComponent.ts +12 -0
  100. package/src/types.ts +4 -0
  101. package/src/views/OverKeyboardView/index.tsx +43 -0
  102. package/src/views/index.ts +1 -0
  103. package/ios/.clang-format +0 -90
  104. package/ios/.swiftformat +0 -5
  105. package/ios/.swiftlint.yml +0 -14
@@ -6,40 +6,51 @@ 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) {
10
- fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup {
11
- return EdgeToEdgeReactViewGroup(reactContext)
12
- }
13
-
14
- fun setEnabled(view: EdgeToEdgeReactViewGroup, enabled: Boolean) {
9
+ class KeyboardControllerViewManagerImpl(
10
+ mReactContext: ReactApplicationContext,
11
+ ) {
12
+ fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup =
13
+ EdgeToEdgeReactViewGroup(reactContext)
14
+
15
+ fun setEnabled(
16
+ view: EdgeToEdgeReactViewGroup,
17
+ enabled: Boolean,
18
+ ) {
15
19
  view.setActive(enabled)
16
20
  }
17
21
 
18
- fun setStatusBarTranslucent(view: EdgeToEdgeReactViewGroup, isStatusBarTranslucent: Boolean) {
22
+ fun setStatusBarTranslucent(
23
+ view: EdgeToEdgeReactViewGroup,
24
+ isStatusBarTranslucent: Boolean,
25
+ ) {
19
26
  view.setStatusBarTranslucent(isStatusBarTranslucent)
20
27
  }
21
28
 
22
- fun setNavigationBarTranslucent(view: EdgeToEdgeReactViewGroup, isNavigationBarTranslucent: Boolean) {
29
+ fun setNavigationBarTranslucent(
30
+ view: EdgeToEdgeReactViewGroup,
31
+ isNavigationBarTranslucent: Boolean,
32
+ ) {
23
33
  view.setNavigationBarTranslucent(isNavigationBarTranslucent)
24
34
  }
25
35
 
26
36
  fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
27
- val map: MutableMap<String, Any> = MapBuilder.of(
28
- "topKeyboardMove",
29
- MapBuilder.of("registrationName", "onKeyboardMove"),
30
- "topKeyboardMoveStart",
31
- MapBuilder.of("registrationName", "onKeyboardMoveStart"),
32
- "topKeyboardMoveEnd",
33
- MapBuilder.of("registrationName", "onKeyboardMoveEnd"),
34
- "topKeyboardMoveInteractive",
35
- MapBuilder.of("registrationName", "onKeyboardMoveInteractive"),
36
- "topFocusedInputLayoutChanged",
37
- MapBuilder.of("registrationName", "onFocusedInputLayoutChanged"),
38
- "topFocusedInputTextChanged",
39
- MapBuilder.of("registrationName", "onFocusedInputTextChanged"),
40
- "topFocusedInputSelectionChanged",
41
- MapBuilder.of("registrationName", "onFocusedInputSelectionChanged"),
42
- )
37
+ val map: MutableMap<String, Any> =
38
+ MapBuilder.of(
39
+ "topKeyboardMove",
40
+ MapBuilder.of("registrationName", "onKeyboardMove"),
41
+ "topKeyboardMoveStart",
42
+ MapBuilder.of("registrationName", "onKeyboardMoveStart"),
43
+ "topKeyboardMoveEnd",
44
+ MapBuilder.of("registrationName", "onKeyboardMoveEnd"),
45
+ "topKeyboardMoveInteractive",
46
+ MapBuilder.of("registrationName", "onKeyboardMoveInteractive"),
47
+ "topFocusedInputLayoutChanged",
48
+ MapBuilder.of("registrationName", "onFocusedInputLayoutChanged"),
49
+ "topFocusedInputTextChanged",
50
+ MapBuilder.of("registrationName", "onFocusedInputTextChanged"),
51
+ "topFocusedInputSelectionChanged",
52
+ MapBuilder.of("registrationName", "onFocusedInputSelectionChanged"),
53
+ )
43
54
 
44
55
  return map
45
56
  }
@@ -5,24 +5,37 @@ import com.facebook.react.uimanager.ThemedReactContext
5
5
  import com.reactnativekeyboardcontroller.views.KeyboardGestureAreaReactViewGroup
6
6
 
7
7
  @Suppress("detekt:UnusedPrivateProperty")
8
- class KeyboardGestureAreaViewManagerImpl(mReactContext: ReactApplicationContext) {
9
- fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup {
10
- return KeyboardGestureAreaReactViewGroup(reactContext)
11
- }
8
+ class KeyboardGestureAreaViewManagerImpl(
9
+ mReactContext: ReactApplicationContext,
10
+ ) {
11
+ fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup =
12
+ KeyboardGestureAreaReactViewGroup(reactContext)
12
13
 
13
- fun setOffset(view: KeyboardGestureAreaReactViewGroup, offset: Double) {
14
+ fun setOffset(
15
+ view: KeyboardGestureAreaReactViewGroup,
16
+ offset: Double,
17
+ ) {
14
18
  view.setOffset(offset)
15
19
  }
16
20
 
17
- fun setInterpolator(view: KeyboardGestureAreaReactViewGroup, interpolator: String) {
21
+ fun setInterpolator(
22
+ view: KeyboardGestureAreaReactViewGroup,
23
+ interpolator: String,
24
+ ) {
18
25
  view.setInterpolator(interpolator)
19
26
  }
20
27
 
21
- fun setScrollKeyboardOffScreenWhenVisible(view: KeyboardGestureAreaReactViewGroup, value: Boolean) {
28
+ fun setScrollKeyboardOffScreenWhenVisible(
29
+ view: KeyboardGestureAreaReactViewGroup,
30
+ value: Boolean,
31
+ ) {
22
32
  view.setScrollKeyboardOffScreenWhenVisible(value)
23
33
  }
24
34
 
25
- fun setScrollKeyboardOnScreenWhenNotVisible(view: KeyboardGestureAreaReactViewGroup, value: Boolean) {
35
+ fun setScrollKeyboardOnScreenWhenNotVisible(
36
+ view: KeyboardGestureAreaReactViewGroup,
37
+ value: Boolean,
38
+ ) {
26
39
  view.setScrollKeyboardOnScreenWhenNotVisible(value)
27
40
  }
28
41
 
@@ -0,0 +1,27 @@
1
+ package com.reactnativekeyboardcontroller.managers
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.reactnativekeyboardcontroller.views.overlay.OverKeyboardHostView
6
+
7
+ @Suppress("detekt:UnusedPrivateProperty")
8
+ class OverKeyboardViewManagerImpl(
9
+ mReactContext: ReactApplicationContext,
10
+ ) {
11
+ fun createViewInstance(reactContext: ThemedReactContext): OverKeyboardHostView = OverKeyboardHostView(reactContext)
12
+
13
+ fun setVisible(
14
+ view: OverKeyboardHostView,
15
+ value: Boolean,
16
+ ) {
17
+ if (value) {
18
+ view.show()
19
+ } else {
20
+ view.hide()
21
+ }
22
+ }
23
+
24
+ companion object {
25
+ const val NAME = "OverKeyboardView"
26
+ }
27
+ }
@@ -30,12 +30,13 @@ class ModalAttachedWatcher(
30
30
  return
31
31
  }
32
32
 
33
- val modal = try {
34
- uiManager?.resolveView(event.viewTag) as? ReactModalHostView
35
- } catch (ignore: Exception) {
36
- Logger.w(TAG, "Can not resolve view for Modal#${event.viewTag}", ignore)
37
- null
38
- }
33
+ val modal =
34
+ try {
35
+ uiManager?.resolveView(event.viewTag) as? ReactModalHostView
36
+ } catch (ignore: Exception) {
37
+ Logger.w(TAG, "Can not resolve view for Modal#${event.viewTag}", ignore)
38
+ null
39
+ }
39
40
 
40
41
  if (modal == null) {
41
42
  return
@@ -46,12 +47,13 @@ class ModalAttachedWatcher(
46
47
  val rootView = window?.decorView?.rootView
47
48
 
48
49
  if (rootView != null) {
49
- val callback = KeyboardAnimationCallback(
50
- view = rootView,
51
- eventPropagationView = view,
52
- context = reactContext,
53
- config = config(),
54
- )
50
+ val callback =
51
+ KeyboardAnimationCallback(
52
+ view = rootView,
53
+ eventPropagationView = view,
54
+ context = reactContext,
55
+ config = config(),
56
+ )
55
57
 
56
58
  ViewCompat.setWindowInsetsAnimationCallback(rootView, callback)
57
59
  ViewCompat.setOnApplyWindowInsetsListener(rootView, callback)
@@ -9,7 +9,9 @@ import com.facebook.react.bridge.UiThreadUtil
9
9
  import com.reactnativekeyboardcontroller.traversal.FocusedInputHolder
10
10
  import com.reactnativekeyboardcontroller.traversal.ViewHierarchyNavigator
11
11
 
12
- class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationContext) {
12
+ class KeyboardControllerModuleImpl(
13
+ private val mReactContext: ReactApplicationContext,
14
+ ) {
13
15
  private val mDefaultMode: Int = getCurrentMode()
14
16
 
15
17
  fun setInputMode(mode: Int) {
@@ -22,10 +24,10 @@ class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationCo
22
24
 
23
25
  fun dismiss() {
24
26
  val activity = mReactContext.currentActivity
25
- val view: View? = activity?.currentFocus
27
+ val view: View? = FocusedInputHolder.get()
26
28
 
27
29
  if (view != null) {
28
- val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
30
+ val imm = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
29
31
  imm?.hideSoftInputFromWindow(view.windowToken, 0)
30
32
  }
31
33
  }
@@ -35,8 +37,7 @@ class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationCo
35
37
  return FocusedInputHolder.focus()
36
38
  }
37
39
 
38
- val activity = mReactContext.currentActivity
39
- val view: View? = activity?.currentFocus
40
+ val view: View? = FocusedInputHolder.get()
40
41
 
41
42
  if (view != null) {
42
43
  ViewHierarchyNavigator.setFocusTo(direction, view)
@@ -51,14 +52,13 @@ class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationCo
51
52
  }
52
53
  }
53
54
 
54
- private fun getCurrentMode(): Int {
55
- return mReactContext
55
+ private fun getCurrentMode(): Int =
56
+ mReactContext
56
57
  .currentActivity
57
58
  ?.window
58
59
  ?.attributes
59
60
  ?.softInputMode
60
61
  ?: WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
61
- }
62
62
 
63
63
  companion object {
64
64
  const val NAME = "KeyboardController"
@@ -14,7 +14,9 @@ import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
14
14
 
15
15
  private val TAG = StatusBarManagerCompatModuleImpl::class.qualifiedName
16
16
 
17
- class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicationContext) {
17
+ class StatusBarManagerCompatModuleImpl(
18
+ private val mReactContext: ReactApplicationContext,
19
+ ) {
18
20
  private var controller: WindowInsetsControllerCompat? = null
19
21
 
20
22
  fun setHidden(hidden: Boolean) {
@@ -28,7 +30,10 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
28
30
  }
29
31
 
30
32
  @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
31
- fun setColor(color: Int, animated: Boolean) {
33
+ fun setColor(
34
+ color: Int,
35
+ animated: Boolean,
36
+ ) {
32
37
  val activity = mReactContext.currentActivity
33
38
  if (activity == null) {
34
39
  Logger.w(TAG, "StatusBarManagerCompatModule: Ignored status bar change, current activity is null.")
@@ -11,9 +11,7 @@ object FocusedInputHolder {
11
11
  input = WeakReference(textInput)
12
12
  }
13
13
 
14
- fun get(): EditText? {
15
- return input?.get()
16
- }
14
+ fun get(): EditText? = input?.get()
17
15
 
18
16
  fun focus() {
19
17
  input?.get()?.focus()
@@ -7,7 +7,10 @@ import com.facebook.react.bridge.UiThreadUtil
7
7
  import com.reactnativekeyboardcontroller.extensions.focus
8
8
 
9
9
  object ViewHierarchyNavigator {
10
- fun setFocusTo(direction: String, view: View) {
10
+ fun setFocusTo(
11
+ direction: String,
12
+ view: View,
13
+ ) {
11
14
  val input = if (direction == "next") findNextEditText(view) else findPreviousEditText(view)
12
15
 
13
16
  UiThreadUtil.runOnUiThread {
@@ -35,16 +38,15 @@ object ViewHierarchyNavigator {
35
38
  return editTexts
36
39
  }
37
40
 
38
- private fun findNextEditText(currentFocus: View): EditText? {
39
- return findEditTextInDirection(currentFocus, 1)
40
- }
41
+ private fun findNextEditText(currentFocus: View): EditText? = findEditTextInDirection(currentFocus, 1)
41
42
 
42
- private fun findPreviousEditText(currentFocus: View): EditText? {
43
- return findEditTextInDirection(currentFocus, -1)
44
- }
43
+ private fun findPreviousEditText(currentFocus: View): EditText? = findEditTextInDirection(currentFocus, -1)
45
44
 
46
45
  @Suppress("detekt:ReturnCount")
47
- private fun findEditTextInDirection(currentFocus: View, direction: Int): EditText? {
46
+ private fun findEditTextInDirection(
47
+ currentFocus: View,
48
+ direction: Int,
49
+ ): EditText? {
48
50
  // Attempt to find the parent view group, return null if not found or not a ViewGroup
49
51
  val parentViewGroup = currentFocus.parent as? ViewGroup ?: return null
50
52
 
@@ -66,7 +68,10 @@ object ViewHierarchyNavigator {
66
68
  return findEditTextInDirection(parentViewGroup, direction)
67
69
  }
68
70
 
69
- private fun findEditTextInHierarchy(viewGroup: ViewGroup, direction: Int): EditText? {
71
+ private fun findEditTextInHierarchy(
72
+ viewGroup: ViewGroup,
73
+ direction: Int,
74
+ ): EditText? {
70
75
  val range = if (direction > 0) 0 until viewGroup.childCount else viewGroup.childCount - 1 downTo 0
71
76
 
72
77
  for (i in range) {
@@ -78,7 +83,10 @@ object ViewHierarchyNavigator {
78
83
  return null
79
84
  }
80
85
 
81
- private fun findEditTextOrGoDeeper(child: View, direction: Int): EditText? {
86
+ private fun findEditTextOrGoDeeper(
87
+ child: View,
88
+ direction: Int,
89
+ ): EditText? {
82
90
  var result: EditText? = null
83
91
 
84
92
  if (isValidTextInput(child)) {
@@ -91,7 +99,5 @@ object ViewHierarchyNavigator {
91
99
  return result
92
100
  }
93
101
 
94
- private fun isValidTextInput(view: View?): Boolean {
95
- return view is EditText && view.isEnabled
96
- }
102
+ private fun isValidTextInput(view: View?): Boolean = view is EditText && view.isEnabled
97
103
  }
@@ -7,20 +7,23 @@ import android.view.Choreographer
7
7
  *
8
8
  * @property callback The function to be executed on each frame.
9
9
  */
10
- class FrameScheduler(private val callback: () -> Unit) {
10
+ class FrameScheduler(
11
+ private val callback: () -> Unit,
12
+ ) {
11
13
  /**
12
14
  * A FrameCallback instance responsible for running the provided callback
13
15
  * on each frame and rescheduling itself for the next frame.
14
16
  */
15
- private val frameCallback = object : Choreographer.FrameCallback {
16
- override fun doFrame(frameTimeNanoSeconds: Long) {
17
- // Execute the callback
18
- callback()
17
+ private val frameCallback =
18
+ object : Choreographer.FrameCallback {
19
+ override fun doFrame(frameTimeNanoSeconds: Long) {
20
+ // Execute the callback
21
+ callback()
19
22
 
20
- // Re-post the callback to the next frame
21
- Choreographer.getInstance().postFrameCallback(this)
23
+ // Re-post the callback to the next frame
24
+ Choreographer.getInstance().postFrameCallback(this)
25
+ }
22
26
  }
23
- }
24
27
 
25
28
  /**
26
29
  * Starts the frame callback, which will continuously call the provided function
@@ -25,7 +25,9 @@ private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName
25
25
 
26
26
  @Suppress("detekt:TooManyFunctions")
27
27
  @SuppressLint("ViewConstructor")
28
- class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
28
+ class EdgeToEdgeReactViewGroup(
29
+ private val reactContext: ThemedReactContext,
30
+ ) : ReactViewGroup(reactContext) {
29
31
  // props
30
32
  private var isStatusBarTranslucent = false
31
33
  private var isNavigationBarTranslucent = false
@@ -36,12 +38,13 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
36
38
  private var wasMounted = false
37
39
  private var callback: KeyboardAnimationCallback? = null
38
40
  private val config: KeyboardAnimationCallbackConfig
39
- get() = KeyboardAnimationCallbackConfig(
40
- persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
41
- deferredInsetTypes = WindowInsetsCompat.Type.ime(),
42
- dispatchMode = WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE,
43
- hasTranslucentNavigationBar = isNavigationBarTranslucent,
44
- )
41
+ get() =
42
+ KeyboardAnimationCallbackConfig(
43
+ persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
44
+ deferredInsetTypes = WindowInsetsCompat.Type.ime(),
45
+ dispatchMode = WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE,
46
+ hasTranslucentNavigationBar = isNavigationBarTranslucent,
47
+ )
45
48
 
46
49
  // managers/watchers
47
50
  private val modalAttachedWatcher = ModalAttachedWatcher(this, reactContext, ::config)
@@ -81,10 +84,11 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
81
84
  if (rootView != null) {
82
85
  ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
83
86
  val content = reactContext.content
84
- val params = FrameLayout.LayoutParams(
85
- FrameLayout.LayoutParams.MATCH_PARENT,
86
- FrameLayout.LayoutParams.MATCH_PARENT,
87
- )
87
+ val params =
88
+ FrameLayout.LayoutParams(
89
+ FrameLayout.LayoutParams.MATCH_PARENT,
90
+ FrameLayout.LayoutParams.MATCH_PARENT,
91
+ )
88
92
 
89
93
  val shouldApplyZeroPaddingTop = !active || this.isStatusBarTranslucent
90
94
  val shouldApplyZeroPaddingBottom = !active || this.isNavigationBarTranslucent
@@ -136,12 +140,13 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
136
140
  val root = reactContext.content
137
141
  root?.addView(eventView)
138
142
 
139
- callback = KeyboardAnimationCallback(
140
- view = this,
141
- eventPropagationView = this,
142
- context = reactContext,
143
- config = config,
144
- )
143
+ callback =
144
+ KeyboardAnimationCallback(
145
+ view = this,
146
+ eventPropagationView = this,
147
+ context = reactContext,
148
+ config = config,
149
+ )
145
150
 
146
151
  eventView?.let {
147
152
  ViewCompat.setWindowInsetsAnimationCallback(it, callback)
@@ -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 = mapOf(
24
- "linear" to LinearInterpolator(),
25
- "ios" to IosInterpolator(),
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(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
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 = this.interpolator.interpolate(
131
- dy.roundToInt(),
132
- this.getWindowHeight() - event.rawY.toInt(),
133
- controller.getCurrentKeyboardHeight(),
134
- offset,
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 = ViewCompat.getRootWindowInsets(this)
145
- ?.isVisible(WindowInsetsCompat.Type.ime()) == true,
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(dy: Float, imeVisible: Boolean) = when {
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
+ }