react-native-fast-image 9.3.7 → 10.1.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.
Files changed (78) hide show
  1. package/README.md +97 -61
  2. package/RNFastImage.podspec +14 -9
  3. package/android/build.gradle +21 -47
  4. package/android/consumer-rules.pro +7 -7
  5. package/android/src/main/AndroidManifest.xml +1 -2
  6. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimatable.java +1 -1
  7. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimated.java +2 -2
  8. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageApng.java +2 -2
  9. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageBlur.java +4 -6
  10. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheControl.java +1 -1
  11. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheLimits.java +4 -1
  12. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheWrite.java +1 -1
  13. package/android/src/main/java/com/fastimage/FastImageCrop.java +92 -0
  14. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageEvent.java +2 -3
  15. package/android/src/main/java/com/fastimage/FastImageEvents.java +74 -0
  16. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGif.java +1 -1
  17. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlide.java +13 -3
  18. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlideModule.java +1 -1
  19. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageKeyedGlideUrl.java +1 -1
  20. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageOkHttpProgressGlideModule.java +49 -14
  21. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageRequestListener.java +6 -1
  22. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSharedDownloads.java +1 -1
  23. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSource.java +12 -1
  24. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSourceSize.java +12 -2
  25. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvg.java +3 -3
  26. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvgRenderer.java +1 -1
  27. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageUrl.java +1 -1
  28. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewConverter.java +24 -9
  29. package/android/src/main/java/com/fastimage/FastImageViewManager.java +591 -0
  30. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewModule.java +50 -28
  31. package/android/src/main/java/com/fastimage/FastImageViewPackage.java +51 -0
  32. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewWithUrl.java +352 -126
  33. package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageWebGlideUrl.java +1 -1
  34. package/app.plugin.js +2 -7
  35. package/dist/index.cjs.js +276 -157
  36. package/dist/index.cjs.js.flow +34 -17
  37. package/dist/index.cjs.web.js +211 -61
  38. package/dist/index.cjs.web.js.flow +34 -17
  39. package/dist/index.d.ts +108 -58
  40. package/dist/index.js +278 -159
  41. package/dist/index.js.flow +34 -17
  42. package/dist/index.web.js +212 -62
  43. package/dist/index.web.js.flow +34 -17
  44. package/dist/objectFit.d.ts +2 -2
  45. package/dist/objectPosition.d.ts +42 -0
  46. package/dist/specs/FastImageViewNativeComponent.d.ts +48 -0
  47. package/dist/specs/NativeFastImageModule.d.ts +11 -0
  48. package/dist/warnings.d.ts +3 -0
  49. package/ios/FastImage/FFFDownsampledImage.h +4 -8
  50. package/ios/FastImage/FFFDownsampledImage.m +0 -34
  51. package/ios/FastImage/FFFastImageModule.h +8 -0
  52. package/ios/FastImage/{FFFastImageViewManager.m → FFFastImageModule.mm} +89 -59
  53. package/ios/FastImage/FFFastImageSource.h +4 -0
  54. package/ios/FastImage/FFFastImageSource.m +5 -1
  55. package/ios/FastImage/FFFastImageView.h +36 -7
  56. package/ios/FastImage/FFFastImageView.m +229 -172
  57. package/ios/FastImage/FFFastImageViewComponentView.h +11 -0
  58. package/ios/FastImage/FFFastImageViewComponentView.mm +325 -0
  59. package/ios/FastImage/RCTConvert+FFFastImage.h +1 -1
  60. package/ios/FastImage/RCTConvert+FFFastImage.m +9 -10
  61. package/package.json +28 -9
  62. package/src/constants.ts +28 -0
  63. package/src/index.js.flow +180 -0
  64. package/src/index.tsx +1128 -0
  65. package/src/index.web.tsx +548 -0
  66. package/src/objectFit.ts +48 -0
  67. package/src/objectPosition.ts +243 -0
  68. package/src/specs/FastImageViewNativeComponent.ts +83 -0
  69. package/src/specs/NativeFastImageModule.ts +35 -0
  70. package/src/warnings.ts +37 -0
  71. package/android/src/main/AndroidManifestNew.xml +0 -3
  72. package/android/src/main/java/com/dylanvann/fastimage/FastImageCookieHandler.java +0 -63
  73. package/android/src/main/java/com/dylanvann/fastimage/FastImageEvents.java +0 -49
  74. package/android/src/main/java/com/dylanvann/fastimage/FastImageProgressListener.java +0 -18
  75. package/android/src/main/java/com/dylanvann/fastimage/FastImageShadowNode.java +0 -21
  76. package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +0 -379
  77. package/android/src/main/java/com/dylanvann/fastimage/FastImageViewPackage.java +0 -27
  78. package/ios/FastImage/FFFastImageViewManager.h +0 -5
