diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index c56fa2ce11a68b..b0e813b3fd8b6e 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -83,6 +83,10 @@ var Image = React.createClass({ * `uri` is a string representing the resource identifier for the image, which * could be an http address, a local file path, or a static image * resource (which should be wrapped in the `require('./path/to/image.png')` function). + * + * `headers` is an object representing the HTTP headers to send along with the request + * for a remote image. + * * This prop can also contain several remote `uri`, specified together with * their width and height. The native side will then choose the best `uri` to display * based on the measured size of the image container. @@ -90,6 +94,7 @@ var Image = React.createClass({ source: PropTypes.oneOfType([ PropTypes.shape({ uri: PropTypes.string, + headers: PropTypes.objectOf(PropTypes.string), }), // Opaque type returned by require('./image.jpg') PropTypes.number, @@ -300,6 +305,7 @@ var Image = React.createClass({ style, shouldNotifyLoadEvents: !!(onLoadStart || onLoad || onLoadEnd || onError), src: sources, + headers: source.headers, loadingIndicatorSrc: loadingIndicatorSource ? loadingIndicatorSource.uri : null, }); @@ -346,6 +352,7 @@ var styles = StyleSheet.create({ var cfg = { nativeOnly: { src: true, + headers: true, loadingIndicatorSrc: true, shouldNotifyLoadEvents: true, }, diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 0d4d46d3faf40b..132bd7e6782652 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -277,8 +277,8 @@ dependencies { compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:recyclerview-v7:23.4.0' compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0' - compile 'com.facebook.fresco:fresco:0.11.0' - compile 'com.facebook.fresco:imagepipeline-okhttp3:0.11.0' + compile 'com.facebook.fresco:fresco:1.0.1' + compile 'com.facebook.fresco:imagepipeline-okhttp3:1.0.1' compile 'com.facebook.soloader:soloader:0.1.0' compile 'com.google.code.findbugs:jsr305:3.0.0' compile 'com.squareup.okhttp3:okhttp:3.4.1' diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java index 36cced20e6a314..af3c791ef8a5eb 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java @@ -28,6 +28,8 @@ import com.facebook.react.modules.network.OkHttpClientProvider; import com.facebook.soloader.SoLoader; +import okhttp3.OkHttpClient; + /** * Module to initialize the Fresco library. * @@ -124,8 +126,10 @@ public static ImagePipelineConfig.Builder getDefaultConfigBuilder(Context contex HashSet requestListeners = new HashSet<>(); requestListeners.add(new SystraceRequestListener()); + OkHttpClient okHttpClient = OkHttpClientProvider.getOkHttpClient(); return OkHttpImagePipelineConfigFactory - .newBuilder(context.getApplicationContext(), OkHttpClientProvider.getOkHttpClient()) + .newBuilder(context.getApplicationContext(), okHttpClient) + .setNetworkFetcher(new ReactOkHttpNetworkFetcher(okHttpClient)) .setDownsampleEnabled(false) .setRequestListeners(requestListeners); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactNetworkImageRequest.java b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactNetworkImageRequest.java new file mode 100644 index 00000000000000..a2bdfca5ba446b --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactNetworkImageRequest.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + *

+ * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.modules.fresco; + +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; +import com.facebook.react.bridge.ReadableMap; + +/** Extended ImageRequest with request headers */ +public class ReactNetworkImageRequest extends ImageRequest { + + /** Headers for the request */ + private final ReadableMap mHeaders; + + public static ReactNetworkImageRequest fromBuilderWithHeaders(ImageRequestBuilder builder, + ReadableMap headers) { + return new ReactNetworkImageRequest(builder, headers); + } + + protected ReactNetworkImageRequest(ImageRequestBuilder builder, ReadableMap headers) { + super(builder); + this.mHeaders = headers; + } + + public ReadableMap getHeaders() { + return mHeaders; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactOkHttpNetworkFetcher.java b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactOkHttpNetworkFetcher.java new file mode 100644 index 00000000000000..9b5b379c505f90 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/ReactOkHttpNetworkFetcher.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + *

+ * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.modules.fresco; + +import android.net.Uri; +import android.os.SystemClock; + +import com.facebook.imagepipeline.backends.okhttp3.OkHttpNetworkFetcher; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableMapKeySetIterator; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +import okhttp3.CacheControl; +import okhttp3.Headers; +import okhttp3.OkHttpClient; +import okhttp3.Request; + +class ReactOkHttpNetworkFetcher extends OkHttpNetworkFetcher { + + private static final String TAG = "ReactOkHttpNetworkFetcher"; + + private final OkHttpClient mOkHttpClient; + private final Executor mCancellationExecutor; + + /** + * @param okHttpClient client to use + */ + public ReactOkHttpNetworkFetcher(OkHttpClient okHttpClient) { + super(okHttpClient); + mOkHttpClient = okHttpClient; + mCancellationExecutor = okHttpClient.dispatcher().executorService(); + } + + private Map getHeaders(ReadableMap readableMap) { + if (readableMap == null) { + return null; + } + ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); + Map map = new HashMap<>(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + String value = readableMap.getString(key); + map.put(key, value); + } + return map; + } + + @Override + public void fetch(final OkHttpNetworkFetchState fetchState, final Callback callback) { + fetchState.submitTime = SystemClock.elapsedRealtime(); + final Uri uri = fetchState.getUri(); + Map requestHeaders = null; + if (fetchState.getContext().getImageRequest() instanceof ReactNetworkImageRequest) { + ReactNetworkImageRequest networkImageRequest = (ReactNetworkImageRequest) + fetchState.getContext().getImageRequest(); + requestHeaders = getHeaders(networkImageRequest.getHeaders()); + } + if (requestHeaders == null) { + requestHeaders = Collections.emptyMap(); + } + final Request request = new Request.Builder() + .cacheControl(new CacheControl.Builder().noStore().build()) + .url(uri.toString()) + .headers(Headers.of(requestHeaders)) + .get() + .build(); + + fetchWithRequest(fetchState, callback, request); + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK index 56e17c9732946a..310aaa80e37174 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK @@ -37,6 +37,7 @@ android_library( react_native_target('java/com/facebook/react/common:common'), react_native_target('java/com/facebook/react/module/annotations:annotations'), react_native_target('java/com/facebook/react/uimanager:uimanager'), + react_native_target('java/com/facebook/react/modules/fresco:fresco'), react_native_target('java/com/facebook/react/uimanager/annotations:annotations'), react_native_target('java/com/facebook/react/views/imagehelper:withmultisource'), ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java index a6fa2bb9c02726..2d5702389ea627 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java @@ -21,6 +21,7 @@ import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.PixelUtil; @@ -171,6 +172,11 @@ public void setLoadHandlersRegistered(ReactImageView view, boolean shouldNotifyL view.setShouldNotifyLoadEvents(shouldNotifyLoadEvents); } + @ReactProp(name = "headers") + public void setHeaders(ReactImageView view, ReadableMap headers) { + view.setHeaders(headers); + } + @Override public @Nullable Map getExportedCustomDirectEventTypeConstants() { return MapBuilder.of( diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java index a099d08a833e8f..e039a323a8afd8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java @@ -54,6 +54,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.FloatUtil; +import com.facebook.react.modules.fresco.ReactNetworkImageRequest; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.events.EventDispatcher; @@ -163,6 +164,7 @@ public void process(Bitmap output, Bitmap source) { private final @Nullable Object mCallerContext; private int mFadeDurationMs = -1; private boolean mProgressiveRenderingEnabled; + private ReadableMap mHeaders; // We can't specify rounding in XML, so have to do so here private static GenericDraweeHierarchy buildHierarchy(Context context) { @@ -324,6 +326,10 @@ private void cornerRadii(float[] computedCorners) { computedCorners[2] = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius; computedCorners[3] = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius; } + + public void setHeaders(ReadableMap headers) { + mHeaders = headers; + } public void maybeUpdateView() { if (!mIsDirty) { @@ -384,12 +390,13 @@ public void maybeUpdateView() { ResizeOptions resizeOptions = doResize ? new ResizeOptions(getWidth(), getHeight()) : null; - ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(mImageSource.getUri()) + ImageRequestBuilder imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(mImageSource.getUri()) .setPostprocessor(postprocessor) .setResizeOptions(resizeOptions) .setAutoRotateEnabled(true) - .setProgressiveRenderingEnabled(mProgressiveRenderingEnabled) - .build(); + .setProgressiveRenderingEnabled(mProgressiveRenderingEnabled); + + ImageRequest imageRequest = ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, mHeaders); // This builder is reused mDraweeControllerBuilder.reset(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK index 315a7957c1ccfc..280fca208ff318 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK @@ -14,6 +14,7 @@ android_library( react_native_target('java/com/facebook/react/bridge:bridge'), react_native_target('java/com/facebook/react/common:common'), react_native_target('java/com/facebook/react/module/annotations:annotations'), + react_native_target('java/com/facebook/react/modules/fresco:fresco'), react_native_target('java/com/facebook/react/uimanager:uimanager'), react_native_target('java/com/facebook/react/uimanager/annotations:annotations'), react_native_target('java/com/facebook/react/views/text:text'), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java index 786f3bfbac16f7..bd0d61ecd570f6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java @@ -25,6 +25,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableType; import com.facebook.react.uimanager.ViewProps; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.views.text.ReactTextInlineImageShadowNode; import com.facebook.react.views.text.TextInlineImageSpan; @@ -36,6 +37,7 @@ public class FrescoBasedReactTextInlineImageShadowNode extends ReactTextInlineImageShadowNode { private @Nullable Uri mUri; + private ReadableMap mHeaders; private final AbstractDraweeControllerBuilder mDraweeControllerBuilder; private final @Nullable Object mCallerContext; private float mWidth = YogaConstants.UNDEFINED; @@ -73,6 +75,11 @@ public void setSource(@Nullable ReadableArray sources) { mUri = uri; } + @ReactProp(name = "headers") + public void setHeaders(ReadableMap headers) { + mHeaders = headers; + } + /** * Besides width/height, all other layout props on inline images are ignored */ @@ -100,6 +107,10 @@ public void setHeight(Dynamic height) { return mUri; } + public ReadableMap getHeaders() { + return mHeaders; + } + // TODO: t9053573 is tracking that this code should be shared private static @Nullable Uri getResourceDrawableUri(Context context, @Nullable String name) { if (name == null || name.isEmpty()) { @@ -131,6 +142,7 @@ public TextInlineImageSpan buildInlineImageSpan() { height, width, getUri(), + getHeaders(), getDraweeControllerBuilder(), getCallerContext()); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.java index 771fdcb742ab0c..0b8dcd19f4a0e6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.java @@ -25,7 +25,9 @@ import com.facebook.drawee.view.DraweeHolder; import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.ImageRequestBuilder; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.views.text.TextInlineImageSpan; +import com.facebook.react.modules.fresco.ReactNetworkImageRequest; /** * FrescoBasedTextInlineImageSpan is a span for Images that are inside . It computes @@ -48,6 +50,7 @@ public class FrescoBasedReactTextInlineImageSpan extends TextInlineImageSpan { private int mHeight; private Uri mUri; private int mWidth; + private ReadableMap mHeaders; private @Nullable TextView mTextView; @@ -56,6 +59,7 @@ public FrescoBasedReactTextInlineImageSpan( int height, int width, @Nullable Uri uri, + ReadableMap headers, AbstractDraweeControllerBuilder draweeControllerBuilder, @Nullable Object callerContext) { mDraweeHolder = new DraweeHolder( @@ -68,6 +72,7 @@ public FrescoBasedReactTextInlineImageSpan( mHeight = height; mWidth = width; mUri = (uri != null) ? uri : Uri.EMPTY; + mHeaders = headers; } /** @@ -126,8 +131,8 @@ public void draw( int bottom, Paint paint) { if (mDrawable == null) { - ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(mUri) - .build(); + ImageRequestBuilder imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(mUri); + ImageRequest imageRequest = ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, mHeaders); DraweeController draweeController = mDraweeControllerBuilder .reset() diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/BUCK index 2f37105e3b592e..c4337f18306e3b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/toolbar/BUCK @@ -6,6 +6,7 @@ android_library( deps = [ YOGA_TARGET, react_native_dep('android_res/com/facebook/catalyst/appcompat:appcompat'), + react_native_dep('libraries/fresco/fresco-react-native:fbcore'), react_native_dep('libraries/fresco/fresco-react-native:fresco-drawee'), react_native_dep('libraries/fresco/fresco-react-native:fresco-react-native'), react_native_dep('libraries/fresco/fresco-react-native:imagepipeline'), diff --git a/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK b/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK index 5f904283173335..18d0568153d93d 100644 --- a/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK +++ b/ReactAndroid/src/main/libraries/fresco/fresco-react-native/BUCK @@ -6,8 +6,8 @@ android_prebuilt_aar( remote_file( name = 'fresco-binary-aar', - url = 'mvn:com.facebook.fresco:fresco:aar:0.11.0', - sha1 = '86df1ab4b0074e1aeceb419593d2ea6d97cdc3b4', + url = 'mvn:com.facebook.fresco:fresco:aar:1.0.1', + sha1 = '87d86ce36812b7b859f6176e253b71b54d4a39e3', ) android_prebuilt_aar( @@ -18,8 +18,8 @@ android_prebuilt_aar( remote_file( name = 'drawee-binary-aar', - url = 'mvn:com.facebook.fresco:drawee:aar:0.11.0', - sha1 = '7c8a4d211b2334b6d52b9de614b52423b16f7704', + url = 'mvn:com.facebook.fresco:drawee:aar:1.0.1', + sha1 = '7eea1c7dd619e7621f6e818c007c30970ac31575', ) android_library( @@ -40,8 +40,8 @@ android_prebuilt_aar( remote_file( name = 'imagepipeline-base-aar', - url = 'mvn:com.facebook.fresco:imagepipeline-base:aar:0.11.0', - sha1 = '0f450cd58350ef5d6734f9772bc780b67cc538c5', + url = 'mvn:com.facebook.fresco:imagepipeline-base:aar:1.0.1', + sha1 = '44d5e4b7c07afaee610ea2dda29535455cd1a20e', ) android_prebuilt_aar( @@ -52,8 +52,8 @@ android_prebuilt_aar( remote_file( name = 'imagepipeline-aar', - url = 'mvn:com.facebook.fresco:imagepipeline:aar:0.11.0', - sha1 = 'd57b234db14899af8c36876d7937d63fddca5b12', + url = 'mvn:com.facebook.fresco:imagepipeline:aar:1.0.1', + sha1 = '78e637099db724c3963df4515d014c9d7232469e', ) prebuilt_jar( @@ -76,8 +76,8 @@ android_prebuilt_aar( remote_file( name = 'fbcore-aar', - url = 'mvn:com.facebook.fresco:fbcore:aar:0.11.0', - sha1 = 'e732e63ea19b19d053eebfe46870157ee79ad034', + url = 'mvn:com.facebook.fresco:fbcore:aar:1.0.1', + sha1 = '25cdfb603c04e96486446da625cf8a90666eb55f', ) android_prebuilt_aar( @@ -88,6 +88,6 @@ android_prebuilt_aar( remote_file( name = 'imagepipeline-okhttp3-binary-aar', - url = 'mvn:com.facebook.fresco:imagepipeline-okhttp3:aar:0.11.0', - sha1 = '3d7e6d1a2f2e973a596aae7d523667b5a3d6d8a5', + url = 'mvn:com.facebook.fresco:imagepipeline-okhttp3:aar:1.0.1', + sha1 = '361e123fd114481ee037199db21337f06994f36e', )