From 46cc4907e3e49f5c7b1ac0a1088866f2958f43a1 Mon Sep 17 00:00:00 2001 From: Sergei Dryganets Date: Mon, 22 Jan 2018 14:46:42 -0800 Subject: [PATCH] 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 --- .../facebook/react/flat/TextNodeRegion.java | 13 ++++++---- .../react/views/text/ReactTextView.java | 11 +++++---- .../react/views/textinput/ReactEditText.java | 24 +++++++++---------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/TextNodeRegion.java b/ReactAndroid/src/main/java/com/facebook/react/flat/TextNodeRegion.java index 7fd6c6256c4e9d..434520209d9855 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/TextNodeRegion.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/TextNodeRegion.java @@ -70,11 +70,14 @@ boolean matchesTag(int tag) { } if (mLayout != null) { - Spanned text = (Spanned) mLayout.getText(); - RCTRawText[] spans = text.getSpans(0, text.length(), RCTRawText.class); - for (RCTRawText span : spans) { - if (span.getReactTag() == tag) { - return true; + CharSequence text = mLayout.getText(); + if (text instanceof Spanned) { + Spanned spannedText = (Spanned) text; + RCTRawText[] spans = spannedText.getSpans(0, text.length(), RCTRawText.class); + for (RCTRawText span : spans) { + if (span.getReactTag() == tag) { + return true; + } } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java index c324646ee58163..41ca82f4cd4992 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java @@ -76,7 +76,7 @@ public void setText(ReactTextUpdate update) { @Override public int reactTagForTouch(float touchX, float touchY) { - Spanned text = (Spanned) getText(); + CharSequence text = getText(); int target = getId(); int x = (int) touchX; @@ -94,20 +94,21 @@ public int reactTagForTouch(float touchX, float touchY) { int lineEndX = (int) layout.getLineRight(line); // 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); // 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 // 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 - ReactTagSpan[] spans = text.getSpans(index, index, ReactTagSpan.class); + ReactTagSpan[] spans = spannedText.getSpans(index, index, ReactTagSpan.class); if (spans != null) { int targetSpanTextLength = text.length(); for (int i = 0; i < spans.length; i++) { - int spanStart = text.getSpanStart(spans[i]); - int spanEnd = text.getSpanEnd(spans[i]); + int spanStart = spannedText.getSpanStart(spans[i]); + int spanEnd = spannedText.getSpanEnd(spans[i]); if (spanEnd > index && (spanEnd - spanStart) <= targetSpanTextLength) { target = spans[i].getReactTag(); targetSpanTextLength = (spanEnd - spanStart); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index f2e8f968e8af50..9f6c48ff43f6ac 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -525,8 +525,8 @@ private void updateImeOptions() { @Override protected boolean verifyDrawable(Drawable drawable) { - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); + if (mContainsImages) { + Spanned text = getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); for (TextInlineImageSpan span : spans) { if (span.getDrawable() == drawable) { @@ -539,8 +539,8 @@ protected boolean verifyDrawable(Drawable drawable) { @Override public void invalidateDrawable(Drawable drawable) { - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); + if (mContainsImages) { + Spanned text = getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); for (TextInlineImageSpan span : spans) { if (span.getDrawable() == drawable) { @@ -554,8 +554,8 @@ public void invalidateDrawable(Drawable drawable) { @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); + if (mContainsImages) { + Spanned text = getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); for (TextInlineImageSpan span : spans) { span.onDetachedFromWindow(); @@ -566,8 +566,8 @@ public void onDetachedFromWindow() { @Override public void onStartTemporaryDetach() { super.onStartTemporaryDetach(); - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); + if (mContainsImages) { + Spanned text = getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); for (TextInlineImageSpan span : spans) { span.onStartTemporaryDetach(); @@ -578,8 +578,8 @@ public void onStartTemporaryDetach() { @Override public void onAttachedToWindow() { super.onAttachedToWindow(); - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); + if (mContainsImages) { + Spanned text = getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); for (TextInlineImageSpan span : spans) { span.onAttachedToWindow(); @@ -590,8 +590,8 @@ public void onAttachedToWindow() { @Override public void onFinishTemporaryDetach() { super.onFinishTemporaryDetach(); - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); + if (mContainsImages) { + Spanned text = getText(); TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); for (TextInlineImageSpan span : spans) { span.onFinishTemporaryDetach();