react-native-keyboard-controller 1.13.3 → 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.
- 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/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 +82 -62
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt +9 -4
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +12 -5
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +12 -10
- 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 +52 -46
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/WindowDimensionListener.kt +13 -7
- package/android/src/main/java/com/reactnativekeyboardcontroller/log/Logger.kt +28 -0
- 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 +15 -13
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +8 -8
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +10 -5
- 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 +24 -19
- 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/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/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/hooks/index.js +2 -4
- package/lib/commonjs/hooks/index.js.map +1 -1
- package/lib/commonjs/index.js +9 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/internal.js +24 -12
- package/lib/commonjs/internal.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/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/hooks/index.js +3 -4
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal.js +24 -12
- package/lib/module/internal.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/KeyboardAwareScrollView/index.tsx +5 -2
- package/src/components/KeyboardToolbar/index.tsx +7 -3
- package/src/hooks/index.ts +3 -5
- package/src/index.ts +1 -0
- package/src/internal.ts +34 -15
- 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/lib/commonjs/hooks/useSyncEffect/index.js +0 -37
- package/lib/commonjs/hooks/useSyncEffect/index.js.map +0 -1
- package/lib/module/hooks/useSyncEffect/index.js +0 -31
- package/lib/module/hooks/useSyncEffect/index.js.map +0 -1
- package/lib/typescript/hooks/useSyncEffect/index.d.ts +0 -14
- package/src/hooks/useSyncEffect/index.ts +0 -36
package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/WindowDimensionListener.kt
CHANGED
|
@@ -8,9 +8,14 @@ import com.reactnativekeyboardcontroller.extensions.content
|
|
|
8
8
|
import com.reactnativekeyboardcontroller.extensions.dp
|
|
9
9
|
import com.reactnativekeyboardcontroller.extensions.emitEvent
|
|
10
10
|
|
|
11
|
-
data class Dimensions(
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
data class Dimensions(
|
|
12
|
+
val width: Double,
|
|
13
|
+
val height: Double,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
class WindowDimensionListener(
|
|
17
|
+
private val context: ThemedReactContext?,
|
|
18
|
+
) {
|
|
14
19
|
private var lastDispatchedDimensions = Dimensions(0.0, 0.0)
|
|
15
20
|
|
|
16
21
|
init {
|
|
@@ -33,10 +38,11 @@ class WindowDimensionListener(private val context: ThemedReactContext?) {
|
|
|
33
38
|
return
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
val newDimensions =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
val newDimensions =
|
|
42
|
+
Dimensions(
|
|
43
|
+
content.width.toFloat().dp,
|
|
44
|
+
content.height.toFloat().dp + content.marginTop.toFloat().dp,
|
|
45
|
+
)
|
|
40
46
|
|
|
41
47
|
if (newDimensions != lastDispatchedDimensions) {
|
|
42
48
|
lastDispatchedDimensions = newDimensions
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller.log
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.reactnativekeyboardcontroller.BuildConfig
|
|
5
|
+
|
|
6
|
+
object Logger {
|
|
7
|
+
private val enabled = BuildConfig.DEBUG
|
|
8
|
+
|
|
9
|
+
fun i(
|
|
10
|
+
tag: String?,
|
|
11
|
+
message: String,
|
|
12
|
+
throwable: Throwable? = null,
|
|
13
|
+
) {
|
|
14
|
+
if (enabled) {
|
|
15
|
+
Log.i(tag, message, throwable)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fun w(
|
|
20
|
+
tag: String?,
|
|
21
|
+
message: String,
|
|
22
|
+
throwable: Throwable? = null,
|
|
23
|
+
) {
|
|
24
|
+
if (enabled) {
|
|
25
|
+
Log.w(tag, message, throwable)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -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(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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(
|
|
22
|
+
fun setStatusBarTranslucent(
|
|
23
|
+
view: EdgeToEdgeReactViewGroup,
|
|
24
|
+
isStatusBarTranslucent: Boolean,
|
|
25
|
+
) {
|
|
19
26
|
view.setStatusBarTranslucent(isStatusBarTranslucent)
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
fun setNavigationBarTranslucent(
|
|
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> =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
class KeyboardGestureAreaViewManagerImpl(
|
|
9
|
+
mReactContext: ReactApplicationContext,
|
|
10
|
+
) {
|
|
11
|
+
fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup =
|
|
12
|
+
KeyboardGestureAreaReactViewGroup(reactContext)
|
|
12
13
|
|
|
13
|
-
fun setOffset(
|
|
14
|
+
fun setOffset(
|
|
15
|
+
view: KeyboardGestureAreaReactViewGroup,
|
|
16
|
+
offset: Double,
|
|
17
|
+
) {
|
|
14
18
|
view.setOffset(offset)
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
fun setInterpolator(
|
|
21
|
+
fun setInterpolator(
|
|
22
|
+
view: KeyboardGestureAreaReactViewGroup,
|
|
23
|
+
interpolator: String,
|
|
24
|
+
) {
|
|
18
25
|
view.setInterpolator(interpolator)
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
fun setScrollKeyboardOffScreenWhenVisible(
|
|
28
|
+
fun setScrollKeyboardOffScreenWhenVisible(
|
|
29
|
+
view: KeyboardGestureAreaReactViewGroup,
|
|
30
|
+
value: Boolean,
|
|
31
|
+
) {
|
|
22
32
|
view.setScrollKeyboardOffScreenWhenVisible(value)
|
|
23
33
|
}
|
|
24
34
|
|
|
25
|
-
fun setScrollKeyboardOnScreenWhenNotVisible(
|
|
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
|
+
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller.modal
|
|
2
2
|
|
|
3
|
-
import android.util.Log
|
|
4
3
|
import android.view.WindowManager
|
|
5
4
|
import androidx.core.view.ViewCompat
|
|
6
5
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
@@ -13,6 +12,7 @@ import com.facebook.react.views.view.ReactViewGroup
|
|
|
13
12
|
import com.reactnativekeyboardcontroller.BuildConfig
|
|
14
13
|
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback
|
|
15
14
|
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallbackConfig
|
|
15
|
+
import com.reactnativekeyboardcontroller.log.Logger
|
|
16
16
|
|
|
17
17
|
private val TAG = ModalAttachedWatcher::class.qualifiedName
|
|
18
18
|
|
|
@@ -30,12 +30,13 @@ class ModalAttachedWatcher(
|
|
|
30
30
|
return
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
val modal =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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(
|
|
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? =
|
|
27
|
+
val view: View? = FocusedInputHolder.get()
|
|
26
28
|
|
|
27
29
|
if (view != null) {
|
|
28
|
-
val imm = activity
|
|
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
|
|
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
|
-
|
|
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"
|
|
@@ -3,18 +3,20 @@ package com.reactnativekeyboardcontroller.modules
|
|
|
3
3
|
import android.animation.ArgbEvaluator
|
|
4
4
|
import android.animation.ValueAnimator
|
|
5
5
|
import android.os.Build
|
|
6
|
-
import android.util.Log
|
|
7
6
|
import androidx.annotation.RequiresApi
|
|
8
7
|
import androidx.core.view.WindowInsetsCompat
|
|
9
8
|
import androidx.core.view.WindowInsetsControllerCompat
|
|
10
9
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
11
10
|
import com.facebook.react.bridge.UiThreadUtil
|
|
12
11
|
import com.reactnativekeyboardcontroller.extensions.rootView
|
|
12
|
+
import com.reactnativekeyboardcontroller.log.Logger
|
|
13
13
|
import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
|
|
14
14
|
|
|
15
15
|
private val TAG = StatusBarManagerCompatModuleImpl::class.qualifiedName
|
|
16
16
|
|
|
17
|
-
class StatusBarManagerCompatModuleImpl(
|
|
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,10 +30,13 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
31
|
-
fun setColor(
|
|
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.")
|
|
35
40
|
return
|
|
36
41
|
}
|
|
37
42
|
|
|
@@ -69,7 +74,7 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
69
74
|
if (this.controller == null) {
|
|
70
75
|
val activity = mReactContext.currentActivity
|
|
71
76
|
if (activity == null) {
|
|
72
|
-
|
|
77
|
+
Logger.w(
|
|
73
78
|
TAG,
|
|
74
79
|
"StatusBarManagerCompatModule: can not get `WindowInsetsControllerCompat` because current activity is null.",
|
|
75
80
|
)
|
package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigator.kt
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
private val frameCallback =
|
|
18
|
+
object : Choreographer.FrameCallback {
|
|
19
|
+
override fun doFrame(frameTimeNanoSeconds: Long) {
|
|
20
|
+
// Execute the callback
|
|
21
|
+
callback()
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
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
|
package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt
CHANGED
|
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.content.res.Configuration
|
|
5
5
|
import android.os.Handler
|
|
6
6
|
import android.os.Looper
|
|
7
|
-
import android.util.Log
|
|
8
7
|
import android.widget.FrameLayout
|
|
9
8
|
import androidx.core.view.ViewCompat
|
|
10
9
|
import androidx.core.view.WindowCompat
|
|
@@ -19,13 +18,16 @@ import com.reactnativekeyboardcontroller.extensions.rootView
|
|
|
19
18
|
import com.reactnativekeyboardcontroller.extensions.setupWindowDimensionsListener
|
|
20
19
|
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback
|
|
21
20
|
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallbackConfig
|
|
21
|
+
import com.reactnativekeyboardcontroller.log.Logger
|
|
22
22
|
import com.reactnativekeyboardcontroller.modal.ModalAttachedWatcher
|
|
23
23
|
|
|
24
24
|
private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName
|
|
25
25
|
|
|
26
26
|
@Suppress("detekt:TooManyFunctions")
|
|
27
27
|
@SuppressLint("ViewConstructor")
|
|
28
|
-
class EdgeToEdgeReactViewGroup(
|
|
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() =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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 =
|
|
85
|
-
FrameLayout.LayoutParams
|
|
86
|
-
|
|
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 =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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)
|
|
@@ -149,7 +154,7 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
149
154
|
it.requestApplyInsetsWhenAttached()
|
|
150
155
|
}
|
|
151
156
|
} else {
|
|
152
|
-
|
|
157
|
+
Logger.w(TAG, "Can not setup keyboard animation listener, since `currentActivity` is null")
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
|
|
@@ -20,14 +20,17 @@ import com.reactnativekeyboardcontroller.interactive.interpolators.LinearInterpo
|
|
|
20
20
|
import kotlin.math.absoluteValue
|
|
21
21
|
import kotlin.math.roundToInt
|
|
22
22
|
|
|
23
|
-
val interpolators =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
23
|
+
val interpolators =
|
|
24
|
+
mapOf(
|
|
25
|
+
"linear" to LinearInterpolator(),
|
|
26
|
+
"ios" to IosInterpolator(),
|
|
27
|
+
)
|
|
27
28
|
|
|
28
29
|
@Suppress("detekt:TooManyFunctions")
|
|
29
30
|
@SuppressLint("ViewConstructor")
|
|
30
|
-
class KeyboardGestureAreaReactViewGroup(
|
|
31
|
+
class KeyboardGestureAreaReactViewGroup(
|
|
32
|
+
private val reactContext: ThemedReactContext,
|
|
33
|
+
) : ReactViewGroup(reactContext) {
|
|
31
34
|
// internal state management
|
|
32
35
|
private var isHandling = false
|
|
33
36
|
private var lastTouchX = 0f
|
|
@@ -127,12 +130,13 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
127
130
|
}
|
|
128
131
|
// If we currently have control, we can update the IME insets to 'scroll'
|
|
129
132
|
// the IME in
|
|
130
|
-
val moveBy =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
val moveBy =
|
|
134
|
+
this.interpolator.interpolate(
|
|
135
|
+
dy.roundToInt(),
|
|
136
|
+
this.getWindowHeight() - event.rawY.toInt(),
|
|
137
|
+
controller.getCurrentKeyboardHeight(),
|
|
138
|
+
offset,
|
|
139
|
+
)
|
|
136
140
|
|
|
137
141
|
if (moveBy != 0) {
|
|
138
142
|
controller.insetBy(moveBy)
|
|
@@ -141,8 +145,10 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
141
145
|
!controller.isInsetAnimationRequestPending() &&
|
|
142
146
|
shouldStartRequest(
|
|
143
147
|
dy = dy,
|
|
144
|
-
imeVisible =
|
|
145
|
-
|
|
148
|
+
imeVisible =
|
|
149
|
+
ViewCompat
|
|
150
|
+
.getRootWindowInsets(this)
|
|
151
|
+
?.isVisible(WindowInsetsCompat.Type.ime()) == true,
|
|
146
152
|
)
|
|
147
153
|
) {
|
|
148
154
|
// If we don't currently have control (and a request isn't pending),
|
|
@@ -210,7 +216,10 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
210
216
|
* Returns true if the given [dy], [IME visibility][imeVisible], and constructor options
|
|
211
217
|
* support a IME animation request.
|
|
212
218
|
*/
|
|
213
|
-
private fun shouldStartRequest(
|
|
219
|
+
private fun shouldStartRequest(
|
|
220
|
+
dy: Float,
|
|
221
|
+
imeVisible: Boolean,
|
|
222
|
+
) = when {
|
|
214
223
|
// If the user is scroll up, return true if scrollImeOnScreenWhenNotVisible is true, and
|
|
215
224
|
// the IME is not currently visible
|
|
216
225
|
dy < 0 -> !imeVisible && scrollKeyboardOnScreenWhenNotVisible
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller.views.overlay
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.uimanager.LayoutShadowNode
|
|
4
|
+
import com.facebook.react.uimanager.ReactShadowNodeImpl
|
|
5
|
+
import com.reactnativekeyboardcontroller.extensions.getDisplaySize
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* We implement the OverKeyboardView by using an Android WindowManager. That will fill the entire window of the
|
|
9
|
+
* application. To get layout to work properly, we need to layout all the elements within the
|
|
10
|
+
* OverKeyboardView's inner content view as if they can fill the entire window. To do that, we need to
|
|
11
|
+
* explicitly set the styleWidth and styleHeight on the LayoutShadowNode of the child of this node
|
|
12
|
+
* to be the window size. This will then cause the children of the Modal to layout as if they can
|
|
13
|
+
* fill the window.
|
|
14
|
+
*/
|
|
15
|
+
internal class OverKeyboardHostShadowNode : LayoutShadowNode() {
|
|
16
|
+
/**
|
|
17
|
+
* We need to set the styleWidth and styleHeight of the one child (represented by the
|
|
18
|
+
* <View></View> within the <RCTOverKeyboardView></RCTOverKeyboardView> in OverKeyboardView.tsx.
|
|
19
|
+
* This needs to fill the entire window.
|
|
20
|
+
*/
|
|
21
|
+
override fun addChildAt(
|
|
22
|
+
child: ReactShadowNodeImpl,
|
|
23
|
+
i: Int,
|
|
24
|
+
) {
|
|
25
|
+
super.addChildAt(child, i)
|
|
26
|
+
val displaySize = themedContext.getDisplaySize()
|
|
27
|
+
child.setStyleWidth(displaySize.x.toFloat())
|
|
28
|
+
child.setStyleHeight(displaySize.y.toFloat())
|
|
29
|
+
}
|
|
30
|
+
}
|