diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m index 5a7cc12bd28eee..d71a9446882efe 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m @@ -49,7 +49,7 @@ - (void)testImageLoading NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader]; } launchOptions:nil]; - [bridge.imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { + [bridge.imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" headers:nil size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); } completionBlock:^(NSError *loadError, id loadedImage) { @@ -79,7 +79,7 @@ - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2]; } launchOptions:nil]; - [bridge.imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { + [bridge.imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" headers:nil size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); } completionBlock:^(NSError *loadError, id loadedImage) { diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index 3ec42358cc65bf..ecc684f38ea13f 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -71,10 +71,14 @@ 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. */ source: PropTypes.oneOfType([ PropTypes.shape({ uri: PropTypes.string, + headers: PropTypes.objectOf(PropTypes.string), }), // Opaque type returned by require('./image.jpg') PropTypes.number, @@ -184,6 +188,7 @@ var Image = React.createClass({ style, shouldNotifyLoadEvents: !!(onLoadStart || onLoad || onLoadEnd), src: source.uri, + headers: source.headers, loadingIndicatorSrc: loadingIndicatorSource ? loadingIndicatorSource.uri : null, }); @@ -227,6 +232,7 @@ var styles = StyleSheet.create({ var cfg = { nativeOnly: { src: true, + headers: true, loadingIndicatorSrc: true, defaultImageSrc: true, imageTag: true, diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index 7c496656869bf2..34cf2e1c3db42f 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -63,10 +63,14 @@ 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 the name of 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. */ source: PropTypes.oneOfType([ PropTypes.shape({ uri: PropTypes.string, + headers: PropTypes.objectOf(PropTypes.string), }), // Opaque type returned by require('./image.jpg') PropTypes.number, diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h index d11c424103d3ea..5d86bdab06041f 100644 --- a/Libraries/Image/RCTImageLoader.h +++ b/Libraries/Image/RCTImageLoader.h @@ -64,6 +64,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void); * select the optimal dimensions for the loaded image. */ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag + headers:(NSDictionary *)headers size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode @@ -74,6 +75,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void); * Loads an image without clipping the result to fit - used by RCTImageView. */ - (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag + headers:(NSDictionary *)headers size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index 8349a2e2b1a162..7aafd9be46b25f 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -202,6 +202,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag callback:(RCTImageLoaderCompletionBlock)callback { return [self loadImageWithTag:imageTag + headers:nil size:CGSizeZero scale:1 resizeMode:RCTResizeModeStretch @@ -259,6 +260,7 @@ - (void)dequeueTasks * the image, or retrieving metadata. */ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag + headers:(NSDictionary *) headers size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode @@ -307,7 +309,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag } // Find suitable image URL loader - NSURLRequest *request = [RCTConvert NSURLRequest:imageTag]; + NSMutableURLRequest *request = [[RCTConvert NSURLRequest:imageTag] mutableCopy]; id loadHandler = [strongSelf imageURLLoaderForURL:request.URL]; if (loadHandler) { cancelLoad = [loadHandler loadImageForURL:request.URL @@ -363,9 +365,13 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag // Add missing png extension if (request.URL.fileURL && request.URL.pathExtension.length == 0) { - NSMutableURLRequest *mutableRequest = [request mutableCopy]; - mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]]; - request = mutableRequest; + request.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]]; + } + + if (headers != nil){ + for (NSString* key in headers) { + [request addValue:[headers objectForKey:key] forHTTPHeaderField:key]; + } } // Check for cached response before reloading @@ -383,7 +389,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag } NSURL *redirectURL = [NSURL URLWithString: location]; - request = [NSURLRequest requestWithURL: redirectURL]; + request = [NSMutableURLRequest requestWithURL:redirectURL]; cachedResponse = [_URLCache cachedResponseForRequest:request]; continue; } @@ -447,6 +453,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag } - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag + headers:(NSDictionary *)headers size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode @@ -454,6 +461,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag completionBlock:(RCTImageLoaderCompletionBlock)completionBlock { return [self loadImageWithoutClipping:imageTag + headers:headers size:size scale:scale resizeMode:resizeMode @@ -464,6 +472,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag } - (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag + headers:(NSDictionary*)headers size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode @@ -489,6 +498,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag }; cancelLoad = [self loadImageOrDataWithTag:imageTag + headers:headers size:size scale:scale resizeMode:resizeMode @@ -631,6 +641,7 @@ - (RCTImageLoaderCancellationBlock)getImageSize:(NSString *)imageTag block:(void(^)(NSError *error, CGSize size))completionBlock { return [self loadImageOrDataWithTag:imageTag + headers:nil size:CGSizeZero scale:1 resizeMode:RCTResizeModeStretch diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m index b78ce896ce291a..b5db3fa8bcbb62 100644 --- a/Libraries/Image/RCTImageView.m +++ b/Libraries/Image/RCTImageView.m @@ -203,6 +203,7 @@ - (void)reloadImage CGFloat blurRadius = _blurRadius; __weak RCTImageView *weakSelf = self; _reloadImageCancellationBlock = [_bridge.imageLoader loadImageWithoutClipping:_source.imageURL.absoluteString + headers:_source.headers size:imageSize scale:imageScale resizeMode:(RCTResizeMode)self.contentMode diff --git a/Libraries/Image/RCTShadowVirtualImage.m b/Libraries/Image/RCTShadowVirtualImage.m index 757a48c24ea056..f05838937d342c 100644 --- a/Libraries/Image/RCTShadowVirtualImage.m +++ b/Libraries/Image/RCTShadowVirtualImage.m @@ -54,6 +54,7 @@ - (void)didSetProps:(NSArray *)changedProps __weak RCTShadowVirtualImage *weakSelf = self; _cancellationBlock = [_bridge.imageLoader loadImageWithTag:_source.imageURL.absoluteString + headers:_source.headers size:imageSize scale:RCTScreenScale() resizeMode:_resizeMode diff --git a/React/Base/RCTImageSource.h b/React/Base/RCTImageSource.h index efcefaa2b89fba..58fae43033a352 100644 --- a/React/Base/RCTImageSource.h +++ b/React/Base/RCTImageSource.h @@ -17,6 +17,7 @@ @interface RCTImageSource : NSObject @property (nonatomic, strong, readonly) NSURL *imageURL; +@property (nonatomic, strong, readonly) NSDictionary *headers; @property (nonatomic, assign, readonly) CGSize size; @property (nonatomic, assign, readonly) CGFloat scale; @@ -26,6 +27,7 @@ * size. Pass a scale of zero if you do not know or wish to specify the scale. */ - (instancetype)initWithURL:(NSURL *)url + headers:(NSDictionary *)headers size:(CGSize)size scale:(CGFloat)scale; diff --git a/React/Base/RCTImageSource.m b/React/Base/RCTImageSource.m index 07518edf928a4d..a2cb2b5ad1048f 100644 --- a/React/Base/RCTImageSource.m +++ b/React/Base/RCTImageSource.m @@ -18,10 +18,11 @@ @interface RCTImageSource () @implementation RCTImageSource -- (instancetype)initWithURL:(NSURL *)url size:(CGSize)size scale:(CGFloat)scale +- (instancetype)initWithURL:(NSURL *)url headers:(NSDictionary *)headers size:(CGSize)size scale:(CGFloat)scale { if ((self = [super init])) { _imageURL = url; + _headers = headers; _size = size; _scale = scale; } @@ -31,6 +32,7 @@ - (instancetype)initWithURL:(NSURL *)url size:(CGSize)size scale:(CGFloat)scale - (instancetype)imageSourceWithSize:(CGSize)size scale:(CGFloat)scale { RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURL:_imageURL + headers:_headers size:size scale:scale]; imageSource.packagerAsset = _packagerAsset; @@ -57,6 +59,7 @@ + (RCTImageSource *)RCTImageSource:(id)json } NSURL *imageURL; + NSDictionary *headers = nil; CGSize size = CGSizeZero; CGFloat scale = 1.0; BOOL packagerAsset = NO; @@ -64,6 +67,23 @@ + (RCTImageSource *)RCTImageSource:(id)json if (!(imageURL = [self NSURL:RCTNilIfNull(json[@"uri"])])) { return nil; } + headers = [self NSDictionary:RCTNilIfNull(json[@"headers"])]; +#if RCT_DEBUG + if (headers != nil) { + BOOL allHeadersAreStrings = YES; + for (NSString *key in headers) { + if (![[headers objectForKey:key] isKindOfClass:[NSString class]]) { + RCTLogError(@"Values of HTTP headers passed to an component must be" + " of type string. Value of header '%@' is not a string.", key); + allHeadersAreStrings = NO; + } + } + if (!allHeadersAreStrings) { + // Set headers to nil here to avoid crashing later. + headers = nil; + } + } +#endif size = [self CGSize:json]; scale = [self CGFloat:json[@"scale"]] ?: [self BOOL:json[@"deprecated"]] ? 0.0 : 1.0; packagerAsset = [self BOOL:json[@"__packager_asset"]]; @@ -75,6 +95,7 @@ + (RCTImageSource *)RCTImageSource:(id)json } RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURL:imageURL + headers:headers size:size scale:scale]; imageSource.packagerAsset = packagerAsset; 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 3f2302ab9ab4d9..eb42cd3d0b7f9f 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 @@ -19,7 +19,6 @@ import com.facebook.common.internal.AndroidPredicates; import com.facebook.common.soloader.SoLoaderShim; import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.imagepipeline.backends.okhttp.OkHttpImagePipelineConfigFactory; import com.facebook.imagepipeline.core.ImagePipelineConfig; import com.facebook.imagepipeline.core.ImagePipelineFactory; import com.facebook.imagepipeline.listener.RequestListener; @@ -99,8 +98,8 @@ private static ImagePipelineConfig getDefaultConfig( } OkHttpClient okHttpClient = OkHttpClientProvider.getOkHttpClient(); - ImagePipelineConfig.Builder builder = - OkHttpImagePipelineConfigFactory.newBuilder(context.getApplicationContext(), okHttpClient); + ImagePipelineConfig.Builder builder = ImagePipelineConfig.newBuilder(context) + .setNetworkFetcher(new NetworkFetcher(okHttpClient)); builder .setDownsampleEnabled(false) diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/NetworkFetcher.java b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/NetworkFetcher.java new file mode 100644 index 00000000000000..3179aea577a366 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/NetworkFetcher.java @@ -0,0 +1,147 @@ +/** + * 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 java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +import android.net.Uri; +import android.os.Looper; +import android.os.SystemClock; + +import com.facebook.common.logging.FLog; +import com.facebook.imagepipeline.backends.okhttp.OkHttpNetworkFetcher; +import com.facebook.imagepipeline.producers.BaseProducerContextCallbacks; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableMapKeySetIterator; +import com.squareup.okhttp.CacheControl; +import com.squareup.okhttp.Call; +import com.squareup.okhttp.Headers; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.ResponseBody; + +class NetworkFetcher extends OkHttpNetworkFetcher { + + private static final String TAG = "NetworkFetcher"; + + private final OkHttpClient mOkHttpClient; + private final Executor mCancellationExecutor; + + /** + * @param okHttpClient client to use + */ + public NetworkFetcher(OkHttpClient okHttpClient) { + super(okHttpClient); + mOkHttpClient = okHttpClient; + mCancellationExecutor = okHttpClient.getDispatcher().getExecutorService(); + } + + 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 NetworkImageRequest) { + NetworkImageRequest networkImageRequest = (NetworkImageRequest) + 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(); + final Call call = mOkHttpClient.newCall(request); + + fetchState.getContext().addCallbacks( + new BaseProducerContextCallbacks() { + @Override + public void onCancellationRequested() { + if (Looper.myLooper() != Looper.getMainLooper()) { + call.cancel(); + } else { + mCancellationExecutor.execute(new Runnable() { + @Override + public void run() { + call.cancel(); + } + }); + } + } + }); + + call.enqueue( + new com.squareup.okhttp.Callback() { + @Override + public void onResponse(Response response) { + fetchState.responseTime = SystemClock.elapsedRealtime(); + final ResponseBody body = response.body(); + try { + long contentLength = body.contentLength(); + if (contentLength < 0) { + contentLength = 0; + } + callback.onResponse(body.byteStream(), (int) contentLength); + } catch (Exception e) { + handleException(call, e, callback); + } finally { + try { + body.close(); + } catch (Exception e) { + FLog.w(TAG, "Exception when closing response body", e); + } + } + } + + @Override + public void onFailure(final Request request, final IOException e) { + handleException(call, e, callback); + } + }); + } + + /** + * Handles exceptions. + * + *

OkHttp notifies callers of cancellations via an IOException. If IOException is caught + * after request cancellation, then the exception is interpreted as successful cancellation + * and onCancellation is called. Otherwise onFailure is called. + */ + private void handleException(final Call call, final Exception e, final Callback callback) { + if (call.isCanceled()) { + callback.onCancellation(); + } else { + callback.onFailure(e); + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/NetworkImageRequest.java b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/NetworkImageRequest.java new file mode 100644 index 00000000000000..d5aa52ad0c94b0 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/NetworkImageRequest.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 NetworkImageRequest extends ImageRequest { + + /** Headers for the request */ + private final ReadableMap mHeaders; + + public static NetworkImageRequest fromBuilder(ImageRequestBuilder builder, + ReadableMap headers) { + return new NetworkImageRequest(builder, headers); + } + + protected NetworkImageRequest(ImageRequestBuilder builder, ReadableMap headers) { + super(builder); + this.mHeaders = headers; + } + + public ReadableMap getHeaders() { + return mHeaders; + } +} 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 4ab1fc6f5deb59..4680b3854ff843 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK @@ -9,6 +9,7 @@ android_library( react_native_target('java/com/facebook/csslayout:csslayout'), 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/modules/fresco:fresco'), react_native_dep('libraries/fresco/fresco-react-native:fbcore'), react_native_dep('libraries/fresco/fresco-react-native:fresco-react-native'), react_native_dep('libraries/fresco/fresco-react-native:fresco-drawee'), 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 eaee5053a74d07..07c5ad1413d4dc 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 @@ -19,6 +19,7 @@ import com.facebook.csslayout.CSSConstants; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.PixelUtil; @@ -157,6 +158,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 7096fc3acd9ba8..b24ec51ee1803a 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 @@ -46,6 +46,8 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.facebook.imagepipeline.request.Postprocessor; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.modules.fresco.NetworkImageRequest; import com.facebook.react.common.SystemClock; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.UIManagerModule; @@ -152,6 +154,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) { @@ -303,6 +306,10 @@ private void cornerRadii(float[] computedCorners) { computedCorners[2] = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius; computedCorners[3] = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius; } + + public void setHeaders(ReadableMap headers) { + mHeaders = headers; + } public void maybeUpdateView() { if (!mIsDirty) { @@ -353,12 +360,13 @@ public void maybeUpdateView() { ResizeOptions resizeOptions = doResize ? new ResizeOptions(getWidth(), getHeight()) : null; - ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(mUri) + ImageRequestBuilder imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(mUri) .setPostprocessor(postprocessor) .setResizeOptions(resizeOptions) .setAutoRotateEnabled(true) - .setProgressiveRenderingEnabled(mProgressiveRenderingEnabled) - .build(); + .setProgressiveRenderingEnabled(mProgressiveRenderingEnabled); + + ImageRequest imageRequest = NetworkImageRequest.fromBuilder(imageRequestBuilder, mHeaders); // This builder is reused mDraweeControllerBuilder.reset();