react-native-fast-image 9.0.3 → 9.2.0
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 +14 -6
- package/android/build.gradle +12 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageGlide.java +7 -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 +948 -196
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +128 -28
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewModule.java +44 -22
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +85 -60
- package/dist/index.cjs.js +2 -2
- package/dist/index.cjs.js.flow +4 -2
- package/dist/index.cjs.web.js +2 -2
- package/dist/index.cjs.web.js.flow +4 -2
- package/dist/index.d.ts +33 -7
- package/dist/index.js +2 -2
- package/dist/index.js.flow +4 -2
- package/dist/index.web.js +2 -2
- package/dist/index.web.js.flow +4 -2
- package/ios/FastImage/FFFastImageViewManager.m +2 -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
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.dylanvann.fastimage;
|
|
2
2
|
|
|
3
|
-
import android.app.Activity;
|
|
4
3
|
import android.content.Context;
|
|
5
4
|
import android.graphics.BitmapFactory;
|
|
6
5
|
import android.graphics.drawable.Drawable;
|
|
@@ -226,8 +225,9 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
|
|
|
226
225
|
}
|
|
227
226
|
};
|
|
228
227
|
// A remote image is downloaded to the disk cache first,
|
|
229
|
-
//
|
|
230
|
-
// (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.
|
|
231
231
|
final boolean download = !imageSource.isWebCache() && imageSource.isRemote();
|
|
232
232
|
pendingPreloads.add(!download ? decode : new Runnable() {
|
|
233
233
|
@Override
|
|
@@ -268,16 +268,18 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
|
|
|
268
268
|
|
|
269
269
|
// Downloads the image into Glide's disk cache without decoding it (if it
|
|
270
270
|
// isn't there), then calls back with its file or the error, on the UI
|
|
271
|
-
// thread.
|
|
272
|
-
//
|
|
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.
|
|
273
274
|
private static void loadFile(Context context, Object model, RequestOptions options, boolean shared, final FileCallback callback) {
|
|
274
|
-
final String key = shared && model instanceof FastImageUrl
|
|
275
|
+
final String key = shared && model instanceof FastImageUrl
|
|
276
|
+
? FastImageSharedDownloads.key(((FastImageUrl) model).url)
|
|
277
|
+
: null;
|
|
275
278
|
FastImageGlide.get(context);
|
|
276
279
|
Glide.with(context)
|
|
277
280
|
.asFile()
|
|
278
281
|
.load(model)
|
|
279
282
|
.apply(options)
|
|
280
|
-
.set(FastImageSharedDownloads.PRELOAD, shared)
|
|
281
283
|
.listener(new RequestListener<File>() {
|
|
282
284
|
@Override
|
|
283
285
|
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> target, boolean isFirstResource) {
|
|
@@ -566,32 +568,38 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
|
|
|
566
568
|
});
|
|
567
569
|
}
|
|
568
570
|
|
|
571
|
+
// The cache functions use the app's context, not the current Activity's:
|
|
572
|
+
// there may be no Activity while JS runs (e.g. after Android destroyed it
|
|
573
|
+
// in the background). Glide clears its memory cache on the main thread.
|
|
569
574
|
@ReactMethod
|
|
570
575
|
public void clearMemoryCache(final Promise promise) {
|
|
571
|
-
final
|
|
572
|
-
|
|
573
|
-
promise.resolve(null);
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
activity.runOnUiThread(new Runnable() {
|
|
576
|
+
final ReactApplicationContext context = getReactApplicationContext();
|
|
577
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
578
578
|
@Override
|
|
579
579
|
public void run() {
|
|
580
|
-
|
|
581
|
-
|
|
580
|
+
try {
|
|
581
|
+
// Not before FastImage has used Glide: there's nothing of
|
|
582
|
+
// its in memory, and starting Glide here would set its
|
|
583
|
+
// disk cache size before a configureCache could.
|
|
584
|
+
if (FastImageGlide.used()) Glide.get(context).clearMemory();
|
|
585
|
+
promise.resolve(cleared());
|
|
586
|
+
} catch (RuntimeException e) {
|
|
587
|
+
promise.resolve(failure(e));
|
|
588
|
+
}
|
|
582
589
|
}
|
|
583
590
|
});
|
|
584
591
|
}
|
|
585
592
|
|
|
593
|
+
// On the native modules' thread: Glide clears its disk cache off the main
|
|
594
|
+
// thread.
|
|
586
595
|
@ReactMethod
|
|
587
596
|
public void clearDiskCache(Promise promise) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
597
|
+
try {
|
|
598
|
+
FastImageGlide.getRegistered(getReactApplicationContext()).clearDiskCache();
|
|
599
|
+
} catch (RuntimeException e) {
|
|
600
|
+
promise.resolve(failure(e));
|
|
591
601
|
return;
|
|
592
602
|
}
|
|
593
|
-
|
|
594
|
-
FastImageGlide.getRegistered(activity.getApplicationContext()).clearDiskCache();
|
|
595
603
|
// And the HTTP cache of `cache: 'web'` images, which Glide doesn't
|
|
596
604
|
// cache (set up when FastImage's components are registered).
|
|
597
605
|
try {
|
|
@@ -599,6 +607,20 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
|
|
|
599
607
|
} catch (IOException e) {
|
|
600
608
|
// Cleared as far as it could.
|
|
601
609
|
}
|
|
602
|
-
promise.resolve(
|
|
610
|
+
promise.resolve(cleared());
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private static WritableMap cleared() {
|
|
614
|
+
WritableMap result = Arguments.createMap();
|
|
615
|
+
result.putBoolean("ok", true);
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Glide failing to start, e.g. when an app's Glide module calls
|
|
620
|
+
// Glide.get() from registerComponents (#962): resolved as an error, not
|
|
621
|
+
// thrown, which would crash the app (on the main thread) or leave the
|
|
622
|
+
// promise unsettled.
|
|
623
|
+
private static WritableMap failure(RuntimeException e) {
|
|
624
|
+
return failure(e.getMessage() != null ? e.getMessage() : e.toString());
|
|
603
625
|
}
|
|
604
626
|
}
|
|
@@ -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
|
|
@@ -695,6 +743,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
695
743
|
// The loading image loaded (FastImageRequestListener).
|
|
696
744
|
void onImageLoaded() {
|
|
697
745
|
mLoadEnded = true;
|
|
746
|
+
endProgress();
|
|
698
747
|
mShownRequest = mLoadingRequest;
|
|
699
748
|
mShownWidth = mLoadingWidth;
|
|
700
749
|
mShownHeight = mLoadingHeight;
|
|
@@ -734,7 +783,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
734
783
|
private boolean isResized() {
|
|
735
784
|
int width = getWidth() - getPaddingLeft() - getPaddingRight();
|
|
736
785
|
int height = getHeight() - getPaddingTop() - getPaddingBottom();
|
|
737
|
-
return requestManager != null && !mNeedsReload
|
|
786
|
+
return requestManager != null && !mDropped && !mNeedsReload
|
|
738
787
|
&& mShownRequest != null && mShownRequest == mLoadingRequest
|
|
739
788
|
&& mShownWidth > 0 && mShownHeight > 0 && width > 0 && height > 0
|
|
740
789
|
&& (width != mShownWidth || height != mShownHeight);
|
|
@@ -752,7 +801,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
752
801
|
clearView(requestManager);
|
|
753
802
|
RequestBuilder<Drawable> builder = shown.clone()
|
|
754
803
|
.error(current.clone())
|
|
755
|
-
.listener(new FastImageRequestListener(null,
|
|
804
|
+
.listener(new FastImageRequestListener(null, true, false));
|
|
756
805
|
if (meanwhile == null) builder = builder.thumbnail(current);
|
|
757
806
|
into(shown, builder, meanwhile);
|
|
758
807
|
}
|
|
@@ -776,7 +825,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
776
825
|
clearView(requestManager);
|
|
777
826
|
RequestBuilder<Drawable> builder = request.clone()
|
|
778
827
|
.error(current.clone())
|
|
779
|
-
.listener(new FastImageRequestListener(null,
|
|
828
|
+
.listener(new FastImageRequestListener(null, true, false));
|
|
780
829
|
if (meanwhile == null) builder = builder.thumbnail(current);
|
|
781
830
|
into(request, builder, meanwhile);
|
|
782
831
|
} else if (mLoadingRequest != null && !mLoadEnded) {
|
|
@@ -848,6 +897,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
848
897
|
// defaultSource then, but not over a thumbnail: show it here, as iOS does.
|
|
849
898
|
void onImageFailed(boolean hadThumbnail) {
|
|
850
899
|
mLoadEnded = true;
|
|
900
|
+
endProgress();
|
|
851
901
|
mShownRequest = null;
|
|
852
902
|
if (!hadThumbnail) return;
|
|
853
903
|
final int load = mLoadCount;
|
|
@@ -863,10 +913,9 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
863
913
|
@SuppressLint("CheckResult")
|
|
864
914
|
public void onAfterUpdate(
|
|
865
915
|
@Nonnull FastImageViewManager manager,
|
|
866
|
-
@Nullable RequestManager requestManager
|
|
867
|
-
|
|
916
|
+
@Nullable RequestManager requestManager) {
|
|
917
|
+
if (mDropped) return;
|
|
868
918
|
mManager = manager;
|
|
869
|
-
mViewsForUrlsMap = viewsForUrlsMap;
|
|
870
919
|
if (mBlurChanged && !mNeedsReload) {
|
|
871
920
|
mBlurChanged = false;
|
|
872
921
|
reblur();
|
|
@@ -887,7 +936,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
887
936
|
if (!mWaitsForSize) return;
|
|
888
937
|
mWaitsForSize = false;
|
|
889
938
|
mWaitedForSize = true;
|
|
890
|
-
onAfterUpdate(manager, requestManager
|
|
939
|
+
onAfterUpdate(manager, requestManager);
|
|
891
940
|
}
|
|
892
941
|
});
|
|
893
942
|
return;
|
|
@@ -917,7 +966,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
917
966
|
// Cancel existing requests.
|
|
918
967
|
clearView(requestManager);
|
|
919
968
|
|
|
920
|
-
|
|
969
|
+
endProgress();
|
|
921
970
|
|
|
922
971
|
// Clear the image.
|
|
923
972
|
setImageDrawable(null);
|
|
@@ -942,38 +991,33 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
942
991
|
// Cancel existing requests.
|
|
943
992
|
clearView(requestManager);
|
|
944
993
|
|
|
945
|
-
|
|
994
|
+
endProgress();
|
|
946
995
|
setImageDrawable(mDefaultSource);
|
|
947
996
|
return;
|
|
948
997
|
}
|
|
949
998
|
|
|
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
|
-
}
|
|
999
|
+
// `imageSource` may be null and we still continue, if `defaultSource` is not null.
|
|
1000
|
+
// What Glide loads: a FastImageUrl for a remote image.
|
|
1001
|
+
Object model = imageSource == null ? null : imageSource.getSourceForLoad();
|
|
959
1002
|
|
|
960
1003
|
// Before the view clears (see shownCopy).
|
|
961
1004
|
Drawable meanwhile = shownRequest != null && imageSource != null ? shownCopy(shownRequest) : null;
|
|
962
1005
|
|
|
963
1006
|
// Cancel existing request.
|
|
964
|
-
this.glideUrl = glideUrl;
|
|
965
1007
|
clearView(requestManager);
|
|
966
1008
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1009
|
+
// The url this load downloads, for its progress: a remote image's
|
|
1010
|
+
// (without Glide, in a destroyed Activity, nothing loads).
|
|
1011
|
+
mLoadingUrl = requestManager != null && model instanceof FastImageUrl
|
|
1012
|
+
? ((FastImageUrl) model).url
|
|
1013
|
+
: null;
|
|
1014
|
+
// A restart (reblur) is the same load: its progress doesn't go back,
|
|
1015
|
+
// and steps already on their way to it still count.
|
|
1016
|
+
if (!restarting) {
|
|
1017
|
+
mProgressSince = FastImageViewManager.progressStep();
|
|
1018
|
+
mProgressFraction = 0;
|
|
976
1019
|
}
|
|
1020
|
+
updateProgressTracking();
|
|
977
1021
|
|
|
978
1022
|
if (imageSource != null && !restarting) {
|
|
979
1023
|
// This is an orphan even without a load/loadend when only loading a placeholder
|
|
@@ -982,7 +1026,6 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
982
1026
|
|
|
983
1027
|
if (requestManager != null) {
|
|
984
1028
|
// Records the image's own size when Glide decodes it, for onLoad.
|
|
985
|
-
Object model = imageSource == null ? null : imageSource.getSourceForLoad();
|
|
986
1029
|
RequestBuilder<Drawable> base =
|
|
987
1030
|
requestManager
|
|
988
1031
|
// This will make this work for remote and local images. e.g.
|
|
@@ -1019,31 +1062,13 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1019
1062
|
builder = builder.thumbnail(fromCache(shownRequest));
|
|
1020
1063
|
}
|
|
1021
1064
|
|
|
1022
|
-
if (
|
|
1023
|
-
builder.listener(new FastImageRequestListener(
|
|
1065
|
+
if (imageSource != null)
|
|
1066
|
+
builder.listener(new FastImageRequestListener(imageSource, thumbnail, true));
|
|
1024
1067
|
|
|
1025
1068
|
into(request, builder, meanwhile);
|
|
1026
1069
|
}
|
|
1027
1070
|
}
|
|
1028
1071
|
|
|
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
1072
|
public void clearView(@Nullable RequestManager requestManager) {
|
|
1048
1073
|
// Glide keeps the view's request under its own tag id, not getTag().
|
|
1049
1074
|
// Without a request this does nothing.
|
package/dist/index.cjs.js
CHANGED
|
@@ -245,8 +245,8 @@ FastImage.preload = (sources) => Promise.resolve(react_native.NativeModules.Fast
|
|
|
245
245
|
uri
|
|
246
246
|
};
|
|
247
247
|
}));
|
|
248
|
-
FastImage.clearMemoryCache = () => react_native.NativeModules.FastImageView.clearMemoryCache();
|
|
249
|
-
FastImage.clearDiskCache = () => react_native.NativeModules.FastImageView.clearDiskCache();
|
|
248
|
+
FastImage.clearMemoryCache = () => Promise.resolve(react_native.NativeModules.FastImageView.clearMemoryCache());
|
|
249
|
+
FastImage.clearDiskCache = () => Promise.resolve(react_native.NativeModules.FastImageView.clearDiskCache());
|
|
250
250
|
FastImage.configureCache = (limits = {}) => Promise.resolve(react_native.NativeModules.FastImageView.configureCache(limits));
|
|
251
251
|
FastImage.writeToCache = (source, file) => Array.isArray(source) ? Promise.resolve({
|
|
252
252
|
ok: false,
|
package/dist/index.cjs.js.flow
CHANGED
|
@@ -73,6 +73,8 @@ export type CachePathResult =
|
|
|
73
73
|
| {| ok: true, path: string |}
|
|
74
74
|
| {| ok: false, error: string |}
|
|
75
75
|
|
|
76
|
+
export type ClearCacheResult = {| ok: true |} | {| ok: false, error: string |}
|
|
77
|
+
|
|
76
78
|
export type Transition = $ReadOnly<{|
|
|
77
79
|
duration?: ?number,
|
|
78
80
|
betweenImages?: ?boolean,
|
|
@@ -147,8 +149,8 @@ declare export default class FastImage extends React$Component<FastImageProps> {
|
|
|
147
149
|
static priority: Priority;
|
|
148
150
|
static cacheControl: CacheControl;
|
|
149
151
|
static preload: PreloadFn;
|
|
150
|
-
static clearMemoryCache: () => Promise<
|
|
151
|
-
static clearDiskCache: () => Promise<
|
|
152
|
+
static clearMemoryCache: () => Promise<ClearCacheResult>;
|
|
153
|
+
static clearDiskCache: () => Promise<ClearCacheResult>;
|
|
152
154
|
static getCachePath: (source: FastImageSource) => Promise<CachePathResult>;
|
|
153
155
|
static configureCache: (limits?: CacheLimits) => Promise<CacheState>;
|
|
154
156
|
static writeToCache: (
|
package/dist/index.cjs.web.js
CHANGED
|
@@ -239,8 +239,8 @@ FastImage.preload = (sources) => Promise.all(sources.map((source) => {
|
|
|
239
239
|
uri
|
|
240
240
|
}));
|
|
241
241
|
}));
|
|
242
|
-
FastImage.clearMemoryCache = () => Promise.resolve();
|
|
243
|
-
FastImage.clearDiskCache = () => Promise.resolve();
|
|
242
|
+
FastImage.clearMemoryCache = () => Promise.resolve(notSupported);
|
|
243
|
+
FastImage.clearDiskCache = () => Promise.resolve(notSupported);
|
|
244
244
|
FastImage.configureCache = () => Promise.resolve({});
|
|
245
245
|
FastImage.writeToCache = () => Promise.resolve(notSupported);
|
|
246
246
|
FastImage.getCachePath = () => Promise.resolve(notSupported);
|