Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
private var listeners: CopyOnWriteArrayList<TextWatcher>?

public var stagedInputType: Int
internal var stagedAutoCapitalize: Int = 0
public var submitBehavior: String? = null
public var dragAndDropFilter: List<String>? = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,9 @@ public open class ReactTextInputManager public constructor() :
}
}

updateStagedInputTypeFlag(view, AUTOCAPITALIZE_FLAGS, autoCapitalizeValue)
// Deferred to onAfterUpdateTransaction() so we can reconcile with the resolved
// keyboard type — AUTOCAPITALIZE_FLAGS collides with numeric inputType flags.
view.stagedAutoCapitalize = autoCapitalizeValue
}

@ReactProp(name = "keyboardType")
Expand Down Expand Up @@ -882,6 +884,7 @@ public open class ReactTextInputManager public constructor() :
override fun onAfterUpdateTransaction(view: ReactEditText) {
super.onAfterUpdateTransaction(view)
view.maybeUpdateTypeface()
reconcileAutoCapitalize(view)
view.commitStagedInputType()
}

Expand Down Expand Up @@ -1129,6 +1132,27 @@ public open class ReactTextInputManager public constructor() :

private const val IME_ACTION_ID = 0x670

// AUTOCAPITALIZE_FLAGS (0x7000) shares bit positions with TYPE_NUMBER_FLAG_SIGNED
// (0x1000) and TYPE_NUMBER_FLAG_DECIMAL (0x2000). We apply autocapitalize here
// after all props are set so the resolved input class determines whether the
// flags are meaningful.
private fun reconcileAutoCapitalize(view: ReactEditText) {
val autoCapValue = view.stagedAutoCapitalize
val inputClass = view.stagedInputType and InputType.TYPE_MASK_CLASS

// Only strip 0x4000 (CAP_SENTENCES) for non-text classes — 0x1000/0x2000 are
// valid numeric flags (SIGNED/DECIMAL) and must not be cleared.
val reconciled =
if (inputClass == InputType.TYPE_CLASS_TEXT) {
(view.stagedInputType and AUTOCAPITALIZE_FLAGS.inv()) or autoCapValue
} else {
view.stagedInputType and InputType.TYPE_TEXT_FLAG_CAP_SENTENCES.inv()
}

if (view.stagedInputType == reconciled) return
view.stagedInputType = reconciled
}

// Sets the correct password type, since numeric and text passwords have different types
private fun checkPasswordType(view: ReactEditText) {
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,65 @@ class ReactTextInputPropertyTest {
assertThat(view.inputType and InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS).isZero
}

@Test
fun testAutoCapitalizeDoesNotStripNumericFlags() {
val numericTypeFlags =
(InputType.TYPE_CLASS_NUMBER or
InputType.TYPE_NUMBER_FLAG_DECIMAL or
InputType.TYPE_NUMBER_FLAG_SIGNED)

manager.updateProperties(view, buildStyles("keyboardType", "numeric"))
assertThat(view.inputType and numericTypeFlags).isEqualTo(numericTypeFlags)

manager.updateProperties(
view,
buildStyles("autoCapitalize", InputType.TYPE_TEXT_FLAG_CAP_SENTENCES),
)
assertThat(view.inputType and InputType.TYPE_NUMBER_FLAG_SIGNED).isNotZero
assertThat(view.inputType and InputType.TYPE_NUMBER_FLAG_DECIMAL).isNotZero
}

@Test
fun testAutoCapitalizeAndNumericKeyboardInSameTransaction() {
val numericTypeFlags =
(InputType.TYPE_CLASS_NUMBER or
InputType.TYPE_NUMBER_FLAG_DECIMAL or
InputType.TYPE_NUMBER_FLAG_SIGNED)

manager.updateProperties(
view,
buildStyles(
"autoCapitalize",
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES,
"keyboardType",
"numeric",
),
)
assertThat(view.inputType and numericTypeFlags).isEqualTo(numericTypeFlags)
assertThat(view.inputType and InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isZero
}

@Test
fun testAutoCapitalizeReappliesWhenKeyboardTypeChangesFromNumericToText() {
// CAP_SENTENCES (0x4000) doesn't share a bit position with any numeric flag,
// unlike CAP_WORDS (0x2000) / CAP_CHARACTERS (0x1000).
manager.updateProperties(
view,
buildStyles(
"autoCapitalize",
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES,
"keyboardType",
"numeric",
),
)
assertThat(view.inputType and InputType.TYPE_MASK_CLASS).isEqualTo(InputType.TYPE_CLASS_NUMBER)
assertThat(view.inputType and InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isZero

manager.updateProperties(view, buildStyles("keyboardType", "default"))
assertThat(view.inputType and InputType.TYPE_MASK_CLASS).isEqualTo(InputType.TYPE_CLASS_TEXT)
assertThat(view.inputType and InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isNotZero
}

@Test
fun testPlaceholder() {
manager.updateProperties(view, buildStyles())
Expand Down