Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing close for canceled bundle downloads #44282

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 1 addition & 3 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ public class com/facebook/react/ReactRootView : android/widget/FrameLayout, com/
protected fun finalize ()V
public fun getAppProperties ()Landroid/os/Bundle;
public fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext;
protected fun getEventDispatcher ()Lcom/facebook/react/uimanager/events/EventDispatcher;
public fun getHeightMeasureSpec ()I
public fun getJSModuleName ()Ljava/lang/String;
public fun getReactInstanceManager ()Lcom/facebook/react/ReactInstanceManager;
Expand Down Expand Up @@ -3776,9 +3777,6 @@ public final class com/facebook/react/runtime/ReactSurfaceView : com/facebook/re
public fun hasActiveReactContext ()Z
public fun hasActiveReactInstance ()Z
public fun isViewAttachedToReactInstance ()Z
public fun onChildEndedNativeGesture (Landroid/view/View;Landroid/view/MotionEvent;)V
public fun onChildStartedNativeGesture (Landroid/view/View;Landroid/view/MotionEvent;)V
public fun requestDisallowInterceptTouchEvent (Z)V
public fun setIsFabric (Z)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
Expand Down Expand Up @@ -192,51 +193,45 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

@Override
public void onChildStartedNativeGesture(View childView, MotionEvent ev) {
if (!isDispatcherReady()) {
EventDispatcher eventDispatcher = getEventDispatcher();
if (eventDispatcher == null) {
return;
}

EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcher(getCurrentReactContext(), getUIManagerType());
if (eventDispatcher != null) {
mJSTouchDispatcher.onChildStartedNativeGesture(ev, eventDispatcher);
if (childView != null && mJSPointerDispatcher != null) {
mJSPointerDispatcher.onChildStartedNativeGesture(childView, ev, eventDispatcher);
}
mJSTouchDispatcher.onChildStartedNativeGesture(ev, eventDispatcher);
if (childView != null && mJSPointerDispatcher != null) {
mJSPointerDispatcher.onChildStartedNativeGesture(childView, ev, eventDispatcher);
}
}

@Override
public void onChildEndedNativeGesture(View childView, MotionEvent ev) {
if (!isDispatcherReady()) {
EventDispatcher eventDispatcher = getEventDispatcher();
if (eventDispatcher == null) {
return;
}

EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcher(getCurrentReactContext(), getUIManagerType());
if (eventDispatcher != null) {
mJSTouchDispatcher.onChildEndedNativeGesture(ev, eventDispatcher);
if (mJSPointerDispatcher != null) {
mJSPointerDispatcher.onChildEndedNativeGesture();
}
mJSTouchDispatcher.onChildEndedNativeGesture(ev, eventDispatcher);
if (mJSPointerDispatcher != null) {
mJSPointerDispatcher.onChildEndedNativeGesture();
}
}

private boolean isDispatcherReady() {
protected @Nullable EventDispatcher getEventDispatcher() {
if (!hasActiveReactContext() || !isViewAttachedToReactInstance()) {
FLog.w(TAG, "Unable to dispatch touch to JS as the catalyst instance has not been attached");
return false;
return null;
}
if (mJSTouchDispatcher == null) {
FLog.w(TAG, "Unable to dispatch touch to JS before the dispatcher is available");
return false;
return null;
}
if (ReactFeatureFlags.dispatchPointerEvents && mJSPointerDispatcher == null) {
FLog.w(TAG, "Unable to dispatch pointer events to JS before the dispatcher is available");
return false;
return null;
}

return true;
return UIManagerHelper.getEventDispatcher(getCurrentReactContext(), getUIManagerType());
}

// By default the JS touch events are dispatched at the root view. This can be overridden in
Expand Down Expand Up @@ -339,8 +334,7 @@ protected void dispatchJSPointerEvent(MotionEvent event, boolean isCapture) {
return;
}

EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcher(getCurrentReactContext(), getUIManagerType());
EventDispatcher eventDispatcher = getEventDispatcher();
if (eventDispatcher != null) {
mJSPointerDispatcher.handleMotionEvent(event, eventDispatcher, isCapture);
}
Expand All @@ -356,8 +350,7 @@ protected void dispatchJSTouchEvent(MotionEvent event) {
return;
}

EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcher(getCurrentReactContext(), getUIManagerType());
EventDispatcher eventDispatcher = getEventDispatcher();
if (eventDispatcher != null) {
mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher);
}
Expand All @@ -367,8 +360,9 @@ protected void dispatchJSTouchEvent(MotionEvent event) {
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// Override in order to still receive events to onInterceptTouchEvent even when some other
// views disallow that, but propagate it up the tree if possible.
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ public void downloadBundleFromURL(
final String bundleURL,
final @Nullable BundleInfo bundleInfo,
Request.Builder requestBuilder) {

final Request request =
requestBuilder.url(bundleURL).addHeader("Accept", "multipart/mixed").build();
mDownloadBundleFromURLCall = Assertions.assertNotNull(mClient.newCall(request));
Expand All @@ -129,20 +128,19 @@ public void onFailure(Call call, IOException e) {

@Override
public void onResponse(Call call, final Response response) throws IOException {
// ignore callback if call was cancelled
if (mDownloadBundleFromURLCall == null || mDownloadBundleFromURLCall.isCanceled()) {
try (Response r = response) {
// ignore callback if call was cancelled
if (mDownloadBundleFromURLCall == null || mDownloadBundleFromURLCall.isCanceled()) {
mDownloadBundleFromURLCall = null;
return;
}
mDownloadBundleFromURLCall = null;
return;
}
mDownloadBundleFromURLCall = null;

final String url = response.request().url().toString();

// Make sure the result is a multipart response and parse the boundary.
String contentType = response.header("content-type");
Pattern regex = Pattern.compile("multipart/mixed;.*boundary=\"([^\"]+)\"");
Matcher match = regex.matcher(contentType);
try (Response r = response) {
final String url = response.request().url().toString();
// Make sure the result is a multipart response and parse the boundary.
String contentType = response.header("content-type");
Pattern regex = Pattern.compile("multipart/mixed;.*boundary=\"([^\"]+)\"");
Matcher match = regex.matcher(contentType);
if (match.find()) {
processMultipartResponse(url, r, match.group(1), outputFile, bundleInfo, callback);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,26 @@
package com.facebook.react.runtime

import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.view.MotionEvent
import android.view.View
import com.facebook.common.logging.FLog
import com.facebook.react.ReactRootView
import com.facebook.react.bridge.ReactContext
import com.facebook.react.config.ReactFeatureFlags
import com.facebook.react.uimanager.IllegalViewOperationException
import com.facebook.react.uimanager.JSPointerDispatcher
import com.facebook.react.uimanager.JSTouchDispatcher
import com.facebook.react.uimanager.RootViewUtil
import com.facebook.react.uimanager.common.UIManagerType
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.systrace.Systrace
import java.util.Objects

/** A view created by [ReactSurface] that's responsible for rendering a React component. */
public class ReactSurfaceView(context: Context?, private val surface: ReactSurfaceImpl) :
ReactRootView(context) {
private val jsTouchDispatcher: JSTouchDispatcher = JSTouchDispatcher(this)
private var jsPointerDispatcher: JSPointerDispatcher? = null
private var wasMeasured = false
private var widthMeasureSpec = 0
private var heightMeasureSpec = 0

init {
if (ReactFeatureFlags.dispatchPointerEvents) {
jsPointerDispatcher = JSPointerDispatcher(this)
}
// Make sure event dispatchers are created
onAttachedToReactInstance()
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
Expand Down Expand Up @@ -68,7 +60,7 @@ public class ReactSurfaceView(context: Context?, private val surface: ReactSurfa
wasMeasured = true
this.widthMeasureSpec = widthMeasureSpec
this.heightMeasureSpec = heightMeasureSpec
val viewportOffset = viewportOffset
val viewportOffset = RootViewUtil.getViewportOffset(this)
surface.updateLayoutSpecs(
widthMeasureSpec, heightMeasureSpec, viewportOffset.x, viewportOffset.y)
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE)
Expand All @@ -77,46 +69,18 @@ public class ReactSurfaceView(context: Context?, private val surface: ReactSurfa
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
// Call updateLayoutSpecs to update locationOnScreen offsets, in case they've changed
if (wasMeasured && changed) {
val viewportOffset = viewportOffset
val viewportOffset = RootViewUtil.getViewportOffset(this)
surface.updateLayoutSpecs(
widthMeasureSpec, heightMeasureSpec, viewportOffset.x, viewportOffset.y)
}
}

private val viewportOffset: Point
get() {
val locationOnScreen = IntArray(2)
getLocationOnScreen(locationOnScreen)

// we need to subtract visibleWindowCoords - to subtract possible window insets, split
// screen or multi window
val visibleWindowFrame = Rect()
getWindowVisibleDisplayFrame(visibleWindowFrame)
locationOnScreen[0] -= visibleWindowFrame.left
locationOnScreen[1] -= visibleWindowFrame.top
return Point(locationOnScreen[0], locationOnScreen[1])
override fun getEventDispatcher(): EventDispatcher? {
val eventDispatcher = surface.getEventDispatcher()
if (eventDispatcher == null) {
FLog.w(TAG, "Unable to dispatch events to JS as the React instance has not been attached")
}

override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
// Override in order to still receive events to onInterceptTouchEvent even when some other
// views disallow that, but propagate it up the tree if possible.
parent?.requestDisallowInterceptTouchEvent(disallowIntercept)
}

/**
* Called when a child starts a native gesture (e.g. a scroll in a ScrollView). Should be called
* from the child's onTouchIntercepted implementation.
*/
override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
val eventDispatcher = surface.eventDispatcher ?: return
jsTouchDispatcher.onChildStartedNativeGesture(ev, eventDispatcher)
childView?.let { jsPointerDispatcher?.onChildStartedNativeGesture(it, ev, eventDispatcher) }
}

override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
val eventDispatcher = surface.eventDispatcher ?: return
jsTouchDispatcher.onChildEndedNativeGesture(ev, eventDispatcher)
jsPointerDispatcher?.onChildEndedNativeGesture()
return eventDispatcher
}

override fun handleException(t: Throwable) {
Expand All @@ -134,34 +98,6 @@ public class ReactSurfaceView(context: Context?, private val surface: ReactSurfa
// This surface view is always on Fabric.
@UIManagerType override fun getUIManagerType(): Int = UIManagerType.FABRIC

override fun dispatchJSTouchEvent(event: MotionEvent) {
val eventDispatcher = surface.eventDispatcher
if (eventDispatcher != null) {
jsTouchDispatcher.handleTouchEvent(event, eventDispatcher)
} else {
FLog.w(
TAG, "Unable to dispatch touch events to JS as the React instance has not been attached")
}
}

override fun dispatchJSPointerEvent(event: MotionEvent, isCapture: Boolean) {
if (jsPointerDispatcher == null) {
if (!ReactFeatureFlags.dispatchPointerEvents) {
return
}
FLog.w(TAG, "Unable to dispatch pointer events to JS before the dispatcher is available")
return
}
val eventDispatcher = surface.eventDispatcher
if (eventDispatcher != null) {
jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, isCapture)
} else {
FLog.w(
TAG,
"Unable to dispatch pointer events to JS as the React instance has not been attached")
}
}

override fun hasActiveReactContext(): Boolean =
surface.isAttached && surface.reactHost.currentReactContext != null

Expand Down