Skip to content

Commit

Permalink
fix: do not overcast to ReactEditText (#776)
Browse files Browse the repository at this point in the history
## 📜 Description

Cast to base `EditText` class whenever it's possible.

## 💡 Motivation and Context

If you deal with native SDKs (i. e. Stripe), then in such SDKs they may
inherit their component from native `EditText` and simply expose them to
JS layer. Casting to `ReactEditText` will eventually fail and part of
this library will not be working (everything about tracking focused
input).

So in this PR I decided to cast everything to `EditText`. Conditional
casting will be only in two places `focus` method and in reflection in
`addTextChangedListener`. But in the last method I still can get a
crash: `Expected receiver of type
com.facebook.react.views.textinput.ReactEditText, but got
com.stripe.android.view.CardNumberEditText`, so to overcome this problem
I decided to handle that exception and call original method.

Resolves android issue in
#775

## 📢 Changelog

<!-- High level overview of important changes -->
<!-- For example: fixed status bar manipulation; added new types
declarations; -->
<!-- If your changes don't affect one of platform/language below - then
remove this platform/language -->

### Android

- cast to `EditText` instead of `ReactEditText` whenever it's possible;
- handle `IllegalArgumentException` in `addOnTextChangedListener`. 

## 🤔 How Has This Been Tested?

Tested on Medium Phone API 35 emulator.

## 📸 Screenshots (if appropriate):

Sorry, but emulator is really laggy 😬 


https://github.com/user-attachments/assets/08024fcc-6233-4fca-97f9-d5c6dfdfc479

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
  • Loading branch information
kirillzyusko authored Jan 24, 2025
1 parent 4f1a798 commit 241d157
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ fun EditText.addOnTextChangedListener(action: (String) -> Unit): TextWatcher {
Logger.w(javaClass.simpleName, "Can not attach listener because casting failed: ${e.message}")
} catch (e: NoSuchFieldException) {
Logger.w(javaClass.simpleName, "Can not attach listener because field `mListeners` not found: ${e.message}")
} catch (e: IllegalArgumentException) {
Logger.w(
javaClass.simpleName,
"Can not attach listener to be the first in the list: ${e.message}. Attaching to the end...",
)
// it's plain EditText - it doesn't have the same problem as ReactEditText
this.addTextChangedListener(listener)
}

return listener
Expand Down Expand Up @@ -147,7 +154,7 @@ val EditText?.keyboardType: String
}

class KeyboardControllerSelectionWatcher(
private val editText: ReactEditText,
private val editText: EditText,
private val action: (start: Int, end: Int, startX: Double, startY: Double, endX: Double, endY: Double) -> Unit,
) {
private var lastSelectionStart: Int = -1
Expand Down Expand Up @@ -233,7 +240,7 @@ class KeyboardControllerSelectionWatcher(
}
}

fun ReactEditText.addOnSelectionChangedListener(
fun EditText.addOnSelectionChangedListener(
action: (start: Int, end: Int, startX: Double, startY: Double, endX: Double, endY: Double) -> Unit,
): () -> Unit {
val listener = KeyboardControllerSelectionWatcher(this, action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import android.text.TextWatcher
import android.view.View
import android.view.View.OnLayoutChangeListener
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener
import android.widget.EditText
import com.facebook.react.bridge.Arguments
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.views.textinput.ReactEditText
import com.facebook.react.views.view.ReactViewGroup
import com.reactnativekeyboardcontroller.events.FocusedInputLayoutChangedEvent
import com.reactnativekeyboardcontroller.events.FocusedInputLayoutChangedEventData
Expand Down Expand Up @@ -46,7 +46,7 @@ class FocusedInputObserver(
private val surfaceId = UIManagerHelper.getSurfaceId(view)

// state variables
private var lastFocusedInput: ReactEditText? = null
private var lastFocusedInput: EditText? = null
private var lastEventDispatched: FocusedInputLayoutChangedEventData = noFocusedInputEvent
private var textWatcher: TextWatcher? = null
private var selectionSubscription: (() -> Unit)? = null
Expand Down Expand Up @@ -105,7 +105,7 @@ class FocusedInputObserver(
selectionSubscription?.invoke()
lastFocusedInput = null
}
if (newFocus is ReactEditText) {
if (newFocus is EditText) {
lastFocusedInput = newFocus
newFocus.addOnLayoutChangeListener(layoutListener)
this.syncUpLayout()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.reactnativekeyboardcontroller.listeners

import android.view.View
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener
import android.widget.EditText
import androidx.core.graphics.Insets
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
Expand All @@ -11,7 +12,6 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.views.textinput.ReactEditText
import com.facebook.react.views.view.ReactViewGroup
import com.reactnativekeyboardcontroller.constants.Keyboard
import com.reactnativekeyboardcontroller.events.KeyboardTransitionEvent
Expand Down Expand Up @@ -67,7 +67,7 @@ class KeyboardAnimationCallback(
// listeners
private val focusListener =
OnGlobalFocusChangeListener { oldFocus, newFocus ->
if (newFocus is ReactEditText) {
if (newFocus is EditText) {
viewTagFocused = newFocus.id

// keyboard is visible and focus has been changed
Expand Down

0 comments on commit 241d157

Please sign in to comment.