From 22de6550d63c16066e2fbd79906d255ff8c8fea1 Mon Sep 17 00:00:00 2001 From: "Andrew Y. Chen" Date: Mon, 29 Aug 2016 12:23:09 -0700 Subject: [PATCH] Add onContentSizeChange prop to WebView Summary: Added support for WebViews to take in an onContentSizeChange prop, which will return a native event that contains the width and height of the html content in the WebView. Also moved the ContentSizeChangeEvent from the recyclerview dir to the uimanager/events dir Reviewed By: andreicoman11 Differential Revision: D3775399 fbshipit-source-id: 19a0579f8345e5853cc7311b80f1f1393c77ab58 --- .../Components/WebView/WebView.android.js | 2 + .../events}/ContentSizeChangeEvent.java | 7 +- .../RecyclerViewBackedScrollView.java | 2 +- .../views/webview/ReactWebViewManager.java | 64 +++++++++++++------ 4 files changed, 51 insertions(+), 24 deletions(-) rename ReactAndroid/src/main/java/com/facebook/react/{views/recyclerview => uimanager/events}/ContentSizeChangeEvent.java (78%) diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js index 9e7b11cd4730c8..9be32cc2caad6e 100644 --- a/Libraries/Components/WebView/WebView.android.js +++ b/Libraries/Components/WebView/WebView.android.js @@ -58,6 +58,7 @@ class WebView extends React.Component { automaticallyAdjustContentInsets: PropTypes.bool, contentInset: EdgeInsetsPropType, onNavigationStateChange: PropTypes.func, + onContentSizeChange: PropTypes.func, startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load style: View.propTypes.style, @@ -219,6 +220,7 @@ class WebView extends React.Component { domStorageEnabled={this.props.domStorageEnabled} contentInset={this.props.contentInset} automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets} + onContentSizeChange={this.props.onContentSizeChange} onLoadingStart={this.onLoadingStart} onLoadingFinish={this.onLoadingFinish} onLoadingError={this.onLoadingError} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/ContentSizeChangeEvent.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ContentSizeChangeEvent.java similarity index 78% rename from ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/ContentSizeChangeEvent.java rename to ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ContentSizeChangeEvent.java index d7ec7e48f8f0ab..03cc05a25c1980 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/ContentSizeChangeEvent.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/ContentSizeChangeEvent.java @@ -1,16 +1,13 @@ // Copyright 2004-present Facebook. All Rights Reserved. -package com.facebook.react.views.recyclerview; +package com.facebook.react.uimanager.events; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; /** - * Event dispatched by {@link RecyclerViewBackedScrollView} when total height of it's children - * changes + * Event dispatched when total width or height of a view's children changes */ public class ContentSizeChangeEvent extends Event { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java index a9d64b95acca4e..d823ac88bf3938 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java @@ -16,6 +16,7 @@ import com.facebook.react.bridge.ReactContext; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.events.ContentSizeChangeEvent; import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.views.scroll.ScrollEvent; import com.facebook.react.views.scroll.ScrollEventType; @@ -174,7 +175,6 @@ public int getTopOffsetForItem(int index) { } return mOffsetForLastPosition; } - } /*package*/ static class ReactListAdapter extends Adapter { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index 2eb92ba77a5f21..efe7bd7d25f4f5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -15,18 +15,18 @@ import java.util.HashMap; import java.util.Map; +import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.Picture; +import android.net.Uri; import android.os.Build; import android.text.TextUtils; import android.view.ViewGroup.LayoutParams; import android.webkit.GeolocationPermissions; +import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; -import android.webkit.WebChromeClient; -import com.facebook.react.views.webview.events.TopLoadingErrorEvent; -import com.facebook.react.views.webview.events.TopLoadingFinishEvent; -import com.facebook.react.views.webview.events.TopLoadingStartEvent; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactContext; @@ -40,10 +40,12 @@ import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.annotations.ReactProp; +import com.facebook.react.uimanager.events.ContentSizeChangeEvent; import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.EventDispatcher; -import android.content.Intent; -import android.net.Uri; +import com.facebook.react.views.webview.events.TopLoadingErrorEvent; +import com.facebook.react.views.webview.events.TopLoadingFinishEvent; +import com.facebook.react.views.webview.events.TopLoadingStartEvent; /** * Manages instances of {@link WebView} @@ -85,6 +87,7 @@ public class ReactWebViewManager extends SimpleViewManager { private static final String BLANK_URL = "about:blank"; private WebViewConfig mWebViewConfig; + private @Nullable WebView.PictureListener mPictureListener; private static class ReactWebViewClient extends WebViewClient { @@ -118,11 +121,11 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("http://") || url.startsWith("https://")) { return false; } else { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - view.getContext().startActivity(intent); - return true; - } + view.getContext().startActivity(intent); + return true; + } } @Override @@ -166,13 +169,6 @@ private void emitFinishEvent(WebView webView, String url) { createWebViewEvent(webView, url))); } - private static void dispatchEvent(WebView webView, Event event) { - ReactContext reactContext = (ReactContext) webView.getContext(); - EventDispatcher eventDispatcher = - reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); - eventDispatcher.dispatchEvent(event); - } - private WritableMap createWebViewEvent(WebView webView, String url) { WritableMap event = Arguments.createMap(); event.putDouble("target", webView.getId()); @@ -295,7 +291,6 @@ public void setDomStorageEnabled(WebView view, boolean enabled) { view.getSettings().setDomStorageEnabled(enabled); } - @ReactProp(name = "userAgent") public void setUserAgent(WebView view, @Nullable String userAgent) { if (userAgent != null) { @@ -368,6 +363,15 @@ public void setSource(WebView view, @Nullable ReadableMap source) { view.loadUrl(BLANK_URL); } + @ReactProp(name = "onContentSizeChange") + public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) { + if (sendContentSizeChangeEvents) { + view.setPictureListener(getPictureListener()); + } else { + view.setPictureListener(null); + } + } + @Override protected void addEventEmitters(ThemedReactContext reactContext, WebView view) { // Do not register default touch emitter and let WebView implementation handle touches @@ -407,4 +411,28 @@ public void onDropViewInstance(WebView webView) { ((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((ReactWebView) webView); ((ReactWebView) webView).cleanupCallbacksAndDestroy(); } + + private WebView.PictureListener getPictureListener() { + if (mPictureListener == null) { + mPictureListener = new WebView.PictureListener() { + @Override + public void onNewPicture(WebView webView, Picture picture) { + dispatchEvent( + webView, + new ContentSizeChangeEvent( + webView.getId(), + webView.getWidth(), + webView.getContentHeight())); + } + }; + } + return mPictureListener; + } + + private static void dispatchEvent(WebView webView, Event event) { + ReactContext reactContext = (ReactContext) webView.getContext(); + EventDispatcher eventDispatcher = + reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); + eventDispatcher.dispatchEvent(event); + } }