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/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
|
|
@@ -6,13 +6,21 @@ import com.reactnativekeyboardcontroller.BuildConfig
|
|
|
6
6
|
object Logger {
|
|
7
7
|
private val enabled = BuildConfig.DEBUG
|
|
8
8
|
|
|
9
|
-
fun i(
|
|
9
|
+
fun i(
|
|
10
|
+
tag: String?,
|
|
11
|
+
message: String,
|
|
12
|
+
throwable: Throwable? = null,
|
|
13
|
+
) {
|
|
10
14
|
if (enabled) {
|
|
11
15
|
Log.i(tag, message, throwable)
|
|
12
16
|
}
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
fun w(
|
|
19
|
+
fun w(
|
|
20
|
+
tag: String?,
|
|
21
|
+
message: String,
|
|
22
|
+
throwable: Throwable? = null,
|
|
23
|
+
) {
|
|
16
24
|
if (enabled) {
|
|
17
25
|
Log.w(tag, message, throwable)
|
|
18
26
|
}
|
|
@@ -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,5 +1,6 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller.modal
|
|
2
2
|
|
|
3
|
+
import android.view.ViewGroup
|
|
3
4
|
import android.view.WindowManager
|
|
4
5
|
import androidx.core.view.ViewCompat
|
|
5
6
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
@@ -10,16 +11,20 @@ import com.facebook.react.uimanager.events.EventDispatcherListener
|
|
|
10
11
|
import com.facebook.react.views.modal.ReactModalHostView
|
|
11
12
|
import com.facebook.react.views.view.ReactViewGroup
|
|
12
13
|
import com.reactnativekeyboardcontroller.BuildConfig
|
|
14
|
+
import com.reactnativekeyboardcontroller.constants.Keyboard
|
|
15
|
+
import com.reactnativekeyboardcontroller.extensions.removeSelf
|
|
13
16
|
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback
|
|
14
17
|
import com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallbackConfig
|
|
15
18
|
import com.reactnativekeyboardcontroller.log.Logger
|
|
16
19
|
|
|
17
20
|
private val TAG = ModalAttachedWatcher::class.qualifiedName
|
|
21
|
+
private val areEventsComingFromOwnWindow = !Keyboard.IS_ANIMATION_EMULATED
|
|
18
22
|
|
|
19
23
|
class ModalAttachedWatcher(
|
|
20
24
|
private val view: ReactViewGroup,
|
|
21
25
|
private val reactContext: ThemedReactContext,
|
|
22
26
|
private val config: () -> KeyboardAnimationCallbackConfig,
|
|
27
|
+
private var callback: () -> KeyboardAnimationCallback?,
|
|
23
28
|
) : EventDispatcherListener {
|
|
24
29
|
private val archType = if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) UIManagerType.FABRIC else UIManagerType.DEFAULT
|
|
25
30
|
private val uiManager = UIManagerHelper.getUIManager(reactContext.reactApplicationContext, archType)
|
|
@@ -30,12 +35,13 @@ class ModalAttachedWatcher(
|
|
|
30
35
|
return
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
val modal =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
val modal =
|
|
39
|
+
try {
|
|
40
|
+
uiManager?.resolveView(event.viewTag) as? ReactModalHostView
|
|
41
|
+
} catch (ignore: Exception) {
|
|
42
|
+
Logger.w(TAG, "Can not resolve view for Modal#${event.viewTag}", ignore)
|
|
43
|
+
null
|
|
44
|
+
}
|
|
39
45
|
|
|
40
46
|
if (modal == null) {
|
|
41
47
|
return
|
|
@@ -43,26 +49,50 @@ class ModalAttachedWatcher(
|
|
|
43
49
|
|
|
44
50
|
val dialog = modal.dialog
|
|
45
51
|
val window = dialog?.window
|
|
46
|
-
val rootView = window?.decorView?.rootView
|
|
52
|
+
val rootView = window?.decorView?.rootView as ViewGroup?
|
|
47
53
|
|
|
48
54
|
if (rootView != null) {
|
|
49
|
-
val
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
val eventView =
|
|
56
|
+
ReactViewGroup(reactContext).apply {
|
|
57
|
+
layoutParams = ViewGroup.LayoutParams(0, 0)
|
|
58
|
+
}
|
|
59
|
+
val callback =
|
|
60
|
+
KeyboardAnimationCallback(
|
|
61
|
+
view = rootView,
|
|
62
|
+
eventPropagationView = view,
|
|
63
|
+
context = reactContext,
|
|
64
|
+
config = config(),
|
|
65
|
+
)
|
|
55
66
|
|
|
67
|
+
rootView.addView(eventView)
|
|
68
|
+
// on Android < 12 all events for `WindowInsetsAnimationCallback`
|
|
69
|
+
// go through main `rootView`, so we don't need to stop main
|
|
70
|
+
// callback - otherwise keyboard transitions will not be animated
|
|
71
|
+
this.callback()?.suspend(areEventsComingFromOwnWindow)
|
|
56
72
|
ViewCompat.setWindowInsetsAnimationCallback(rootView, callback)
|
|
57
|
-
ViewCompat.setOnApplyWindowInsetsListener(
|
|
73
|
+
ViewCompat.setOnApplyWindowInsetsListener(eventView, callback)
|
|
74
|
+
|
|
75
|
+
if (areEventsComingFromOwnWindow) {
|
|
76
|
+
// when modal is shown then keyboard will be hidden by default
|
|
77
|
+
//
|
|
78
|
+
// - if events are coming from main window - then keyboard position
|
|
79
|
+
// will be synchronized from main window callback
|
|
80
|
+
// - if events are coming from modal window - then we need to update
|
|
81
|
+
// position ourself, because callback can be attached after keyboard
|
|
82
|
+
// auto-dismissal and we may miss some events and keyboard position
|
|
83
|
+
// will be outdated
|
|
84
|
+
callback.syncKeyboardPosition(0.0, false)
|
|
85
|
+
}
|
|
58
86
|
|
|
59
|
-
dialog
|
|
87
|
+
dialog?.setOnDismissListener {
|
|
60
88
|
callback.syncKeyboardPosition()
|
|
61
89
|
callback.destroy()
|
|
90
|
+
eventView.removeSelf()
|
|
91
|
+
this.callback()?.suspend(false)
|
|
62
92
|
}
|
|
63
93
|
|
|
64
94
|
// imitating edge-to-edge mode behavior
|
|
65
|
-
window
|
|
95
|
+
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
|
|
66
96
|
}
|
|
67
97
|
}
|
|
68
98
|
|
|
@@ -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"
|
|
@@ -2,6 +2,7 @@ package com.reactnativekeyboardcontroller.modules
|
|
|
2
2
|
|
|
3
3
|
import android.animation.ArgbEvaluator
|
|
4
4
|
import android.animation.ValueAnimator
|
|
5
|
+
import android.app.Activity
|
|
5
6
|
import android.os.Build
|
|
6
7
|
import androidx.annotation.RequiresApi
|
|
7
8
|
import androidx.core.view.WindowInsetsCompat
|
|
@@ -11,11 +12,15 @@ import com.facebook.react.bridge.UiThreadUtil
|
|
|
11
12
|
import com.reactnativekeyboardcontroller.extensions.rootView
|
|
12
13
|
import com.reactnativekeyboardcontroller.log.Logger
|
|
13
14
|
import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
|
|
15
|
+
import java.lang.ref.WeakReference
|
|
14
16
|
|
|
15
17
|
private val TAG = StatusBarManagerCompatModuleImpl::class.qualifiedName
|
|
16
18
|
|
|
17
|
-
class StatusBarManagerCompatModuleImpl(
|
|
19
|
+
class StatusBarManagerCompatModuleImpl(
|
|
20
|
+
private val mReactContext: ReactApplicationContext,
|
|
21
|
+
) {
|
|
18
22
|
private var controller: WindowInsetsControllerCompat? = null
|
|
23
|
+
private var lastActivity = WeakReference<Activity?>(null)
|
|
19
24
|
|
|
20
25
|
fun setHidden(hidden: Boolean) {
|
|
21
26
|
UiThreadUtil.runOnUiThread {
|
|
@@ -28,7 +33,10 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
31
|
-
fun setColor(
|
|
36
|
+
fun setColor(
|
|
37
|
+
color: Int,
|
|
38
|
+
animated: Boolean,
|
|
39
|
+
) {
|
|
32
40
|
val activity = mReactContext.currentActivity
|
|
33
41
|
if (activity == null) {
|
|
34
42
|
Logger.w(TAG, "StatusBarManagerCompatModule: Ignored status bar change, current activity is null.")
|
|
@@ -66,8 +74,9 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
private fun getController(): WindowInsetsControllerCompat? {
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
val activity = mReactContext.currentActivity
|
|
78
|
+
|
|
79
|
+
if (this.controller == null || activity != lastActivity.get()) {
|
|
71
80
|
if (activity == null) {
|
|
72
81
|
Logger.w(
|
|
73
82
|
TAG,
|
|
@@ -77,6 +86,7 @@ class StatusBarManagerCompatModuleImpl(private val mReactContext: ReactApplicati
|
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
val window = activity.window
|
|
89
|
+
lastActivity = WeakReference(activity)
|
|
80
90
|
|
|
81
91
|
this.controller = WindowInsetsControllerCompat(window, window.decorView)
|
|
82
92
|
}
|
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
|
@@ -25,7 +25,9 @@ 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,15 +38,16 @@ 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
|
-
private val modalAttachedWatcher = ModalAttachedWatcher(this, reactContext, ::config)
|
|
50
|
+
private val modalAttachedWatcher = ModalAttachedWatcher(this, reactContext, ::config, ::getKeyboardCallback)
|
|
48
51
|
|
|
49
52
|
init {
|
|
50
53
|
reactContext.setupWindowDimensionsListener()
|
|
@@ -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)
|
|
@@ -190,6 +195,10 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
|
|
|
190
195
|
}
|
|
191
196
|
// endregion
|
|
192
197
|
|
|
198
|
+
// region Helpers
|
|
199
|
+
private fun getKeyboardCallback(): KeyboardAnimationCallback? = this.callback
|
|
200
|
+
// endregion
|
|
201
|
+
|
|
193
202
|
// region Props setters
|
|
194
203
|
fun setStatusBarTranslucent(isStatusBarTranslucent: Boolean) {
|
|
195
204
|
this.isStatusBarTranslucent = isStatusBarTranslucent
|