Skip to content

Commit

Permalink
Fix local images in production builds. (#324)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
DylanVann authored Oct 23, 2018
1 parent 65ba0c4 commit 47e36ed
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 100 deletions.
64 changes: 64 additions & 0 deletions android/src/main/java/com/dylanvann/fastimage/FastImageSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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());
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
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);

Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected FastImageViewWithUrl createViewInstance(ThemedReactContext reactContex

@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);
Expand All @@ -68,8 +68,9 @@ public void setSrc(FastImageViewWithUrl view, @Nullable ReadableMap source) {
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;
Expand All @@ -92,16 +93,18 @@ public void setSrc(FastImageViewWithUrl view, @Nullable ReadableMap source) {
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);
Expand All @@ -120,35 +123,29 @@ public void onDropViewInstance(FastImageViewWithUrl view) {
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
Expand All @@ -172,14 +169,18 @@ public float getGranularityPercentage() {
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) {
return false;
}
if (context instanceof Activity) {
final Activity activity = (Activity) context;
if (isAcitityDestroyed(activity)) {
if (isActivityDestroyed(activity)) {
return false;
}
}
Expand All @@ -188,22 +189,19 @@ private static boolean isValidContextForGlide(final Context context) {
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 {
return activity.isFinishing() || activity.isChangingConfigurations();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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 {

Expand All @@ -32,15 +33,20 @@ public void preload(final ReadableArray sources) {
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();
}
Expand Down
2 changes: 1 addition & 1 deletion react-native-fast-image-example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Original file line number Diff line number Diff line change
Expand Up @@ -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",
);
Expand All @@ -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",
);
Expand Down
36 changes: 0 additions & 36 deletions react-native-fast-image-example/rn-cli.config.js

This file was deleted.

Loading

0 comments on commit 47e36ed

Please sign in to comment.