Android: Support HTTP headers for source prop on <Image> components

Summary:
A copy of https://github.com/facebook/react-native/pull/7791 because of our very imperfect tools that mirror the changes from pull requests in the fb monorepo. The internal Phabricator revision for #7791 is in an 'abandoned' state (by foghina probably because of changing teams) and Phabricator doesn't allow me to claim that revision and merge it. Therefore I'm creating a new one.

(It's not foghina's fault, no one probably knew about this "abandoned Phabricator revision" edge case, don't remember we hit it before.)

Will try to keep attribution (git blame) to rigdern when merging.
Closes https://github.com/facebook/react-native/pull/12448

Differential Revision: D4584743

Pulled By: mkonicek

fbshipit-source-id: 66e5b88134fca1980adc4cd8a2ff17c42e10022c
This commit is contained in:
Martin Konicek 2017-02-18 04:33:59 -08:00 committed by Facebook Github Bot
parent c7f2c53fbf
commit 8c0e6ecfc0
13 changed files with 180 additions and 20 deletions

View File

@ -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,
},

View File

@ -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'

View File

@ -28,6 +28,8 @@ import com.facebook.react.modules.common.ModuleDataCleaner;
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 class FrescoModule extends ReactContextBaseJavaModule implements
HashSet<RequestListener> 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);
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* <p/>
* 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;
}
}

View File

@ -0,0 +1,81 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* <p/>
* 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<String, String> getHeaders(ReadableMap readableMap) {
if (readableMap == null) {
return null;
}
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
Map<String, String> 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<String, String> 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);
}
}

View File

@ -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'),
],

View File

@ -21,6 +21,7 @@ import com.facebook.drawee.backends.pipeline.Fresco;
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 class ReactImageManager extends SimpleViewManager<ReactImageView> {
view.setShouldNotifyLoadEvents(shouldNotifyLoadEvents);
}
@ReactProp(name = "headers")
public void setHeaders(ReactImageView view, ReadableMap headers) {
view.setHeaders(headers);
}
@Override
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
return MapBuilder.of(

View File

@ -54,6 +54,7 @@ import com.facebook.react.bridge.ReactContext;
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 class ReactImageView extends GenericDraweeView {
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 @@ public class ReactImageView extends GenericDraweeView {
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 class ReactImageView extends GenericDraweeView {
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();

View File

@ -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'),

View File

@ -25,6 +25,7 @@ import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
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 @@ import com.facebook.react.views.text.TextInlineImageSpan;
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 class FrescoBasedReactTextInlineImageShadowNode extends ReactTextInlineIm
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 class FrescoBasedReactTextInlineImageShadowNode extends ReactTextInlineIm
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 class FrescoBasedReactTextInlineImageShadowNode extends ReactTextInlineIm
height,
width,
getUri(),
getHeaders(),
getDraweeControllerBuilder(),
getCallerContext());
}

View File

@ -25,7 +25,9 @@ import com.facebook.drawee.interfaces.DraweeController;
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 <Text/>. 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 class FrescoBasedReactTextInlineImageSpan extends TextInlineImageSpan {
int height,
int width,
@Nullable Uri uri,
ReadableMap headers,
AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
mDraweeHolder = new DraweeHolder(
@ -68,6 +72,7 @@ public class FrescoBasedReactTextInlineImageSpan extends TextInlineImageSpan {
mHeight = height;
mWidth = width;
mUri = (uri != null) ? uri : Uri.EMPTY;
mHeaders = headers;
}
/**
@ -126,8 +131,8 @@ public class FrescoBasedReactTextInlineImageSpan extends TextInlineImageSpan {
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()

View File

@ -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'),

View File

@ -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',
)