react-native-keyboard-controller 1.16.0 → 1.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +5 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +4 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +13 -11
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +6 -0
- package/ios/KeyboardControllerModuleImpl.swift +3 -2
- package/ios/extensions/UIResponder.swift +7 -22
- package/ios/extensions/UIView.swift +12 -0
- package/ios/observers/KeyboardMovementObserver.swift +40 -56
- package/package.json +2 -1
- package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolderTest.kt +0 -45
- package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigatorTest.kt +0 -198
package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt
CHANGED
|
@@ -24,6 +24,11 @@ class KeyboardControllerViewManager(
|
|
|
24
24
|
|
|
25
25
|
override fun createViewInstance(context: ThemedReactContext): ReactViewGroup = manager.createViewInstance(context)
|
|
26
26
|
|
|
27
|
+
override fun onAfterUpdateTransaction(view: ReactViewGroup) {
|
|
28
|
+
super.onAfterUpdateTransaction(view)
|
|
29
|
+
manager.setEdgeToEdge(view as EdgeToEdgeReactViewGroup)
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
@ReactProp(name = "statusBarTranslucent")
|
|
28
33
|
override fun setStatusBarTranslucent(
|
|
29
34
|
view: ReactViewGroup,
|
|
@@ -44,6 +44,10 @@ class KeyboardControllerViewManagerImpl(
|
|
|
44
44
|
view.setPreserveEdgeToEdge(isPreservingEdgeToEdge)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
fun setEdgeToEdge(view: EdgeToEdgeReactViewGroup) {
|
|
48
|
+
view.setEdgeToEdge()
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
|
|
48
52
|
val map: MutableMap<String, Any> =
|
|
49
53
|
MapBuilder.of(
|
package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt
CHANGED
|
@@ -33,6 +33,7 @@ class EdgeToEdgeReactViewGroup(
|
|
|
33
33
|
private var isNavigationBarTranslucent = false
|
|
34
34
|
private var isPreservingEdgeToEdge = false
|
|
35
35
|
private var active = false
|
|
36
|
+
private var isEdgeToEdge = false
|
|
36
37
|
|
|
37
38
|
// internal class members
|
|
38
39
|
private var eventView: ReactViewGroup? = null
|
|
@@ -124,12 +125,18 @@ class EdgeToEdgeReactViewGroup(
|
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
fun setEdgeToEdge() {
|
|
129
|
+
val nextValue = active || isPreservingEdgeToEdge
|
|
130
|
+
|
|
131
|
+
if (isEdgeToEdge != nextValue) {
|
|
132
|
+
isEdgeToEdge = nextValue
|
|
133
|
+
|
|
134
|
+
reactContext.currentActivity?.let {
|
|
135
|
+
WindowCompat.setDecorFitsSystemWindows(
|
|
136
|
+
it.window,
|
|
137
|
+
!isEdgeToEdge,
|
|
138
|
+
)
|
|
139
|
+
}
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
|
|
@@ -182,17 +189,12 @@ class EdgeToEdgeReactViewGroup(
|
|
|
182
189
|
|
|
183
190
|
// region State managers
|
|
184
191
|
private fun enable() {
|
|
185
|
-
this.goToEdgeToEdge(true)
|
|
186
192
|
this.setupWindowInsets()
|
|
187
193
|
this.setupKeyboardCallbacks()
|
|
188
194
|
modalAttachedWatcher.enable()
|
|
189
195
|
}
|
|
190
196
|
|
|
191
197
|
private fun disable() {
|
|
192
|
-
if (!isPreservingEdgeToEdge) {
|
|
193
|
-
this.goToEdgeToEdge(false)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
198
|
this.setupWindowInsets()
|
|
197
199
|
this.removeKeyboardCallbacks()
|
|
198
200
|
modalAttachedWatcher.disable()
|
package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt
CHANGED
|
@@ -3,6 +3,7 @@ package com.reactnativekeyboardcontroller
|
|
|
3
3
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
4
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
5
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
6
|
+
import com.facebook.react.views.view.ReactViewGroup
|
|
6
7
|
import com.facebook.react.views.view.ReactViewManager
|
|
7
8
|
import com.reactnativekeyboardcontroller.managers.KeyboardControllerViewManagerImpl
|
|
8
9
|
import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
|
|
@@ -17,6 +18,11 @@ class KeyboardControllerViewManager(
|
|
|
17
18
|
override fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup =
|
|
18
19
|
manager.createViewInstance(reactContext)
|
|
19
20
|
|
|
21
|
+
override fun onAfterUpdateTransaction(view: ReactViewGroup) {
|
|
22
|
+
super.onAfterUpdateTransaction(view)
|
|
23
|
+
manager.setEdgeToEdge(view as EdgeToEdgeReactViewGroup)
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
@ReactProp(name = "enabled")
|
|
21
27
|
fun setEnabled(
|
|
22
28
|
view: EdgeToEdgeReactViewGroup,
|
|
@@ -14,9 +14,10 @@ public class KeyboardControllerModuleImpl: NSObject {
|
|
|
14
14
|
|
|
15
15
|
@objc
|
|
16
16
|
public static func dismiss(_ keepFocus: Bool) {
|
|
17
|
-
|
|
17
|
+
let responder = UIResponder.current
|
|
18
18
|
|
|
19
19
|
if keepFocus {
|
|
20
|
+
guard let input = responder as? TextInput else { return }
|
|
20
21
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onTextInputTapped(_:)))
|
|
21
22
|
tapGesture.name = keyboardRevealGestureName
|
|
22
23
|
input.addGestureRecognizer(tapGesture)
|
|
@@ -31,7 +32,7 @@ public class KeyboardControllerModuleImpl: NSObject {
|
|
|
31
32
|
object: input
|
|
32
33
|
)
|
|
33
34
|
} else {
|
|
34
|
-
|
|
35
|
+
responder?.resignFirstResponder()
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -11,28 +11,9 @@ import UIKit
|
|
|
11
11
|
|
|
12
12
|
@objc
|
|
13
13
|
public extension UIResponder {
|
|
14
|
-
private weak static var _currentFirstResponder: UIResponder?
|
|
15
|
-
|
|
16
14
|
static var current: UIResponder? {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return UIResponder._currentFirstResponder
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
internal func findFirstResponder(sender _: AnyObject) {
|
|
23
|
-
let type = String(describing: type(of: self))
|
|
24
|
-
// handle `contextMenuHidden` prop - in this case the parent is considered as a first responder
|
|
25
|
-
// (but actually its children is an actual input), so we apply correction here and point out
|
|
26
|
-
// to the actual first responder (first children)
|
|
27
|
-
let isChildrenActuallyFirstResponder =
|
|
28
|
-
type == "RCTMultilineTextInputView" ||
|
|
29
|
-
type == "RCTSinglelineTextInputView" ||
|
|
30
|
-
type == "RCTTextInputComponentView"
|
|
31
|
-
if isChildrenActuallyFirstResponder {
|
|
32
|
-
UIResponder._currentFirstResponder = (self as? UIView)?.subviews[0]
|
|
33
|
-
} else {
|
|
34
|
-
UIResponder._currentFirstResponder = self
|
|
35
|
-
}
|
|
15
|
+
guard let window = UIApplication.shared.activeWindow else { return nil }
|
|
16
|
+
return window.findFirstResponder()
|
|
36
17
|
}
|
|
37
18
|
}
|
|
38
19
|
|
|
@@ -49,7 +30,11 @@ public extension Optional where Wrapped == UIResponder {
|
|
|
49
30
|
guard let superview = (self as? UIView)?.superview else { return nil }
|
|
50
31
|
|
|
51
32
|
#if KEYBOARD_CONTROLLER_NEW_ARCH_ENABLED
|
|
52
|
-
|
|
33
|
+
if superview.responds(to: Selector(("nativeId"))) == true {
|
|
34
|
+
return (superview as NSObject).value(forKey: "nativeId") as? String
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return nil
|
|
53
38
|
#else
|
|
54
39
|
return superview.nativeID
|
|
55
40
|
#endif
|
|
@@ -34,6 +34,18 @@ public extension UIView {
|
|
|
34
34
|
return false
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
func findFirstResponder() -> UIView? {
|
|
39
|
+
if isFirstResponder {
|
|
40
|
+
return self
|
|
41
|
+
}
|
|
42
|
+
for subview in subviews {
|
|
43
|
+
if let responder = subview.findFirstResponder() {
|
|
44
|
+
return responder
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return nil
|
|
48
|
+
}
|
|
37
49
|
}
|
|
38
50
|
|
|
39
51
|
public extension Optional where Wrapped == UIView {
|
|
@@ -33,7 +33,7 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
33
33
|
private var _windowsCount: Int = 0
|
|
34
34
|
private var prevKeyboardPosition = 0.0
|
|
35
35
|
private var displayLink: CADisplayLink?
|
|
36
|
-
private var
|
|
36
|
+
private var interactiveKeyboardObserver: NSKeyValueObservation?
|
|
37
37
|
private var isMounted = false
|
|
38
38
|
// state variables
|
|
39
39
|
private var _keyboardHeight: CGFloat = 0.0
|
|
@@ -93,73 +93,57 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
private func setupKVObserver() {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
guard interactiveKeyboardObserver == nil, let view = keyboardView else { return }
|
|
97
|
+
|
|
98
|
+
interactiveKeyboardObserver = view.observe(\.center, options: .new) { [weak self] _, change in
|
|
99
|
+
guard let self = self, let changeValue = change.newValue else { return }
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
hasKVObserver = true
|
|
102
|
-
keyboardView?.addObserver(self, forKeyPath: "center", options: .new, context: nil)
|
|
101
|
+
self.keyboardDidMoveInteractively(changeValue: changeValue)
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
104
|
|
|
106
105
|
private func removeKVObserver() {
|
|
107
|
-
|
|
106
|
+
interactiveKeyboardObserver?.invalidate()
|
|
107
|
+
interactiveKeyboardObserver = nil
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private func keyboardDidMoveInteractively(changeValue: CGPoint) {
|
|
111
|
+
// if we are currently animating keyboard -> we need to ignore values from KVO
|
|
112
|
+
if displayLink != nil {
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
// if keyboard height is not equal to its bounds - we can ignore
|
|
116
|
+
// values, since they'll be invalid and will cause UI jumps
|
|
117
|
+
if floor(keyboardView?.bounds.size.height ?? 0) != floor(_keyboardHeight) {
|
|
108
118
|
return
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
let keyboardFrameY = changeValue.y
|
|
122
|
+
let keyboardWindowH = keyboardView?.window?.bounds.size.height ?? 0
|
|
123
|
+
let keyboardPosition = keyboardWindowH - keyboardFrameY
|
|
114
124
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
context _: UnsafeMutableRawPointer?
|
|
121
|
-
) {
|
|
122
|
-
if keyPath == "center", object as? NSObject == _keyboardView {
|
|
123
|
-
// if we are currently animating keyboard -> we need to ignore values from KVO
|
|
124
|
-
if displayLink != nil {
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
// if keyboard height is not equal to its bounds - we can ignore
|
|
128
|
-
// values, since they'll be invalid and will cause UI jumps
|
|
129
|
-
if floor(keyboardView?.bounds.size.height ?? 0) != floor(_keyboardHeight) {
|
|
130
|
-
return
|
|
131
|
-
}
|
|
125
|
+
let position = CGFloat.interpolate(
|
|
126
|
+
inputRange: [_keyboardHeight / 2, -_keyboardHeight / 2],
|
|
127
|
+
outputRange: [_keyboardHeight, 0],
|
|
128
|
+
currentValue: keyboardPosition
|
|
129
|
+
) - KeyboardAreaExtender.shared.offset
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
let position = CGFloat.interpolate(
|
|
141
|
-
inputRange: [_keyboardHeight / 2, -_keyboardHeight / 2],
|
|
142
|
-
outputRange: [_keyboardHeight, 0],
|
|
143
|
-
currentValue: keyboardPosition
|
|
144
|
-
) - KeyboardAreaExtender.shared.offset
|
|
145
|
-
|
|
146
|
-
if position == 0 {
|
|
147
|
-
// it will be triggered before `keyboardWillDisappear` and
|
|
148
|
-
// we don't need to trigger `onInteractive` handler for that
|
|
149
|
-
// since it will be handled in `keyboardWillDisappear` function
|
|
150
|
-
return
|
|
151
|
-
}
|
|
131
|
+
if position == 0 {
|
|
132
|
+
// it will be triggered before `keyboardWillDisappear` and
|
|
133
|
+
// we don't need to trigger `onInteractive` handler for that
|
|
134
|
+
// since it will be handled in `keyboardWillDisappear` function
|
|
135
|
+
return
|
|
136
|
+
}
|
|
152
137
|
|
|
153
|
-
|
|
138
|
+
prevKeyboardPosition = position
|
|
154
139
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
140
|
+
onEvent(
|
|
141
|
+
"onKeyboardMoveInteractive",
|
|
142
|
+
position as NSNumber,
|
|
143
|
+
position / CGFloat(keyboardHeight) as NSNumber,
|
|
144
|
+
-1,
|
|
145
|
+
tag
|
|
146
|
+
)
|
|
163
147
|
}
|
|
164
148
|
|
|
165
149
|
@objc public func unmount() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-keyboard-controller",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.2",
|
|
4
4
|
"description": "Keyboard manager which works in identical way on both iOS and Android",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"jest",
|
|
18
18
|
"!lib/typescript/example",
|
|
19
19
|
"!android/build",
|
|
20
|
+
"!android/src/test",
|
|
20
21
|
"!android/.gradle",
|
|
21
22
|
"!android/gradle",
|
|
22
23
|
"!android/gradlew",
|
package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolderTest.kt
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
package com.reactnativekeyboardcontroller.traversal
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.widget.EditText
|
|
5
|
-
import androidx.test.core.app.ApplicationProvider
|
|
6
|
-
import org.junit.Assert.assertEquals
|
|
7
|
-
import org.junit.Assert.assertFalse
|
|
8
|
-
import org.junit.Assert.assertNull
|
|
9
|
-
import org.junit.Assert.assertTrue
|
|
10
|
-
import org.junit.Test
|
|
11
|
-
import org.junit.runner.RunWith
|
|
12
|
-
import org.robolectric.RobolectricTestRunner
|
|
13
|
-
|
|
14
|
-
@RunWith(RobolectricTestRunner::class)
|
|
15
|
-
class FocusedInputHolderTest {
|
|
16
|
-
@Test
|
|
17
|
-
fun `FocusedInputHolder should hold a weak reference`() {
|
|
18
|
-
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
19
|
-
var input: EditText? = EditText(context)
|
|
20
|
-
|
|
21
|
-
FocusedInputHolder.set(input as EditText)
|
|
22
|
-
|
|
23
|
-
assertEquals(FocusedInputHolder.get(), input)
|
|
24
|
-
|
|
25
|
-
input = null
|
|
26
|
-
|
|
27
|
-
@Suppress("detekt:ExplicitGarbageCollectionCall")
|
|
28
|
-
System.gc()
|
|
29
|
-
|
|
30
|
-
assertNull(FocusedInputHolder.get())
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
@Test
|
|
34
|
-
fun `focus() should request focus on expected field`() {
|
|
35
|
-
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
36
|
-
val input = EditText(context)
|
|
37
|
-
|
|
38
|
-
assertFalse(input.hasFocus())
|
|
39
|
-
|
|
40
|
-
FocusedInputHolder.set(input)
|
|
41
|
-
FocusedInputHolder.focus()
|
|
42
|
-
|
|
43
|
-
assertTrue(input.hasFocus())
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
package com.reactnativekeyboardcontroller.traversal
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.widget.EditText
|
|
5
|
-
import android.widget.LinearLayout
|
|
6
|
-
import androidx.test.core.app.ApplicationProvider
|
|
7
|
-
import com.reactnativekeyboardcontroller.extensions.focus
|
|
8
|
-
import org.junit.Assert.assertTrue
|
|
9
|
-
import org.junit.Before
|
|
10
|
-
import org.junit.Test
|
|
11
|
-
import org.junit.runner.RunWith
|
|
12
|
-
import org.robolectric.RobolectricTestRunner
|
|
13
|
-
import org.robolectric.shadows.ShadowLooper
|
|
14
|
-
|
|
15
|
-
@RunWith(RobolectricTestRunner::class)
|
|
16
|
-
class ViewHierarchyNavigatorTest {
|
|
17
|
-
private lateinit var layout: LinearLayout
|
|
18
|
-
private lateinit var editText1: EditText
|
|
19
|
-
private lateinit var editText2: EditText
|
|
20
|
-
private lateinit var editText3: EditText
|
|
21
|
-
private lateinit var editText4: EditText
|
|
22
|
-
private lateinit var editText5: EditText
|
|
23
|
-
private lateinit var editText6: EditText
|
|
24
|
-
private lateinit var editText7: EditText
|
|
25
|
-
private lateinit var editText8: EditText
|
|
26
|
-
private lateinit var editText9: EditText
|
|
27
|
-
private lateinit var editText10: EditText
|
|
28
|
-
private lateinit var editText11: EditText
|
|
29
|
-
private lateinit var editText12: EditText
|
|
30
|
-
private lateinit var editText13: EditText
|
|
31
|
-
|
|
32
|
-
@Suppress("detekt:CyclomaticComplexMethod")
|
|
33
|
-
@Before
|
|
34
|
-
fun setUp() {
|
|
35
|
-
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
36
|
-
|
|
37
|
-
editText1 = EditText(context).apply { id = 1 }
|
|
38
|
-
editText2 = EditText(context).apply { id = 2 }
|
|
39
|
-
editText3 =
|
|
40
|
-
EditText(context).apply {
|
|
41
|
-
id = 3
|
|
42
|
-
isEnabled = false
|
|
43
|
-
}
|
|
44
|
-
editText4 =
|
|
45
|
-
EditText(context).apply {
|
|
46
|
-
id = 4
|
|
47
|
-
isEnabled = false
|
|
48
|
-
}
|
|
49
|
-
editText5 = EditText(context).apply { id = 5 }
|
|
50
|
-
editText6 = EditText(context).apply { id = 6 }
|
|
51
|
-
editText7 = EditText(context).apply { id = 7 }
|
|
52
|
-
editText8 = EditText(context).apply { id = 8 }
|
|
53
|
-
editText9 = EditText(context).apply { id = 9 }
|
|
54
|
-
editText10 = EditText(context).apply { id = 10 }
|
|
55
|
-
editText11 = EditText(context).apply { id = 11 }
|
|
56
|
-
editText12 = EditText(context).apply { id = 12 }
|
|
57
|
-
editText13 = EditText(context).apply { id = 13 }
|
|
58
|
-
|
|
59
|
-
layout =
|
|
60
|
-
LinearLayout(context).apply {
|
|
61
|
-
addView(editText1)
|
|
62
|
-
addView(editText2)
|
|
63
|
-
addView(editText3)
|
|
64
|
-
addView(editText4)
|
|
65
|
-
addView(
|
|
66
|
-
LinearLayout(context).apply {
|
|
67
|
-
addView(editText5)
|
|
68
|
-
addView(editText6)
|
|
69
|
-
addView(editText7)
|
|
70
|
-
},
|
|
71
|
-
)
|
|
72
|
-
addView(editText8)
|
|
73
|
-
addView(editText9)
|
|
74
|
-
addView(editText10)
|
|
75
|
-
addView(editText11)
|
|
76
|
-
addView(editText12)
|
|
77
|
-
addView(editText13)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
@Test
|
|
82
|
-
fun `getAllInputFields returns all EditTexts in ViewGroup`() {
|
|
83
|
-
val editTexts = ViewHierarchyNavigator.getAllInputFields(layout)
|
|
84
|
-
|
|
85
|
-
// 13 (all) - 2 (disabled)
|
|
86
|
-
assertTrue(editTexts.size == 11)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@Test
|
|
90
|
-
fun `setFocusTo to 'next' should set focus to next field`() {
|
|
91
|
-
editText1.focus()
|
|
92
|
-
|
|
93
|
-
ViewHierarchyNavigator.setFocusTo("next", editText1)
|
|
94
|
-
|
|
95
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
96
|
-
|
|
97
|
-
assertTrue(editText2.hasFocus())
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@Test
|
|
101
|
-
fun `setFocusTo to 'prev' should set focus to previous field`() {
|
|
102
|
-
editText2.focus()
|
|
103
|
-
|
|
104
|
-
ViewHierarchyNavigator.setFocusTo("prev", editText2)
|
|
105
|
-
|
|
106
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
107
|
-
|
|
108
|
-
assertTrue(editText1.hasFocus())
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
@Test
|
|
112
|
-
fun `setFocusTo to 'next' should skip non-editable fields`() {
|
|
113
|
-
editText2.focus()
|
|
114
|
-
|
|
115
|
-
ViewHierarchyNavigator.setFocusTo("next", editText2)
|
|
116
|
-
|
|
117
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
118
|
-
|
|
119
|
-
assertTrue(editText5.hasFocus())
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
@Test
|
|
123
|
-
fun `setFocusTo to 'prev' should skip non-editable fields`() {
|
|
124
|
-
editText5.focus()
|
|
125
|
-
|
|
126
|
-
ViewHierarchyNavigator.setFocusTo("prev", editText5)
|
|
127
|
-
|
|
128
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
129
|
-
|
|
130
|
-
assertTrue(editText2.hasFocus())
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
@Test
|
|
134
|
-
fun `setFocusTo to 'next' should set focus relatively to current group`() {
|
|
135
|
-
editText5.focus()
|
|
136
|
-
|
|
137
|
-
ViewHierarchyNavigator.setFocusTo("next", editText5)
|
|
138
|
-
|
|
139
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
140
|
-
|
|
141
|
-
assertTrue(editText6.hasFocus())
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
@Test
|
|
145
|
-
fun `setFocusTo to 'prev' should set focus relatively to current group`() {
|
|
146
|
-
editText7.focus()
|
|
147
|
-
|
|
148
|
-
ViewHierarchyNavigator.setFocusTo("prev", editText7)
|
|
149
|
-
|
|
150
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
151
|
-
|
|
152
|
-
assertTrue(editText6.hasFocus())
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
@Test
|
|
156
|
-
fun `setFocusTo to 'next' should correctly exit from current group`() {
|
|
157
|
-
editText7.focus()
|
|
158
|
-
|
|
159
|
-
ViewHierarchyNavigator.setFocusTo("next", editText7)
|
|
160
|
-
|
|
161
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
162
|
-
|
|
163
|
-
assertTrue(editText8.hasFocus())
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
@Test
|
|
167
|
-
fun `setFocusTo to 'prev' should set focus to last element in group`() {
|
|
168
|
-
editText8.focus()
|
|
169
|
-
|
|
170
|
-
ViewHierarchyNavigator.setFocusTo("prev", editText8)
|
|
171
|
-
|
|
172
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
173
|
-
|
|
174
|
-
assertTrue(editText7.hasFocus())
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
@Test
|
|
178
|
-
fun `setFocusTo to 'next' should do nothing if it's last element`() {
|
|
179
|
-
editText13.focus()
|
|
180
|
-
|
|
181
|
-
ViewHierarchyNavigator.setFocusTo("next", editText13)
|
|
182
|
-
|
|
183
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
184
|
-
|
|
185
|
-
assertTrue(editText13.hasFocus())
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
@Test
|
|
189
|
-
fun `setFocusTo to 'prev' should do nothing if it's first element`() {
|
|
190
|
-
editText1.focus()
|
|
191
|
-
|
|
192
|
-
ViewHierarchyNavigator.setFocusTo("prev", editText1)
|
|
193
|
-
|
|
194
|
-
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
|
|
195
|
-
|
|
196
|
-
assertTrue(editText1.hasFocus())
|
|
197
|
-
}
|
|
198
|
-
}
|