@nativescript-community/ui-image 5.0.12 → 5.0.14
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 +8 -0
- package/index-common.d.ts +30 -1
- package/index-common.js.map +1 -1
- package/index.android.d.ts +11 -4
- package/index.android.js +159 -71
- package/index.android.js.map +1 -1
- package/index.d.ts +2 -2
- package/index.ios.d.ts +9 -6
- package/index.ios.js +31 -31
- package/index.ios.js.map +1 -1
- package/package.json +2 -2
- package/platforms/android/java/com/nativescript/image/CacheKeyStore.java +15 -27
- package/platforms/android/java/com/nativescript/image/CustomGlideModule.java +35 -154
- package/platforms/android/java/com/nativescript/image/CustomGlideUrl.java +2 -2
- package/platforms/android/java/com/nativescript/image/EvictionManager.java +331 -333
- package/platforms/android/java/com/nativescript/image/GlideConfiguration.java +28 -0
- package/platforms/android/java/com/nativescript/image/ModelSignatureDiskLruCacheWrapper.java +321 -0
- package/platforms/android/java/com/nativescript/image/ModelSignatureMemoryCache.java +155 -0
- package/platforms/android/java/com/nativescript/image/SaveKeysRequestListener.java +69 -125
- package/platforms/android/java/com/nativescript/image/SharedPrefCacheKeyStore.java +75 -49
- package/typings/ui_image.android.d.ts +10 -17
- package/platforms/android/java/com/nativescript/image/CapturingEngineKeyFactory.java +0 -43
- package/platforms/android/java/com/nativescript/image/RecordingDigest.java +0 -48
- package/platforms/android/java/com/nativescript/image/RecreatedResourceKey.java +0 -95
|
@@ -13,13 +13,13 @@ import com.bumptech.glide.load.engine.cache.MemorySizeCalculator;
|
|
|
13
13
|
import com.bumptech.glide.load.engine.cache.DiskCache;
|
|
14
14
|
import com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper;
|
|
15
15
|
import com.bumptech.glide.load.engine.cache.MemoryCache;
|
|
16
|
-
import com.bumptech.glide.load.engine.cache.
|
|
17
|
-
import com.bumptech.glide.load.engine.CapturingEngineKeyFactory;
|
|
16
|
+
import com.bumptech.glide.load.engine.cache.ModelSignatureDiskLruCacheWrapper;
|
|
18
17
|
import com.bumptech.glide.load.model.GlideUrl;
|
|
19
18
|
import com.bumptech.glide.module.AppGlideModule;
|
|
20
19
|
import com.bumptech.glide.RequestBuilder;
|
|
21
20
|
import com.bumptech.glide.signature.ObjectKey;
|
|
22
21
|
import okhttp3.OkHttpClient;
|
|
22
|
+
import java.io.File;
|
|
23
23
|
import java.io.InputStream;
|
|
24
24
|
import java.lang.reflect.Field;
|
|
25
25
|
import java.lang.reflect.Modifier;
|
|
@@ -29,6 +29,37 @@ public class CustomGlideModule extends AppGlideModule {
|
|
|
29
29
|
private static final String TAG = "MyAppGlideModule";
|
|
30
30
|
private static final String INJECT_TAG = "EngineKeyFactoryInject";
|
|
31
31
|
|
|
32
|
+
private File getInternalCacheDirectory(@NonNull Context context, final String diskCacheName) {
|
|
33
|
+
File cacheDirectory = context.getCacheDir();
|
|
34
|
+
if (cacheDirectory == null) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (diskCacheName != null) {
|
|
38
|
+
return new File(cacheDirectory, diskCacheName);
|
|
39
|
+
}
|
|
40
|
+
return cacheDirectory;
|
|
41
|
+
}
|
|
42
|
+
public File getCacheDirectory(@NonNull Context context, final String diskCacheName) {
|
|
43
|
+
File internalCacheDirectory = getInternalCacheDirectory(context, diskCacheName);
|
|
44
|
+
|
|
45
|
+
// Already used internal cache, so keep using that one,
|
|
46
|
+
// thus avoiding using both external and internal with transient errors.
|
|
47
|
+
if (internalCacheDirectory != null && internalCacheDirectory.exists()) {
|
|
48
|
+
return internalCacheDirectory;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
File cacheDirectory = context.getExternalCacheDir();
|
|
52
|
+
|
|
53
|
+
// Shared storage is not available.
|
|
54
|
+
if (cacheDirectory == null || !cacheDirectory.canWrite()) {
|
|
55
|
+
return internalCacheDirectory;
|
|
56
|
+
}
|
|
57
|
+
if (diskCacheName != null) {
|
|
58
|
+
return new File(cacheDirectory, diskCacheName);
|
|
59
|
+
}
|
|
60
|
+
return cacheDirectory;
|
|
61
|
+
}
|
|
62
|
+
|
|
32
63
|
@Override
|
|
33
64
|
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
|
|
34
65
|
|
|
@@ -51,7 +82,7 @@ public class CustomGlideModule extends AppGlideModule {
|
|
|
51
82
|
}
|
|
52
83
|
|
|
53
84
|
// Use our custom memory cache wrapper
|
|
54
|
-
|
|
85
|
+
ModelSignatureMemoryCache memoryCache = new ModelSignatureMemoryCache(memoryCacheSize);
|
|
55
86
|
EvictionManager.get().setMemoryCache(memoryCache);
|
|
56
87
|
builder.setMemoryCache(memoryCache);
|
|
57
88
|
|
|
@@ -60,7 +91,7 @@ public class CustomGlideModule extends AppGlideModule {
|
|
|
60
91
|
builder.setDiskCache(new DiskCache.Factory() {
|
|
61
92
|
@Override
|
|
62
93
|
public DiskCache build() {
|
|
63
|
-
|
|
94
|
+
ModelSignatureDiskLruCacheWrapper dc = ModelSignatureDiskLruCacheWrapper.create(getCacheDirectory(context, config.getDiskCacheName()), config.getDiskCacheSize());
|
|
64
95
|
EvictionManager.get().setDiskCache(dc);
|
|
65
96
|
return dc;
|
|
66
97
|
}
|
|
@@ -73,160 +104,10 @@ public class CustomGlideModule extends AppGlideModule {
|
|
|
73
104
|
// Create base OkHttp client
|
|
74
105
|
OkHttpClient client = new OkHttpClient.Builder().build();
|
|
75
106
|
|
|
76
|
-
// SINGLE LOADER: CustomUrlLoader handles both CustomGlideUrl and GlideUrl
|
|
77
|
-
// This replaces both the old CustomUrlLoader and UrlTrackingModelLoader
|
|
78
107
|
registry.replace(
|
|
79
108
|
GlideUrl.class,
|
|
80
109
|
InputStream.class,
|
|
81
110
|
new CustomUrlLoader.Factory(client));
|
|
82
|
-
// Listener: called when an EngineKey is created. Update the stored keys to
|
|
83
|
-
// include engineKey.
|
|
84
|
-
CapturingEngineKeyFactory.Listener listener = (engineKey, model) -> {
|
|
85
|
-
// Log.i("JS", "CapturingEngineKeyFactory.Listener 1" + engineKey + " " + model);
|
|
86
|
-
if (model == null)
|
|
87
|
-
return;
|
|
88
|
-
String id = String.valueOf(model);
|
|
89
|
-
|
|
90
|
-
// Read either persistent or in-memory stored keys
|
|
91
|
-
CacheKeyStore.StoredKeys s = EvictionManager.get().getKeyStore().get(id);
|
|
92
|
-
if (s == null) {
|
|
93
|
-
// if key store exists but no entry, create minimal
|
|
94
|
-
s = new CacheKeyStore.StoredKeys(
|
|
95
|
-
new com.bumptech.glide.signature.ObjectKey(id),
|
|
96
|
-
new com.bumptech.glide.signature.ObjectKey("signature-none"),
|
|
97
|
-
com.bumptech.glide.request.target.Target.SIZE_ORIGINAL,
|
|
98
|
-
com.bumptech.glide.request.target.Target.SIZE_ORIGINAL,
|
|
99
|
-
null,
|
|
100
|
-
null,
|
|
101
|
-
android.graphics.Bitmap.class,
|
|
102
|
-
new Options(),
|
|
103
|
-
null,
|
|
104
|
-
null);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Build an updated StoredKeys that preserves everything and sets engineKey
|
|
108
|
-
CacheKeyStore.StoredKeys updated = new CacheKeyStore.StoredKeys(
|
|
109
|
-
s.sourceKey,
|
|
110
|
-
s.signature,
|
|
111
|
-
s.width,
|
|
112
|
-
s.height,
|
|
113
|
-
s.transformation, // may be null; preserved for in-process fallback
|
|
114
|
-
s.transformationKeyBytes, // raw bytes if recorded (preferred)
|
|
115
|
-
s.decodedResourceClass,
|
|
116
|
-
s.options,
|
|
117
|
-
s.optionsKeyBytes,
|
|
118
|
-
engineKey // set the captured engineKey
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// Put updated entry into the in-memory store so later EvictionManager can
|
|
122
|
-
// remove memory entry.
|
|
123
|
-
EvictionManager.get().getKeyStore().put(id, updated);
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
// Create the capturing factory
|
|
127
|
-
// instantiate the capturing factory (public class in
|
|
128
|
-
// com.bumptech.glide.load.engine)
|
|
129
|
-
Object capturingFactory = new com.bumptech.glide.load.engine.CapturingEngineKeyFactory(listener);
|
|
130
|
-
|
|
131
|
-
// inject it into Glide's Engine reflectively, passing it as Object (no
|
|
132
|
-
// EngineKeyFactory compile-time ref)
|
|
133
|
-
try {
|
|
134
|
-
injectEngineKeyFactoryIntoGlide(glide, capturingFactory);
|
|
135
|
-
Log.i(TAG, "Injected capturing EngineKeyFactory into Glide engine");
|
|
136
|
-
} catch (Exception e) {
|
|
137
|
-
Log.w(TAG, "Failed to inject capturing EngineKeyFactory", e);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Reflectively find Glide.engine and replace its EngineKeyFactory-typed field
|
|
144
|
-
* with capturingFactory.
|
|
145
|
-
* This tries to be resilient across minor Glide 5.x binary differences.
|
|
146
|
-
*/
|
|
147
|
-
private void injectEngineKeyFactoryIntoGlide(Object glideInstance, Object capturingFactory) throws Exception {
|
|
148
|
-
if (glideInstance == null) {
|
|
149
|
-
throw new IllegalArgumentException("glideInstance == null");
|
|
150
|
-
}
|
|
151
|
-
// 1) find Engine field on Glide
|
|
152
|
-
Field engineField = null;
|
|
153
|
-
for (Field f : glideInstance.getClass().getDeclaredFields()) {
|
|
154
|
-
if (f.getType().getName().contains("Engine")) {
|
|
155
|
-
engineField = f;
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (engineField == null) {
|
|
160
|
-
throw new NoSuchFieldException("Could not find Engine field on Glide");
|
|
161
|
-
}
|
|
162
|
-
engineField.setAccessible(true);
|
|
163
|
-
Object engineInstance = engineField.get(glideInstance);
|
|
164
|
-
if (engineInstance == null) {
|
|
165
|
-
throw new IllegalStateException("Glide.engine is null");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// 2) find the keyFactory-like field on Engine
|
|
169
|
-
Field targetField = null;
|
|
170
|
-
Class<?> engineClass = engineInstance.getClass();
|
|
171
|
-
Class<?> cur = engineClass;
|
|
172
|
-
while (cur != null && targetField == null) {
|
|
173
|
-
for (Field f : cur.getDeclaredFields()) {
|
|
174
|
-
String typeName = f.getType().getName();
|
|
175
|
-
if (typeName.contains("EngineKeyFactory") || typeName.endsWith("EngineKeyFactory")
|
|
176
|
-
|| f.getName().toLowerCase().contains("keyfactory")) {
|
|
177
|
-
targetField = f;
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
cur = cur.getSuperclass();
|
|
182
|
-
}
|
|
183
|
-
if (targetField == null) {
|
|
184
|
-
throw new NoSuchFieldException(
|
|
185
|
-
"Could not find EngineKeyFactory-like field in Engine class: " + engineClass.getName());
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
targetField.setAccessible(true);
|
|
189
|
-
|
|
190
|
-
// Log current value
|
|
191
|
-
Object before = targetField.get(engineInstance);
|
|
192
|
-
int beforeHash = System.identityHashCode(before);
|
|
193
|
-
String beforeClass = (before == null) ? "null" : before.getClass().getName();
|
|
194
|
-
|
|
195
|
-
// If field is final, try to remove final modifier so setting works reliably
|
|
196
|
-
try {
|
|
197
|
-
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
|
198
|
-
modifiersField.setAccessible(true);
|
|
199
|
-
int mods = targetField.getModifiers();
|
|
200
|
-
if (Modifier.isFinal(mods)) {
|
|
201
|
-
modifiersField.setInt(targetField, mods & ~Modifier.FINAL);
|
|
202
|
-
}
|
|
203
|
-
} catch (NoSuchFieldException nsf) {
|
|
204
|
-
// Android ART may not expose 'modifiers' the same way; ignore if not available
|
|
205
|
-
Log.i(INJECT_TAG, "Could not access Field.modifiers to clear final; continuing");
|
|
206
|
-
} catch (Throwable t) {
|
|
207
|
-
Log.w(INJECT_TAG, "Failed to clear final modifier (continuing attempt to set field)", t);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// 3) set the new factory instance
|
|
211
|
-
try {
|
|
212
|
-
targetField.set(engineInstance, capturingFactory);
|
|
213
|
-
} catch (IllegalAccessException | IllegalArgumentException iae) {
|
|
214
|
-
// Log and rethrow so caller can inspect
|
|
215
|
-
Log.w(INJECT_TAG, "targetField.set(...) failed", iae);
|
|
216
|
-
throw iae;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// 4) read back and log to verify
|
|
220
|
-
Object after = targetField.get(engineInstance);
|
|
221
|
-
int afterHash = System.identityHashCode(after);
|
|
222
|
-
String afterClass = (after == null) ? "null" : after.getClass().getName();
|
|
223
|
-
|
|
224
|
-
// quick verification: are they the same instance we tried to set?
|
|
225
|
-
boolean sameInstance = (after == capturingFactory);
|
|
226
|
-
if (!sameInstance) {
|
|
227
|
-
throw new IllegalStateException(
|
|
228
|
-
"Injection did not set the expected capturingFactory instance. afterClass=" + afterClass);
|
|
229
|
-
}
|
|
230
111
|
}
|
|
231
112
|
|
|
232
113
|
@Override
|
|
@@ -2,6 +2,7 @@ package com.nativescript.image;
|
|
|
2
2
|
|
|
3
3
|
import com.bumptech.glide.load.model.GlideUrl;
|
|
4
4
|
import com.bumptech.glide.load.model.LazyHeaders;
|
|
5
|
+
import com.bumptech.glide.load.model.Headers;
|
|
5
6
|
import java.util.Map;
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -16,8 +17,7 @@ public class CustomGlideUrl extends GlideUrl {
|
|
|
16
17
|
Map<String, String> headers,
|
|
17
18
|
ImageProgressCallback progressCallback,
|
|
18
19
|
ImageLoadSourceCallback loadSourceCallback) {
|
|
19
|
-
|
|
20
|
-
super(url, buildLazyHeaders(headers));
|
|
20
|
+
super(url, headers != null ? buildLazyHeaders(headers) : Headers.DEFAULT);
|
|
21
21
|
this.progressCallback = progressCallback;
|
|
22
22
|
this.loadSourceCallback = loadSourceCallback;
|
|
23
23
|
}
|