@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,25 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import okhttp3.Interceptor;
|
|
5
|
+
import okhttp3.Response;
|
|
6
|
+
|
|
7
|
+
public class ProgressInterceptor implements Interceptor {
|
|
8
|
+
private final String url;
|
|
9
|
+
private final ImageProgressCallback callback;
|
|
10
|
+
|
|
11
|
+
public ProgressInterceptor(String url, ImageProgressCallback callback) {
|
|
12
|
+
this.url = url;
|
|
13
|
+
this.callback = callback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Override
|
|
17
|
+
public Response intercept(Chain chain) throws IOException {
|
|
18
|
+
Response originalResponse = chain.proceed(chain.request());
|
|
19
|
+
|
|
20
|
+
// Wrap response body with progress tracking
|
|
21
|
+
return originalResponse.newBuilder()
|
|
22
|
+
.body(new ProgressResponseBody(originalResponse.body(), url, callback))
|
|
23
|
+
.build();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import okhttp3.MediaType;
|
|
5
|
+
import okhttp3.ResponseBody;
|
|
6
|
+
import okio.Buffer;
|
|
7
|
+
import okio.BufferedSource;
|
|
8
|
+
import okio.ForwardingSource;
|
|
9
|
+
import okio.Okio;
|
|
10
|
+
import okio.Source;
|
|
11
|
+
|
|
12
|
+
public class ProgressResponseBody extends ResponseBody {
|
|
13
|
+
private final ResponseBody responseBody;
|
|
14
|
+
private final String url;
|
|
15
|
+
private final ImageProgressCallback callback;
|
|
16
|
+
private BufferedSource bufferedSource;
|
|
17
|
+
|
|
18
|
+
public ProgressResponseBody(ResponseBody responseBody, String url, ImageProgressCallback callback) {
|
|
19
|
+
this.responseBody = responseBody;
|
|
20
|
+
this.url = url;
|
|
21
|
+
this.callback = callback;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public MediaType contentType() {
|
|
26
|
+
return responseBody.contentType();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Override
|
|
30
|
+
public long contentLength() {
|
|
31
|
+
return responseBody.contentLength();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Override
|
|
35
|
+
public BufferedSource source() {
|
|
36
|
+
if (bufferedSource == null) {
|
|
37
|
+
bufferedSource = Okio.buffer(source(responseBody.source()));
|
|
38
|
+
}
|
|
39
|
+
return bufferedSource;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private Source source(Source source) {
|
|
43
|
+
return new ForwardingSource(source) {
|
|
44
|
+
long totalBytesRead = 0L;
|
|
45
|
+
long lastNotified = 0L;
|
|
46
|
+
static final long NOTIFY_INTERVAL = 8192L; // 8KB
|
|
47
|
+
|
|
48
|
+
@Override
|
|
49
|
+
public long read(Buffer sink, long byteCount) throws IOException {
|
|
50
|
+
long bytesRead = super.read(sink, byteCount);
|
|
51
|
+
|
|
52
|
+
if (bytesRead != -1) {
|
|
53
|
+
totalBytesRead += bytesRead;
|
|
54
|
+
|
|
55
|
+
// Notify if we've read enough bytes or at completion
|
|
56
|
+
if (totalBytesRead - lastNotified >= NOTIFY_INTERVAL ||
|
|
57
|
+
totalBytesRead >= contentLength()) {
|
|
58
|
+
|
|
59
|
+
if (callback != null) {
|
|
60
|
+
callback.onProgress(url, totalBytesRead, contentLength());
|
|
61
|
+
}
|
|
62
|
+
lastNotified = totalBytesRead;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return bytesRead;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import java.io.ByteArrayOutputStream;
|
|
4
|
+
import java.security.MessageDigest;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Small MessageDigest that records raw bytes passed to update(...).
|
|
8
|
+
* We don't compute a digest here: we simply capture the bytes that callers
|
|
9
|
+
* write by calling
|
|
10
|
+
* update(byte[]) so we can later replay them into another MessageDigest.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* RecordingDigest r = new RecordingDigest();
|
|
14
|
+
* transformation.updateDiskCacheKey(r);
|
|
15
|
+
* byte[] bytes = r.digest(); // returns the concatenated bytes that
|
|
16
|
+
* transformation wrote
|
|
17
|
+
*/
|
|
18
|
+
public final class RecordingDigest extends MessageDigest {
|
|
19
|
+
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
20
|
+
|
|
21
|
+
public RecordingDigest() {
|
|
22
|
+
// algorithm name is arbitrary here; we do not rely on MessageDigest's actual
|
|
23
|
+
// hashing.
|
|
24
|
+
super("NONE");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Override
|
|
28
|
+
protected void engineUpdate(byte input) {
|
|
29
|
+
out.write(input);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Override
|
|
33
|
+
protected void engineUpdate(byte[] input, int offset, int len) {
|
|
34
|
+
out.write(input, offset, len);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Override
|
|
38
|
+
protected byte[] engineDigest() {
|
|
39
|
+
byte[] result = out.toByteArray();
|
|
40
|
+
out.reset();
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Override
|
|
45
|
+
protected void engineReset() {
|
|
46
|
+
out.reset();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import com.bumptech.glide.load.Key;
|
|
5
|
+
import java.nio.ByteBuffer;
|
|
6
|
+
import java.security.MessageDigest;
|
|
7
|
+
import java.util.Objects;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Recreates ResourceCacheKey updateDiskCacheKey ordering but accepts
|
|
11
|
+
* precomputed transformation
|
|
12
|
+
* and options key bytes. This avoids the need to reconstruct
|
|
13
|
+
* Transformation/Options objects.
|
|
14
|
+
*
|
|
15
|
+
* Ordering mirrors Glide's ResourceCacheKey:
|
|
16
|
+
* signature.updateDiskCacheKey(md)
|
|
17
|
+
* sourceKey.updateDiskCacheKey(md)
|
|
18
|
+
* md.update(dimensions)
|
|
19
|
+
* md.update(transformationKeyBytes) -- if present
|
|
20
|
+
* md.update(optionsKeyBytes) -- if present
|
|
21
|
+
* md.update(decodedResourceClass.getName().getBytes(...))
|
|
22
|
+
*/
|
|
23
|
+
public final class RecreatedResourceKey implements Key {
|
|
24
|
+
private final Key sourceKey;
|
|
25
|
+
private final Key signature;
|
|
26
|
+
private final int width;
|
|
27
|
+
private final int height;
|
|
28
|
+
private final byte[] transformationKeyBytes; // may be null
|
|
29
|
+
private final Class<?> decodedResourceClass;
|
|
30
|
+
private final byte[] optionsKeyBytes; // may be null
|
|
31
|
+
|
|
32
|
+
public RecreatedResourceKey(
|
|
33
|
+
Key sourceKey,
|
|
34
|
+
Key signature,
|
|
35
|
+
int width,
|
|
36
|
+
int height,
|
|
37
|
+
byte[] transformationKeyBytes,
|
|
38
|
+
Class<?> decodedResourceClass,
|
|
39
|
+
byte[] optionsKeyBytes) {
|
|
40
|
+
this.sourceKey = Objects.requireNonNull(sourceKey);
|
|
41
|
+
this.signature = Objects.requireNonNull(signature);
|
|
42
|
+
this.width = width;
|
|
43
|
+
this.height = height;
|
|
44
|
+
this.transformationKeyBytes = transformationKeyBytes;
|
|
45
|
+
this.decodedResourceClass = Objects.requireNonNull(decodedResourceClass);
|
|
46
|
+
this.optionsKeyBytes = optionsKeyBytes;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Override
|
|
50
|
+
public boolean equals(Object o) {
|
|
51
|
+
if (this == o)
|
|
52
|
+
return true;
|
|
53
|
+
if (!(o instanceof RecreatedResourceKey))
|
|
54
|
+
return false;
|
|
55
|
+
RecreatedResourceKey that = (RecreatedResourceKey) o;
|
|
56
|
+
return width == that.width
|
|
57
|
+
&& height == that.height
|
|
58
|
+
&& sourceKey.equals(that.sourceKey)
|
|
59
|
+
&& signature.equals(that.signature)
|
|
60
|
+
&& java.util.Arrays.equals(transformationKeyBytes, that.transformationKeyBytes)
|
|
61
|
+
&& decodedResourceClass.equals(that.decodedResourceClass)
|
|
62
|
+
&& java.util.Arrays.equals(optionsKeyBytes, that.optionsKeyBytes);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Override
|
|
66
|
+
public int hashCode() {
|
|
67
|
+
int result = sourceKey.hashCode();
|
|
68
|
+
result = 31 * result + signature.hashCode();
|
|
69
|
+
result = 31 * result + width;
|
|
70
|
+
result = 31 * result + height;
|
|
71
|
+
result = 31 * result + java.util.Arrays.hashCode(transformationKeyBytes);
|
|
72
|
+
result = 31 * result + decodedResourceClass.hashCode();
|
|
73
|
+
result = 31 * result + java.util.Arrays.hashCode(optionsKeyBytes);
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Override
|
|
78
|
+
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
|
79
|
+
byte[] dimensions = ByteBuffer.allocate(8).putInt(width).putInt(height).array();
|
|
80
|
+
|
|
81
|
+
signature.updateDiskCacheKey(messageDigest);
|
|
82
|
+
sourceKey.updateDiskCacheKey(messageDigest);
|
|
83
|
+
messageDigest.update(dimensions);
|
|
84
|
+
|
|
85
|
+
if (transformationKeyBytes != null) {
|
|
86
|
+
messageDigest.update(transformationKeyBytes);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (optionsKeyBytes != null) {
|
|
90
|
+
messageDigest.update(optionsKeyBytes);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
messageDigest.update(decodedResourceClass.getName().getBytes(CHARSET));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import android.graphics.drawable.Drawable;
|
|
4
|
+
import android.util.Log;
|
|
5
|
+
|
|
6
|
+
import androidx.annotation.Nullable;
|
|
7
|
+
|
|
8
|
+
import com.bumptech.glide.load.Key;
|
|
9
|
+
import com.bumptech.glide.load.Options;
|
|
10
|
+
import com.bumptech.glide.load.Transformation;
|
|
11
|
+
import com.bumptech.glide.request.RequestListener;
|
|
12
|
+
import com.bumptech.glide.request.RequestOptions;
|
|
13
|
+
import com.bumptech.glide.request.target.Target;
|
|
14
|
+
import com.bumptech.glide.load.engine.GlideException;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* RequestListener that records the disk & transformation key bytes used for a
|
|
18
|
+
* request
|
|
19
|
+
* and persists them via EvictionManager so later evictions / presence checks
|
|
20
|
+
* can recreate
|
|
21
|
+
* the exact disk keys without needing to recreate Transformation objects.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* - Preferred: pass a RequestOptions instance; this listener will extract the
|
|
25
|
+
* internal Options
|
|
26
|
+
* via ExtractRequestOptions (cached reflection) so your existing RequestOptions
|
|
27
|
+
* usage continues to work.
|
|
28
|
+
* - Alternative: pass an Options instance directly if you already have it.
|
|
29
|
+
*
|
|
30
|
+
* The listener returns false from callbacks so it does not short-circuit other
|
|
31
|
+
* listeners or Glide's handling.
|
|
32
|
+
*/
|
|
33
|
+
public final class SaveKeysRequestListener implements RequestListener<Drawable> {
|
|
34
|
+
private static final String TAG = "SaveKeysRequestListener";
|
|
35
|
+
|
|
36
|
+
private final String id; // canonical id you use to later evict (typically the URL string or normalized
|
|
37
|
+
// model id)
|
|
38
|
+
private final Object model; // the same model object you pass to Glide.load(...)
|
|
39
|
+
private final Key sourceKey;
|
|
40
|
+
private final Key signature;
|
|
41
|
+
private final int width;
|
|
42
|
+
private final int height;
|
|
43
|
+
@Nullable
|
|
44
|
+
private final Transformation<?> transformation;
|
|
45
|
+
private final Options options;
|
|
46
|
+
private final Class<?> decodedResourceClass;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Construct directly with an Options instance.
|
|
50
|
+
*/
|
|
51
|
+
public SaveKeysRequestListener(
|
|
52
|
+
String id,
|
|
53
|
+
Object model,
|
|
54
|
+
Key sourceKey,
|
|
55
|
+
Key signature,
|
|
56
|
+
int width,
|
|
57
|
+
int height,
|
|
58
|
+
@Nullable Transformation<?> transformation,
|
|
59
|
+
RequestOptions options,
|
|
60
|
+
Class<?> decodedResourceClass) {
|
|
61
|
+
this.id = id;
|
|
62
|
+
this.model = model;
|
|
63
|
+
this.sourceKey = sourceKey;
|
|
64
|
+
this.signature = signature;
|
|
65
|
+
this.width = width;
|
|
66
|
+
this.height = height;
|
|
67
|
+
this.transformation = transformation;
|
|
68
|
+
this.options = (options == null) ? new Options() : ExtractRequestOptions.getFrom(options);
|
|
69
|
+
this.decodedResourceClass = (decodedResourceClass == null) ? Drawable.class : decodedResourceClass;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Override
|
|
73
|
+
public boolean onLoadFailed(GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
|
74
|
+
// We don't persist anything on failure (could be added). Do not consume the
|
|
75
|
+
// event.
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@Override
|
|
80
|
+
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target,
|
|
81
|
+
com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
|
|
82
|
+
try {
|
|
83
|
+
byte[] transformationBytes = null;
|
|
84
|
+
byte[] optionsBytes = null;
|
|
85
|
+
|
|
86
|
+
// Record transformation key bytes if we have a transformation instance
|
|
87
|
+
if (transformation != null) {
|
|
88
|
+
try {
|
|
89
|
+
RecordingDigest rd = new RecordingDigest();
|
|
90
|
+
transformation.updateDiskCacheKey(rd);
|
|
91
|
+
transformationBytes = rd.digest();
|
|
92
|
+
} catch (Throwable t) {
|
|
93
|
+
Log.w(TAG, "Failed to record transformation key bytes for id=" + id, t);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Record options key bytes (Options.updateDiskCacheKey)
|
|
98
|
+
try {
|
|
99
|
+
RecordingDigest rd2 = new RecordingDigest();
|
|
100
|
+
options.updateDiskCacheKey(rd2);
|
|
101
|
+
optionsBytes = rd2.digest();
|
|
102
|
+
} catch (Throwable t) {
|
|
103
|
+
Log.w(TAG, "Failed to record options key bytes for id=" + id, t);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Attempt to preserve existing engineKey if present
|
|
107
|
+
com.bumptech.glide.load.Key engineKeyToKeep = null;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
CacheKeyStore.StoredKeys existing = null;
|
|
111
|
+
if (EvictionManager.get() != null) {
|
|
112
|
+
// use the public API to fetch current stored keys for this id (may be
|
|
113
|
+
// persistent or in-memory)
|
|
114
|
+
existing = EvictionManager.get().getKeyStore().get(id);
|
|
115
|
+
}
|
|
116
|
+
if (existing != null && existing.engineKey != null) {
|
|
117
|
+
engineKeyToKeep = existing.engineKey;
|
|
118
|
+
}
|
|
119
|
+
} catch (Throwable t) {
|
|
120
|
+
Log.w(TAG, "Failed to read existing stored keys to preserve engineKey for id=" + id, t);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Build StoredKeys and persist via EvictionManager
|
|
124
|
+
CacheKeyStore.StoredKeys stored = new CacheKeyStore.StoredKeys(
|
|
125
|
+
sourceKey,
|
|
126
|
+
signature,
|
|
127
|
+
width,
|
|
128
|
+
height,
|
|
129
|
+
transformation, // may be null; kept for in-process fallback
|
|
130
|
+
transformationBytes, // recorded transformation bytes (preferred)
|
|
131
|
+
decodedResourceClass,
|
|
132
|
+
options,
|
|
133
|
+
optionsBytes,
|
|
134
|
+
engineKeyToKeep // preserve any engineKey we found
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
EvictionManager.get().saveKeys(id, stored);
|
|
138
|
+
} catch (Throwable t) {
|
|
139
|
+
Log.w(TAG, "Unexpected error in SaveKeysRequestListener.onResourceReady for id=" + id, t);
|
|
140
|
+
}
|
|
141
|
+
// Do not consume the event; allow other listeners and Glide to continue normal
|
|
142
|
+
// handling.
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import android.graphics.Matrix;
|
|
4
|
+
import android.graphics.RectF;
|
|
5
|
+
import android.graphics.drawable.Drawable;
|
|
6
|
+
import android.widget.ImageView;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Utilities to compute an image -> view matrix that respects an
|
|
10
|
+
* ImageView.ScaleType
|
|
11
|
+
* while also applying an image rotation (degrees).
|
|
12
|
+
*
|
|
13
|
+
* Notes:
|
|
14
|
+
* - Rotation is applied around the drawable's center before fitting/scaling.
|
|
15
|
+
* - The output matrix maps drawable coordinates to view coordinates.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* Matrix m = new Matrix();
|
|
19
|
+
* ScaleUtils.getImageMatrix(drawable, viewWidth, viewHeight, rotationDegrees,
|
|
20
|
+
* scaleType, m);
|
|
21
|
+
*/
|
|
22
|
+
public final class ScaleUtils {
|
|
23
|
+
|
|
24
|
+
private ScaleUtils() {
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Compute a matrix that maps the drawable (drawable-local coords:
|
|
29
|
+
* 0..intrinsicW/H)
|
|
30
|
+
* into view coordinates, applying rotation (degrees) about the drawable center
|
|
31
|
+
* first,
|
|
32
|
+
* then the scaleType fit.
|
|
33
|
+
*
|
|
34
|
+
* rotationDegrees is a float to allow smooth animated rotation.
|
|
35
|
+
*/
|
|
36
|
+
public static void getImageMatrix(
|
|
37
|
+
Drawable d,
|
|
38
|
+
int viewWidth,
|
|
39
|
+
int viewHeight,
|
|
40
|
+
float rotationDegrees,
|
|
41
|
+
ImageView.ScaleType scaleType,
|
|
42
|
+
Matrix outMatrix) {
|
|
43
|
+
if (d == null) {
|
|
44
|
+
outMatrix.reset();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
float dW = d.getIntrinsicWidth();
|
|
48
|
+
float dH = d.getIntrinsicHeight();
|
|
49
|
+
if (dW <= 0 || dH <= 0 || viewWidth == 0 || viewHeight == 0) {
|
|
50
|
+
outMatrix.reset();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
RectF src = new RectF(0, 0, dW, dH);
|
|
55
|
+
Matrix m = new Matrix();
|
|
56
|
+
|
|
57
|
+
// 1) rotate around drawable center (drawable-local coords)
|
|
58
|
+
if ((rotationDegrees % 360f) != 0f) {
|
|
59
|
+
m.postRotate(rotationDegrees, dW / 2f, dH / 2f);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2) compute rotated drawable bounds in drawable-local coords
|
|
63
|
+
RectF rotated = new RectF();
|
|
64
|
+
m.mapRect(rotated, src);
|
|
65
|
+
|
|
66
|
+
float rW = rotated.width();
|
|
67
|
+
float rH = rotated.height();
|
|
68
|
+
|
|
69
|
+
float scaleX = (float) viewWidth / rW;
|
|
70
|
+
float scaleY = (float) viewHeight / rH;
|
|
71
|
+
|
|
72
|
+
switch (scaleType) {
|
|
73
|
+
case CENTER:
|
|
74
|
+
// no scaling, center only; rotation already applied
|
|
75
|
+
break;
|
|
76
|
+
case CENTER_CROP: {
|
|
77
|
+
float scale = Math.max(scaleX, scaleY);
|
|
78
|
+
m.postScale(scale, scale, dW / 2f, dH / 2f);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case CENTER_INSIDE: {
|
|
82
|
+
float scale = Math.min(1f, Math.min(scaleX, scaleY));
|
|
83
|
+
m.postScale(scale, scale, dW / 2f, dH / 2f);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case FIT_CENTER:
|
|
87
|
+
case FIT_START:
|
|
88
|
+
case FIT_END:
|
|
89
|
+
case FIT_XY: {
|
|
90
|
+
if (scaleType == ImageView.ScaleType.FIT_XY) {
|
|
91
|
+
m.postScale(scaleX, scaleY, dW / 2f, dH / 2f);
|
|
92
|
+
} else {
|
|
93
|
+
float scale = Math.min(scaleX, scaleY);
|
|
94
|
+
m.postScale(scale, scale, dW / 2f, dH / 2f);
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case MATRIX:
|
|
99
|
+
default:
|
|
100
|
+
// Let the caller manage the matrix in MATRIX mode; here treat like FIT_CENTER.
|
|
101
|
+
float scale = Math.min(scaleX, scaleY);
|
|
102
|
+
m.postScale(scale, scale, dW / 2f, dH / 2f);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 3) translate to position according to scaleType
|
|
107
|
+
RectF mapped = new RectF();
|
|
108
|
+
m.mapRect(mapped, src);
|
|
109
|
+
|
|
110
|
+
float dx = 0f;
|
|
111
|
+
float dy = 0f;
|
|
112
|
+
|
|
113
|
+
if (scaleType == ImageView.ScaleType.FIT_START) {
|
|
114
|
+
dx = 0f - mapped.left;
|
|
115
|
+
dy = 0f - mapped.top;
|
|
116
|
+
} else if (scaleType == ImageView.ScaleType.FIT_END) {
|
|
117
|
+
dx = viewWidth - mapped.right;
|
|
118
|
+
dy = viewHeight - mapped.bottom;
|
|
119
|
+
} else {
|
|
120
|
+
// center
|
|
121
|
+
dx = viewWidth * 0.5f - mapped.centerX();
|
|
122
|
+
dy = viewHeight * 0.5f - mapped.centerY();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
m.postTranslate(dx, dy);
|
|
126
|
+
|
|
127
|
+
outMatrix.set(m);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
package com.nativescript.image;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.content.SharedPreferences;
|
|
5
|
+
import android.util.Base64;
|
|
6
|
+
import org.json.JSONException;
|
|
7
|
+
import org.json.JSONObject;
|
|
8
|
+
import com.bumptech.glide.load.Key;
|
|
9
|
+
import com.bumptech.glide.signature.ObjectKey;
|
|
10
|
+
import com.bumptech.glide.load.Options;
|
|
11
|
+
import com.bumptech.glide.load.Transformation;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* SharedPreferences-backed minimal persistent store.
|
|
15
|
+
* Stores:
|
|
16
|
+
* - source (string)
|
|
17
|
+
* - signature (string)
|
|
18
|
+
* - width, height
|
|
19
|
+
* - decodedResourceClass (string)
|
|
20
|
+
* - transformationKeyBytes (base64)
|
|
21
|
+
* - optionsKeyBytes (base64)
|
|
22
|
+
*
|
|
23
|
+
* For transformations: we store recorded bytes (from RecordingDigest) rather
|
|
24
|
+
* than trying to
|
|
25
|
+
* re-create the Transformation object.
|
|
26
|
+
*/
|
|
27
|
+
public class SharedPrefCacheKeyStore {
|
|
28
|
+
private static final String PREFS = "glide_cache_keys_v2";
|
|
29
|
+
private final SharedPreferences prefs;
|
|
30
|
+
|
|
31
|
+
public SharedPrefCacheKeyStore(Context context) {
|
|
32
|
+
this.prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public void put(String id, CacheKeyStore.StoredKeys keys) {
|
|
36
|
+
try {
|
|
37
|
+
JSONObject j = new JSONObject();
|
|
38
|
+
j.put("source", keys.sourceKey == null ? JSONObject.NULL : keys.sourceKey.toString());
|
|
39
|
+
j.put("signature", keys.signature == null ? JSONObject.NULL : keys.signature.toString());
|
|
40
|
+
j.put("width", keys.width);
|
|
41
|
+
j.put("height", keys.height);
|
|
42
|
+
j.put("decodedResourceClass",
|
|
43
|
+
keys.decodedResourceClass == null ? JSONObject.NULL : keys.decodedResourceClass.getName());
|
|
44
|
+
j.put("transformationBytes", keys.transformationKeyBytes == null ? JSONObject.NULL
|
|
45
|
+
: Base64.encodeToString(keys.transformationKeyBytes, Base64.NO_WRAP));
|
|
46
|
+
j.put("optionsBytes",
|
|
47
|
+
keys.optionsKeyBytes == null ? JSONObject.NULL : Base64.encodeToString(keys.optionsKeyBytes, Base64.NO_WRAP));
|
|
48
|
+
prefs.edit().putString(id, j.toString()).apply();
|
|
49
|
+
} catch (JSONException e) {
|
|
50
|
+
// log if you want
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public CacheKeyStore.StoredKeys get(String id) {
|
|
55
|
+
String s = prefs.getString(id, null);
|
|
56
|
+
if (s == null)
|
|
57
|
+
return null;
|
|
58
|
+
try {
|
|
59
|
+
JSONObject j = new JSONObject(s);
|
|
60
|
+
String source = j.optString("source", null);
|
|
61
|
+
String signature = j.optString("signature", null);
|
|
62
|
+
int width = j.optInt("width", com.bumptech.glide.request.target.Target.SIZE_ORIGINAL);
|
|
63
|
+
int height = j.optInt("height", com.bumptech.glide.request.target.Target.SIZE_ORIGINAL);
|
|
64
|
+
String decodedName = j.optString("decodedResourceClass", android.graphics.Bitmap.class.getName());
|
|
65
|
+
String transformationBase64 = j.optString("transformationBytes", null);
|
|
66
|
+
String optionsBase64 = j.optString("optionsBytes", null);
|
|
67
|
+
|
|
68
|
+
Key sourceKey = source != null && !"null".equals(source) ? new ObjectKey(source) : new ObjectKey(id);
|
|
69
|
+
Key signatureKey = signature != null && !"null".equals(signature) ? new ObjectKey(signature)
|
|
70
|
+
: new ObjectKey("signature-none");
|
|
71
|
+
Class<?> decodedClass = Class.forName(decodedName);
|
|
72
|
+
byte[] transformationBytes = (transformationBase64 == null || "null".equals(transformationBase64)) ? null
|
|
73
|
+
: Base64.decode(transformationBase64, Base64.NO_WRAP);
|
|
74
|
+
byte[] optionsBytes = (optionsBase64 == null || "null".equals(optionsBase64)) ? null
|
|
75
|
+
: Base64.decode(optionsBase64, Base64.NO_WRAP);
|
|
76
|
+
|
|
77
|
+
// We cannot re-create a reliable Options instance from optionsBytes. We store
|
|
78
|
+
// optionsBytes and will replay them
|
|
79
|
+
// into the ResourceCacheKey digest when deleting. For in-memory Options pass an
|
|
80
|
+
// empty instance or the caller's instance.
|
|
81
|
+
Options options = new Options();
|
|
82
|
+
return new CacheKeyStore.StoredKeys(sourceKey, signatureKey, width, height, /* transformation */ null,
|
|
83
|
+
transformationBytes, decodedClass, options, optionsBytes, /* engineKey */ null);
|
|
84
|
+
} catch (Exception e) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public void remove(String id) {
|
|
90
|
+
prefs.edit().remove(id).apply();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -1,42 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"uses": [
|
|
3
|
-
"com.facebook.drawee.drawable:ScalingUtils",
|
|
4
|
-
"com.facebook.drawee.drawable:ScalingUtils.ScaleType",
|
|
5
|
-
"com.facebook.drawee.generic:RoundingParams",
|
|
6
|
-
"com.facebook.drawee.drawable:ProgressBarDrawable",
|
|
7
|
-
"com.facebook.drawee.view:DraweeView",
|
|
8
|
-
"com.facebook.drawee.interfaces:DraweeHierarchy",
|
|
9
|
-
"com.facebook.drawee.interfaces:SettableDraweeHierarchy",
|
|
10
|
-
"com.facebook.drawee.interfaces:SimpleDraweeControllerBuilder",
|
|
11
|
-
"com.facebook.drawee.interfaces:DraweeController",
|
|
12
|
-
"com.facebook.drawee.generic:GenericDraweeHierarchyBuilder",
|
|
13
|
-
"com.facebook.drawee.generic:GenericDraweeHierarchy",
|
|
14
|
-
"com.facebook.imagepipeline.request:ImageRequestBuilder",
|
|
15
|
-
"com.facebook.imagepipeline.request:ImageRequest",
|
|
16
|
-
"com.facebook.imagepipeline.core:ImagePipelineConfig",
|
|
17
|
-
"com.facebook.imagepipeline.core:ImagePipelineConfig.Builder",
|
|
18
|
-
"com.facebook.imagepipeline.core:ImagePipeline",
|
|
19
|
-
"com.facebook.imagepipeline.common:RotationOptions",
|
|
20
|
-
"com.facebook.imagepipeline.common:ResizeOptions",
|
|
21
|
-
"com.facebook.drawee.backends.pipeline:Fresco",
|
|
22
|
-
"com.facebook.drawee.backends.pipeline:PipelineDraweeControllerBuilder",
|
|
23
|
-
"com.facebook.drawee.backends.pipeline:PipelineDraweeController",
|
|
24
|
-
"com.facebook.drawee.controller:AbstractDraweeControllerBuilder",
|
|
25
|
-
"com.facebook.drawee.controller:AbstractDraweeController",
|
|
26
|
-
"com.facebook.drawee.backends.pipeline.info:ImagePerfDataListener",
|
|
27
|
-
"com.facebook.drawee.backends.pipeline.info:ImagePerfData",
|
|
28
|
-
"com.facebook.drawee.controller:ControllerListener",
|
|
29
|
-
"com.nativescript.image:ScalingUtils*",
|
|
30
|
-
"com.nativescript.image:DraweeView",
|
|
31
|
-
"com.nativescript.image:OkHttpNetworkFetcher",
|
|
32
|
-
"com.nativescript.image:ScalingBlurPostprocessor",
|
|
33
|
-
"com.facebook.imagepipeline.producers:NetworkFetcher",
|
|
34
|
-
"com.facebook.imagepipeline.backends.okhttp3:OkHttpImagePipelineConfigFactory",
|
|
35
|
-
"okhttp3:OkHttpClient",
|
|
36
|
-
"android.graphics.drawable:Animatable",
|
|
37
|
-
"com.facebook.imagepipeline.image:ImageInfo",
|
|
38
|
-
"com.facebook.common.util:UriUtil",
|
|
39
3
|
"android.net:Uri",
|
|
40
|
-
"
|
|
4
|
+
"com.nativescript.image:SharedPrefCacheKeyStore",
|
|
5
|
+
"com.nativescript.image:CacheKeyStore",
|
|
6
|
+
"com.nativescript.image:EvictionManager",
|
|
7
|
+
"com.nativescript.image:EvictionManager.DiskPresenceCallback",
|
|
8
|
+
"com.nativescript.image:EvictionManager.EvictionCallback",
|
|
9
|
+
"com.nativescript.image:ImageProgressCallback",
|
|
10
|
+
"com.nativescript.image:ImageLoadSourceCallback",
|
|
11
|
+
"com.nativescript.image:CustomGlideUrl",
|
|
12
|
+
"com.nativescript.image:ConditionalCrossFadeFactory",
|
|
13
|
+
"com.nativescript.image:SaveKeysRequestListener",
|
|
14
|
+
"com.nativescript.image:CompositeRequestListener",
|
|
15
|
+
"com.nativescript.image:MatrixDrawableImageViewTarget",
|
|
16
|
+
"com.nativescript.image:MatrixImageView",
|
|
17
|
+
"com.bumptech.glide:Glide",
|
|
18
|
+
"com.bumptech.glide.signature:ObjectKey",
|
|
19
|
+
"com.bumptech.glide.request:RequestOptions",
|
|
20
|
+
"com.bumptech.glide.request:RequestBuilder",
|
|
21
|
+
"com.bumptech.glide.request:RequestListener",
|
|
22
|
+
"com.bumptech.glide.request.target:Target",
|
|
23
|
+
"com.bumptech.glide.request.target:ViewTarget",
|
|
24
|
+
"com.bumptech.glide:RequestManager",
|
|
25
|
+
"com.bumptech.glide.load:MultiTransformation",
|
|
26
|
+
"com.bumptech.glide.load:Transformation",
|
|
27
|
+
"com.bumptech.glide.load:DataSource",
|
|
28
|
+
"com.bumptech.glide.load:Key",
|
|
29
|
+
"com.bumptech.glide.load.resource.drawable:DrawableTransitionOptions",
|
|
30
|
+
"com.bumptech.glide.load.engine:DiskCacheStrategy",
|
|
31
|
+
"com.bumptech.glide.request.transition:Transition",
|
|
32
|
+
"com.bumptech.glide.request.transition:DrawableCrossFadeFactory",
|
|
33
|
+
"android.graphics.drawable:Drawable",
|
|
34
|
+
"android.graphics.drawable:BitmapDrawable",
|
|
35
|
+
"android.widget:ImageView:ScaleType",
|
|
36
|
+
"android.widget:ImageView",
|
|
37
|
+
"androidx.appcompat.widget:AppCompatImageView",
|
|
38
|
+
"android.graphics.PorterDuff:Mode",
|
|
39
|
+
"jp.wasabeef.glide.transformations:BlurTransformation",
|
|
40
|
+
"jp.wasabeef.glide.transformations:CropCircleTransformation",
|
|
41
|
+
"jp.wasabeef.glide.transformations:RoundedCornersTransformation",
|
|
42
|
+
"jp.wasabeef.glide.transformations:ColorFilterTransformation"
|
|
41
43
|
]
|
|
42
44
|
}
|