Skip to content

Commit

Permalink
Partial Mitigation for Samsung TextInput Hangs
Browse files Browse the repository at this point in the history
Summary:
In facebook#35936 we observed that the presence of AbsoluteSizeSpan may lead to hangs when using the Grammarly keyboard on Samsung.

This mitigation makes it so that we do not emit this span in the most common cases, when it is sufficient to set `android:textSize`. In simple cases, it causes typing into the TextInput to no longer hang.

This does not resolve the issue for TextInputs which meaningfully use layout-effecting spans (or at least font size), such as non-uniform text size within the input. We could potentially do further work to reduce the number of spans emitted in these scenarios, but this may be fighting a losing battle against the platform.

Changelog:
[Android][Fixed] - Partial Mitigation for Samsung TextInput Hangs (Paper)

Reviewed By: cortinico

Differential Revision: D42721684

fbshipit-source-id: 5599070f10a385c6063683e3ac7a5acbbdc10ae9
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Jan 25, 2023
1 parent 6f7428e commit da23fdf
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ protected Spannable spannedFromShadowNode(
priority++;
}

// Mitigation for https://github.com/facebook/react-native/issues/35936 (S318090)
flattenAbsoluteSizeSpans(sb);

textShadowNode.mTextAttributes.setHeightOfTallestInlineViewOrImage(
heightOfTallestInlineViewOrImage);

Expand All @@ -318,6 +321,29 @@ protected Spannable spannedFromShadowNode(
return sb;
}

private void flattenAbsoluteSizeSpans(SpannableStringBuilder sb) {
// If spans cover the entire input with the same size as
// getEffectiveFontSize() we can omit them
ReactAbsoluteSizeSpan[] spans = sb.getSpans(0, sb.length(), ReactAbsoluteSizeSpan.class);

int lastSpanIndex = -1;
final int expectedTextSize = mTextAttributes.getEffectiveFontSize();

for (ReactAbsoluteSizeSpan span : spans) {
if (sb.getSpanStart(span) > lastSpanIndex + 1 || span.getSize() != expectedTextSize) {
return;
}

lastSpanIndex = sb.getSpanEnd(span) - 1;
}

if (lastSpanIndex >= sb.length() - 1) {
for (ReactAbsoluteSizeSpan span : spans) {
sb.removeSpan(span);
}
}
}

protected TextAttributes mTextAttributes;

protected boolean mIsColorSet = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ public long measure(
mPreparedSpannableText,
"Spannable element has not been prepared in onBeforeLayout");

// Unflatten AbsoluteSizeSpan for measurement (see flattenAbsoluteSizeSpans() in
// ReactBaseTextShadowNode)
if (text.getSpans(0, text.length(), ReactAbsoluteSizeSpan.class).length == 0) {
text.setSpan(
new ReactAbsoluteSizeSpan(mTextAttributes.getEffectiveFontSize()),
0,
text.length(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}

Layout layout = measureSpannedText(text, width, widthMode);

if (mAdjustsFontSizeToFit) {
Expand Down

0 comments on commit da23fdf

Please sign in to comment.