Skip to content

Commit 4e69ddd

Browse files
committed
Merge branch 'fjcaetano-feature/prefetch'
2 parents 1d25cb2 + 46e08b4 commit 4e69ddd

10 files changed

+244
-90
lines changed

FastImage.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import React, { PropTypes, Component } from 'react'
2-
import { requireNativeComponent, Image, View } from 'react-native'
2+
import {
3+
requireNativeComponent,
4+
Image,
5+
NativeModules,
6+
View,
7+
} from 'react-native'
38

49
const resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource')
510

11+
const FastImageViewNativeModule = NativeModules.FastImageView
12+
613
class FastImage extends Component {
714
setNativeProps(nativeProps) {
815
this._root.setNativeProps(nativeProps)
@@ -50,6 +57,10 @@ FastImage.priority = {
5057
high: 'high',
5158
}
5259

60+
FastImage.preload = sources => {
61+
FastImageViewNativeModule.preload(sources)
62+
}
63+
5364
const FastImageSourcePropType = PropTypes.shape({
5465
uri: PropTypes.string,
5566
headers: PropTypes.objectOf(PropTypes.string),

README.md

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ and
3636
- [x] Aggressively cache images.
3737
- [x] Add authorization headers.
3838
- [x] Prioritize images.
39+
- [x] Preload images.
3940
- [x] GIF support.
4041

4142
## Usage
@@ -63,13 +64,13 @@ const YourImage = () =>
6364

6465
## Properties
6566

66-
`source?: object`
67+
### `source?: object`
6768

6869
Source for the remote image to load.
6970

7071
---
7172

72-
`source.uri?: string`
73+
### `source.uri?: string`
7374

7475
Remote url to load the image from. e.g. `'https://facebook.github.io/react/img/logo_og.png'`.
7576

@@ -81,15 +82,15 @@ Headers to load the image with. e.g. `{ Authorization: 'someAuthToken' }`.
8182

8283
---
8384

84-
`source.priority?: enum`
85+
### `source.priority?: enum`
8586

8687
- `FastImage.priority.low` - Low Priority
8788
- `FastImage.priority.normal` **(Default)** - Normal Priority
8889
- `FastImage.priority.high` - High Priority
8990

9091
---
9192

92-
`resizeMode?: enum`
93+
### `resizeMode?: enum`
9394

9495
- `FastImage.resizeMode.contain` **(Default)** - 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).
9596
- `FastImage.resizeMode.cover` - 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 larger than the corresponding dimension of the view (minus padding).
@@ -98,16 +99,44 @@ Headers to load the image with. e.g. `{ Authorization: 'someAuthToken' }`.
9899

99100
---
100101

101-
`onLoad?: () => void`
102+
### `onLoad?: () => void`
102103

103104
Called on a successful image fetch.
104105

105106
---
106107

107-
`onError?: () => void`
108+
### `onError?: () => void`
108109

109110
Called on an image fetching error.
110111

112+
---
113+
114+
### `children`
115+
116+
`FastImage` does not currently support children.
117+
Absolute positioning can be used as an alternative.
118+
119+
(This is because `FastImage` supplies a `android.widget.imageview` and not a `android.view.viewgroup`.)
120+
121+
## Static Methods
122+
123+
### `FastImage.preload: (source[]) => void`
124+
125+
Preload images to display later. e.g.
126+
127+
```js
128+
FastImage.preload([
129+
{
130+
uri: 'https://facebook.github.io/react/img/logo_og.png',
131+
headers: { Authorization: 'someAuthToken' },
132+
},
133+
{
134+
uri: 'https://facebook.github.io/react/img/logo_og.png',
135+
headers: { Authorization: 'someAuthToken' },
136+
},
137+
])
138+
```
139+
111140
## Development
112141

113142
```bash
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.dylanvann.fastimage;
2+
3+
import android.widget.ImageView;
4+
import android.widget.ImageView.ScaleType;
5+
6+
import com.bumptech.glide.Priority;
7+
import com.bumptech.glide.load.model.GlideUrl;
8+
import com.bumptech.glide.load.model.LazyHeaders;
9+
import com.facebook.react.bridge.NoSuchKeyException;
10+
import com.facebook.react.bridge.ReadableMap;
11+
import com.facebook.react.bridge.ReadableMapKeySetIterator;
12+
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
16+
class FastImageViewConverter {
17+
static GlideUrl glideUrl(ReadableMap source) {
18+
final String uriProp = source.getString("uri");
19+
// Get the headers prop and add to glideUrl.
20+
GlideUrl glideUrl;
21+
try {
22+
final ReadableMap headersMap = source.getMap("headers");
23+
ReadableMapKeySetIterator headersIterator = headersMap.keySetIterator();
24+
LazyHeaders.Builder headersBuilder = new LazyHeaders.Builder();
25+
while (headersIterator.hasNextKey()) {
26+
String key = headersIterator.nextKey();
27+
String value = headersMap.getString(key);
28+
headersBuilder.addHeader(key, value);
29+
}
30+
LazyHeaders headers = headersBuilder.build();
31+
glideUrl = new GlideUrl(uriProp, headers);
32+
} catch (NoSuchKeyException e) {
33+
// If there is no headers object.
34+
glideUrl = new GlideUrl(uriProp);
35+
}
36+
return glideUrl;
37+
}
38+
39+
private static Map<String, Priority> REACT_PRIORITY_MAP =
40+
new HashMap<String, Priority>() {{
41+
put("low", Priority.LOW);
42+
put("normal", Priority.NORMAL);
43+
put("high", Priority.HIGH);
44+
}};
45+
46+
static Priority priority(ReadableMap source) {
47+
// Get the priority prop.
48+
String priorityProp = "normal";
49+
try {
50+
priorityProp = source.getString("priority");
51+
} catch (Exception e) {
52+
// Noop.
53+
}
54+
final Priority priority = REACT_PRIORITY_MAP.get(priorityProp);
55+
return priority;
56+
}
57+
58+
private static Map<String, ImageView.ScaleType> REACT_RESIZE_MODE_MAP =
59+
new HashMap<String, ImageView.ScaleType>() {{
60+
put("contain", ScaleType.FIT_CENTER);
61+
put("cover", ScaleType.CENTER_CROP);
62+
put("stretch", ScaleType.FIT_XY);
63+
put("center", ScaleType.CENTER);
64+
}};
65+
66+
public static ScaleType scaleType(String resizeMode) {
67+
if (resizeMode == null) resizeMode = "contain";
68+
final ImageView.ScaleType scaleType = REACT_RESIZE_MODE_MAP.get(resizeMode);
69+
return scaleType;
70+
}
71+
}

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

Lines changed: 11 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,17 @@
44
import android.graphics.drawable.ColorDrawable;
55
import android.graphics.drawable.Drawable;
66
import android.widget.ImageView;
7-
import android.widget.ImageView.ScaleType;
87

9-
import com.bumptech.glide.DrawableRequestBuilder;
10-
import com.bumptech.glide.DrawableTypeRequest;
118
import com.bumptech.glide.Glide;
129
import com.bumptech.glide.Priority;
13-
import com.bumptech.glide.RequestManager;
1410
import com.bumptech.glide.load.data.DataFetcher;
1511
import com.bumptech.glide.load.model.GlideUrl;
16-
import com.bumptech.glide.load.model.LazyHeaders;
1712
import com.bumptech.glide.load.model.stream.StreamModelLoader;
1813
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
1914
import com.bumptech.glide.request.RequestListener;
2015
import com.bumptech.glide.request.target.ImageViewTarget;
2116
import com.bumptech.glide.request.target.Target;
22-
import com.bumptech.glide.signature.StringSignature;
23-
import com.facebook.react.bridge.NoSuchKeyException;
2417
import com.facebook.react.bridge.ReadableMap;
25-
import com.facebook.react.bridge.ReadableMapKeySetIterator;
2618
import com.facebook.react.bridge.WritableMap;
2719
import com.facebook.react.bridge.WritableNativeMap;
2820
import com.facebook.react.common.MapBuilder;
@@ -33,9 +25,7 @@
3325

3426
import java.io.IOException;
3527
import java.io.InputStream;
36-
import java.util.HashMap;
3728
import java.util.Map;
38-
import java.util.UUID;
3929

4030
import javax.annotation.Nullable;
4131

@@ -49,21 +39,6 @@ class FastImageViewManager extends SimpleViewManager<ImageView> {
4939

5040
private static Drawable TRANSPARENT_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
5141

52-
private static Map<String, Priority> REACT_PRIORITY_MAP =
53-
new HashMap<String, Priority>() {{
54-
put("low", Priority.LOW);
55-
put("normal", Priority.NORMAL);
56-
put("high", Priority.HIGH);
57-
}};
58-
59-
private static Map<String, ImageView.ScaleType> REACT_RESIZE_MODE_MAP =
60-
new HashMap<String, ImageView.ScaleType>() {{
61-
put("contain", ScaleType.FIT_CENTER);
62-
put("cover", ScaleType.CENTER_CROP);
63-
put("stretch", ScaleType.FIT_XY);
64-
put("center", ScaleType.CENTER);
65-
}};
66-
6742
@Override
6843
public String getName() {
6944
return REACT_CLASS;
@@ -125,51 +100,27 @@ public void setSrc(ImageView view, @Nullable ReadableMap source) {
125100
return;
126101
}
127102

128-
final String uriProp = source.getString("uri");
129-
130-
// Get the headers prop and add to glideUrl.
131-
GlideUrl glideUrl;
132-
try {
133-
final ReadableMap headersMap = source.getMap("headers");
134-
ReadableMapKeySetIterator headersIterator = headersMap.keySetIterator();
135-
LazyHeaders.Builder headersBuilder = new LazyHeaders.Builder();
136-
while (headersIterator.hasNextKey()) {
137-
String key = headersIterator.nextKey();
138-
String value = headersMap.getString(key);
139-
headersBuilder.addHeader(key, value);
140-
}
141-
LazyHeaders headers = headersBuilder.build();
142-
glideUrl = new GlideUrl(uriProp, headers);
143-
} catch (NoSuchKeyException e) {
144-
// If there is no headers object.
145-
glideUrl = new GlideUrl(uriProp);
146-
}
103+
// Get the GlideUrl which contains header info.
104+
final GlideUrl glideUrl = FastImageViewConverter.glideUrl(source);
147105

148-
// Get the priority prop.
149-
String priorityProp = "normal";
150-
try {
151-
priorityProp = source.getString("priority");
152-
} catch (Exception e) {
153-
// Noop.
154-
}
155-
final Priority priority = REACT_PRIORITY_MAP.get(priorityProp);
106+
// Get priority.
107+
final Priority priority = FastImageViewConverter.priority(source);
156108

157109
// Cancel existing request.
158110
Glide.clear(view);
159111

160112
Glide
161-
.with(view.getContext())
162-
.load(glideUrl)
163-
.priority(priority)
164-
.placeholder(TRANSPARENT_DRAWABLE)
165-
.listener(LISTENER)
166-
.into(view);
113+
.with(view.getContext())
114+
.load(glideUrl)
115+
.priority(priority)
116+
.placeholder(TRANSPARENT_DRAWABLE)
117+
.listener(LISTENER)
118+
.into(view);
167119
}
168120

169121
@ReactProp(name = "resizeMode")
170122
public void setResizeMode(ImageView view, String resizeMode) {
171-
if (resizeMode == null) resizeMode = "contain";
172-
final ImageView.ScaleType scaleType = REACT_RESIZE_MODE_MAP.get(resizeMode);
123+
final ImageView.ScaleType scaleType = FastImageViewConverter.scaleType(resizeMode);
173124
view.setScaleType(scaleType);
174125
}
175126

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.dylanvann.fastimage;
2+
3+
import android.app.Activity;
4+
import android.graphics.Color;
5+
import android.graphics.drawable.ColorDrawable;
6+
import android.graphics.drawable.Drawable;
7+
8+
import com.bumptech.glide.Glide;
9+
import com.bumptech.glide.Priority;
10+
import com.bumptech.glide.load.engine.DiskCacheStrategy;
11+
import com.bumptech.glide.load.model.GlideUrl;
12+
import com.facebook.react.bridge.ReactApplicationContext;
13+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
14+
import com.facebook.react.bridge.ReactMethod;
15+
import com.facebook.react.bridge.ReadableArray;
16+
import com.facebook.react.bridge.ReadableMap;
17+
18+
class FastImageViewModule extends ReactContextBaseJavaModule {
19+
20+
private static final String REACT_CLASS = "FastImageView";
21+
22+
FastImageViewModule(ReactApplicationContext reactContext) {
23+
super(reactContext);
24+
}
25+
26+
@Override
27+
public String getName() {
28+
return REACT_CLASS;
29+
}
30+
31+
private static Drawable TRANSPARENT_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
32+
33+
@ReactMethod
34+
public void preload(final ReadableArray sources) {
35+
final Activity activity = getCurrentActivity();
36+
activity.runOnUiThread(new Runnable() {
37+
@Override
38+
public void run() {
39+
for (int i = 0; i < sources.size(); i++) {
40+
final ReadableMap source = sources.getMap(i);
41+
final GlideUrl glideUrl = FastImageViewConverter.glideUrl(source);
42+
final Priority priority = FastImageViewConverter.priority(source);
43+
Glide
44+
.with(activity.getApplicationContext())
45+
.load(glideUrl)
46+
.priority(priority)
47+
.placeholder(TRANSPARENT_DRAWABLE)
48+
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
49+
.preload();
50+
}
51+
}
52+
});
53+
}
54+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.dylanvann.fastimage;
22

3+
import com.dylanvann.fastimage.FastImageViewModule;
4+
35
import com.facebook.react.ReactPackage;
46
import com.facebook.react.bridge.NativeModule;
57
import com.facebook.react.bridge.JavaScriptModule;
@@ -10,10 +12,9 @@
1012
import java.util.List;
1113

1214
public class FastImageViewPackage implements ReactPackage {
13-
1415
@Override
1516
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
16-
return Collections.emptyList();
17+
return Collections.<NativeModule>singletonList(new FastImageViewModule(reactContext));
1718
}
1819

1920
@Override

0 commit comments

Comments
 (0)