expo-image 2.0.0 → 2.0.1

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 CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 2.0.1 — 2024-11-19
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Android] Fixes Gif animations never stopping even when loop count is set to 1. ([#32944](https://github.com/expo/expo/pull/32944) by [@lukmccall](https://github.com/lukmccall))
18
+ - [Android] Fixed `borderColor` is not applied. ([#33026](https://github.com/expo/expo/pull/33026) by [@lukmccall](https://github.com/lukmccall))
19
+ - [Android] Fixed `border` related props weren't applied correctly. ([#33078](https://github.com/expo/expo/pull/33078) by [@lukmccall](https://github.com/lukmccall))
20
+
21
+ ### 💡 Others
22
+
23
+ - [Android] migrate ImageView to CSSBackgroundDrawable ([#33024](https://github.com/expo/expo/pull/33024) by [@vonovak](https://github.com/vonovak))
24
+
13
25
  ## 2.0.0 — 2024-11-11
14
26
 
15
27
  _This version does not introduce any user-facing changes._
@@ -18,7 +18,7 @@ android {
18
18
  namespace "expo.modules.image"
19
19
  defaultConfig {
20
20
  versionCode 1
21
- versionName "2.0.0"
21
+ versionName "2.0.1"
22
22
  consumerProguardFiles("proguard-rules.pro")
23
23
 
24
24
  buildConfigField("boolean", "ALLOW_GLIDE_LOGS", project.properties.get("EXPO_ALLOW_GLIDE_LOGS", "false"))
@@ -44,7 +44,7 @@ dependencies {
44
44
  kapt "com.github.bumptech.glide:compiler:${GLIDE_VERSION}"
45
45
  api 'com.caverock:androidsvg-aar:1.4'
46
46
 
47
- implementation "com.github.penfeizhou.android.animation:glide-plugin:3.0.1"
47
+ implementation "com.github.penfeizhou.android.animation:glide-plugin:3.0.2"
48
48
  implementation "com.github.bumptech.glide:avif-integration:${GLIDE_VERSION}"
49
49
 
50
50
  api 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
@@ -16,10 +16,6 @@ import com.bumptech.glide.load.model.Headers
16
16
  import com.bumptech.glide.load.model.LazyHeaders
17
17
  import com.bumptech.glide.request.RequestListener
18
18
  import com.bumptech.glide.request.target.Target
19
- import com.facebook.react.uimanager.PixelUtil
20
- import com.facebook.react.uimanager.Spacing
21
- import com.facebook.react.uimanager.ViewProps
22
- import com.facebook.yoga.YogaConstants
23
19
  import com.github.penfeizhou.animation.apng.APNGDrawable
24
20
  import com.github.penfeizhou.animation.gif.GifDrawable
25
21
  import com.github.penfeizhou.animation.webp.WebPDrawable
@@ -231,57 +227,6 @@ class ExpoImageModule : Module() {
231
227
  view.transition = transition
232
228
  }
233
229
 
234
- PropGroup(
235
- ViewProps.BORDER_RADIUS to 0,
236
- ViewProps.BORDER_TOP_LEFT_RADIUS to 1,
237
- ViewProps.BORDER_TOP_RIGHT_RADIUS to 2,
238
- ViewProps.BORDER_BOTTOM_RIGHT_RADIUS to 3,
239
- ViewProps.BORDER_BOTTOM_LEFT_RADIUS to 4,
240
- ViewProps.BORDER_TOP_START_RADIUS to 5,
241
- ViewProps.BORDER_TOP_END_RADIUS to 6,
242
- ViewProps.BORDER_BOTTOM_START_RADIUS to 7,
243
- ViewProps.BORDER_BOTTOM_END_RADIUS to 8
244
- ) { view: ExpoImageViewWrapper, index: Int, borderRadius: Float? ->
245
- val radius = makeYogaUndefinedIfNegative(borderRadius ?: YogaConstants.UNDEFINED)
246
- view.setBorderRadius(index, radius)
247
- }
248
-
249
- PropGroup(
250
- ViewProps.BORDER_WIDTH to Spacing.ALL,
251
- ViewProps.BORDER_LEFT_WIDTH to Spacing.LEFT,
252
- ViewProps.BORDER_RIGHT_WIDTH to Spacing.RIGHT,
253
- ViewProps.BORDER_TOP_WIDTH to Spacing.TOP,
254
- ViewProps.BORDER_BOTTOM_WIDTH to Spacing.BOTTOM,
255
- ViewProps.BORDER_START_WIDTH to Spacing.START,
256
- ViewProps.BORDER_END_WIDTH to Spacing.END
257
- ) { view: ExpoImageViewWrapper, index: Int, width: Float? ->
258
- val pixelWidth = makeYogaUndefinedIfNegative(width ?: YogaConstants.UNDEFINED)
259
- .ifYogaDefinedUse(PixelUtil::toPixelFromDIP)
260
- view.setBorderWidth(index, pixelWidth)
261
- }
262
-
263
- PropGroup(
264
- ViewProps.BORDER_COLOR to Spacing.ALL,
265
- ViewProps.BORDER_LEFT_COLOR to Spacing.LEFT,
266
- ViewProps.BORDER_RIGHT_COLOR to Spacing.RIGHT,
267
- ViewProps.BORDER_TOP_COLOR to Spacing.TOP,
268
- ViewProps.BORDER_BOTTOM_COLOR to Spacing.BOTTOM,
269
- ViewProps.BORDER_START_COLOR to Spacing.START,
270
- ViewProps.BORDER_END_COLOR to Spacing.END
271
- ) { view: ExpoImageViewWrapper, index: Int, color: Int? ->
272
- val rgbComponent = if (color == null) YogaConstants.UNDEFINED.toInt() else (color and 0x00FFFFFF)
273
- val alphaComponent = if (color == null) YogaConstants.UNDEFINED else (color ushr 24).toFloat()
274
- view.setBorderColor(index, rgbComponent, alphaComponent)
275
- }
276
-
277
- Prop("borderStyle") { view: ExpoImageViewWrapper, borderStyle: String? ->
278
- view.borderStyle = borderStyle
279
- }
280
-
281
- Prop("backgroundColor") { view: ExpoImageViewWrapper, color: Int? ->
282
- view.backgroundColor = color
283
- }
284
-
285
230
  Prop("tintColor") { view: ExpoImageViewWrapper, color: Int? ->
286
231
  view.tintColor = color
287
232
  }
@@ -3,7 +3,6 @@ package expo.modules.image
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.graphics.Canvas
6
- import android.graphics.Color
7
6
  import android.graphics.PorterDuff
8
7
  import android.graphics.RectF
9
8
  import android.graphics.drawable.BitmapDrawable
@@ -12,13 +11,11 @@ import android.util.Log
12
11
  import androidx.appcompat.widget.AppCompatImageView
13
12
  import androidx.core.graphics.transform
14
13
  import androidx.core.view.isVisible
15
- import com.facebook.react.modules.i18nmanager.I18nUtil
16
- import com.facebook.react.uimanager.PixelUtil
17
- import com.facebook.react.views.view.ReactViewBackgroundDrawable
18
- import expo.modules.image.drawing.OutlineProvider
14
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI
19
15
  import expo.modules.image.enums.ContentFit
20
16
  import expo.modules.image.records.ContentPosition
21
17
 
18
+ @OptIn(UnstableReactNativeAPI::class)
22
19
  @SuppressLint("ViewConstructor")
23
20
  class ExpoImageView(
24
21
  context: Context
@@ -40,27 +37,8 @@ class ExpoImageView(
40
37
  return target
41
38
  }
42
39
 
43
- private val outlineProvider = OutlineProvider(context)
44
-
45
40
  private var transformationMatrixChanged = false
46
41
 
47
- private val borderDrawableLazyHolder = lazy {
48
- ReactViewBackgroundDrawable(context).apply {
49
- callback = this@ExpoImageView
50
-
51
- outlineProvider.borderRadiiConfig
52
- .map { it.ifYogaDefinedUse(PixelUtil::toPixelFromDIP) }
53
- .withIndex()
54
- .forEach { (i, radius) ->
55
- if (i == 0) {
56
- setRadius(radius)
57
- } else {
58
- setRadius(radius, i - 1)
59
- }
60
- }
61
- }
62
- }
63
-
64
42
  override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
65
43
  super.onLayout(changed, left, top, right, bottom)
66
44
  applyTransformationMatrix()
@@ -106,13 +84,9 @@ class ExpoImageView(
106
84
  }
107
85
  }
108
86
 
109
- private val borderDrawable
110
- get() = borderDrawableLazyHolder.value
111
-
112
87
  init {
113
88
  clipToOutline = true
114
89
  scaleType = ScaleType.MATRIX
115
- super.setOutlineProvider(outlineProvider)
116
90
  }
117
91
 
118
92
  // region Component Props
@@ -134,65 +108,11 @@ class ExpoImageView(
134
108
  transformationMatrixChanged = true
135
109
  }
136
110
 
137
- internal fun setBorderRadius(position: Int, borderRadius: Float) {
138
- val isInvalidated = outlineProvider.setBorderRadius(borderRadius, position)
139
- if (isInvalidated) {
140
- invalidateOutline()
141
- if (!outlineProvider.hasEqualCorners()) {
142
- invalidate()
143
- }
144
- }
145
-
146
- // Setting the border-radius doesn't necessarily mean that a border
147
- // should to be drawn. Only update the border-drawable when needed.
148
- if (borderDrawableLazyHolder.isInitialized()) {
149
- val radius = borderRadius.ifYogaDefinedUse(PixelUtil::toPixelFromDIP)
150
- borderDrawableLazyHolder.value.apply {
151
- if (position == 0) {
152
- setRadius(radius)
153
- } else {
154
- setRadius(radius, position - 1)
155
- }
156
- }
157
- }
158
- }
159
-
160
- internal fun setBorderWidth(position: Int, width: Float) {
161
- borderDrawable.setBorderWidth(position, width)
162
- }
163
-
164
- internal fun setBorderColor(position: Int, rgb: Int) {
165
- borderDrawable.setBorderColor(position, rgb)
166
- }
167
-
168
- internal fun setBorderStyle(style: String?) {
169
- borderDrawable.setBorderStyle(style)
170
- }
171
-
172
- internal fun setBackgroundColor(color: Int?) {
173
- if (color == null) {
174
- setBackgroundColor(Color.TRANSPARENT)
175
- } else {
176
- setBackgroundColor(color)
177
- }
178
- }
179
-
180
111
  internal fun setTintColor(color: Int?) {
181
112
  color?.let { setColorFilter(it, PorterDuff.Mode.SRC_IN) } ?: clearColorFilter()
182
113
  }
183
114
 
184
- override fun invalidateDrawable(drawable: Drawable) {
185
- super.invalidateDrawable(drawable)
186
- if (borderDrawableLazyHolder.isInitialized() && drawable === borderDrawable) {
187
- invalidate()
188
- }
189
- }
190
-
191
115
  override fun draw(canvas: Canvas) {
192
- // When the border-radii are not all the same, a convex-path
193
- // is used for the Outline. Unfortunately clipping is not supported
194
- // for convex-paths and we fallback to Canvas clipping.
195
- outlineProvider.clipCanvasIfNeeded(canvas, this)
196
116
  // If we encounter a recycled bitmap here, it suggests an issue where we may have failed to
197
117
  // finish clearing the image bitmap before the UI attempts to display it.
198
118
  // One solution could be to suppress the error and assume that the second image view is currently responsible for displaying the correct view.
@@ -206,22 +126,4 @@ class ExpoImageView(
206
126
  }
207
127
  super.draw(canvas)
208
128
  }
209
-
210
- public override fun onDraw(canvas: Canvas) {
211
- super.onDraw(canvas)
212
- // Draw borders on top of the background and image
213
- if (borderDrawableLazyHolder.isInitialized()) {
214
- val newLayoutDirection = if (I18nUtil.instance.isRTL(context)) {
215
- LAYOUT_DIRECTION_RTL
216
- } else {
217
- LAYOUT_DIRECTION_LTR
218
- }
219
-
220
- borderDrawable.apply {
221
- layoutDirection = newLayoutDirection
222
- setBounds(0, 0, width, height)
223
- draw(canvas)
224
- }
225
- }
226
- }
227
129
  }
@@ -18,7 +18,6 @@ import com.bumptech.glide.RequestManager
18
18
  import com.bumptech.glide.load.engine.DiskCacheStrategy
19
19
  import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
20
20
  import com.bumptech.glide.request.RequestOptions
21
- import com.facebook.yoga.YogaConstants
22
21
  import expo.modules.image.enums.ContentFit
23
22
  import expo.modules.image.enums.Priority
24
23
  import expo.modules.image.events.GlideRequestListener
@@ -115,18 +114,6 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
115
114
  transformationMatrixChanged = true
116
115
  }
117
116
 
118
- internal var borderStyle: String? = null
119
- set(value) {
120
- field = value
121
- activeView.setBorderStyle(value)
122
- }
123
-
124
- internal var backgroundColor: Int? = null
125
- set(value) {
126
- field = value
127
- activeView.setBackgroundColor(value)
128
- }
129
-
130
117
  internal var tintColor: Int? = null
131
118
  set(value) {
132
119
  field = value
@@ -179,25 +166,6 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
179
166
  internal var priority: Priority = Priority.NORMAL
180
167
  internal var cachePolicy: CachePolicy = CachePolicy.DISK
181
168
 
182
- private var borderRadius = FloatArray(9) { YogaConstants.UNDEFINED }
183
- private var borderWidth = FloatArray(9) { YogaConstants.UNDEFINED }
184
- private var borderColor = Array(9) { YogaConstants.UNDEFINED.toInt() to YogaConstants.UNDEFINED }
185
-
186
- fun setBorderRadius(index: Int, radius: Float) {
187
- borderRadius[index] = radius
188
- activeView.setBorderRadius(index, radius)
189
- }
190
-
191
- fun setBorderWidth(index: Int, width: Float) {
192
- borderWidth[index] = width
193
- activeView.setBorderWidth(index, width)
194
- }
195
-
196
- fun setBorderColor(index: Int, rgb: Int, alpha: Float) {
197
- borderColor[index] = rgb to alpha
198
- activeView.setBorderColor(index, rgb)
199
- }
200
-
201
169
  fun setIsAnimating(setAnimating: Boolean) {
202
170
  val resource = activeView.drawable
203
171
 
@@ -237,20 +205,9 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
237
205
  private fun copyProps(view: ExpoImageView) {
238
206
  view.contentFit = contentFit
239
207
  view.contentPosition = contentPosition
240
- view.setBorderStyle(borderStyle)
241
- view.setBackgroundColor(backgroundColor)
242
208
  view.setTintColor(tintColor)
243
209
  view.isFocusable = isFocusableProp
244
210
  view.contentDescription = accessibilityLabel
245
- borderColor.forEachIndexed { index, (rgb, alpha) ->
246
- view.setBorderColor(index, rgb)
247
- }
248
- borderRadius.forEachIndexed { index, value ->
249
- view.setBorderRadius(index, value)
250
- }
251
- borderWidth.forEachIndexed { index, value ->
252
- view.setBorderWidth(index, value)
253
- }
254
211
  setIsScreenReaderFocusable(view, accessible)
255
212
  }
256
213
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-image",
3
3
  "title": "Expo Image",
4
- "version": "2.0.0",
4
+ "version": "2.0.1",
5
5
  "description": "A cross-platform, performant image component for React Native and Expo with Web support",
6
6
  "main": "src/index.ts",
7
7
  "types": "build/index.d.ts",
@@ -41,5 +41,5 @@
41
41
  "optional": true
42
42
  }
43
43
  },
44
- "gitHead": "8f2567ad26ca782cd2d7ab7fa77a34979e2b4e01"
44
+ "gitHead": "128718d43bac2eaed764b3551469b95400f2363e"
45
45
  }