-
-
Notifications
You must be signed in to change notification settings - Fork 153
Expand file tree
/
Copy pathViewHierarchyNavigator.kt
More file actions
130 lines (107 loc) · 4.08 KB
/
ViewHierarchyNavigator.kt
File metadata and controls
130 lines (107 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.reactnativekeyboardcontroller.traversal
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import com.facebook.react.bridge.UiThreadUtil
import com.reactnativekeyboardcontroller.extensions.focus
import com.reactnativekeyboardcontroller.views.KeyboardToolbarGroupReactViewGroup
object ViewHierarchyNavigator {
fun setFocusTo(
direction: String,
view: View,
) {
val input = if (direction == "next") findNextEditText(view) else findPreviousEditText(view)
UiThreadUtil.runOnUiThread {
input.focus()
}
}
fun getAllInputFields(viewGroup: View?): List<EditText> {
val editTexts = mutableListOf<EditText>()
// Helper function to recursively search for EditText views
fun findEditTexts(view: View?) {
if (isValidTextInput(view)) {
editTexts.add(view as EditText)
} else if (view is ViewGroup && view !is KeyboardToolbarGroupReactViewGroup) {
for (i in 0 until view.childCount) {
findEditTexts(view.getChildAt(i))
}
}
}
// If the root is a group itself, search within it (for group-scoped queries)
if (viewGroup is KeyboardToolbarGroupReactViewGroup) {
for (i in 0 until viewGroup.childCount) {
findEditTexts(viewGroup.getChildAt(i))
}
} else {
findEditTexts(viewGroup)
}
return editTexts
}
/**
* Finds the closest [KeyboardToolbarGroupReactViewGroup] ancestor of the given view.
* Returns null if the view is not inside any group.
*/
fun findGroupAncestor(view: View?): KeyboardToolbarGroupReactViewGroup? {
var current = view?.parent
while (current != null) {
if (current is KeyboardToolbarGroupReactViewGroup) {
return current
}
current = current.parent
}
return null
}
private fun findNextEditText(currentFocus: View): EditText? = findEditTextInDirection(currentFocus, 1)
private fun findPreviousEditText(currentFocus: View): EditText? = findEditTextInDirection(currentFocus, -1)
@Suppress("detekt:ReturnCount")
private fun findEditTextInDirection(
currentFocus: View,
direction: Int,
): EditText? {
// Attempt to find the parent view group, return null if not found or not a ViewGroup
val parentViewGroup = currentFocus.parent as? ViewGroup ?: return null
// Find the index of the current EditText in its parent
val currentIndex = parentViewGroup.indexOfChild(currentFocus)
// Determine the start index and end condition for the loop based on the direction
var i = if (direction > 0) currentIndex + 1 else currentIndex - 1
val end = if (direction > 0) parentViewGroup.childCount else -1
// Iterate over siblings in the specified direction
while (i != end) {
val nextChild = parentViewGroup.getChildAt(i)
findEditTextOrGoDeeper(nextChild, direction)?.let { return it } // Return if an EditText is found
i += direction
}
// Don't navigate outside the group boundary
if (parentViewGroup is KeyboardToolbarGroupReactViewGroup) {
return null
}
// Recurse to the parent's parent if no sibling EditText is found
return findEditTextInDirection(parentViewGroup, direction)
}
private fun findEditTextInHierarchy(
viewGroup: ViewGroup,
direction: Int,
): EditText? {
val range = if (direction > 0) 0 until viewGroup.childCount else viewGroup.childCount - 1 downTo 0
for (i in range) {
val child = viewGroup.getChildAt(i)
findEditTextOrGoDeeper(child, direction)?.let { return it }
}
// No EditText found in the current view group
return null
}
private fun findEditTextOrGoDeeper(
child: View,
direction: Int,
): EditText? {
var result: EditText? = null
if (isValidTextInput(child)) {
result = child as EditText
} else if (child is ViewGroup && child !is KeyboardToolbarGroupReactViewGroup) {
// If the child is a ViewGroup, check its children recursively
result = findEditTextInHierarchy(child, direction)
}
return result
}
private fun isValidTextInput(view: View?): Boolean = view is EditText && view.isEnabled
}