Unverified Commit 47e36edc authored by Dylan Vann's avatar Dylan Vann Committed by GitHub

Fix local images in production builds. (#324)

* Revert changes to make yarn link work.

* Fixed local images in production builds.

This closes #273, closes #296, closes #246.

* Remove unused code and fix some warnings.
parent 65ba0c47
package com.dylanvann.fastimage;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.Headers;
import com.facebook.react.views.imagehelper.ImageSource;
import javax.annotation.Nullable;
public class FastImageSource extends ImageSource {
private static final String LOCAL_RESOURCE_SCHEME = "res";
private static final String ANDROID_RESOURCE_SCHEME = "android.resource";
private Headers mHeaders;
private Uri mUri;
public static boolean isLocalResourceUri (Uri uri) {
return LOCAL_RESOURCE_SCHEME.equals(uri.getScheme());
}
public static boolean isResourceUri (Uri uri) {
return ANDROID_RESOURCE_SCHEME.equals(uri.getScheme());
}
public FastImageSource(Context context, String source) {
this(context, source, null);
}
public FastImageSource(Context context, String source, @Nullable Headers headers) {
this(context, source, 0.0d, 0.0d, headers);
}
public FastImageSource(Context context, String source, double width, double height, @Nullable Headers headers) {
super(context, source, width, height);
mHeaders = headers == null ? Headers.DEFAULT : headers;
mUri = super.getUri();
if (isLocalResourceUri(mUri)) {
// Convert res:/ scheme to android.resource:// so
// glide can understand the uri.
mUri = Uri.parse(mUri.toString().replace("res:/", ANDROID_RESOURCE_SCHEME + "://" + context.getPackageName() + "/"));
}
}
public boolean isBase64Resource () {
return mUri != null && "data".equals(mUri.getScheme());
}
@Override
public Uri getUri() {
return mUri;
}
public Headers getHeaders() {
return mHeaders;
}
public GlideUrl getGlideUrl() {
return new GlideUrl(getUri().toString(), getHeaders());
}
}
package com.dylanvann.fastimage;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.Image;
import android.net.Uri;
import android.util.Log;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.Headers;
import com.bumptech.glide.load.model.LazyHeaders;
import com.bumptech.glide.request.RequestOptions;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.NoSuchKeyException;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.views.imagehelper.ImageSource;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
class FastImageViewConverter {
private static final Drawable TRANSPARENT_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
......@@ -43,27 +54,31 @@ class FastImageViewConverter {
put("stretch", ScaleType.FIT_XY);
put("center", ScaleType.CENTER);
}};
// Resolve the source uri to a file path that android understands.
static FastImageSource getImageSource(Context context, ReadableMap source) {
return new FastImageSource(context, source.getString("uri"), getHeaders(source));
}
static GlideUrl getGlideUrl(ReadableMap source) {
final String uriProp = source.getString("uri");
// Get the headers prop and add to glideUrl.
GlideUrl glideUrl;
try {
final ReadableMap headersMap = source.getMap("headers");
ReadableMapKeySetIterator headersIterator = headersMap.keySetIterator();
LazyHeaders.Builder headersBuilder = new LazyHeaders.Builder();
while (headersIterator.hasNextKey()) {
String key = headersIterator.nextKey();
String value = headersMap.getString(key);
headersBuilder.addHeader(key, value);
static Headers getHeaders(ReadableMap source) {
Headers headers = Headers.DEFAULT;
if (source.hasKey("headers")) {
ReadableMap headersMap = source.getMap("headers");
ReadableMapKeySetIterator iterator = headersMap.keySetIterator();
LazyHeaders.Builder builder = new LazyHeaders.Builder();
while (iterator.hasNextKey()) {
String header = iterator.nextKey();
String value = headersMap.getString(header);
builder.addHeader(header, value);
}
LazyHeaders headers = headersBuilder.build();
glideUrl = new GlideUrl(uriProp, headers);
} catch (NoSuchKeyException e) {
// If there is no headers object.
glideUrl = new GlideUrl(uriProp);
headers = builder.build();
}
return glideUrl;
return headers;
}
static RequestOptions getOptions(ReadableMap source) {
......
......@@ -54,7 +54,7 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
@ReactProp(name = "source")
public void setSrc(FastImageViewWithUrl view, @Nullable ReadableMap source) {
if (source == null) {
if (source == null || !source.hasKey("uri") || isNullOrEmpty(source.getString("uri"))) {
// Cancel existing requests.
if (requestManager != null) {
requestManager.clear(view);
......@@ -68,8 +68,9 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
return;
}
// Get the GlideUrl which contains header info.
final GlideUrl glideUrl = FastImageViewConverter.getGlideUrl(source);
//final GlideUrl glideUrl = FastImageViewConverter.getGlideUrl(view.getContext(), source);
final FastImageSource imageSource = FastImageViewConverter.getImageSource(view.getContext(), source);
final GlideUrl glideUrl = imageSource.getGlideUrl();
// Cancel existing request.
view.glideUrl = glideUrl;
......@@ -92,16 +93,18 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_START_EVENT, new WritableNativeMap());
final String stringUrl = glideUrl.toString();
if (requestManager != null) {
requestManager
// This will make this work for remote and local images. e.g.
// - file:///
// - content://
// - res:/
// - android.resource://
// - data:image/png;base64
.load(stringUrl.startsWith("http") ? glideUrl : stringUrl)
.load(
imageSource.isBase64Resource() ? imageSource.getSource() :
imageSource.isResource() ? imageSource.getUri() : imageSource.getGlideUrl()
)
.apply(FastImageViewConverter.getOptions(source))
.listener(new FastImageRequestListener(key))
.into(view);
......@@ -120,35 +123,29 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
if (requestManager != null) {
requestManager.clear(view);
}
if (view.glideUrl == null) {
super.onDropViewInstance(view);
return;
}
final String key = view.glideUrl.toString();
FastImageOkHttpProgressGlideModule.forget(key);
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
if (viewsForKey != null) {
viewsForKey.remove(view);
if (viewsForKey.size() == 0) VIEWS_FOR_URLS.remove(key);
if (view.glideUrl != null) {
final String key = view.glideUrl.toString();
FastImageOkHttpProgressGlideModule.forget(key);
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
if (viewsForKey != null) {
viewsForKey.remove(view);
if (viewsForKey.size() == 0) VIEWS_FOR_URLS.remove(key);
}
}
super.onDropViewInstance(view);
}
@Override
@Nullable
public Map getExportedCustomDirectEventTypeConstants() {
return MapBuilder.of(
REACT_ON_LOAD_START_EVENT,
MapBuilder.of("registrationName", REACT_ON_LOAD_START_EVENT),
REACT_ON_PROGRESS_EVENT,
MapBuilder.of("registrationName", REACT_ON_PROGRESS_EVENT),
REACT_ON_LOAD_EVENT,
MapBuilder.of("registrationName", REACT_ON_LOAD_EVENT),
REACT_ON_ERROR_EVENT,
MapBuilder.of("registrationName", REACT_ON_ERROR_EVENT),
REACT_ON_LOAD_END_EVENT,
MapBuilder.of("registrationName", REACT_ON_LOAD_END_EVENT)
);
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
return MapBuilder.<String, Object>builder()
.put(REACT_ON_LOAD_START_EVENT, MapBuilder.of("registrationName", REACT_ON_LOAD_START_EVENT))
.put(REACT_ON_PROGRESS_EVENT, MapBuilder.of("registrationName", REACT_ON_PROGRESS_EVENT))
.put(REACT_ON_LOAD_EVENT, MapBuilder.of("registrationName", REACT_ON_LOAD_EVENT))
.put(REACT_ON_ERROR_EVENT, MapBuilder.of("registrationName", REACT_ON_ERROR_EVENT))
.put(REACT_ON_LOAD_END_EVENT, MapBuilder.of("registrationName", REACT_ON_LOAD_END_EVENT))
.build();
}
@Override
......@@ -172,6 +169,10 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
return 0.5f;
}
private boolean isNullOrEmpty(final String url) {
return url == null || url.trim().isEmpty();
}
private static boolean isValidContextForGlide(final Context context) {
if (context == null) {
......@@ -179,7 +180,7 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
}
if (context instanceof Activity) {
final Activity activity = (Activity) context;
if (isAcitityDestroyed(activity)) {
if (isActivityDestroyed(activity)) {
return false;
}
}
......@@ -188,16 +189,14 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
final Context baseContext = ((ThemedReactContext) context).getBaseContext();
if (baseContext instanceof Activity) {
final Activity baseActivity = (Activity) baseContext;
if (baseActivity == null || isAcitityDestroyed(baseActivity)) {
return false;
}
return !isActivityDestroyed(baseActivity);
}
}
return true;
}
private static boolean isAcitityDestroyed (Activity activity) {
private static boolean isActivityDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return activity.isDestroyed() || activity.isFinishing();
} else {
......@@ -205,5 +204,4 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
}
}
}
......@@ -9,6 +9,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.views.imagehelper.ImageSource;
class FastImageViewModule extends ReactContextBaseJavaModule {
......@@ -32,15 +33,20 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
public void run() {
for (int i = 0; i < sources.size(); i++) {
final ReadableMap source = sources.getMap(i);
final GlideUrl glideUrl = FastImageViewConverter.getGlideUrl(source);
final String stringUrl = glideUrl.toString();
final FastImageSource imageSource = FastImageViewConverter.getImageSource(activity, source);
Glide
.with(activity.getApplicationContext())
// This will make this work for remote and local images. e.g.
// - file:///
// - content://
// - res:/
// - android.resource://
// - data:image/png;base64
.load(stringUrl.startsWith("http") ? glideUrl : stringUrl)
.load(
imageSource.isBase64Resource() ? imageSource.getSource() :
imageSource.isResource() ? imageSource.getUri() : imageSource.getGlideUrl()
)
.apply(FastImageViewConverter.getOptions(source))
.preload();
}
......
......@@ -4,6 +4,6 @@ project(':react-native-vector-icons').projectDir = new File(rootProject.projectD
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
include ':react-native-fast-image'
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../../android')
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
include ':app'
......@@ -1427,7 +1427,7 @@
DEAD_CODE_STRIPPING = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../ios",
"$(SRCROOT)/../node_modules/react-native-fast-image/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
);
......@@ -1451,7 +1451,7 @@
CURRENT_PROJECT_VERSION = 1;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../ios",
"$(SRCROOT)/../node_modules/react-native-fast-image/ios",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
);
......
const path = require('path')
const extraNodeModules = {
'prop-types': path.resolve(__dirname, 'node_modules/prop-types'),
react: path.resolve(__dirname, 'node_modules/react'),
'react-native': path.resolve(__dirname, 'node_modules/react-native'),
}
const blacklistPath = path.resolve(__dirname, '../node_modules/react-native')
const blacklistRegexes = [new RegExp(`${blacklistPath}/.*`)]
const watchFolder = path.resolve(__dirname, '../')
const watchFolders = [watchFolder]
const metroVersion = require('metro/package.json').version
const metroVersionComponents = metroVersion.match(/^(\d+)\.(\d+)\.(\d+)/)
if (
metroVersionComponents[1] === '0' &&
parseInt(metroVersionComponents[2], 10) >= 43
) {
module.exports = {
resolver: {
extraNodeModules,
blacklistRE: require('metro-config/src/defaults/blacklist')(
blacklistRegexes,
),
},
watchFolders,
}
} else {
module.exports = {
extraNodeModules,
getBlacklistRE: () => require('metro/src/blacklist')(blacklistRegexes),
getProjectRoots: () => [path.resolve(__dirname)].concat(watchFolders),
}
}
......@@ -9,8 +9,6 @@ import {
StyleSheet,
} from 'react-native'
const resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource')
const FastImageViewNativeModule = NativeModules.FastImageView
class FastImage extends Component {
......@@ -34,7 +32,7 @@ class FastImage extends Component {
...props
} = this.props
const resolvedSource = resolveAssetSource(source)
const resolvedSource = Image.resolveAssetSource(source)
if (fallback) {
return (
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment