react-native-controlled-input 0.21.0 → 0.22.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/android/src/main/java/com/controlledinput/ControlledInputView.kt +23 -76
- package/lib/module/ControlledInputViewNativeComponent.ts +0 -1
- package/lib/typescript/src/ControlledInputViewNativeComponent.d.ts +0 -1
- package/lib/typescript/src/ControlledInputViewNativeComponent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ControlledInputViewNativeComponent.ts +0 -1
|
@@ -2,7 +2,6 @@ package com.controlledinput
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.util.AttributeSet
|
|
5
|
-
import android.util.Log
|
|
6
5
|
import android.util.TypedValue
|
|
7
6
|
import android.view.View
|
|
8
7
|
import android.view.inputmethod.InputMethodManager
|
|
@@ -32,14 +31,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|
|
32
31
|
/**
|
|
33
32
|
* - [shouldUseAndroidLayout]: requestLayout posts measureAndLayout
|
|
34
33
|
* - onMeasure skips child [ComposeView] until attached (window + WindowRecomposer)
|
|
35
|
-
*
|
|
36
|
-
* @see expo.modules.kotlin.views.ExpoComposeView
|
|
37
|
-
* @see expo.modules.kotlin.views.ExpoView
|
|
38
34
|
*/
|
|
39
35
|
class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
40
|
-
companion object {
|
|
41
|
-
private const val TAG = "ControlledInputView"
|
|
42
|
-
}
|
|
43
36
|
constructor(context: Context) : super(context) {
|
|
44
37
|
configureComponent(context)
|
|
45
38
|
}
|
|
@@ -67,8 +60,9 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
67
60
|
private var windowLifecycleBound = false
|
|
68
61
|
|
|
69
62
|
/**
|
|
70
|
-
* Hidden [EditText] used only as
|
|
71
|
-
*
|
|
63
|
+
* Hidden [EditText] used only as [FocusedInputObserver.lastFocusedInput] from
|
|
64
|
+
* https://github.com/kirillzyusko/react-native-keyboard-controller; [syncUpLayout] reads
|
|
65
|
+
* [EditText]-scoped geometry. It is NOT focused and does not participate in the focus
|
|
72
66
|
* chain — we push state via reflection + synthetic selection events instead.
|
|
73
67
|
*/
|
|
74
68
|
private val kbcLayoutHost: EditText by lazy {
|
|
@@ -85,8 +79,9 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
85
79
|
}
|
|
86
80
|
|
|
87
81
|
/**
|
|
88
|
-
* EdgeToEdgeViewRegistry → KeyboardAnimationCallback + FocusedInputObserver
|
|
89
|
-
*
|
|
82
|
+
* EdgeToEdgeViewRegistry → KeyboardAnimationCallback + FocusedInputObserver
|
|
83
|
+
* (https://github.com/kirillzyusko/react-native-keyboard-controller).
|
|
84
|
+
* Null if that library is missing or not initialized.
|
|
90
85
|
*/
|
|
91
86
|
private fun resolveKbcCallbackAndObserver(): Pair<Any, Any>? {
|
|
92
87
|
try {
|
|
@@ -94,49 +89,25 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
94
89
|
Class.forName("com.reactnativekeyboardcontroller.views.EdgeToEdgeViewRegistry")
|
|
95
90
|
val registryInstance = registryClass.getField("INSTANCE").get(null)
|
|
96
91
|
val edgeToEdgeView =
|
|
97
|
-
registryClass.getDeclaredMethod("get").invoke(registryInstance)
|
|
98
|
-
?: run {
|
|
99
|
-
Log.w(TAG, "resolveKbcCallbackAndObserver: EdgeToEdgeViewRegistry.get() == null")
|
|
100
|
-
return null
|
|
101
|
-
}
|
|
92
|
+
registryClass.getDeclaredMethod("get").invoke(registryInstance) ?: return null
|
|
102
93
|
|
|
103
94
|
val callbackField =
|
|
104
95
|
edgeToEdgeView.javaClass.declaredFields.firstOrNull {
|
|
105
96
|
it.type.simpleName == "KeyboardAnimationCallback"
|
|
106
|
-
}
|
|
107
|
-
?: run {
|
|
108
|
-
Log.w(TAG, "resolveKbcCallbackAndObserver: KeyboardAnimationCallback field not found")
|
|
109
|
-
return null
|
|
110
|
-
}
|
|
97
|
+
} ?: return null
|
|
111
98
|
callbackField.isAccessible = true
|
|
112
|
-
val callback =
|
|
113
|
-
callbackField.get(edgeToEdgeView)
|
|
114
|
-
?: run {
|
|
115
|
-
Log.w(TAG, "resolveKbcCallbackAndObserver: callback == null")
|
|
116
|
-
return null
|
|
117
|
-
}
|
|
99
|
+
val callback = callbackField.get(edgeToEdgeView) ?: return null
|
|
118
100
|
|
|
119
101
|
val observerField =
|
|
120
102
|
callback.javaClass.declaredFields.firstOrNull {
|
|
121
103
|
it.type.simpleName == "FocusedInputObserver"
|
|
122
|
-
}
|
|
123
|
-
?: run {
|
|
124
|
-
Log.w(TAG, "resolveKbcCallbackAndObserver: FocusedInputObserver field not found")
|
|
125
|
-
return null
|
|
126
|
-
}
|
|
104
|
+
} ?: return null
|
|
127
105
|
observerField.isAccessible = true
|
|
128
|
-
val observer =
|
|
129
|
-
observerField.get(callback)
|
|
130
|
-
?: run {
|
|
131
|
-
Log.w(TAG, "resolveKbcCallbackAndObserver: layoutObserver == null")
|
|
132
|
-
return null
|
|
133
|
-
}
|
|
106
|
+
val observer = observerField.get(callback) ?: return null
|
|
134
107
|
return Pair(callback, observer)
|
|
135
108
|
} catch (_: ClassNotFoundException) {
|
|
136
|
-
Log.d(TAG, "resolveKbcCallbackAndObserver: keyboard-controller not on classpath")
|
|
137
109
|
return null
|
|
138
|
-
} catch (
|
|
139
|
-
Log.w(TAG, "resolveKbcCallbackAndObserver: ${e.javaClass.simpleName}: ${e.message}")
|
|
110
|
+
} catch (_: Exception) {
|
|
140
111
|
return null
|
|
141
112
|
}
|
|
142
113
|
}
|
|
@@ -146,9 +117,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
146
117
|
val f = callback.javaClass.getDeclaredField("viewTagFocused")
|
|
147
118
|
f.isAccessible = true
|
|
148
119
|
f.setInt(callback, id)
|
|
149
|
-
|
|
150
|
-
} catch (e: Exception) {
|
|
151
|
-
Log.w(TAG, "setKbcViewTagFocused: ${e.javaClass.simpleName}: ${e.message}")
|
|
120
|
+
} catch (_: Exception) {
|
|
152
121
|
}
|
|
153
122
|
}
|
|
154
123
|
|
|
@@ -160,14 +129,14 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
160
129
|
holderClass
|
|
161
130
|
.getMethod("set", EditText::class.java)
|
|
162
131
|
.invoke(instance, kbcLayoutHost)
|
|
163
|
-
} catch (
|
|
164
|
-
Log.w(TAG, "setKbcFocusedInputHolder: ${e.javaClass.simpleName}: ${e.message}")
|
|
132
|
+
} catch (_: Exception) {
|
|
165
133
|
}
|
|
166
134
|
}
|
|
167
135
|
|
|
168
136
|
/**
|
|
169
|
-
* `selection.end.y` for
|
|
170
|
-
* in [InputStyle]), else measured
|
|
137
|
+
* `selection.end.y` for https://github.com/kirillzyusko/react-native-keyboard-controller / JS
|
|
138
|
+
* customHeight: prefer explicit style height (dp, same as padding in [InputStyle]), else measured
|
|
139
|
+
* view height in dp.
|
|
171
140
|
*/
|
|
172
141
|
private fun approximateSelectionEndYDp(): Double {
|
|
173
142
|
viewModel.inputStyle.value?.height?.takeIf { it > 0 }?.let { return it }
|
|
@@ -192,11 +161,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
192
161
|
val dataClz =
|
|
193
162
|
Class.forName("com.reactnativekeyboardcontroller.events.FocusedInputSelectionChangedEventData")
|
|
194
163
|
val dataCtor =
|
|
195
|
-
dataClz.declaredConstructors.singleOrNull { it.parameterTypes.size == 7 }
|
|
196
|
-
?: run {
|
|
197
|
-
Log.w(TAG, "dispatchSyntheticKbcSelectionEvent: no 7-arg data ctor")
|
|
198
|
-
return
|
|
199
|
-
}
|
|
164
|
+
dataClz.declaredConstructors.singleOrNull { it.parameterTypes.size == 7 } ?: return
|
|
200
165
|
dataCtor.isAccessible = true
|
|
201
166
|
val data =
|
|
202
167
|
dataCtor.newInstance(targetId, 0.0, 0.0, 0.0, endY, 0, 0)
|
|
@@ -213,21 +178,16 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
213
178
|
|
|
214
179
|
UIManagerHelper.getEventDispatcherForReactTag(reactContext, propagationId)
|
|
215
180
|
?.dispatchEvent(event)
|
|
216
|
-
|
|
217
|
-
TAG,
|
|
218
|
-
"dispatchSyntheticKbcSelectionEvent: propagationId=$propagationId target=$targetId endY(dp)=$endY",
|
|
219
|
-
)
|
|
220
|
-
} catch (e: Exception) {
|
|
221
|
-
Log.w(TAG, "dispatchSyntheticKbcSelectionEvent: ${e.javaClass.simpleName}: ${e.message}")
|
|
181
|
+
} catch (_: Exception) {
|
|
222
182
|
}
|
|
223
183
|
}
|
|
224
184
|
|
|
225
185
|
/**
|
|
226
|
-
* Pushes ControlledInput state into
|
|
227
|
-
* viewTagFocused, lastFocusedInput, FocusedInputHolder,
|
|
186
|
+
* Pushes ControlledInput state into https://github.com/kirillzyusko/react-native-keyboard-controller
|
|
187
|
+
* without stealing Compose focus: viewTagFocused, lastFocusedInput, FocusedInputHolder,
|
|
188
|
+
* syncUpLayout, synthetic selection.
|
|
228
189
|
*/
|
|
229
190
|
private fun syncKeyboardControllerFocusedInput() {
|
|
230
|
-
Log.d(TAG, "syncKeyboardControllerFocusedInput: id=$id")
|
|
231
191
|
kbcLayoutHost.id = id
|
|
232
192
|
viewModel.inputStyle.value?.fontSize?.toFloat()?.let {
|
|
233
193
|
kbcLayoutHost.setTextSize(TypedValue.COMPLEX_UNIT_SP, it)
|
|
@@ -246,11 +206,9 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
246
206
|
val syncMethod = observer.javaClass.getDeclaredMethod("syncUpLayout")
|
|
247
207
|
syncMethod.isAccessible = true
|
|
248
208
|
syncMethod.invoke(observer)
|
|
249
|
-
Log.d(TAG, "syncKeyboardControllerFocusedInput: syncUpLayout() ok")
|
|
250
209
|
|
|
251
210
|
dispatchSyntheticKbcSelectionEvent(observer)
|
|
252
|
-
} catch (
|
|
253
|
-
Log.w(TAG, "syncKeyboardControllerFocusedInput: ${e.javaClass.simpleName}: ${e.message}")
|
|
211
|
+
} catch (_: Exception) {
|
|
254
212
|
}
|
|
255
213
|
}
|
|
256
214
|
|
|
@@ -259,11 +217,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
259
217
|
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
|
260
218
|
super.onLayout(changed, l, t, r, b)
|
|
261
219
|
kbcLayoutHost.layout(0, 0, width, height)
|
|
262
|
-
if (changed) {
|
|
263
|
-
val loc = IntArray(2)
|
|
264
|
-
getLocationOnScreen(loc)
|
|
265
|
-
Log.d(TAG, "onLayout: view=${width}x${height} screenX=${loc[0]} screenY=${loc[1]}")
|
|
266
|
-
}
|
|
267
220
|
}
|
|
268
221
|
|
|
269
222
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
@@ -295,12 +248,10 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
295
248
|
|
|
296
249
|
override fun onAttachedToWindow() {
|
|
297
250
|
super.onAttachedToWindow()
|
|
298
|
-
Log.d(TAG, "onAttachedToWindow: id=$id")
|
|
299
251
|
bindComposeToWindowLifecycle()
|
|
300
252
|
}
|
|
301
253
|
|
|
302
254
|
override fun onDetachedFromWindow() {
|
|
303
|
-
Log.d(TAG, "onDetachedFromWindow: id=$id")
|
|
304
255
|
if (usesLocalFallbackLifecycle) {
|
|
305
256
|
lifecycleRegistry.currentState = Lifecycle.State.CREATED
|
|
306
257
|
}
|
|
@@ -345,7 +296,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
345
296
|
}
|
|
346
297
|
|
|
347
298
|
fun blur() {
|
|
348
|
-
Log.d(TAG, "blur() called from JS ref")
|
|
349
299
|
blurSignal.value = blurSignal.value + 1
|
|
350
300
|
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
351
301
|
imm.hideSoftInputFromWindow(windowToken, 0)
|
|
@@ -353,7 +303,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
353
303
|
}
|
|
354
304
|
|
|
355
305
|
fun focus() {
|
|
356
|
-
Log.d(TAG, "focus() called from JS ref")
|
|
357
306
|
focusSignal.value = focusSignal.value + 1
|
|
358
307
|
}
|
|
359
308
|
|
|
@@ -421,7 +370,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
421
370
|
)
|
|
422
371
|
},
|
|
423
372
|
onFocus = {
|
|
424
|
-
Log.d(TAG, "Compose onFocus id=$id")
|
|
425
373
|
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
426
374
|
val viewId = this@ControlledInputView.id
|
|
427
375
|
UIManagerHelper
|
|
@@ -430,7 +378,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
430
378
|
post { syncKeyboardControllerFocusedInput() }
|
|
431
379
|
},
|
|
432
380
|
onBlur = {
|
|
433
|
-
Log.d(TAG, "Compose onBlur id=$id")
|
|
434
381
|
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
435
382
|
val viewId = this@ControlledInputView.id
|
|
436
383
|
UIManagerHelper
|
|
@@ -25,7 +25,6 @@ export interface BlurEvent {
|
|
|
25
25
|
export interface InputStyle {
|
|
26
26
|
color?: ColorValue;
|
|
27
27
|
fontSize?: Double;
|
|
28
|
-
/** Matches style.height (dp); used for keyboard-controller customHeight on Android. */
|
|
29
28
|
height?: Double;
|
|
30
29
|
fontFamily?: string;
|
|
31
30
|
paddingTop?: Double;
|
|
@@ -10,7 +10,6 @@ export interface BlurEvent {
|
|
|
10
10
|
export interface InputStyle {
|
|
11
11
|
color?: ColorValue;
|
|
12
12
|
fontSize?: Double;
|
|
13
|
-
/** Matches style.height (dp); used for keyboard-controller customHeight on Android. */
|
|
14
13
|
height?: Double;
|
|
15
14
|
fontFamily?: string;
|
|
16
15
|
paddingTop?: Double;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlledInputViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ControlledInputViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EACV,oBAAoB,EACpB,MAAM,EACP,MAAM,2CAA2C,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;CAE1B;AAED,MAAM,WAAW,SAAS;CAEzB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"ControlledInputViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ControlledInputViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EACV,oBAAoB,EACpB,MAAM,EACP,MAAM,2CAA2C,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;CAE1B;AAED,MAAM,WAAW,SAAS;CAEzB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;IAC/D,OAAO,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;CACvE;AAED,eAAO,MAAM,QAAQ,EAAE,cAErB,CAAC;;AAEH,wBAA0E"}
|
package/package.json
CHANGED
|
@@ -25,7 +25,6 @@ export interface BlurEvent {
|
|
|
25
25
|
export interface InputStyle {
|
|
26
26
|
color?: ColorValue;
|
|
27
27
|
fontSize?: Double;
|
|
28
|
-
/** Matches style.height (dp); used for keyboard-controller customHeight on Android. */
|
|
29
28
|
height?: Double;
|
|
30
29
|
fontFamily?: string;
|
|
31
30
|
paddingTop?: Double;
|