Skip to content

Commit ec7c453

Browse files
DylanVanna-sapozhnikov-sdgdanielgindiggWho
authored
feat: support defaultSource on iOS and Android (#921)
Co-authored-by: Anton Sapozhnikov <[email protected]> Co-authored-by: Daniel Cohen Gindi <[email protected]> Co-authored-by: 张贵广 <[email protected]>
1 parent cc9da99 commit ec7c453

18 files changed

+600
-261
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ Headers to load the image with. e.g. `{ Authorization: 'someAuthToken' }`.
143143

144144
---
145145

146+
### `defaultSource?: number`
147+
148+
- An asset loaded with `require(...)`.
149+
- Note that like the built-in `Image` implementation, on Android `defaultSource` does not work in debug mode. This is due to the fact that assets are sent from the dev server, but RN's functions only know how to load it from `res`.
150+
151+
---
152+
146153
### `resizeMode?: enum`
147154

148155
- `FastImage.resizeMode.contain` - Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).

android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
@GlideModule
3333
public class FastImageOkHttpProgressGlideModule extends LibraryGlideModule {
3434

35-
private static DispatchingProgressListener progressListener = new DispatchingProgressListener();
35+
private static final DispatchingProgressListener progressListener = new DispatchingProgressListener();
3636

3737
@Override
3838
public void registerComponents(

android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ public class FastImageRequestListener implements RequestListener<Drawable> {
1616
static final String REACT_ON_ERROR_EVENT = "onFastImageError";
1717
static final String REACT_ON_LOAD_EVENT = "onFastImageLoad";
1818
static final String REACT_ON_LOAD_END_EVENT = "onFastImageLoadEnd";
19-
20-
private String key;
19+
private final String key;
2120

2221
FastImageRequestListener(String key) {
2322
this.key = key;

android/src/main/java/com/dylanvann/fastimage/FastImageSource.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class FastImageSource extends ImageSource {
1717
private static final String ANDROID_RESOURCE_SCHEME = "android.resource";
1818
private static final String ANDROID_CONTENT_SCHEME = "content";
1919
private static final String LOCAL_FILE_SCHEME = "file";
20-
private Headers mHeaders;
20+
private final Headers mHeaders;
2121
private Uri mUri;
2222

2323
public static boolean isBase64Uri(Uri uri) {

android/src/main/java/com/dylanvann/fastimage/FastImageViewConverter.java

+17-22
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package com.dylanvann.fastimage;
22

3+
import static com.bumptech.glide.request.RequestOptions.signatureOf;
4+
35
import android.content.Context;
46
import android.graphics.Color;
57
import android.graphics.drawable.ColorDrawable;
68
import android.graphics.drawable.Drawable;
7-
import android.media.Image;
8-
import android.net.Uri;
9-
import android.util.Log;
109
import android.widget.ImageView;
1110
import android.widget.ImageView.ScaleType;
1211

1312
import com.bumptech.glide.Priority;
1413
import com.bumptech.glide.load.engine.DiskCacheStrategy;
15-
import com.bumptech.glide.load.model.GlideUrl;
1614
import com.bumptech.glide.load.model.Headers;
1715
import com.bumptech.glide.load.model.LazyHeaders;
1816
import com.bumptech.glide.request.RequestOptions;
@@ -21,18 +19,12 @@
2119
import com.facebook.react.bridge.NoSuchKeyException;
2220
import com.facebook.react.bridge.ReadableMap;
2321
import com.facebook.react.bridge.ReadableMapKeySetIterator;
24-
import com.facebook.react.views.imagehelper.ImageSource;
2522

26-
import java.io.File;
27-
import java.net.URI;
28-
import java.net.URISyntaxException;
2923
import java.util.HashMap;
3024
import java.util.Map;
3125

3226
import javax.annotation.Nullable;
3327

34-
import static com.bumptech.glide.request.RequestOptions.signatureOf;
35-
3628
class FastImageViewConverter {
3729
private static final Drawable TRANSPARENT_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
3830

@@ -57,10 +49,13 @@ class FastImageViewConverter {
5749
put("stretch", ScaleType.FIT_XY);
5850
put("center", ScaleType.CENTER_INSIDE);
5951
}};
60-
52+
6153
// Resolve the source uri to a file path that android understands.
62-
static FastImageSource getImageSource(Context context, ReadableMap source) {
63-
return new FastImageSource(context, source.getString("uri"), getHeaders(source));
54+
static @Nullable
55+
FastImageSource getImageSource(Context context, @Nullable ReadableMap source) {
56+
return source == null
57+
? null
58+
: new FastImageSource(context, source.getString("uri"), getHeaders(source));
6459
}
6560

6661
static Headers getHeaders(ReadableMap source) {
@@ -90,8 +85,8 @@ static RequestOptions getOptions(Context context, FastImageSource imageSource, R
9085
// Get cache control method.
9186
final FastImageCacheControl cacheControl = FastImageViewConverter.getCacheControl(source);
9287
DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.AUTOMATIC;
93-
Boolean onlyFromCache = false;
94-
Boolean skipMemoryCache = false;
88+
boolean onlyFromCache = false;
89+
boolean skipMemoryCache = false;
9590
switch (cacheControl) {
9691
case WEB:
9792
// If using none then OkHttp integration should be used for caching.
@@ -107,12 +102,12 @@ static RequestOptions getOptions(Context context, FastImageSource imageSource, R
107102
}
108103

109104
RequestOptions options = new RequestOptions()
110-
.diskCacheStrategy(diskCacheStrategy)
111-
.onlyRetrieveFromCache(onlyFromCache)
112-
.skipMemoryCache(skipMemoryCache)
113-
.priority(priority)
114-
.placeholder(TRANSPARENT_DRAWABLE);
115-
105+
.diskCacheStrategy(diskCacheStrategy)
106+
.onlyRetrieveFromCache(onlyFromCache)
107+
.skipMemoryCache(skipMemoryCache)
108+
.priority(priority)
109+
.placeholder(TRANSPARENT_DRAWABLE);
110+
116111
if (imageSource.isResource()) {
117112
// Every local resource (drawable) in Android has its own unique numeric id, which are
118113
// generated at build time. Although these ids are unique, they are not guaranteed unique
@@ -123,7 +118,7 @@ static RequestOptions getOptions(Context context, FastImageSource imageSource, R
123118
options = options.apply(signatureOf(ApplicationVersionSignature.obtain(context)));
124119
}
125120

126-
return options;
121+
return options;
127122
}
128123

129124
private static FastImageCacheControl getCacheControl(ReadableMap source) {
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package com.dylanvann.fastimage;
22

3+
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT;
4+
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_END_EVENT;
5+
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_EVENT;
6+
37
import android.app.Activity;
48
import android.content.Context;
59
import android.content.ContextWrapper;
610
import android.graphics.PorterDuff;
711
import android.os.Build;
812

13+
import androidx.annotation.NonNull;
14+
915
import com.bumptech.glide.Glide;
1016
import com.bumptech.glide.RequestManager;
11-
import com.bumptech.glide.load.model.GlideUrl;
12-
import com.bumptech.glide.request.Request;
1317
import com.facebook.react.bridge.ReadableMap;
1418
import com.facebook.react.bridge.WritableMap;
1519
import com.facebook.react.bridge.WritableNativeMap;
@@ -18,36 +22,33 @@
1822
import com.facebook.react.uimanager.ThemedReactContext;
1923
import com.facebook.react.uimanager.annotations.ReactProp;
2024
import com.facebook.react.uimanager.events.RCTEventEmitter;
25+
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
2126

22-
import java.util.ArrayList;
23-
import java.util.Collections;
2427
import java.util.List;
2528
import java.util.Map;
2629
import java.util.WeakHashMap;
2730

2831
import javax.annotation.Nullable;
2932

30-
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT;
31-
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_END_EVENT;
32-
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_EVENT;
33-
3433
class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> implements FastImageProgressListener {
3534

36-
private static final String REACT_CLASS = "FastImageView";
37-
private static final String REACT_ON_LOAD_START_EVENT = "onFastImageLoadStart";
38-
private static final String REACT_ON_PROGRESS_EVENT = "onFastImageProgress";
35+
static final String REACT_CLASS = "FastImageView";
36+
static final String REACT_ON_LOAD_START_EVENT = "onFastImageLoadStart";
37+
static final String REACT_ON_PROGRESS_EVENT = "onFastImageProgress";
3938
private static final Map<String, List<FastImageViewWithUrl>> VIEWS_FOR_URLS = new WeakHashMap<>();
4039

4140
@Nullable
4241
private RequestManager requestManager = null;
4342

43+
@NonNull
4444
@Override
4545
public String getName() {
4646
return REACT_CLASS;
4747
}
4848

49+
@NonNull
4950
@Override
50-
protected FastImageViewWithUrl createViewInstance(ThemedReactContext reactContext) {
51+
protected FastImageViewWithUrl createViewInstance(@NonNull ThemedReactContext reactContext) {
5152
if (isValidContextForGlide(reactContext)) {
5253
requestManager = Glide.with(reactContext);
5354
}
@@ -56,76 +57,15 @@ protected FastImageViewWithUrl createViewInstance(ThemedReactContext reactContex
5657
}
5758

5859
@ReactProp(name = "source")
59-
public void setSrc(FastImageViewWithUrl view, @Nullable ReadableMap source) {
60-
if (source == null || !source.hasKey("uri") || isNullOrEmpty(source.getString("uri"))) {
61-
// Cancel existing requests.
62-
clearView(view);
63-
64-
if (view.glideUrl != null) {
65-
FastImageOkHttpProgressGlideModule.forget(view.glideUrl.toStringUrl());
66-
}
67-
// Clear the image.
68-
view.setImageDrawable(null);
69-
return;
70-
}
71-
72-
//final GlideUrl glideUrl = FastImageViewConverter.getGlideUrl(view.getContext(), source);
73-
final FastImageSource imageSource = FastImageViewConverter.getImageSource(view.getContext(), source);
74-
if (imageSource.getUri().toString().length() == 0) {
75-
ThemedReactContext context = (ThemedReactContext) view.getContext();
76-
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
77-
int viewId = view.getId();
78-
WritableMap event = new WritableNativeMap();
79-
event.putString("message", "Invalid source prop:" + source);
80-
eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, event);
81-
82-
// Cancel existing requests.
83-
if (requestManager != null) {
84-
requestManager.clear(view);
85-
}
86-
87-
if (view.glideUrl != null) {
88-
FastImageOkHttpProgressGlideModule.forget(view.glideUrl.toStringUrl());
89-
}
90-
// Clear the image.
91-
view.setImageDrawable(null);
92-
return;
93-
}
94-
95-
final GlideUrl glideUrl = imageSource.getGlideUrl();
96-
97-
// Cancel existing request.
98-
view.glideUrl = glideUrl;
99-
clearView(view);
100-
101-
String key = glideUrl.toStringUrl();
102-
FastImageOkHttpProgressGlideModule.expect(key, this);
103-
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
104-
if (viewsForKey != null && !viewsForKey.contains(view)) {
105-
viewsForKey.add(view);
106-
} else if (viewsForKey == null) {
107-
List<FastImageViewWithUrl> newViewsForKeys = new ArrayList<>(Collections.singletonList(view));
108-
VIEWS_FOR_URLS.put(key, newViewsForKeys);
109-
}
60+
public void setSource(FastImageViewWithUrl view, @Nullable ReadableMap source) {
61+
view.setSource(source);
62+
}
11063

111-
ThemedReactContext context = (ThemedReactContext) view.getContext();
112-
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
113-
int viewId = view.getId();
114-
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_START_EVENT, new WritableNativeMap());
115-
116-
if (requestManager != null) {
117-
requestManager
118-
// This will make this work for remote and local images. e.g.
119-
// - file:///
120-
// - content://
121-
// - res:/
122-
// - android.resource://
123-
// - data:image/png;base64
124-
.load(imageSource.getSourceForLoad())
125-
.apply(FastImageViewConverter.getOptions(context, imageSource, source))
126-
.listener(new FastImageRequestListener(key))
127-
.into(view);
128-
}
64+
@ReactProp(name = "defaultSource")
65+
public void setDefaultSource(FastImageViewWithUrl view, @Nullable String source) {
66+
view.setDefaultSource(
67+
ResourceDrawableIdHelper.getInstance()
68+
.getResourceDrawable(view.getContext(), source));
12969
}
13070

13171
@ReactProp(name = "tintColor", customType = "Color")
@@ -144,9 +84,9 @@ public void setResizeMode(FastImageViewWithUrl view, String resizeMode) {
14484
}
14585

14686
@Override
147-
public void onDropViewInstance(FastImageViewWithUrl view) {
87+
public void onDropViewInstance(@NonNull FastImageViewWithUrl view) {
14888
// This will cancel existing requests.
149-
clearView(view);
89+
view.clearView(requestManager);
15090

15191
if (view.glideUrl != null) {
15292
final String key = view.glideUrl.toString();
@@ -193,11 +133,6 @@ public float getGranularityPercentage() {
193133
return 0.5f;
194134
}
195135

196-
private boolean isNullOrEmpty(final String url) {
197-
return url == null || url.trim().isEmpty();
198-
}
199-
200-
201136
private static boolean isValidContextForGlide(final Context context) {
202137
Activity activity = getActivityFromContext(context);
203138

@@ -235,14 +170,14 @@ private static boolean isActivityDestroyed(Activity activity) {
235170
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
236171
return activity.isDestroyed() || activity.isFinishing();
237172
} else {
238-
return activity.isDestroyed() || activity.isFinishing() || activity.isChangingConfigurations();
173+
return activity.isFinishing() || activity.isChangingConfigurations();
239174
}
240175

241176
}
242177

243-
private void clearView(FastImageViewWithUrl view) {
244-
if (requestManager != null && view != null && view.getTag() != null && view.getTag() instanceof Request) {
245-
requestManager.clear(view);
246-
}
178+
@Override
179+
protected void onAfterUpdateTransaction(@NonNull FastImageViewWithUrl view) {
180+
super.onAfterUpdateTransaction(view);
181+
view.onAfterUpdate(this, requestManager, VIEWS_FOR_URLS);
247182
}
248183
}

android/src/main/java/com/dylanvann/fastimage/FastImageViewModule.java

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import android.app.Activity;
44

5+
import androidx.annotation.NonNull;
6+
57
import com.bumptech.glide.Glide;
68
import com.bumptech.glide.load.model.GlideUrl;
79
import com.facebook.react.bridge.Promise;
@@ -20,6 +22,7 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
2022
super(reactContext);
2123
}
2224

25+
@NonNull
2326
@Override
2427
public String getName() {
2528
return REACT_CLASS;
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.dylanvann.fastimage;
22

3+
import androidx.annotation.NonNull;
4+
35
import com.facebook.react.ReactPackage;
46
import com.facebook.react.bridge.NativeModule;
57
import com.facebook.react.bridge.ReactApplicationContext;
@@ -9,13 +11,15 @@
911
import java.util.List;
1012

1113
public class FastImageViewPackage implements ReactPackage {
14+
@NonNull
1215
@Override
13-
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
16+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
1417
return Collections.<NativeModule>singletonList(new FastImageViewModule(reactContext));
1518
}
1619

20+
@NonNull
1721
@Override
18-
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
22+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
1923
return Collections.<ViewManager>singletonList(new FastImageViewManager());
2024
}
2125
}

0 commit comments

Comments
 (0)