react-native-fast-image 9.1.0 → 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 +2 -2
- 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 +948 -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 +85 -60
- package/dist/index.d.ts +3 -1
- 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
|
|
@@ -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.d.ts
CHANGED
|
@@ -464,7 +464,9 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
464
464
|
onLoad?(event: OnLoadEvent): void;
|
|
465
465
|
/**
|
|
466
466
|
* Called on an image fetching error, with a message describing it (e.g.
|
|
467
|
-
* the HTTP status code).
|
|
467
|
+
* the HTTP status code). On iOS and Android, a download that gets nothing
|
|
468
|
+
* from the server for 15 seconds fails too (on Android, unless the app's
|
|
469
|
+
* OkHttp client has timeouts of its own).
|
|
468
470
|
*
|
|
469
471
|
* @example
|
|
470
472
|
* ```jsx
|