@nativescript-community/ui-image 4.6.6 → 5.0.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/CHANGELOG.md +7 -0
- package/README.md +4 -20
- package/index-common.d.ts +47 -18
- package/index-common.js +5 -69
- package/index-common.js.map +1 -1
- package/index.android.d.ts +33 -60
- package/index.android.js +597 -702
- package/index.android.js.map +1 -1
- package/index.d.ts +17 -92
- package/index.ios.d.ts +5 -10
- package/index.ios.js +72 -54
- package/index.ios.js.map +1 -1
- package/package.json +4 -4
- package/platforms/android/include.gradle +21 -16
- package/platforms/android/java/com/nativescript/image/CacheKeyStore.java +65 -0
- package/platforms/android/java/com/nativescript/image/CapturingEngineKeyFactory.java +43 -0
- package/platforms/android/java/com/nativescript/image/CompositeRequestListener.java +58 -0
- package/platforms/android/java/com/nativescript/image/ConditionalCrossFadeFactory.java +33 -0
- package/platforms/android/java/com/nativescript/image/CustomDataFetcher.java +124 -0
- package/platforms/android/java/com/nativescript/image/CustomGlideModule.java +220 -0
- package/platforms/android/java/com/nativescript/image/CustomGlideUrl.java +52 -0
- package/platforms/android/java/com/nativescript/image/CustomUrlLoader.java +74 -0
- package/platforms/android/java/com/nativescript/image/EvictionManager.java +735 -0
- package/platforms/android/java/com/nativescript/image/ExtractRequestOptions.java +109 -0
- package/platforms/android/java/com/nativescript/image/ImageLoadSourceCallback.java +5 -0
- package/platforms/android/java/com/nativescript/image/ImageProgressCallback.java +5 -0
- package/platforms/android/java/com/nativescript/image/LoadSourceInterceptor.java +28 -0
- package/platforms/android/java/com/nativescript/image/MatrixDrawable.java +200 -0
- package/platforms/android/java/com/nativescript/image/MatrixDrawableImageViewTarget.java +154 -0
- package/platforms/android/java/com/nativescript/image/MatrixImageView.java +696 -0
- package/platforms/android/java/com/nativescript/image/ProgressInterceptor.java +25 -0
- package/platforms/android/java/com/nativescript/image/ProgressResponseBody.java +70 -0
- package/platforms/android/java/com/nativescript/image/RecordingDigest.java +48 -0
- package/platforms/android/java/com/nativescript/image/RecreatedResourceKey.java +95 -0
- package/platforms/android/java/com/nativescript/image/SaveKeysRequestListener.java +145 -0
- package/platforms/android/java/com/nativescript/image/ScaleUtils.java +129 -0
- package/platforms/android/java/com/nativescript/image/SharedPrefCacheKeyStore.java +92 -0
- package/platforms/android/native-api-usage.json +39 -37
- package/platforms/ios/Podfile +1 -1
- package/references.d.ts +0 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/typings/android.d.ts +4 -27
- package/typings/glide.android.d.ts +9395 -0
- package/typings/glide.okhttp.android.d.ts +104 -0
- package/typings/glide.transform.android.d.ts +540 -0
- package/typings/ui_image.android.d.ts +517 -0
- package/platforms/android/java/com/nativescript/image/BaseDataSubscriber.java +0 -22
- package/platforms/android/java/com/nativescript/image/BaseDataSubscriberListener.java +0 -9
- package/platforms/android/java/com/nativescript/image/DraweeView.java +0 -371
- package/platforms/android/java/com/nativescript/image/NetworkImageRequest.java +0 -55
- package/platforms/android/java/com/nativescript/image/OkHttpNetworkFetcher.java +0 -56
- package/platforms/android/java/com/nativescript/image/ScalingBlurPostprocessor.java +0 -64
- package/platforms/android/java/com/nativescript/image/ScalingUtils.java +0 -519
- package/typings/fresco-processors.d.ts +0 -53
- package/typings/fresco.d.ts +0 -12070
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.util.Log;
|
|
5
|
+
import androidx.annotation.NonNull;
|
|
6
|
+
import com.bumptech.glide.Glide;
|
|
7
|
+
import com.bumptech.glide.GlideBuilder;
|
|
8
|
+
import com.bumptech.glide.Registry;
|
|
9
|
+
import com.bumptech.glide.annotation.GlideModule;
|
|
10
|
+
import com.bumptech.glide.load.Key;
|
|
11
|
+
import com.bumptech.glide.load.Options;
|
|
12
|
+
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator;
|
|
13
|
+
import com.bumptech.glide.load.engine.cache.DiskCache;
|
|
14
|
+
import com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper;
|
|
15
|
+
import com.bumptech.glide.load.engine.cache.MemoryCache;
|
|
16
|
+
import com.bumptech.glide.load.engine.cache.LruResourceCache;
|
|
17
|
+
import com.bumptech.glide.load.engine.CapturingEngineKeyFactory;
|
|
18
|
+
import com.bumptech.glide.load.model.GlideUrl;
|
|
19
|
+
import com.bumptech.glide.module.AppGlideModule;
|
|
20
|
+
import com.bumptech.glide.RequestBuilder;
|
|
21
|
+
import com.bumptech.glide.signature.ObjectKey;
|
|
22
|
+
import okhttp3.OkHttpClient;
|
|
23
|
+
import java.io.InputStream;
|
|
24
|
+
import java.lang.reflect.Field;
|
|
25
|
+
import java.lang.reflect.Modifier;
|
|
26
|
+
|
|
27
|
+
@GlideModule
|
|
28
|
+
public class CustomGlideModule extends AppGlideModule {
|
|
29
|
+
private static final String TAG = "MyAppGlideModule";
|
|
30
|
+
private static final String INJECT_TAG = "EngineKeyFactoryInject";
|
|
31
|
+
|
|
32
|
+
@Override
|
|
33
|
+
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
|
|
34
|
+
|
|
35
|
+
// Use our custom memory cache wrapper
|
|
36
|
+
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
|
|
37
|
+
.setMemoryCacheScreens(2)
|
|
38
|
+
.build();
|
|
39
|
+
LruResourceCache memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
|
|
40
|
+
EvictionManager.get().setMemoryCache(memoryCache);
|
|
41
|
+
builder.setMemoryCache(memoryCache);
|
|
42
|
+
// Set a disk cache factory that also registers the disk cache instance with
|
|
43
|
+
// EvictionManager.
|
|
44
|
+
builder.setDiskCache(new DiskCache.Factory() {
|
|
45
|
+
@Override
|
|
46
|
+
public DiskCache build() {
|
|
47
|
+
DiskCache dc = DiskLruCacheWrapper.create(context.getCacheDir(), 250 * 1024 * 1024);
|
|
48
|
+
EvictionManager.get().setDiskCache(dc);
|
|
49
|
+
return dc;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Override
|
|
55
|
+
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
|
|
56
|
+
|
|
57
|
+
// Create base OkHttp client
|
|
58
|
+
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
59
|
+
|
|
60
|
+
// SINGLE LOADER: CustomUrlLoader handles both CustomGlideUrl and GlideUrl
|
|
61
|
+
// This replaces both the old CustomUrlLoader and UrlTrackingModelLoader
|
|
62
|
+
registry.replace(
|
|
63
|
+
GlideUrl.class,
|
|
64
|
+
InputStream.class,
|
|
65
|
+
new CustomUrlLoader.Factory(client));
|
|
66
|
+
// Listener: called when an EngineKey is created. Update the stored keys to
|
|
67
|
+
// include engineKey.
|
|
68
|
+
CapturingEngineKeyFactory.Listener listener = (engineKey, model) -> {
|
|
69
|
+
// Log.i("JS", "CapturingEngineKeyFactory.Listener 1" + engineKey + " " + model);
|
|
70
|
+
if (model == null)
|
|
71
|
+
return;
|
|
72
|
+
String id = String.valueOf(model);
|
|
73
|
+
|
|
74
|
+
// Read either persistent or in-memory stored keys
|
|
75
|
+
CacheKeyStore.StoredKeys s = EvictionManager.get().getKeyStore().get(id);
|
|
76
|
+
if (s == null) {
|
|
77
|
+
// if key store exists but no entry, create minimal
|
|
78
|
+
s = new CacheKeyStore.StoredKeys(
|
|
79
|
+
new com.bumptech.glide.signature.ObjectKey(id),
|
|
80
|
+
new com.bumptech.glide.signature.ObjectKey("signature-none"),
|
|
81
|
+
com.bumptech.glide.request.target.Target.SIZE_ORIGINAL,
|
|
82
|
+
com.bumptech.glide.request.target.Target.SIZE_ORIGINAL,
|
|
83
|
+
null,
|
|
84
|
+
null,
|
|
85
|
+
android.graphics.Bitmap.class,
|
|
86
|
+
new Options(),
|
|
87
|
+
null,
|
|
88
|
+
null);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Build an updated StoredKeys that preserves everything and sets engineKey
|
|
92
|
+
CacheKeyStore.StoredKeys updated = new CacheKeyStore.StoredKeys(
|
|
93
|
+
s.sourceKey,
|
|
94
|
+
s.signature,
|
|
95
|
+
s.width,
|
|
96
|
+
s.height,
|
|
97
|
+
s.transformation, // may be null; preserved for in-process fallback
|
|
98
|
+
s.transformationKeyBytes, // raw bytes if recorded (preferred)
|
|
99
|
+
s.decodedResourceClass,
|
|
100
|
+
s.options,
|
|
101
|
+
s.optionsKeyBytes,
|
|
102
|
+
engineKey // set the captured engineKey
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Put updated entry into the in-memory store so later EvictionManager can
|
|
106
|
+
// remove memory entry.
|
|
107
|
+
EvictionManager.get().getKeyStore().put(id, updated);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Create the capturing factory
|
|
111
|
+
// instantiate the capturing factory (public class in
|
|
112
|
+
// com.bumptech.glide.load.engine)
|
|
113
|
+
Object capturingFactory = new com.bumptech.glide.load.engine.CapturingEngineKeyFactory(listener);
|
|
114
|
+
|
|
115
|
+
// inject it into Glide's Engine reflectively, passing it as Object (no
|
|
116
|
+
// EngineKeyFactory compile-time ref)
|
|
117
|
+
try {
|
|
118
|
+
injectEngineKeyFactoryIntoGlide(glide, capturingFactory);
|
|
119
|
+
Log.i(TAG, "Injected capturing EngineKeyFactory into Glide engine");
|
|
120
|
+
} catch (Exception e) {
|
|
121
|
+
Log.w(TAG, "Failed to inject capturing EngineKeyFactory", e);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Reflectively find Glide.engine and replace its EngineKeyFactory-typed field
|
|
128
|
+
* with capturingFactory.
|
|
129
|
+
* This tries to be resilient across minor Glide 5.x binary differences.
|
|
130
|
+
*/
|
|
131
|
+
private void injectEngineKeyFactoryIntoGlide(Object glideInstance, Object capturingFactory) throws Exception {
|
|
132
|
+
if (glideInstance == null) {
|
|
133
|
+
throw new IllegalArgumentException("glideInstance == null");
|
|
134
|
+
}
|
|
135
|
+
// 1) find Engine field on Glide
|
|
136
|
+
Field engineField = null;
|
|
137
|
+
for (Field f : glideInstance.getClass().getDeclaredFields()) {
|
|
138
|
+
if (f.getType().getName().contains("Engine")) {
|
|
139
|
+
engineField = f;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (engineField == null) {
|
|
144
|
+
throw new NoSuchFieldException("Could not find Engine field on Glide");
|
|
145
|
+
}
|
|
146
|
+
engineField.setAccessible(true);
|
|
147
|
+
Object engineInstance = engineField.get(glideInstance);
|
|
148
|
+
if (engineInstance == null) {
|
|
149
|
+
throw new IllegalStateException("Glide.engine is null");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 2) find the keyFactory-like field on Engine
|
|
153
|
+
Field targetField = null;
|
|
154
|
+
Class<?> engineClass = engineInstance.getClass();
|
|
155
|
+
Class<?> cur = engineClass;
|
|
156
|
+
while (cur != null && targetField == null) {
|
|
157
|
+
for (Field f : cur.getDeclaredFields()) {
|
|
158
|
+
String typeName = f.getType().getName();
|
|
159
|
+
if (typeName.contains("EngineKeyFactory") || typeName.endsWith("EngineKeyFactory")
|
|
160
|
+
|| f.getName().toLowerCase().contains("keyfactory")) {
|
|
161
|
+
targetField = f;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
cur = cur.getSuperclass();
|
|
166
|
+
}
|
|
167
|
+
if (targetField == null) {
|
|
168
|
+
throw new NoSuchFieldException(
|
|
169
|
+
"Could not find EngineKeyFactory-like field in Engine class: " + engineClass.getName());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
targetField.setAccessible(true);
|
|
173
|
+
|
|
174
|
+
// Log current value
|
|
175
|
+
Object before = targetField.get(engineInstance);
|
|
176
|
+
int beforeHash = System.identityHashCode(before);
|
|
177
|
+
String beforeClass = (before == null) ? "null" : before.getClass().getName();
|
|
178
|
+
|
|
179
|
+
// If field is final, try to remove final modifier so setting works reliably
|
|
180
|
+
try {
|
|
181
|
+
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
|
182
|
+
modifiersField.setAccessible(true);
|
|
183
|
+
int mods = targetField.getModifiers();
|
|
184
|
+
if (Modifier.isFinal(mods)) {
|
|
185
|
+
modifiersField.setInt(targetField, mods & ~Modifier.FINAL);
|
|
186
|
+
}
|
|
187
|
+
} catch (NoSuchFieldException nsf) {
|
|
188
|
+
// Android ART may not expose 'modifiers' the same way; ignore if not available
|
|
189
|
+
Log.i(INJECT_TAG, "Could not access Field.modifiers to clear final; continuing");
|
|
190
|
+
} catch (Throwable t) {
|
|
191
|
+
Log.w(INJECT_TAG, "Failed to clear final modifier (continuing attempt to set field)", t);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 3) set the new factory instance
|
|
195
|
+
try {
|
|
196
|
+
targetField.set(engineInstance, capturingFactory);
|
|
197
|
+
} catch (IllegalAccessException | IllegalArgumentException iae) {
|
|
198
|
+
// Log and rethrow so caller can inspect
|
|
199
|
+
Log.w(INJECT_TAG, "targetField.set(...) failed", iae);
|
|
200
|
+
throw iae;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 4) read back and log to verify
|
|
204
|
+
Object after = targetField.get(engineInstance);
|
|
205
|
+
int afterHash = System.identityHashCode(after);
|
|
206
|
+
String afterClass = (after == null) ? "null" : after.getClass().getName();
|
|
207
|
+
|
|
208
|
+
// quick verification: are they the same instance we tried to set?
|
|
209
|
+
boolean sameInstance = (after == capturingFactory);
|
|
210
|
+
if (!sameInstance) {
|
|
211
|
+
throw new IllegalStateException(
|
|
212
|
+
"Injection did not set the expected capturingFactory instance. afterClass=" + afterClass);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@Override
|
|
217
|
+
public boolean isManifestParsingEnabled() {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import com.bumptech.glide.load.model.GlideUrl;
|
|
4
|
+
import com.bumptech.glide.load.model.LazyHeaders;
|
|
5
|
+
import java.util.Map;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Custom GlideUrl with progress and load source callbacks.
|
|
9
|
+
* Headers are automatically included in cache key by Glide's LazyHeaders.
|
|
10
|
+
*/
|
|
11
|
+
public class CustomGlideUrl extends GlideUrl {
|
|
12
|
+
private final ImageProgressCallback progressCallback;
|
|
13
|
+
private final ImageLoadSourceCallback loadSourceCallback;
|
|
14
|
+
|
|
15
|
+
public CustomGlideUrl(String url,
|
|
16
|
+
Map<String, String> headers,
|
|
17
|
+
ImageProgressCallback progressCallback,
|
|
18
|
+
ImageLoadSourceCallback loadSourceCallback) {
|
|
19
|
+
// Use LazyHeaders.Builder - Glide automatically includes these in cache key!
|
|
20
|
+
super(url, buildLazyHeaders(headers));
|
|
21
|
+
this.progressCallback = progressCallback;
|
|
22
|
+
this.loadSourceCallback = loadSourceCallback;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private static LazyHeaders buildLazyHeaders(Map<String, String> headers) {
|
|
26
|
+
LazyHeaders.Builder builder = new LazyHeaders.Builder();
|
|
27
|
+
|
|
28
|
+
if (headers != null) {
|
|
29
|
+
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
|
30
|
+
builder.addHeader(entry.getKey(), entry.getValue());
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return builder.build();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public ImageProgressCallback getProgressCallback() {
|
|
38
|
+
return progressCallback;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public ImageLoadSourceCallback getLoadSourceCallback() {
|
|
42
|
+
return loadSourceCallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public boolean hasProgressListener() {
|
|
46
|
+
return progressCallback != null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public boolean hasLoadSourceListener() {
|
|
50
|
+
return loadSourceCallback != null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.bumptech.glide.load.Options;
|
|
6
|
+
import com.bumptech.glide.load.data.DataFetcher;
|
|
7
|
+
import com.bumptech.glide.load.model.GlideUrl;
|
|
8
|
+
import com.bumptech.glide.load.model.ModelLoader;
|
|
9
|
+
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
|
10
|
+
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
|
|
11
|
+
import okhttp3.Call;
|
|
12
|
+
import okhttp3.OkHttpClient;
|
|
13
|
+
import java.io.InputStream;
|
|
14
|
+
import android.util.Log;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Unified ModelLoader that handles both CustomGlideUrl and standard GlideUrl.
|
|
18
|
+
* This eliminates the need for UrlTrackingModelLoader.
|
|
19
|
+
*/
|
|
20
|
+
public class CustomUrlLoader implements ModelLoader<GlideUrl, InputStream> {
|
|
21
|
+
private final Call.Factory client;
|
|
22
|
+
|
|
23
|
+
public CustomUrlLoader(Call.Factory client) {
|
|
24
|
+
this.client = client;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Nullable
|
|
28
|
+
@Override
|
|
29
|
+
public LoadData<InputStream> buildLoadData(
|
|
30
|
+
@NonNull GlideUrl model,
|
|
31
|
+
int width,
|
|
32
|
+
int height,
|
|
33
|
+
@NonNull Options options) {
|
|
34
|
+
|
|
35
|
+
// Create the appropriate data fetcher
|
|
36
|
+
DataFetcher<InputStream> fetcher;
|
|
37
|
+
|
|
38
|
+
if (model instanceof CustomGlideUrl) {
|
|
39
|
+
// Use CustomDataFetcher for CustomGlideUrl (supports progress/load source)
|
|
40
|
+
fetcher = new CustomDataFetcher(client, (CustomGlideUrl) model);
|
|
41
|
+
} else {
|
|
42
|
+
// Use standard OkHttp fetcher for regular GlideUrl
|
|
43
|
+
fetcher = new com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher(
|
|
44
|
+
client,
|
|
45
|
+
model);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new LoadData<>(model, fetcher);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Override
|
|
52
|
+
public boolean handles(@NonNull GlideUrl model) {
|
|
53
|
+
// Handles both CustomGlideUrl and standard GlideUrl
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
|
|
58
|
+
private final OkHttpClient client;
|
|
59
|
+
|
|
60
|
+
public Factory(OkHttpClient client) {
|
|
61
|
+
this.client = client;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@NonNull
|
|
65
|
+
@Override
|
|
66
|
+
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
|
|
67
|
+
return new CustomUrlLoader(client);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Override
|
|
71
|
+
public void teardown() {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|