ClassCastException fix: getText() returns CharSequence not Spanned.

Summary:
ClassCastException fix: getText() returns CharSequence not Spanned.
From the other hand, EditTexts getText method returns Editable which extends Spanned.
This commit fixes two similar bugs one in flat TextView and another in standard TextView.
Also, it removes redundant checks in the ReactEditText.

Application without this change sporadically crashes with the following stack trace:
```
java.lang.ClassCastException: java.lang.String cannot be cast to android.text.Spanned
	at com.facebook.react.views.text.ReactTextView.reactTagForTouch(ReactTextView.java:195)
	at com.facebook.react.uimanager.TouchTargetHelper.getTouchTargetForView(TouchTargetHelper.java:269)
	at com.facebook.react.uimanager.TouchTargetHelper.findTargetTagAndCoordinatesForTouch$58866680(TouchTargetHelper.java:101)
	at com.facebook.react.uimanager.JSTouchDispatcher.handleTouchEvent(JSTouchDispatcher.java:77)
	at com.facebook.react.ReactRootView.dispatchJSTouchEvent(ReactRootView.java:151)
	at com.facebook.react.ReactRootView.onInterceptTouchEvent(ReactRootView.java:127)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2110)

```
Closes https://github.com/facebook/react-native/pull/15452

Differential Revision: D6775986

Pulled By: hramos

fbshipit-source-id: 6de929937cbbb3e7bd98a708a40700f883cbaef0
This commit is contained in:
Sergei Dryganets 2018-01-22 14:46:42 -08:00 committed by Facebook Github Bot
parent f5975a97ad
commit 46cc4907e3
3 changed files with 26 additions and 22 deletions

View File

@ -70,11 +70,14 @@ import android.text.Spanned;
} }
if (mLayout != null) { if (mLayout != null) {
Spanned text = (Spanned) mLayout.getText(); CharSequence text = mLayout.getText();
RCTRawText[] spans = text.getSpans(0, text.length(), RCTRawText.class); if (text instanceof Spanned) {
for (RCTRawText span : spans) { Spanned spannedText = (Spanned) text;
if (span.getReactTag() == tag) { RCTRawText[] spans = spannedText.getSpans(0, text.length(), RCTRawText.class);
return true; for (RCTRawText span : spans) {
if (span.getReactTag() == tag) {
return true;
}
} }
} }
} }

View File

@ -76,7 +76,7 @@ public class ReactTextView extends TextView implements ReactCompoundView {
@Override @Override
public int reactTagForTouch(float touchX, float touchY) { public int reactTagForTouch(float touchX, float touchY) {
Spanned text = (Spanned) getText(); CharSequence text = getText();
int target = getId(); int target = getId();
int x = (int) touchX; int x = (int) touchX;
@ -94,20 +94,21 @@ public class ReactTextView extends TextView implements ReactCompoundView {
int lineEndX = (int) layout.getLineRight(line); int lineEndX = (int) layout.getLineRight(line);
// TODO(5966918): Consider extending touchable area for text spans by some DP constant // TODO(5966918): Consider extending touchable area for text spans by some DP constant
if (x >= lineStartX && x <= lineEndX) { if (text instanceof Spanned && x >= lineStartX && x <= lineEndX) {
Spanned spannedText = (Spanned) text;
int index = layout.getOffsetForHorizontal(line, x); int index = layout.getOffsetForHorizontal(line, x);
// We choose the most inner span (shortest) containing character at the given index // We choose the most inner span (shortest) containing character at the given index
// if no such span can be found we will send the textview's react id as a touch handler // if no such span can be found we will send the textview's react id as a touch handler
// In case when there are more than one spans with same length we choose the last one // In case when there are more than one spans with same length we choose the last one
// from the spans[] array, since it correspond to the most inner react element // from the spans[] array, since it correspond to the most inner react element
ReactTagSpan[] spans = text.getSpans(index, index, ReactTagSpan.class); ReactTagSpan[] spans = spannedText.getSpans(index, index, ReactTagSpan.class);
if (spans != null) { if (spans != null) {
int targetSpanTextLength = text.length(); int targetSpanTextLength = text.length();
for (int i = 0; i < spans.length; i++) { for (int i = 0; i < spans.length; i++) {
int spanStart = text.getSpanStart(spans[i]); int spanStart = spannedText.getSpanStart(spans[i]);
int spanEnd = text.getSpanEnd(spans[i]); int spanEnd = spannedText.getSpanEnd(spans[i]);
if (spanEnd > index && (spanEnd - spanStart) <= targetSpanTextLength) { if (spanEnd > index && (spanEnd - spanStart) <= targetSpanTextLength) {
target = spans[i].getReactTag(); target = spans[i].getReactTag();
targetSpanTextLength = (spanEnd - spanStart); targetSpanTextLength = (spanEnd - spanStart);

View File

@ -525,8 +525,8 @@ public class ReactEditText extends EditText {
@Override @Override
protected boolean verifyDrawable(Drawable drawable) { protected boolean verifyDrawable(Drawable drawable) {
if (mContainsImages && getText() instanceof Spanned) { if (mContainsImages) {
Spanned text = (Spanned) getText(); Spanned text = getText();
TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) { for (TextInlineImageSpan span : spans) {
if (span.getDrawable() == drawable) { if (span.getDrawable() == drawable) {
@ -539,8 +539,8 @@ public class ReactEditText extends EditText {
@Override @Override
public void invalidateDrawable(Drawable drawable) { public void invalidateDrawable(Drawable drawable) {
if (mContainsImages && getText() instanceof Spanned) { if (mContainsImages) {
Spanned text = (Spanned) getText(); Spanned text = getText();
TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) { for (TextInlineImageSpan span : spans) {
if (span.getDrawable() == drawable) { if (span.getDrawable() == drawable) {
@ -554,8 +554,8 @@ public class ReactEditText extends EditText {
@Override @Override
public void onDetachedFromWindow() { public void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
if (mContainsImages && getText() instanceof Spanned) { if (mContainsImages) {
Spanned text = (Spanned) getText(); Spanned text = getText();
TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) { for (TextInlineImageSpan span : spans) {
span.onDetachedFromWindow(); span.onDetachedFromWindow();
@ -566,8 +566,8 @@ public class ReactEditText extends EditText {
@Override @Override
public void onStartTemporaryDetach() { public void onStartTemporaryDetach() {
super.onStartTemporaryDetach(); super.onStartTemporaryDetach();
if (mContainsImages && getText() instanceof Spanned) { if (mContainsImages) {
Spanned text = (Spanned) getText(); Spanned text = getText();
TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) { for (TextInlineImageSpan span : spans) {
span.onStartTemporaryDetach(); span.onStartTemporaryDetach();
@ -578,8 +578,8 @@ public class ReactEditText extends EditText {
@Override @Override
public void onAttachedToWindow() { public void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
if (mContainsImages && getText() instanceof Spanned) { if (mContainsImages) {
Spanned text = (Spanned) getText(); Spanned text = getText();
TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) { for (TextInlineImageSpan span : spans) {
span.onAttachedToWindow(); span.onAttachedToWindow();
@ -590,8 +590,8 @@ public class ReactEditText extends EditText {
@Override @Override
public void onFinishTemporaryDetach() { public void onFinishTemporaryDetach() {
super.onFinishTemporaryDetach(); super.onFinishTemporaryDetach();
if (mContainsImages && getText() instanceof Spanned) { if (mContainsImages) {
Spanned text = (Spanned) getText(); Spanned text = getText();
TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) { for (TextInlineImageSpan span : spans) {
span.onFinishTemporaryDetach(); span.onFinishTemporaryDetach();