Skip to content

Commit 8c0e6ec

Browse files
Martin Konicekfacebook-github-bot
authored andcommitted
Android: Support HTTP headers for source prop on <Image> components
Summary: A copy of #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 #12448 Differential Revision: D4584743 Pulled By: mkonicek fbshipit-source-id: 66e5b88134fca1980adc4cd8a2ff17c42e10022c
1 parent c7f2c53 commit 8c0e6ec

13 files changed

Lines changed: 180 additions & 20 deletions

File tree

Libraries/Image/Image.android.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,18 @@ var Image = React.createClass({
8383
* `uri` is a string representing the resource identifier for the image, which
8484
* could be an http address, a local file path, or a static image
8585
* resource (which should be wrapped in the `require('./path/to/image.png')` function).
86+
*
87+
* `headers` is an object representing the HTTP headers to send along with the request
88+
* for a remote image.
89+
*
8690
* This prop can also contain several remote `uri`, specified together with
8791
* their width and height. The native side will then choose the best `uri` to display
8892
* based on the measured size of the image container.
8993
*/
9094
source: PropTypes.oneOfType([
9195
PropTypes.shape({
9296
uri: PropTypes.string,
97+
headers: PropTypes.objectOf(PropTypes.string),
9398
}),
9499
// Opaque type returned by require('./image.jpg')
95100
PropTypes.number,
@@ -300,6 +305,7 @@ var Image = React.createClass({
300305
style,
301306
shouldNotifyLoadEvents: !!(onLoadStart || onLoad || onLoadEnd || onError),
302307
src: sources,
308+
headers: source.headers,
303309
loadingIndicatorSrc: loadingIndicatorSource ? loadingIndicatorSource.uri : null,
304310
});
305311

@@ -346,6 +352,7 @@ var styles = StyleSheet.create({
346352
var cfg = {
347353
nativeOnly: {
348354
src: true,
355+
headers: true,
349356
loadingIndicatorSrc: true,
350357
shouldNotifyLoadEvents: true,
351358
},

ReactAndroid/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,8 @@ dependencies {
277277
compile 'com.android.support:appcompat-v7:23.0.1'
278278
compile 'com.android.support:recyclerview-v7:23.4.0'
279279
compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0'
280-
compile 'com.facebook.fresco:fresco:0.11.0'
281-
compile 'com.facebook.fresco:imagepipeline-okhttp3:0.11.0'
280+
compile 'com.facebook.fresco:fresco:1.0.1'
281+
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.0.1'
282282
compile 'com.facebook.soloader:soloader:0.1.0'
283283
compile 'com.google.code.findbugs:jsr305:3.0.0'
284284
compile 'com.squareup.okhttp3:okhttp:3.4.1'

ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.facebook.react.modules.network.OkHttpClientProvider;
2929
import com.facebook.soloader.SoLoader;
3030

31+
import okhttp3.OkHttpClient;
32+
3133
/**
3234
* Module to initialize the Fresco library.
3335
*
@@ -124,8 +126,10 @@ public static ImagePipelineConfig.Builder getDefaultConfigBuilder(Context contex
124126
HashSet<RequestListener> requestListeners = new HashSet<>();
125127
requestListeners.add(new SystraceRequestListener());
126128

129+
OkHttpClient okHttpClient = OkHttpClientProvider.getOkHttpClient();
127130
return OkHttpImagePipelineConfigFactory
128-
.newBuilder(context.getApplicationContext(), OkHttpClientProvider.getOkHttpClient())
131+
.newBuilder(context.getApplicationContext(), okHttpClient)
132+
.setNetworkFetcher(new ReactOkHttpNetworkFetcher(okHttpClient))
129133
.setDownsampleEnabled(false)
130134
.setRequestListeners(requestListeners);
131135
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
* <p/>
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
package com.facebook.react.modules.fresco;
11+
12+
import com.facebook.imagepipeline.request.ImageRequest;
13+
import com.facebook.imagepipeline.request.ImageRequestBuilder;
14+
import com.facebook.react.bridge.ReadableMap;
15+
16+
/** Extended ImageRequest with request headers */
17+
public class ReactNetworkImageRequest extends ImageRequest {
18+
19+
/** Headers for the request */
20+
private final ReadableMap mHeaders;
21+
22+
public static ReactNetworkImageRequest fromBuilderWithHeaders(ImageRequestBuilder builder,
23+
ReadableMap headers) {
24+
return new ReactNetworkImageRequest(builder, headers);
25+
}
26+
27+
protected ReactNetworkImageRequest(ImageRequestBuilder builder, ReadableMap headers) {
28+
super(builder);
29+
this.mHeaders = headers;
30+
}
31+
32+
public ReadableMap getHeaders() {
33+
return mHeaders;
34+
}
35+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
* <p/>
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
package com.facebook.react.modules.fresco;
11+
12+
import android.net.Uri;
13+
import android.os.SystemClock;
14+
15+
import com.facebook.imagepipeline.backends.okhttp3.OkHttpNetworkFetcher;
16+
import com.facebook.react.bridge.ReadableMap;
17+
import com.facebook.react.bridge.ReadableMapKeySetIterator;
18+
19+
import java.util.Collections;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.concurrent.Executor;
23+
24+
import okhttp3.CacheControl;
25+
import okhttp3.Headers;
26+
import okhttp3.OkHttpClient;
27+
import okhttp3.Request;
28+
29+
class ReactOkHttpNetworkFetcher extends OkHttpNetworkFetcher {
30+
31+
private static final String TAG = "ReactOkHttpNetworkFetcher";
32+
33+
private final OkHttpClient mOkHttpClient;
34+
private final Executor mCancellationExecutor;
35+
36+
/**
37+
* @param okHttpClient client to use
38+
*/
39+
public ReactOkHttpNetworkFetcher(OkHttpClient okHttpClient) {
40+
super(okHttpClient);
41+
mOkHttpClient = okHttpClient;
42+
mCancellationExecutor = okHttpClient.dispatcher().executorService();
43+
}
44+
45+
private Map<String, String> getHeaders(ReadableMap readableMap) {
46+
if (readableMap == null) {
47+
return null;
48+
}
49+
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
50+
Map<String, String> map = new HashMap<>();
51+
while (iterator.hasNextKey()) {
52+
String key = iterator.nextKey();
53+
String value = readableMap.getString(key);
54+
map.put(key, value);
55+
}
56+
return map;
57+
}
58+
59+
@Override
60+
public void fetch(final OkHttpNetworkFetchState fetchState, final Callback callback) {
61+
fetchState.submitTime = SystemClock.elapsedRealtime();
62+
final Uri uri = fetchState.getUri();
63+
Map<String, String> requestHeaders = null;
64+
if (fetchState.getContext().getImageRequest() instanceof ReactNetworkImageRequest) {
65+
ReactNetworkImageRequest networkImageRequest = (ReactNetworkImageRequest)
66+
fetchState.getContext().getImageRequest();
67+
requestHeaders = getHeaders(networkImageRequest.getHeaders());
68+
}
69+
if (requestHeaders == null) {
70+
requestHeaders = Collections.emptyMap();
71+
}
72+
final Request request = new Request.Builder()
73+
.cacheControl(new CacheControl.Builder().noStore().build())
74+
.url(uri.toString())
75+
.headers(Headers.of(requestHeaders))
76+
.get()
77+
.build();
78+
79+
fetchWithRequest(fetchState, callback, request);
80+
}
81+
}

ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ android_library(
3737
react_native_target('java/com/facebook/react/common:common'),
3838
react_native_target('java/com/facebook/react/module/annotations:annotations'),
3939
react_native_target('java/com/facebook/react/uimanager:uimanager'),
40+
react_native_target('java/com/facebook/react/modules/fresco:fresco'),
4041
react_native_target('java/com/facebook/react/uimanager/annotations:annotations'),
4142
react_native_target('java/com/facebook/react/views/imagehelper:withmultisource'),
4243
],

ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
2222
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
2323
import com.facebook.react.bridge.ReadableArray;
24+
import com.facebook.react.bridge.ReadableMap;
2425
import com.facebook.react.common.MapBuilder;
2526
import com.facebook.react.module.annotations.ReactModule;
2627
import com.facebook.react.uimanager.PixelUtil;
@@ -171,6 +172,11 @@ public void setLoadHandlersRegistered(ReactImageView view, boolean shouldNotifyL
171172
view.setShouldNotifyLoadEvents(shouldNotifyLoadEvents);
172173
}
173174

175+
@ReactProp(name = "headers")
176+
public void setHeaders(ReactImageView view, ReadableMap headers) {
177+
view.setHeaders(headers);
178+
}
179+
174180
@Override
175181
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
176182
return MapBuilder.of(

ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import com.facebook.react.bridge.ReadableArray;
5555
import com.facebook.react.bridge.ReadableMap;
5656
import com.facebook.react.uimanager.FloatUtil;
57+
import com.facebook.react.modules.fresco.ReactNetworkImageRequest;
5758
import com.facebook.react.uimanager.PixelUtil;
5859
import com.facebook.react.uimanager.UIManagerModule;
5960
import com.facebook.react.uimanager.events.EventDispatcher;
@@ -163,6 +164,7 @@ public void process(Bitmap output, Bitmap source) {
163164
private final @Nullable Object mCallerContext;
164165
private int mFadeDurationMs = -1;
165166
private boolean mProgressiveRenderingEnabled;
167+
private ReadableMap mHeaders;
166168

167169
// We can't specify rounding in XML, so have to do so here
168170
private static GenericDraweeHierarchy buildHierarchy(Context context) {
@@ -324,6 +326,10 @@ private void cornerRadii(float[] computedCorners) {
324326
computedCorners[2] = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius;
325327
computedCorners[3] = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius;
326328
}
329+
330+
public void setHeaders(ReadableMap headers) {
331+
mHeaders = headers;
332+
}
327333

328334
public void maybeUpdateView() {
329335
if (!mIsDirty) {
@@ -384,12 +390,13 @@ public void maybeUpdateView() {
384390

385391
ResizeOptions resizeOptions = doResize ? new ResizeOptions(getWidth(), getHeight()) : null;
386392

387-
ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(mImageSource.getUri())
393+
ImageRequestBuilder imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(mImageSource.getUri())
388394
.setPostprocessor(postprocessor)
389395
.setResizeOptions(resizeOptions)
390396
.setAutoRotateEnabled(true)
391-
.setProgressiveRenderingEnabled(mProgressiveRenderingEnabled)
392-
.build();
397+
.setProgressiveRenderingEnabled(mProgressiveRenderingEnabled);
398+
399+
ImageRequest imageRequest = ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, mHeaders);
393400

394401
// This builder is reused
395402
mDraweeControllerBuilder.reset();

ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ android_library(
1414
react_native_target('java/com/facebook/react/bridge:bridge'),
1515
react_native_target('java/com/facebook/react/common:common'),
1616
react_native_target('java/com/facebook/react/module/annotations:annotations'),
17+
react_native_target('java/com/facebook/react/modules/fresco:fresco'),
1718
react_native_target('java/com/facebook/react/uimanager:uimanager'),
1819
react_native_target('java/com/facebook/react/uimanager/annotations:annotations'),
1920
react_native_target('java/com/facebook/react/views/text:text'),

ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.facebook.react.bridge.ReadableArray;
2626
import com.facebook.react.bridge.ReadableType;
2727
import com.facebook.react.uimanager.ViewProps;
28+
import com.facebook.react.bridge.ReadableMap;
2829
import com.facebook.react.uimanager.annotations.ReactProp;
2930
import com.facebook.react.views.text.ReactTextInlineImageShadowNode;
3031
import com.facebook.react.views.text.TextInlineImageSpan;
@@ -36,6 +37,7 @@
3637
public class FrescoBasedReactTextInlineImageShadowNode extends ReactTextInlineImageShadowNode {
3738

3839
private @Nullable Uri mUri;
40+
private ReadableMap mHeaders;
3941
private final AbstractDraweeControllerBuilder mDraweeControllerBuilder;
4042
private final @Nullable Object mCallerContext;
4143
private float mWidth = YogaConstants.UNDEFINED;
@@ -73,6 +75,11 @@ public void setSource(@Nullable ReadableArray sources) {
7375
mUri = uri;
7476
}
7577

78+
@ReactProp(name = "headers")
79+
public void setHeaders(ReadableMap headers) {
80+
mHeaders = headers;
81+
}
82+
7683
/**
7784
* Besides width/height, all other layout props on inline images are ignored
7885
*/
@@ -100,6 +107,10 @@ public void setHeight(Dynamic height) {
100107
return mUri;
101108
}
102109

110+
public ReadableMap getHeaders() {
111+
return mHeaders;
112+
}
113+
103114
// TODO: t9053573 is tracking that this code should be shared
104115
private static @Nullable Uri getResourceDrawableUri(Context context, @Nullable String name) {
105116
if (name == null || name.isEmpty()) {
@@ -131,6 +142,7 @@ public TextInlineImageSpan buildInlineImageSpan() {
131142
height,
132143
width,
133144
getUri(),
145+
getHeaders(),
134146
getDraweeControllerBuilder(),
135147
getCallerContext());
136148
}

0 commit comments

Comments
 (0)