react-native-fast-image 9.1.0 → 9.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/android/build.gradle +12 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +23 -156
- package/android/src/main/java/com/dylanvann/fastimage/FastImageProgressListener.java +4 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +1 -4
- package/android/src/main/java/com/dylanvann/fastimage/FastImageSharedDownloads.java +961 -196
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +128 -28
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewModule.java +9 -6
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +98 -63
- package/dist/index.d.ts +6 -2
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ import android.app.Activity;
|
|
|
8
8
|
import android.content.Context;
|
|
9
9
|
import android.content.ContextWrapper;
|
|
10
10
|
import android.graphics.PorterDuff;
|
|
11
|
+
import android.util.Log;
|
|
11
12
|
|
|
12
13
|
import androidx.annotation.NonNull;
|
|
13
14
|
|
|
@@ -21,25 +22,53 @@ import com.facebook.react.common.MapBuilder;
|
|
|
21
22
|
import com.facebook.react.uimanager.LayoutShadowNode;
|
|
22
23
|
import com.facebook.react.uimanager.SimpleViewManager;
|
|
23
24
|
import com.facebook.react.bridge.ReactContext;
|
|
25
|
+
import com.facebook.react.bridge.UiThreadUtil;
|
|
24
26
|
import com.facebook.react.uimanager.ThemedReactContext;
|
|
25
27
|
import com.facebook.react.uimanager.PixelUtil;
|
|
26
28
|
import com.facebook.react.uimanager.PointerEvents;
|
|
27
29
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
28
30
|
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
|
29
31
|
|
|
32
|
+
import java.util.ArrayList;
|
|
33
|
+
import java.util.HashMap;
|
|
34
|
+
import java.util.LinkedHashSet;
|
|
30
35
|
import java.util.List;
|
|
31
36
|
import java.util.Locale;
|
|
32
37
|
import java.util.Map;
|
|
33
|
-
import java.util.
|
|
38
|
+
import java.util.Set;
|
|
39
|
+
import java.util.concurrent.atomic.AtomicLong;
|
|
34
40
|
|
|
35
41
|
import javax.annotation.Nullable;
|
|
36
42
|
|
|
37
|
-
class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl>
|
|
43
|
+
class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> {
|
|
38
44
|
|
|
39
45
|
static final String REACT_CLASS = "FastImageView";
|
|
40
46
|
static final String REACT_ON_LOAD_START_EVENT = "onFastImageLoadStart";
|
|
41
47
|
static final String REACT_ON_PROGRESS_EVENT = "onFastImageProgress";
|
|
42
|
-
|
|
48
|
+
static final String LOG_TAG = "FastImage";
|
|
49
|
+
// Download progress, by the key downloads are shared by
|
|
50
|
+
// (FastImageSharedDownloads.key): the views that get it, those with an onProgress from
|
|
51
|
+
// their load's onLoadStart to its onLoad or onError, and the latest step a
|
|
52
|
+
// download reported, until the UI thread sends it. The UI thread adds and
|
|
53
|
+
// removes views, and downloads report steps on their own threads: only
|
|
54
|
+
// used under its lock.
|
|
55
|
+
private static final Map<String, Progress> PROGRESS = new HashMap<>();
|
|
56
|
+
// Numbers the steps downloads report, so a view can tell those reported
|
|
57
|
+
// before its load started (see FastImageViewWithUrl.takesProgress).
|
|
58
|
+
private static final AtomicLong REPORTED_STEPS = new AtomicLong();
|
|
59
|
+
// Whether any view gets progress (set under PROGRESS's lock), so downloads
|
|
60
|
+
// can skip reporting without taking any lock.
|
|
61
|
+
private static volatile boolean tracking = false;
|
|
62
|
+
|
|
63
|
+
private static final class Progress {
|
|
64
|
+
final Set<FastImageViewWithUrl> views = new LinkedHashSet<>();
|
|
65
|
+
// A step waits to be sent: later ones replace it meanwhile, so a url
|
|
66
|
+
// has at most one post to the UI thread at a time.
|
|
67
|
+
boolean posted;
|
|
68
|
+
long loaded;
|
|
69
|
+
long total;
|
|
70
|
+
long step;
|
|
71
|
+
}
|
|
43
72
|
|
|
44
73
|
@NonNull
|
|
45
74
|
@Override
|
|
@@ -143,10 +172,10 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
|
|
|
143
172
|
view.setPaused(paused);
|
|
144
173
|
}
|
|
145
174
|
|
|
146
|
-
// Set when the image has an onProgress (see
|
|
175
|
+
// Set when the image has an onProgress (see onDownloadProgress).
|
|
147
176
|
@ReactProp(name = "trackProgress")
|
|
148
177
|
public void setTrackProgress(FastImageViewWithUrl view, boolean trackProgress) {
|
|
149
|
-
view.trackProgress
|
|
178
|
+
view.setTrackProgress(trackProgress);
|
|
150
179
|
}
|
|
151
180
|
|
|
152
181
|
@ReactProp(name = "resizeMode")
|
|
@@ -160,12 +189,9 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
|
|
|
160
189
|
|
|
161
190
|
@Override
|
|
162
191
|
public void onDropViewInstance(@NonNull FastImageViewWithUrl view) {
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// Same key as when the view was tracked (toStringUrl, not toString,
|
|
167
|
-
// which differ for urls that need escaping).
|
|
168
|
-
view.untrackUrl(VIEWS_FOR_URLS);
|
|
192
|
+
// Cancels its request, and nothing loads or tracks progress for it
|
|
193
|
+
// any more (the progress map doesn't keep it, or its Activity).
|
|
194
|
+
view.drop();
|
|
169
195
|
|
|
170
196
|
super.onDropViewInstance(view);
|
|
171
197
|
}
|
|
@@ -181,26 +207,100 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
|
|
|
181
207
|
.build();
|
|
182
208
|
}
|
|
183
209
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
for (FastImageViewWithUrl view : viewsForKey) {
|
|
192
|
-
if (!view.trackProgress) continue;
|
|
193
|
-
WritableMap event = new WritableNativeMap();
|
|
194
|
-
event.putInt("loaded", (int) bytesRead);
|
|
195
|
-
event.putInt("total", (int) expectedLength);
|
|
196
|
-
FastImageEvents.send(view, REACT_ON_PROGRESS_EVENT, event);
|
|
210
|
+
// On the UI thread (FastImageViewWithUrl's updateProgressTracking).
|
|
211
|
+
static void trackProgress(String key, FastImageViewWithUrl view) {
|
|
212
|
+
synchronized (PROGRESS) {
|
|
213
|
+
Progress progress = PROGRESS.get(key);
|
|
214
|
+
if (progress == null) {
|
|
215
|
+
progress = new Progress();
|
|
216
|
+
PROGRESS.put(key, progress);
|
|
197
217
|
}
|
|
218
|
+
progress.views.add(view);
|
|
219
|
+
tracking = true;
|
|
198
220
|
}
|
|
199
221
|
}
|
|
200
222
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
// On the UI thread. A key's entry goes once it has no views.
|
|
224
|
+
static void untrackProgress(String key, FastImageViewWithUrl view) {
|
|
225
|
+
synchronized (PROGRESS) {
|
|
226
|
+
Progress progress = PROGRESS.get(key);
|
|
227
|
+
if (progress == null) return;
|
|
228
|
+
progress.views.remove(view);
|
|
229
|
+
if (progress.views.isEmpty()) PROGRESS.remove(key);
|
|
230
|
+
tracking = !PROGRESS.isEmpty();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Whether any view gets progress now (a hint: a view can start loading
|
|
235
|
+
// just after).
|
|
236
|
+
static boolean wantsProgress() {
|
|
237
|
+
return tracking;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// The latest step downloads reported (for a load that starts now).
|
|
241
|
+
static long progressStep() {
|
|
242
|
+
return REPORTED_STEPS.get();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// A download's progress (FastImageSharedDownloads), on its thread, which
|
|
246
|
+
// only notes it if a view wants it (so downloads of images without an
|
|
247
|
+
// onProgress post nothing). It's sent from the UI thread, where
|
|
248
|
+
// views start and end their loads: a view only gets it during its own load,
|
|
249
|
+
// not after its onLoad or once it loads again, and progress posted during
|
|
250
|
+
// a download comes before the onLoad Glide posts after it.
|
|
251
|
+
static void onDownloadProgress(final String key, long loaded, long total) {
|
|
252
|
+
synchronized (PROGRESS) {
|
|
253
|
+
Progress progress = PROGRESS.get(key);
|
|
254
|
+
if (progress == null) return;
|
|
255
|
+
// While a post is waiting, it keeps the furthest step: another
|
|
256
|
+
// download of the key may report one behind it meanwhile. A step
|
|
257
|
+
// kept keeps its number, so a load that started after it (e.g. a
|
|
258
|
+
// reload, whose download reports behind it) doesn't take it.
|
|
259
|
+
if (!progress.posted || (double) loaded / total >= (double) progress.loaded / progress.total) {
|
|
260
|
+
progress.loaded = loaded;
|
|
261
|
+
progress.total = total;
|
|
262
|
+
progress.step = REPORTED_STEPS.incrementAndGet();
|
|
263
|
+
}
|
|
264
|
+
if (progress.posted) return;
|
|
265
|
+
progress.posted = true;
|
|
266
|
+
}
|
|
267
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
268
|
+
@Override
|
|
269
|
+
public void run() {
|
|
270
|
+
sendProgress(key);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private static void sendProgress(String key) {
|
|
276
|
+
List<FastImageViewWithUrl> views;
|
|
277
|
+
long loaded;
|
|
278
|
+
long total;
|
|
279
|
+
long step;
|
|
280
|
+
synchronized (PROGRESS) {
|
|
281
|
+
Progress progress = PROGRESS.get(key);
|
|
282
|
+
// Sent already, or no view wants it any more.
|
|
283
|
+
if (progress == null || !progress.posted) return;
|
|
284
|
+
progress.posted = false;
|
|
285
|
+
loaded = progress.loaded;
|
|
286
|
+
total = progress.total;
|
|
287
|
+
step = progress.step;
|
|
288
|
+
views = new ArrayList<>(progress.views);
|
|
289
|
+
}
|
|
290
|
+
for (FastImageViewWithUrl view : views) {
|
|
291
|
+
if (!view.takesProgress(step, loaded, total)) continue;
|
|
292
|
+
WritableMap event = new WritableNativeMap();
|
|
293
|
+
event.putInt("loaded", (int) loaded);
|
|
294
|
+
event.putInt("total", (int) total);
|
|
295
|
+
try {
|
|
296
|
+
FastImageEvents.send(view, REACT_ON_PROGRESS_EVENT, event);
|
|
297
|
+
} catch (RuntimeException e) {
|
|
298
|
+
// E.g. the view's React instance is gone: progress isn't worth
|
|
299
|
+
// crashing the app for.
|
|
300
|
+
// (Not the key: it has the request's headers.)
|
|
301
|
+
Log.w(LOG_TAG, "Couldn't send onProgress", e);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
204
304
|
}
|
|
205
305
|
|
|
206
306
|
private static boolean isValidContextForGlide(final Context context) {
|
|
@@ -269,6 +369,6 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
|
|
|
269
369
|
@Override
|
|
270
370
|
protected void onAfterUpdateTransaction(@NonNull FastImageViewWithUrl view) {
|
|
271
371
|
super.onAfterUpdateTransaction(view);
|
|
272
|
-
view.onAfterUpdate(this, view.requestManager
|
|
372
|
+
view.onAfterUpdate(this, view.requestManager);
|
|
273
373
|
}
|
|
274
374
|
}
|
|
@@ -225,8 +225,9 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
|
|
|
225
225
|
}
|
|
226
226
|
};
|
|
227
227
|
// A remote image is downloaded to the disk cache first,
|
|
228
|
-
//
|
|
229
|
-
// (FastImageSharedDownloads),
|
|
228
|
+
// then decoded from there. Views that load it meanwhile
|
|
229
|
+
// share the download (FastImageSharedDownloads), and those
|
|
230
|
+
// that get to downloading just after it ended read its file.
|
|
230
231
|
final boolean download = !imageSource.isWebCache() && imageSource.isRemote();
|
|
231
232
|
pendingPreloads.add(!download ? decode : new Runnable() {
|
|
232
233
|
@Override
|
|
@@ -267,16 +268,18 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
|
|
|
267
268
|
|
|
268
269
|
// Downloads the image into Glide's disk cache without decoding it (if it
|
|
269
270
|
// isn't there), then calls back with its file or the error, on the UI
|
|
270
|
-
// thread.
|
|
271
|
-
//
|
|
271
|
+
// thread. Views that load the image meanwhile share its download
|
|
272
|
+
// (FastImageSharedDownloads); for a preload (`shared`), those whose
|
|
273
|
+
// request gets to downloading just after it finished read its file.
|
|
272
274
|
private static void loadFile(Context context, Object model, RequestOptions options, boolean shared, final FileCallback callback) {
|
|
273
|
-
final String key = shared && model instanceof FastImageUrl
|
|
275
|
+
final String key = shared && model instanceof FastImageUrl
|
|
276
|
+
? FastImageSharedDownloads.key(((FastImageUrl) model).url)
|
|
277
|
+
: null;
|
|
274
278
|
FastImageGlide.get(context);
|
|
275
279
|
Glide.with(context)
|
|
276
280
|
.asFile()
|
|
277
281
|
.load(model)
|
|
278
282
|
.apply(options)
|
|
279
|
-
.set(FastImageSharedDownloads.PRELOAD, shared)
|
|
280
283
|
.listener(new RequestListener<File>() {
|
|
281
284
|
@Override
|
|
282
285
|
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> target, boolean isFirstResource) {
|
|
@@ -46,11 +46,6 @@ import com.facebook.react.bridge.WritableNativeMap;
|
|
|
46
46
|
import com.facebook.react.uimanager.PointerEvents;
|
|
47
47
|
import com.facebook.react.uimanager.ReactPointerEventsView;
|
|
48
48
|
|
|
49
|
-
import java.util.ArrayList;
|
|
50
|
-
import java.util.Collections;
|
|
51
|
-
import java.util.List;
|
|
52
|
-
import java.util.Map;
|
|
53
|
-
|
|
54
49
|
import javax.annotation.Nonnull;
|
|
55
50
|
|
|
56
51
|
class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEventsView {
|
|
@@ -58,7 +53,6 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
58
53
|
private ReadableMap mSource = null;
|
|
59
54
|
private Drawable mDefaultSource = null;
|
|
60
55
|
|
|
61
|
-
public GlideUrl glideUrl;
|
|
62
56
|
// Null when the view was created in a destroyed Activity (nothing loads).
|
|
63
57
|
@Nullable
|
|
64
58
|
final RequestManager requestManager;
|
|
@@ -108,8 +102,6 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
108
102
|
// What the last update was called with, to load once the view has a size.
|
|
109
103
|
@Nullable
|
|
110
104
|
private FastImageViewManager mManager;
|
|
111
|
-
@Nullable
|
|
112
|
-
private Map<String, List<FastImageViewWithUrl>> mViewsForUrlsMap;
|
|
113
105
|
|
|
114
106
|
public void setSources(@Nullable ReadableArray sources) {
|
|
115
107
|
mNeedsReload = true;
|
|
@@ -163,7 +155,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
163
155
|
post(new Runnable() {
|
|
164
156
|
@Override
|
|
165
157
|
public void run() {
|
|
166
|
-
if (mNeedsReload && mManager != null) onAfterUpdate(mManager, requestManager
|
|
158
|
+
if (mNeedsReload && mManager != null) onAfterUpdate(mManager, requestManager);
|
|
167
159
|
}
|
|
168
160
|
});
|
|
169
161
|
return true;
|
|
@@ -214,8 +206,64 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
214
206
|
|
|
215
207
|
// Pauses GIFs on the frame they're showing (the view's own animation).
|
|
216
208
|
private boolean mPaused = false;
|
|
217
|
-
//
|
|
218
|
-
|
|
209
|
+
// Progress (see FastImageViewManager.onDownloadProgress): whether the image
|
|
210
|
+
// has an onProgress, the url its current load downloads (null for a local
|
|
211
|
+
// image, and once the load has ended), the progress key the view gets
|
|
212
|
+
// progress for (the loading url's, while it has an onProgress), the last
|
|
213
|
+
// step downloads reported before the load started, and the fraction the
|
|
214
|
+
// load has had.
|
|
215
|
+
private boolean mTrackProgress = false;
|
|
216
|
+
@Nullable
|
|
217
|
+
private GlideUrl mLoadingUrl;
|
|
218
|
+
@Nullable
|
|
219
|
+
private String mTrackedKey;
|
|
220
|
+
private long mProgressSince;
|
|
221
|
+
private double mProgressFraction;
|
|
222
|
+
// Dropped by React (FastImageViewManager.onDropViewInstance): reloads it
|
|
223
|
+
// posted to itself don't load anything, or track progress, any more.
|
|
224
|
+
private boolean mDropped = false;
|
|
225
|
+
|
|
226
|
+
void setTrackProgress(boolean trackProgress) {
|
|
227
|
+
mTrackProgress = trackProgress;
|
|
228
|
+
updateProgressTracking();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// The load ended (onLoad or onError), or the view no longer loads its
|
|
232
|
+
// source: no more progress, and the progress map no longer keeps the view
|
|
233
|
+
// (and its Activity) alive (#384).
|
|
234
|
+
void endProgress() {
|
|
235
|
+
mLoadingUrl = null;
|
|
236
|
+
updateProgressTracking();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
void drop() {
|
|
240
|
+
mDropped = true;
|
|
241
|
+
clearView(requestManager);
|
|
242
|
+
endProgress();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Whether a step a download reported is for the current load, and doesn't
|
|
246
|
+
// go back: one reported before it started is for an earlier load, e.g. of
|
|
247
|
+
// the same url before resizeMode changed, posted while that load still
|
|
248
|
+
// ran, and one below what the load has had is from another download of
|
|
249
|
+
// the key (e.g. one that started after the first had ended).
|
|
250
|
+
boolean takesProgress(long step, long loaded, long total) {
|
|
251
|
+
if (step <= mProgressSince) return false;
|
|
252
|
+
double fraction = (double) loaded / total;
|
|
253
|
+
if (fraction < mProgressFraction) return false;
|
|
254
|
+
mProgressFraction = fraction;
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private void updateProgressTracking() {
|
|
259
|
+
String key = mTrackProgress && mLoadingUrl != null && !mDropped
|
|
260
|
+
? FastImageSharedDownloads.key(mLoadingUrl)
|
|
261
|
+
: null;
|
|
262
|
+
if (key == null ? mTrackedKey == null : key.equals(mTrackedKey)) return;
|
|
263
|
+
if (mTrackedKey != null) FastImageViewManager.untrackProgress(mTrackedKey, this);
|
|
264
|
+
mTrackedKey = key;
|
|
265
|
+
if (key != null) FastImageViewManager.trackProgress(key, this);
|
|
266
|
+
}
|
|
219
267
|
|
|
220
268
|
// The `transition` prop (from the next load): how long a loaded image
|
|
221
269
|
// takes to fade in, in milliseconds (0 for no fade), whether it also fades
|
|
@@ -240,14 +288,20 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
240
288
|
// Fades a loaded image in, unless it's from a cache that skipOnCacheHit
|
|
241
289
|
// skips, as Glide's and Coil's cross-fades and Fresco decide: from the
|
|
242
290
|
// memory cache it shows at once (by default), and from the disk cache
|
|
243
|
-
// too with 'all'. Downloads, local files and bundled images fade.
|
|
291
|
+
// too with 'all'. Downloads, local files and bundled images fade. A `web`
|
|
292
|
+
// image isn't kept in the memory cache, so one from its HTTP cache
|
|
293
|
+
// without the network (a disk cache hit, see FastImageSharedDownloads)
|
|
294
|
+
// counts as one from memory: shown again, e.g. in a list scrolled back
|
|
295
|
+
// up, it shows at once.
|
|
244
296
|
private final class FadeFactory implements TransitionFactory<Drawable> {
|
|
245
297
|
private final int duration;
|
|
246
298
|
private final String skipOnCacheHit;
|
|
299
|
+
private final boolean webCache;
|
|
247
300
|
|
|
248
|
-
FadeFactory(int duration, String skipOnCacheHit) {
|
|
301
|
+
FadeFactory(int duration, String skipOnCacheHit, boolean webCache) {
|
|
249
302
|
this.duration = duration;
|
|
250
303
|
this.skipOnCacheHit = skipOnCacheHit;
|
|
304
|
+
this.webCache = webCache;
|
|
251
305
|
}
|
|
252
306
|
|
|
253
307
|
@Override
|
|
@@ -266,6 +320,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
266
320
|
private boolean skips(DataSource dataSource) {
|
|
267
321
|
if (skipOnCacheHit.equals("none")) return false;
|
|
268
322
|
if (dataSource == DataSource.MEMORY_CACHE) return true;
|
|
323
|
+
if (webCache && dataSource == DataSource.DATA_DISK_CACHE) return true;
|
|
269
324
|
// Glide also keeps a local file's or bundled image's decoded
|
|
270
325
|
// image in its disk cache (at the size it was decoded at, and not
|
|
271
326
|
// for GIFs), so with 'all' they usually only fade the first time.
|
|
@@ -695,6 +750,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
695
750
|
// The loading image loaded (FastImageRequestListener).
|
|
696
751
|
void onImageLoaded() {
|
|
697
752
|
mLoadEnded = true;
|
|
753
|
+
endProgress();
|
|
698
754
|
mShownRequest = mLoadingRequest;
|
|
699
755
|
mShownWidth = mLoadingWidth;
|
|
700
756
|
mShownHeight = mLoadingHeight;
|
|
@@ -734,7 +790,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
734
790
|
private boolean isResized() {
|
|
735
791
|
int width = getWidth() - getPaddingLeft() - getPaddingRight();
|
|
736
792
|
int height = getHeight() - getPaddingTop() - getPaddingBottom();
|
|
737
|
-
return requestManager != null && !mNeedsReload
|
|
793
|
+
return requestManager != null && !mDropped && !mNeedsReload
|
|
738
794
|
&& mShownRequest != null && mShownRequest == mLoadingRequest
|
|
739
795
|
&& mShownWidth > 0 && mShownHeight > 0 && width > 0 && height > 0
|
|
740
796
|
&& (width != mShownWidth || height != mShownHeight);
|
|
@@ -752,7 +808,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
752
808
|
clearView(requestManager);
|
|
753
809
|
RequestBuilder<Drawable> builder = shown.clone()
|
|
754
810
|
.error(current.clone())
|
|
755
|
-
.listener(new FastImageRequestListener(null,
|
|
811
|
+
.listener(new FastImageRequestListener(null, true, false));
|
|
756
812
|
if (meanwhile == null) builder = builder.thumbnail(current);
|
|
757
813
|
into(shown, builder, meanwhile);
|
|
758
814
|
}
|
|
@@ -776,7 +832,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
776
832
|
clearView(requestManager);
|
|
777
833
|
RequestBuilder<Drawable> builder = request.clone()
|
|
778
834
|
.error(current.clone())
|
|
779
|
-
.listener(new FastImageRequestListener(null,
|
|
835
|
+
.listener(new FastImageRequestListener(null, true, false));
|
|
780
836
|
if (meanwhile == null) builder = builder.thumbnail(current);
|
|
781
837
|
into(request, builder, meanwhile);
|
|
782
838
|
} else if (mLoadingRequest != null && !mLoadEnded) {
|
|
@@ -848,6 +904,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
848
904
|
// defaultSource then, but not over a thumbnail: show it here, as iOS does.
|
|
849
905
|
void onImageFailed(boolean hadThumbnail) {
|
|
850
906
|
mLoadEnded = true;
|
|
907
|
+
endProgress();
|
|
851
908
|
mShownRequest = null;
|
|
852
909
|
if (!hadThumbnail) return;
|
|
853
910
|
final int load = mLoadCount;
|
|
@@ -863,10 +920,9 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
863
920
|
@SuppressLint("CheckResult")
|
|
864
921
|
public void onAfterUpdate(
|
|
865
922
|
@Nonnull FastImageViewManager manager,
|
|
866
|
-
@Nullable RequestManager requestManager
|
|
867
|
-
|
|
923
|
+
@Nullable RequestManager requestManager) {
|
|
924
|
+
if (mDropped) return;
|
|
868
925
|
mManager = manager;
|
|
869
|
-
mViewsForUrlsMap = viewsForUrlsMap;
|
|
870
926
|
if (mBlurChanged && !mNeedsReload) {
|
|
871
927
|
mBlurChanged = false;
|
|
872
928
|
reblur();
|
|
@@ -887,7 +943,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
887
943
|
if (!mWaitsForSize) return;
|
|
888
944
|
mWaitsForSize = false;
|
|
889
945
|
mWaitedForSize = true;
|
|
890
|
-
onAfterUpdate(manager, requestManager
|
|
946
|
+
onAfterUpdate(manager, requestManager);
|
|
891
947
|
}
|
|
892
948
|
});
|
|
893
949
|
return;
|
|
@@ -917,7 +973,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
917
973
|
// Cancel existing requests.
|
|
918
974
|
clearView(requestManager);
|
|
919
975
|
|
|
920
|
-
|
|
976
|
+
endProgress();
|
|
921
977
|
|
|
922
978
|
// Clear the image.
|
|
923
979
|
setImageDrawable(null);
|
|
@@ -942,38 +998,33 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
942
998
|
// Cancel existing requests.
|
|
943
999
|
clearView(requestManager);
|
|
944
1000
|
|
|
945
|
-
|
|
1001
|
+
endProgress();
|
|
946
1002
|
setImageDrawable(mDefaultSource);
|
|
947
1003
|
return;
|
|
948
1004
|
}
|
|
949
1005
|
|
|
950
|
-
// `imageSource` may be null and we still continue, if `defaultSource` is not null
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
String key = glideUrl == null ? null : glideUrl.toStringUrl();
|
|
954
|
-
|
|
955
|
-
// Loading a different url: stop tracking the old one.
|
|
956
|
-
if (this.glideUrl != null && !this.glideUrl.toStringUrl().equals(key)) {
|
|
957
|
-
untrackUrl(viewsForUrlsMap);
|
|
958
|
-
}
|
|
1006
|
+
// `imageSource` may be null and we still continue, if `defaultSource` is not null.
|
|
1007
|
+
// What Glide loads: a FastImageUrl for a remote image.
|
|
1008
|
+
Object model = imageSource == null ? null : imageSource.getSourceForLoad();
|
|
959
1009
|
|
|
960
1010
|
// Before the view clears (see shownCopy).
|
|
961
1011
|
Drawable meanwhile = shownRequest != null && imageSource != null ? shownCopy(shownRequest) : null;
|
|
962
1012
|
|
|
963
1013
|
// Cancel existing request.
|
|
964
|
-
this.glideUrl = glideUrl;
|
|
965
1014
|
clearView(requestManager);
|
|
966
1015
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1016
|
+
// The url this load downloads, for its progress: a remote image's
|
|
1017
|
+
// (without Glide, in a destroyed Activity, nothing loads).
|
|
1018
|
+
mLoadingUrl = requestManager != null && model instanceof FastImageUrl
|
|
1019
|
+
? ((FastImageUrl) model).url
|
|
1020
|
+
: null;
|
|
1021
|
+
// A restart (reblur) is the same load: its progress doesn't go back,
|
|
1022
|
+
// and steps already on their way to it still count.
|
|
1023
|
+
if (!restarting) {
|
|
1024
|
+
mProgressSince = FastImageViewManager.progressStep();
|
|
1025
|
+
mProgressFraction = 0;
|
|
976
1026
|
}
|
|
1027
|
+
updateProgressTracking();
|
|
977
1028
|
|
|
978
1029
|
if (imageSource != null && !restarting) {
|
|
979
1030
|
// This is an orphan even without a load/loadend when only loading a placeholder
|
|
@@ -982,7 +1033,6 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
982
1033
|
|
|
983
1034
|
if (requestManager != null) {
|
|
984
1035
|
// Records the image's own size when Glide decodes it, for onLoad.
|
|
985
|
-
Object model = imageSource == null ? null : imageSource.getSourceForLoad();
|
|
986
1036
|
RequestBuilder<Drawable> base =
|
|
987
1037
|
requestManager
|
|
988
1038
|
// This will make this work for remote and local images. e.g.
|
|
@@ -1013,37 +1063,22 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1013
1063
|
boolean replacing = shownRequest != null && model != null;
|
|
1014
1064
|
if (mTransitionDuration > 0 && !switching && (!replacing || mTransitionBetweenImages)) {
|
|
1015
1065
|
builder = builder.transition(DrawableTransitionOptions.with(
|
|
1016
|
-
new FadeFactory(
|
|
1066
|
+
new FadeFactory(
|
|
1067
|
+
mTransitionDuration,
|
|
1068
|
+
mTransitionSkipOnCacheHit,
|
|
1069
|
+
imageSource != null && imageSource.isWebCache())));
|
|
1017
1070
|
}
|
|
1018
1071
|
if (thumbnail) {
|
|
1019
1072
|
builder = builder.thumbnail(fromCache(shownRequest));
|
|
1020
1073
|
}
|
|
1021
1074
|
|
|
1022
|
-
if (
|
|
1023
|
-
builder.listener(new FastImageRequestListener(
|
|
1075
|
+
if (imageSource != null)
|
|
1076
|
+
builder.listener(new FastImageRequestListener(imageSource, thumbnail, true));
|
|
1024
1077
|
|
|
1025
1078
|
into(request, builder, meanwhile);
|
|
1026
1079
|
}
|
|
1027
1080
|
}
|
|
1028
1081
|
|
|
1029
|
-
// Removes this view from the list of views for its current url (used to send
|
|
1030
|
-
// progress events), which otherwise kept it, and its Activity, alive after
|
|
1031
|
-
// its source changed (#384). The url's progress listener is only forgotten
|
|
1032
|
-
// when no other view uses it.
|
|
1033
|
-
void untrackUrl(@Nonnull Map<String, List<FastImageViewWithUrl>> viewsForUrlsMap) {
|
|
1034
|
-
if (glideUrl == null) return;
|
|
1035
|
-
String key = glideUrl.toStringUrl();
|
|
1036
|
-
List<FastImageViewWithUrl> viewsForKey = viewsForUrlsMap.get(key);
|
|
1037
|
-
if (viewsForKey != null) {
|
|
1038
|
-
viewsForKey.remove(this);
|
|
1039
|
-
if (viewsForKey.isEmpty()) viewsForUrlsMap.remove(key);
|
|
1040
|
-
}
|
|
1041
|
-
if (viewsForKey == null || viewsForKey.isEmpty()) {
|
|
1042
|
-
FastImageOkHttpProgressGlideModule.forget(key);
|
|
1043
|
-
}
|
|
1044
|
-
glideUrl = null;
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
1082
|
public void clearView(@Nullable RequestManager requestManager) {
|
|
1048
1083
|
// Glide keeps the view's request under its own tag id, not getTag().
|
|
1049
1084
|
// Without a request this does nothing.
|
package/dist/index.d.ts
CHANGED
|
@@ -122,7 +122,9 @@ export type Transition = {
|
|
|
122
122
|
* usually only the first time (the disk cache usually keeps them too).
|
|
123
123
|
* - `'none'`: always fades.
|
|
124
124
|
*
|
|
125
|
-
* Downloads, local files and bundled images (`require()`) fade in.
|
|
125
|
+
* Downloads, local files and bundled images (`require()`) fade in. On
|
|
126
|
+
* Android, a `cache: 'web'` image from its HTTP cache, without a request,
|
|
127
|
+
* counts as one from the memory cache (these images aren't kept there).
|
|
126
128
|
* @default 'memory'
|
|
127
129
|
*/
|
|
128
130
|
skipOnCacheHit?: 'none' | 'memory' | 'all' | null;
|
|
@@ -464,7 +466,9 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
464
466
|
onLoad?(event: OnLoadEvent): void;
|
|
465
467
|
/**
|
|
466
468
|
* Called on an image fetching error, with a message describing it (e.g.
|
|
467
|
-
* the HTTP status code).
|
|
469
|
+
* the HTTP status code). On iOS and Android, a download that gets nothing
|
|
470
|
+
* from the server for 15 seconds fails too (on Android, unless the app's
|
|
471
|
+
* OkHttp client has timeouts of its own).
|
|
468
472
|
*
|
|
469
473
|
* @example
|
|
470
474
|
* ```jsx
|