<lambda>null1 package expo.modules.image
2
3 import android.view.View
4 import androidx.core.view.doOnDetach
5 import com.bumptech.glide.Glide
6 import com.bumptech.glide.load.model.GlideUrl
7 import com.facebook.react.uimanager.PixelUtil
8 import com.facebook.react.uimanager.Spacing
9 import com.facebook.react.uimanager.ViewProps
10 import com.facebook.yoga.YogaConstants
11 import expo.modules.image.enums.ContentFit
12 import expo.modules.image.enums.Priority
13 import expo.modules.image.records.CachePolicy
14 import expo.modules.image.records.ContentPosition
15 import expo.modules.image.records.ImageTransition
16 import expo.modules.image.records.SourceMap
17 import expo.modules.kotlin.functions.Queues
18 import expo.modules.kotlin.modules.Module
19 import expo.modules.kotlin.modules.ModuleDefinition
20 import expo.modules.kotlin.views.ViewDefinitionBuilder
21
22 class ExpoImageModule : Module() {
23 override fun definition() = ModuleDefinition {
24 Name("ExpoImage")
25
26 Function("prefetch") { urls: List<String> ->
27 val context = appContext.reactContext ?: return@Function
28 urls.forEach {
29 Glide
30 .with(context)
31 .download(GlideUrl(it))
32 .submit()
33 }
34 }
35
36 AsyncFunction("clearMemoryCache") {
37 val activity = appContext.currentActivity ?: return@AsyncFunction false
38 Glide.get(activity).clearMemory()
39 return@AsyncFunction true
40 }.runOnQueue(Queues.MAIN)
41
42 AsyncFunction("clearDiskCache") {
43 val activity = appContext.currentActivity ?: return@AsyncFunction false
44 activity.let {
45 Glide.get(activity).clearDiskCache()
46 }
47
48 return@AsyncFunction true
49 }
50
51 View(ExpoImageViewWrapper::class) {
52 Events(
53 "onLoadStart",
54 "onProgress",
55 "onError",
56 "onLoad"
57 )
58
59 Prop("source") { view: ExpoImageViewWrapper, sources: List<SourceMap>? ->
60 view.sources = sources ?: emptyList()
61 }
62
63 Prop("contentFit") { view: ExpoImageViewWrapper, contentFit: ContentFit? ->
64 view.contentFit = contentFit ?: ContentFit.Cover
65 }
66
67 Prop("placeholderContentFit") { view: ExpoImageViewWrapper, placeholderContentFit: ContentFit? ->
68 view.placeholderContentFit = placeholderContentFit ?: ContentFit.ScaleDown
69 }
70
71 Prop("contentPosition") { view: ExpoImageViewWrapper, contentPosition: ContentPosition? ->
72 view.contentPosition = contentPosition ?: ContentPosition.center
73 }
74
75 Prop("blurRadius") { view: ExpoImageViewWrapper, blurRadius: Int? ->
76 view.blurRadius = blurRadius?.takeIf { it > 0 }
77 }
78
79 Prop("transition") { view: ExpoImageViewWrapper, transition: ImageTransition? ->
80 view.transition = transition
81 }
82
83 PropGroup(
84 ViewProps.BORDER_RADIUS to 0,
85 ViewProps.BORDER_TOP_LEFT_RADIUS to 1,
86 ViewProps.BORDER_TOP_RIGHT_RADIUS to 2,
87 ViewProps.BORDER_BOTTOM_RIGHT_RADIUS to 3,
88 ViewProps.BORDER_BOTTOM_LEFT_RADIUS to 4,
89 ViewProps.BORDER_TOP_START_RADIUS to 5,
90 ViewProps.BORDER_TOP_END_RADIUS to 6,
91 ViewProps.BORDER_BOTTOM_START_RADIUS to 7,
92 ViewProps.BORDER_BOTTOM_END_RADIUS to 8
93 ) { view: ExpoImageViewWrapper, index: Int, borderRadius: Float? ->
94 val radius = makeYogaUndefinedIfNegative(borderRadius ?: YogaConstants.UNDEFINED)
95 view.setBorderRadius(index, radius)
96 }
97
98 PropGroup(
99 ViewProps.BORDER_WIDTH to Spacing.ALL,
100 ViewProps.BORDER_LEFT_WIDTH to Spacing.LEFT,
101 ViewProps.BORDER_RIGHT_WIDTH to Spacing.RIGHT,
102 ViewProps.BORDER_TOP_WIDTH to Spacing.TOP,
103 ViewProps.BORDER_BOTTOM_WIDTH to Spacing.BOTTOM,
104 ViewProps.BORDER_START_WIDTH to Spacing.START,
105 ViewProps.BORDER_END_WIDTH to Spacing.END
106 ) { view: ExpoImageViewWrapper, index: Int, width: Float? ->
107 val pixelWidth = makeYogaUndefinedIfNegative(width ?: YogaConstants.UNDEFINED)
108 .ifYogaDefinedUse(PixelUtil::toPixelFromDIP)
109 view.setBorderWidth(index, pixelWidth)
110 }
111
112 PropGroup(
113 ViewProps.BORDER_COLOR to Spacing.ALL,
114 ViewProps.BORDER_LEFT_COLOR to Spacing.LEFT,
115 ViewProps.BORDER_RIGHT_COLOR to Spacing.RIGHT,
116 ViewProps.BORDER_TOP_COLOR to Spacing.TOP,
117 ViewProps.BORDER_BOTTOM_COLOR to Spacing.BOTTOM,
118 ViewProps.BORDER_START_COLOR to Spacing.START,
119 ViewProps.BORDER_END_COLOR to Spacing.END
120 ) { view: ExpoImageViewWrapper, index: Int, color: Int? ->
121 val rgbComponent = if (color == null) YogaConstants.UNDEFINED else (color and 0x00FFFFFF).toFloat()
122 val alphaComponent = if (color == null) YogaConstants.UNDEFINED else (color ushr 24).toFloat()
123 view.setBorderColor(index, rgbComponent, alphaComponent)
124 }
125
126 Prop("borderStyle") { view: ExpoImageViewWrapper, borderStyle: String? ->
127 view.borderStyle = borderStyle
128 }
129
130 Prop("backgroundColor") { view: ExpoImageViewWrapper, color: Int? ->
131 view.backgroundColor = color
132 }
133
134 Prop("tintColor") { view: ExpoImageViewWrapper, color: Int? ->
135 view.tintColor = color
136 }
137
138 Prop("placeholder") { view: ExpoImageViewWrapper, placeholder: List<SourceMap>? ->
139 view.placeholders = placeholder ?: emptyList()
140 }
141
142 Prop("accessible") { view: ExpoImageViewWrapper, accessible: Boolean? ->
143 view.accessible = accessible ?: false
144 }
145
146 Prop("accessibilityLabel") { view: ExpoImageViewWrapper, accessibilityLabel: String? ->
147 view.accessibilityLabel = accessibilityLabel
148 }
149
150 Prop("focusable") { view: ExpoImageViewWrapper, isFocusable: Boolean? ->
151 view.isFocusableProp = isFocusable ?: false
152 }
153
154 Prop("priority") { view: ExpoImageViewWrapper, priority: Priority? ->
155 view.priority = priority ?: Priority.NORMAL
156 }
157
158 Prop("cachePolicy") { view: ExpoImageViewWrapper, cachePolicy: CachePolicy? ->
159 view.cachePolicy = cachePolicy ?: CachePolicy.DISK
160 }
161
162 Prop("recyclingKey") { view: ExpoImageViewWrapper, recyclingKey: String? ->
163 view.recyclingKey = recyclingKey
164 }
165
166 Prop("allowDownscaling") { view: ExpoImageViewWrapper, allowDownscaling: Boolean? ->
167 view.allowDownscaling = allowDownscaling ?: true
168 }
169
170 OnViewDidUpdateProps { view: ExpoImageViewWrapper ->
171 view.rerenderIfNeeded()
172 }
173
174 OnViewDestroys { view: ExpoImageViewWrapper ->
175 view.doOnDetach {
176 view.onViewDestroys()
177 }
178 }
179 }
180 }
181 }
182
183 // TODO(@lukmccall): Remove when the same functionality will be defined by the expo-modules-core in SDK 48
184 @Suppress("FunctionName")
PropGroupnull185 private inline fun <reified T : View, reified PropType, reified CustomValueType> ViewDefinitionBuilder<T>.PropGroup(
186 vararg props: Pair<String, CustomValueType>,
187 noinline body: (view: T, value: CustomValueType, prop: PropType) -> Unit
188 ) {
189 for ((name, value) in props) {
190 Prop<T, PropType>(name) { view, prop -> body(view, value, prop) }
191 }
192 }
193