Modal on android make app crash when any accessibility service is ON
See original GitHub issuethe Modal component throws an exception when the modal is opening with any accessibilty service is ON (like TalkBack) I have found the cause of the issue, and paste some related code. But I haven’t known how to solve the problem. Any helps will be great.
the stack trace:
java.lang.IllegalArgumentException:` parameter must be a descendant of this view
at android.view.ViewGroup.offsetRectBetweenParentAndChild(ViewGroup.java:5334)
at android.view.ViewGroup.offsetDescendantRectToMyCoords(ViewGroup.java:5263)
at android.view.ViewGroup$ViewLocationHolder.init(ViewGroup.java:7730)
at android.view.ViewGroup$ViewLocationHolder.obtain(ViewGroup.java:7664)
at android.view.ViewGroup$ChildListForAccessibility.init(ViewGroup.java:7599)
at android.view.ViewGroup$ChildListForAccessibility.obtain(ViewGroup.java:7567)
at android.view.ViewGroup.addChildrenForAccessibility(ViewGroup.java:1927)
at android.view.ViewGroup.addChildrenForAccessibility(ViewGroup.java:1936)
at android.view.ViewGroup.onInitializeAccessibilityNodeInfoInternal(ViewGroup.java:2978)
at android.view.View.onInitializeAccessibilityNodeInfo(View.java:6084)
at android.view.View.createAccessibilityNodeInfoInternal(View.java:6043)
at android.view.View.createAccessibilityNodeInfo(View.java:6028)
at android.view.accessibility.AccessibilityRecord.setSource(AccessibilityRecord.java:145)
at android.view.accessibility.AccessibilityRecord.setSource(AccessibilityRecord.java:119)
at android.view.View.onInitializeAccessibilityEventInternal(View.java:5980)
at android.view.View.onInitializeAccessibilityEvent(View.java:5968)
at android.view.View.sendAccessibilityEventUncheckedInternal(View.java:5833)
at android.view.View.sendAccessibilityEventUnchecked(View.java:5818)
at android.view.ViewRootImpl$SendWindowContentChangedAccessibilityEvent.run(ViewRootImpl.java:7154)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
related code: android.view.View:
public void notifySubtreeAccessibilityStateChangedIfNeeded() {
if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
return;
}
if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
if (mParent != null) {
try {
mParent.notifySubtreeAccessibilityStateChanged(
this, this, AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
}
}
}
}
When no accessibility service is on, the method notifySubtreeAccessibilityStateChangedIfNeeded return immediately, but, when any accessibility is on, the method will do more things. The program will run into offsetRectBetweenParentAndChild method in ViewGroup, which throws the exception. android.view.ViewGroup:
void offsetRectBetweenParentAndChild(View descendant, Rect rect,
boolean offsetFromChildToParent, boolean clipToBounds) {
// already in the same coord system :)
if (descendant == this) {
return;
}
ViewParent theParent = descendant.mParent;
// search and offset up to the parent
while ((theParent != null)
&& (theParent instanceof View)
&& (theParent != this)) {
if (offsetFromChildToParent) {
rect.offset(descendant.mLeft - descendant.mScrollX,
descendant.mTop - descendant.mScrollY);
if (clipToBounds) {
View p = (View) theParent;
boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
p.mBottom - p.mTop);
if (!intersected) {
rect.setEmpty();
}
}
} else {
if (clipToBounds) {
View p = (View) theParent;
boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
p.mBottom - p.mTop);
if (!intersected) {
rect.setEmpty();
}
}
rect.offset(descendant.mScrollX - descendant.mLeft,
descendant.mScrollY - descendant.mTop);
}
descendant = (View) theParent;
theParent = descendant.mParent;
}
// now that we are up to this view, need to offset one more time
// to get into our coordinate space
if (theParent == this) {
if (offsetFromChildToParent) {
rect.offset(descendant.mLeft - descendant.mScrollX,
descendant.mTop - descendant.mScrollY);
} else {
rect.offset(descendant.mScrollX - descendant.mLeft,
descendant.mScrollY - descendant.mTop);
}
} else {
throw new IllegalArgumentException("parameter must be a descendant of this view");
}
}
In the method, it walks through the view hierarchy from the descendant with the while loop, until it reaches the GroupView instance. But when it comes the Modal component, the loop quit with theParent is an instance of ViewRootImpl, and the condition as last of the method fails, so the method throws the exception.
Issue Analytics
- State:
- Created 7 years ago
- Comments:11 (1 by maintainers)
Yes. That was fixed in https://github.com/facebook/react-native/commit/57c40d9a6f5a901cf108b72ab18f4bae9fc246e2#diff-6453d554bf967fe816a1aa3735c9fe62
Closing this since it has been fixed.