Skip to content

Commit

Permalink
Android: Support HTTP headers for source prop on <Image> components
Browse files Browse the repository at this point in the history
Summary:
A copy of facebook#7791 because of our very imperfect tools that mirror the changes from pull requests in the fb monorepo. The internal Phabricator revision for facebook#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 facebook#12448

Differential Revision: D4584743

Pulled By: mkonicek

fbshipit-source-id: 66e5b88134fca1980adc4cd8a2ff17c42e10022c
  • Loading branch information
Martin Konicek authored and dudeinthemirror committed Mar 1, 2017
1 parent e84fcf2 commit 6935a63
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 20 deletions.
7 changes: 7 additions & 0 deletions Libraries/Image/Image.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,18 @@ 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.
*/
source: PropTypes.oneOfType([
PropTypes.shape({
uri: PropTypes.string,
headers: PropTypes.objectOf(PropTypes.string),
}),
// Opaque type returned by require('./image.jpg')
PropTypes.number,
Expand Down Expand Up @@ -300,6 +305,7 @@ var Image = React.createClass({
style,
shouldNotifyLoadEvents: !!(onLoadStart || onLoad || onLoadEnd || onError),
src: sources,
headers: source.headers,
loadingIndicatorSrc: loadingIndicatorSource ? loadingIndicatorSource.uri : null,
});

Expand Down Expand Up @@ -346,6 +352,7 @@ var styles = StyleSheet.create({
var cfg = {
nativeOnly: {
src: true,
headers: true,
loadingIndicatorSrc: true,
shouldNotifyLoadEvents: true,
},
Expand Down
4 changes: 2 additions & 2 deletions ReactAndroid/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -124,8 +126,10 @@ public static ImagePipelineConfig.Builder getDefaultConfigBuilder(Context contex
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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -131,6 +142,7 @@ public TextInlineImageSpan buildInlineImageSpan() {
height,
width,
getUri(),
getHeaders(),
getDraweeControllerBuilder(),
getCallerContext());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Text/>. It computes
Expand All @@ -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;

Expand All @@ -56,6 +59,7 @@ public FrescoBasedReactTextInlineImageSpan(
int height,
int width,
@Nullable Uri uri,
ReadableMap headers,
AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
mDraweeHolder = new DraweeHolder(
Expand All @@ -68,6 +72,7 @@ public FrescoBasedReactTextInlineImageSpan(
mHeight = height;
mWidth = width;
mUri = (uri != null) ? uri : Uri.EMPTY;
mHeaders = headers;
}

/**
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Loading

0 comments on commit 6935a63

Please sign in to comment.