@@ -0,0 +1,243 @@
1
+ // objectPosition, as CSS's object-position, shared by the native (index.tsx)
2
+ // and web (index.web.tsx) versions: a CSS position string or an object of
3
+ // edges, resolved to what CSS computes for each axis.
4
+ import { warnOnce } from './warnings'
5
+
6
+ // Metro's (and React Native's Jest preset's) development flag; other web
7
+ // bundlers may not define it.
8
+ declare const __DEV__: boolean | undefined
9
+
10
+ /** A length in points (dp on Android), or a percentage: see [`objectPosition`](#objectposition). */
11
+ export type PositionLength = number | `${number}%`
12
+
13
+ /**
14
+ * Where the image is in the view: see [`objectPosition`](#objectposition).
15
+ */
16
+ export type ObjectPosition =
17
+ | string
18
+ | ((
19
+ | { top?: PositionLength; bottom?: never }
20
+ | { bottom: PositionLength; top?: never }
21
+ ) &
22
+ (
23
+ | { left?: PositionLength; right?: never }
24
+ | { right: PositionLength; left?: never }
25
+ ))
26
+
27
+ // One axis as CSS computes it, calc(fraction * 100% + offset): the image's
28
+ // edge is at (view - image) * fraction + offset, in points.
29
+ export type ResolvedAxis = { fraction: number; offset: number }
30
+ export type ResolvedPosition = { x: ResolvedAxis; y: ResolvedAxis }
31
+
32
+ const START: ResolvedAxis = { fraction: 0, offset: 0 }
33
+ const CENTER: ResolvedAxis = { fraction: 0.5, offset: 0 }
34
+ const END: ResolvedAxis = { fraction: 1, offset: 0 }
35
+
36
+ /** The default, `50% 50%`. */
37
+ export const CENTERED: ResolvedPosition = { x: CENTER, y: CENTER }
38
+
39
+ type Keyword = 'left' | 'right' | 'top' | 'bottom' | 'center'
40
+ type Token = { keyword: Keyword } | { length: ResolvedAxis }
41
+
42
+ const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i
43
+
44
+ // A length in px, a percentage, or 0 without a unit.
45
+ function length(text: string): ResolvedAxis | null {
46
+ const lower = text.toLowerCase()
47
+ if (lower.endsWith('%')) {
48
+ const number = lower.slice(0, -1)
49
+ return NUMBER.test(number)
50
+ ? { fraction: Number(number) / 100, offset: 0 }
51
+ : null
52
+ }
53
+ if (lower.endsWith('px')) {
54
+ const number = lower.slice(0, -2)
55
+ return NUMBER.test(number)
56
+ ? { fraction: 0, offset: Number(number) }
57
+ : null
58
+ }
59
+ return NUMBER.test(lower) && Number(lower) === 0 ? START : null
60
+ }
61
+
62
+ function token(text: string): Token | null {
63
+ const lower = text.toLowerCase()
64
+ if (
65
+ lower === 'left' ||
66
+ lower === 'right' ||
67
+ lower === 'top' ||
68
+ lower === 'bottom' ||
69
+ lower === 'center'
70
+ ) {
71
+ return { keyword: lower }
72
+ }
73
+ const axis = length(text)
74
+ return axis ? { length: axis } : null
75
+ }
76
+
77
+ const X: Partial<Record<Keyword, ResolvedAxis>> = {
78
+ left: START,
79
+ center: CENTER,
80
+ right: END,
81
+ }
82
+ const Y: Partial<Record<Keyword, ResolvedAxis>> = {
83
+ top: START,
84
+ center: CENTER,
85
+ bottom: END,
86
+ }
87
+
88
+ const x = (t: Token) => ('length' in t ? t.length : X[t.keyword])
89
+ const y = (t: Token) => ('length' in t ? t.length : Y[t.keyword])
90
+
91
+ // An offset from an edge: from the left or top as it is, from the right or
92
+ // bottom as 100% minus it.
93
+ function fromEdge(edge: Keyword, axis: ResolvedAxis): ResolvedAxis {
94
+ return edge === 'left' || edge === 'top'
95
+ ? axis
96
+ : { fraction: 1 - axis.fraction, offset: -axis.offset || 0 }
97
+ }
98
+
99
+ // CSS's <position> (CSS Values 4), as object-position takes it: 1, 2 or 4
100
+ // values, keywords in any case, lengths in px or %, 0 without a unit. Null if
101
+ // it isn't one (CSS ignores it then).
102
+ function parse(text: string): ResolvedPosition | null {
103
+ const tokens: Token[] = []
104
+ for (const part of text.trim().split(/\s+/)) {
105
+ const t = token(part)
106
+ if (!t) return null
107
+ tokens.push(t)
108
+ }
109
+ if (tokens.length === 1) {
110
+ const [a] = tokens
111
+ if ('keyword' in a && (a.keyword === 'top' || a.keyword === 'bottom')) {
112
+ return { x: CENTER, y: Y[a.keyword]! }
113
+ }
114
+ return { x: x(a)!, y: CENTER }
115
+ }
116
+ if (tokens.length === 2) {
117
+ const [a, b] = tokens
118
+ // [left | center | right | <length-percentage>]
119
+ // [top | center | bottom | <length-percentage>]
120
+ const ax = x(a)
121
+ const by = y(b)
122
+ if (ax && by) return { x: ax, y: by }
123
+ // Or two keywords in either order: [left | center | right] &&
124
+ // [top | center | bottom].
125
+ if ('keyword' in a && 'keyword' in b) {
126
+ const bx = X[b.keyword]
127
+ const ay = Y[a.keyword]
128
+ if (bx && ay) return { x: bx, y: ay }
129
+ }
130
+ return null
131
+ }
132
+ if (tokens.length === 4) {
133
+ // [[left | right] <length-percentage>] &&
134
+ // [[top | bottom] <length-percentage>]
135
+ const [a, b, c, d] = tokens
136
+ if (
137
+ !(
138
+ 'keyword' in a &&
139
+ 'length' in b &&
140
+ 'keyword' in c &&
141
+ 'length' in d
142
+ )
143
+ ) {
144
+ return null
145
+ }
146
+ const horizontal = (k: Keyword) => k === 'left' || k === 'right'
147
+ const vertical = (k: Keyword) => k === 'top' || k === 'bottom'
148
+ if (horizontal(a.keyword) && vertical(c.keyword)) {
149
+ return {
150
+ x: fromEdge(a.keyword, b.length),
151
+ y: fromEdge(c.keyword, d.length),
152
+ }
153
+ }
154
+ if (vertical(a.keyword) && horizontal(c.keyword)) {
155
+ return {
156
+ x: fromEdge(c.keyword, d.length),
157
+ y: fromEdge(a.keyword, b.length),
158
+ }
159
+ }
160
+ }
161
+ return null
162
+ }
163
+
164
+ const parsed = new Map<string, ResolvedPosition | null>()
165
+
166
+ function objectLength(value: unknown): ResolvedAxis | null {
167
+ if (typeof value === 'number') {
168
+ return Number.isFinite(value) ? { fraction: 0, offset: value } : null
169
+ }
170
+ return typeof value === 'string' && value.endsWith('%')
171
+ ? length(value)
172
+ : null
173
+ }
174
+
175
+ /**
176
+ * The position for objectPosition, or null if it isn't valid. A string is
177
+ * CSS's syntax; an object has one of `top` and `bottom` and one of `left`
178
+ * and `right`, each in points or a percentage, and a missing axis is
179
+ * centered. Unset is centered.
180
+ */
181
+ export function resolveObjectPosition(
182
+ position: ObjectPosition | null | undefined,
183
+ ): ResolvedPosition | null {
184
+ if (position == null) return CENTERED
185
+ if (typeof position === 'string') {
186
+ // The same few strings, in every row of a list.
187
+ let result = parsed.get(position)
188
+ if (result === undefined) {
189
+ result = parse(position)
190
+ if (parsed.size < 100) parsed.set(position, result)
191
+ }
192
+ return result
193
+ }
194
+ if (typeof position !== 'object') return null
195
+ const edges = position as Record<string, unknown>
196
+ const axis = (start: Keyword, end: Keyword): ResolvedAxis | null => {
197
+ const hasStart = edges[start] != null
198
+ const hasEnd = edges[end] != null
199
+ if (hasStart && hasEnd) return null
200
+ if (!hasStart && !hasEnd) return CENTER
201
+ const edge = hasStart ? start : end
202
+ const value = objectLength(edges[edge])
203
+ return value && fromEdge(edge, value)
204
+ }
205
+ const resolvedX = axis('left', 'right')
206
+ const resolvedY = axis('top', 'bottom')
207
+ return resolvedX && resolvedY ? { x: resolvedX, y: resolvedY } : null
208
+ }
209
+
210
+ // Without float noise (1 - 1.2 is -0.19999999999999996).
211
+ const round = (n: number) => Math.round(n * 1e6) / 1e6
212
+
213
+ /**
214
+ * The position for objectPosition, or centered if it isn't valid (as CSS
215
+ * ignores it), with a warning in development.
216
+ */
217
+ export function objectPositionOrCentered(
218
+ position: ObjectPosition | null | undefined,
219
+ ): ResolvedPosition {
220
+ const resolved = resolveObjectPosition(position)
221
+ if (resolved) return resolved
222
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
223
+ const value = JSON.stringify(position)
224
+ warnOnce(
225
+ `objectPosition ${value}`,
226
+ `react-native-fast-image: objectPosition ${value} isn't a position, so the image is centered. Use CSS's syntax with px or % (e.g. 'right 10px bottom 20%'), or { top | bottom, left | right } in points or percentages.`,
227
+ 'warn',
228
+ )
229
+ }
230
+ return CENTERED
231
+ }
232
+
233
+ /** The position as CSS (for the web version's object-position). */
234
+ export function cssPosition({ x, y }: ResolvedPosition) {
235
+ const axis = ({ fraction, offset }: ResolvedAxis) => {
236
+ const percent = round(fraction * 100)
237
+ const px = round(offset)
238
+ if (px === 0) return `${percent}%`
239
+ if (percent === 0) return `${px}px`
240
+ return `calc(${percent}% ${px < 0 ? '-' : '+'} ${Math.abs(px)}px)`
241
+ }
242
+ return `${axis(x)} ${axis(y)}`
243
+ }
@@ -0,0 +1,83 @@
1
+ // The native image view, from React Native's Codegen (the New Architecture's
2
+ // component). FastImage (index.tsx) renders it as its only view, and turns
3
+ // its props into these.
4
+ import type {
5
+ CodegenTypes,
6
+ ColorValue,
7
+ HostComponent,
8
+ ViewProps,
9
+ } from 'react-native'
10
+ // React Native's Babel plugin replaces the call below with the component's
11
+ // view config, so the import isn't used when the app runs.
12
+ import { codegenNativeComponent } from 'react-native'
13
+
14
+ type LoadStartEvent = Readonly<{}>
15
+
16
+ // progress is loaded / total, from 0 to 1 (the views don't send an unknown
17
+ // total).
18
+ type ProgressEvent = Readonly<{
19
+ loaded: CodegenTypes.Double
20
+ total: CodegenTypes.Double
21
+ progress: CodegenTypes.Double
22
+ }>
23
+
24
+ // The image's own size.
25
+ type LoadEvent = Readonly<{
26
+ width: CodegenTypes.Double
27
+ height: CodegenTypes.Double
28
+ }>
29
+
30
+ type ErrorEvent = Readonly<{
31
+ error: string
32
+ }>
33
+
34
+ // onLoad's or onError's event, with whether it loaded.
35
+ type LoadEndEvent = Readonly<{
36
+ ok: boolean
37
+ width?: CodegenTypes.Double
38
+ height?: CodegenTypes.Double
39
+ error?: string
40
+ }>
41
+
42
+ export interface NativeProps extends ViewProps {
43
+ // A source ({ uri, headers, priority, cache, ... }), as the native
44
+ // sides read it.
45
+ source?: CodegenTypes.UnsafeMixed
46
+ // Several sources (sizes of one image), picked from for the view's size.
47
+ sources?: CodegenTypes.UnsafeMixed
48
+ // A resolved asset source ({ uri, width, height, scale }).
49
+ defaultSource?: CodegenTypes.UnsafeMixed
50
+ // objectFit, resolved (fill, contain, cover, none or scale-down).
51
+ objectFit?: CodegenTypes.WithDefault<string, 'cover'>
52
+ // objectPosition, as CSS computes it: on each axis the image's left or top
53
+ // edge is at (view - image) * fraction + offset, in points.
54
+ objectPositionX?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.5>
55
+ objectPositionXOffset?: CodegenTypes.Float
56
+ objectPositionY?: CodegenTypes.WithDefault<CodegenTypes.Float, 0.5>
57
+ objectPositionYOffset?: CodegenTypes.Float
58
+ tintColor?: ColorValue
59
+ recyclingKey?: string
60
+ loopCount?: CodegenTypes.WithDefault<CodegenTypes.Int32, -1>
61
+ imageRendering?: string
62
+ paused?: boolean
63
+ transitionDuration?: CodegenTypes.Double
64
+ transitionBetweenImages?: boolean
65
+ transitionSkipOnCacheHit?: string
66
+ downsample?: CodegenTypes.WithDefault<boolean, true>
67
+ blurRadius?: CodegenTypes.Float
68
+ // The events JS has a handler for, as bits: 1 onFastImageLoadStart,
69
+ // 2 onFastImageProgress, 4 onFastImageLoad, 8 onFastImageError, 16
70
+ // onFastImageLoadEnd. The native views only send those (most images have
71
+ // no handlers), and only track progress with 2. (Not `events`: iOS's
72
+ // ViewProps already has a member by that name.)
73
+ handledEvents?: CodegenTypes.Int32
74
+ onFastImageLoadStart?: CodegenTypes.DirectEventHandler<LoadStartEvent>
75
+ onFastImageProgress?: CodegenTypes.DirectEventHandler<ProgressEvent>
76
+ onFastImageLoad?: CodegenTypes.DirectEventHandler<LoadEvent>
77
+ onFastImageError?: CodegenTypes.DirectEventHandler<ErrorEvent>
78
+ onFastImageLoadEnd?: CodegenTypes.DirectEventHandler<LoadEndEvent>
79
+ }
80
+
81
+ export default codegenNativeComponent<NativeProps>(
82
+ 'FastImageView',
83
+ ) as HostComponent<NativeProps>
@@ -0,0 +1,35 @@
1
+ // FastImage's functions (preload, the caches), as a TurboModule from React
2
+ // Native's Codegen. FastImage (index.tsx) checks their arguments and shapes
3
+ // their results.
4
+ import type { CodegenTypes, TurboModule } from 'react-native'
5
+ import { TurboModuleRegistry } from 'react-native'
6
+
7
+ export interface Spec extends TurboModule {
8
+ // A result per source, in order: { ok, width, height } or { ok, error }.
9
+ preload(
10
+ sources: Array<CodegenTypes.UnsafeObject>,
11
+ ): Promise<Array<CodegenTypes.UnsafeObject>>
12
+ // { ok, path } or { ok, error }.
13
+ getCachePath(
14
+ source: CodegenTypes.UnsafeObject,
15
+ ): Promise<CodegenTypes.UnsafeObject>
16
+ // { ok, path } or { ok, error }.
17
+ writeToCache(
18
+ source: CodegenTypes.UnsafeObject,
19
+ file: string,
20
+ ): Promise<CodegenTypes.UnsafeObject>
21
+ // { maxDiskSize: number, ..., reset: [names] }: the limits to set, and the
22
+ // names of those to reset (null isn't sent: iOS would drop it). Resolves
23
+ // with the limits in effect.
24
+ configureCache(
25
+ limits: CodegenTypes.UnsafeObject,
26
+ ): Promise<CodegenTypes.UnsafeObject>
27
+ // { ok } or { ok, error }.
28
+ clearMemoryCache(): Promise<CodegenTypes.UnsafeObject>
29
+ clearDiskCache(): Promise<CodegenTypes.UnsafeObject>
30
+ }
31
+
32
+ // null where the app doesn't have the native module (Jest, Expo Go, an app
33
+ // not rebuilt after installing): FastImage's functions throw when they're
34
+ // called, rather than importing FastImage throwing.
35
+ export default TurboModuleRegistry.get<Spec>('FastImageModule')
@@ -0,0 +1,37 @@
1
+ // FastImage's development warnings and errors, each once per app (a list would
2
+ // repeat it for every image), shared by the native (index.tsx) and web
3
+ // (index.web.tsx) versions.
4
+ import { Children } from 'react'
5
+ import type { ReactNode } from 'react'
6
+
7
+ const warned = new Set<string>()
8
+
9
+ export function warnOnce(
10
+ key: string,
11
+ message: string,
12
+ level: 'warn' | 'error',
13
+ ) {
14
+ if (!warned.has(key)) {
15
+ warned.add(key)
16
+ console[level](message)
17
+ }
18
+ }
19
+
20
+ // For the tests (test/setup.ts): `bun test` loads this module once for the
21
+ // native and web versions' tests together.
22
+ export function resetWarnings() {
23
+ warned.clear()
24
+ }
25
+
26
+ // FastImage doesn't render children: on native it's a single view, and the
27
+ // web version does the same. toArray, as a Touchable clones its child with
28
+ // children: undefined.
29
+ export function warnIfChildren(children: unknown) {
30
+ if (Children.toArray(children as ReactNode).length > 0) {
31
+ warnOnce(
32
+ 'children',
33
+ "react-native-fast-image: FastImage doesn't render children (since 10.0): use FastImageBackground for content over an image.",
34
+ 'error',
35
+ )
36
+ }
37
+ }
@@ -1,3 +0,0 @@
1
- <!-- Used instead of AndroidManifest.xml when the namespace is set in build.gradle. -->
2
- <manifest>
3
- </manifest>
@@ -1,63 +0,0 @@
1
- package com.dylanvann.fastimage;
2
-
3
- import android.text.TextUtils;
4
- import android.webkit.CookieManager;
5
-
6
- import androidx.annotation.Nullable;
7
-
8
- import java.net.CookieHandler;
9
- import java.net.URI;
10
- import java.util.Collections;
11
- import java.util.List;
12
- import java.util.Map;
13
-
14
- // Reads and stores cookies in Android's cookie store (the WebView's
15
- // CookieManager), which React Native's networking (fetch) and its Image
16
- // component use. Image requests then send the cookies the app's other requests
17
- // got, as on iOS. Does what React Native's ForwardingCookieHandler does,
18
- // without the ReactContext that one needs (up to at least 0.77). Used through
19
- // OkHttp's JavaNetCookieJar, which parses the cookies.
20
- final class FastImageCookieHandler extends CookieHandler {
21
- @Nullable
22
- private CookieManager cookieManager;
23
-
24
- @Override
25
- public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) {
26
- CookieManager cookieManager = cookieManager();
27
- if (cookieManager == null) return Collections.emptyMap();
28
- String cookies = cookieManager.getCookie(uri.toString());
29
- if (TextUtils.isEmpty(cookies)) return Collections.emptyMap();
30
- return Collections.singletonMap("Cookie", Collections.singletonList(cookies));
31
- }
32
-
33
- @Override
34
- public void put(URI uri, Map<String, List<String>> responseHeaders) {
35
- CookieManager cookieManager = cookieManager();
36
- if (cookieManager == null) return;
37
- String url = uri.toString();
38
- boolean added = false;
39
- for (Map.Entry<String, List<String>> header : responseHeaders.entrySet()) {
40
- String name = header.getKey();
41
- if (!"Set-Cookie".equalsIgnoreCase(name) && !"Set-Cookie2".equalsIgnoreCase(name)) continue;
42
- for (String cookie : header.getValue()) {
43
- cookieManager.setCookie(url, cookie);
44
- added = true;
45
- }
46
- }
47
- if (added) cookieManager.flush();
48
- }
49
-
50
- // null while the WebView isn't available (not installed, or being
51
- // updated): requests go without cookies then, instead of failing.
52
- @Nullable
53
- private synchronized CookieManager cookieManager() {
54
- if (cookieManager == null) {
55
- try {
56
- cookieManager = CookieManager.getInstance();
57
- } catch (Exception e) {
58
- return null;
59
- }
60
- }
61
- return cookieManager;
62
- }
63
- }
@@ -1,49 +0,0 @@
1
- package com.dylanvann.fastimage;
2
-
3
- import android.view.View;
4
-
5
- import com.facebook.react.bridge.Arguments;
6
- import com.facebook.react.bridge.ReactContext;
7
- import com.facebook.react.bridge.WritableMap;
8
- import com.facebook.react.uimanager.UIManagerHelper;
9
- import com.facebook.react.uimanager.events.EventDispatcher;
10
-
11
- // Sends FastImage view events through React Native's EventDispatcher.
12
- final class FastImageEvents {
13
- private FastImageEvents() {
14
- }
15
-
16
- static void send(View view, String name) {
17
- send(view, name, Arguments.createMap());
18
- }
19
-
20
- // onLoadEnd with the load's result: ok with the image's size, as onLoad
21
- // sent it.
22
- static void sendLoadEnd(View view, int width, int height) {
23
- WritableMap event = Arguments.createMap();
24
- event.putBoolean("ok", true);
25
- event.putInt("width", width);
26
- event.putInt("height", height);
27
- send(view, FastImageRequestListener.REACT_ON_LOAD_END_EVENT, event);
28
- }
29
-
30
- // onLoadEnd with the load's result: not ok, with the error onError sent.
31
- static void sendLoadEnd(View view, String error) {
32
- WritableMap event = Arguments.createMap();
33
- event.putBoolean("ok", false);
34
- event.putString("error", error);
35
- send(view, FastImageRequestListener.REACT_ON_LOAD_END_EVENT, event);
36
- }
37
-
38
- static void send(View view, String name, WritableMap data) {
39
- ReactContext context = FastImageViewManager.getReactContext(view.getContext());
40
- if (context == null) return;
41
- // The view's event dispatcher, on either architecture (also
42
- // bridgeless).
43
- EventDispatcher dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.getId());
44
- if (dispatcher != null) {
45
- dispatcher.dispatchEvent(new FastImageEvent(
46
- UIManagerHelper.getSurfaceId(view), view.getId(), name, data));
47
- }
48
- }
49
- }
@@ -1,18 +0,0 @@
1
- package com.dylanvann.fastimage;
2
-
3
- // No longer used: FastImage sends progress through FastImageViewManager's
4
- // onDownloadProgress. Kept, as a public type, so code that refers to it still
5
- // compiles.
6
- @Deprecated
7
- public interface FastImageProgressListener {
8
-
9
- void onProgress(String key, long bytesRead, long expectedLength);
10
-
11
- /**
12
- * Control how often the listener needs an update. 0% and 100% will always be dispatched.
13
- *
14
- * @return in percentage (0.2 = call {@link #onProgress} around every 0.2 percent of progress)
15
- */
16
- float getGranularityPercentage();
17
-
18
- }
@@ -1,21 +0,0 @@
1
- package com.dylanvann.fastimage;
2
-
3
- import com.facebook.react.uimanager.LayoutShadowNode;
4
- import com.facebook.react.uimanager.UIViewOperationQueue;
5
-
6
- // Legacy architecture only. React Native only lays out a view when its frame
7
- // changes, so a view that stays 0×0 at its parent's origin is never laid out,
8
- // and Glide waits for its size forever (#865). When the computed size is zero,
9
- // tell the view, so it can lay itself out and load as it does on the New
10
- // Architecture. Remove with legacy architecture support.
11
- public class FastImageShadowNode extends LayoutShadowNode {
12
- static final Object ZERO_LAYOUT = new Object();
13
-
14
- @Override
15
- public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
16
- super.onCollectExtraUpdates(uiViewOperationQueue);
17
- if (Math.round(getLayoutWidth()) == 0 && Math.round(getLayoutHeight()) == 0) {
18
- uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), ZERO_LAYOUT);
19
- }
20
- }
21
- }