@onekeyfe/react-native-image 3.0.103 → 3.0.105

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.
@@ -1,5 +1,7 @@
1
1
  package com.margelo.nitro.onekeyimage
2
2
 
3
+ import android.content.Context
4
+ import android.content.ContextWrapper
3
5
  import android.graphics.Canvas
4
6
  import android.graphics.Color
5
7
  import android.graphics.Paint
@@ -7,6 +9,7 @@ import android.graphics.drawable.Animatable
7
9
  import android.graphics.drawable.Drawable
8
10
  import android.view.View
9
11
  import android.widget.ImageView
12
+ import androidx.fragment.app.FragmentActivity
10
13
  import com.bumptech.glide.Glide
11
14
  import com.bumptech.glide.load.DataSource
12
15
  import com.bumptech.glide.load.engine.DiskCacheStrategy
@@ -22,6 +25,17 @@ import com.margelo.nitro.skeleton.OneKeySkeletonRenderer
22
25
  import com.margelo.nitro.views.RecyclableView
23
26
  import kotlin.math.ceil
24
27
 
28
+ private fun Context.findFragmentActivity(): FragmentActivity? {
29
+ var currentContext: Context? = this
30
+ while (currentContext is ContextWrapper) {
31
+ if (currentContext is FragmentActivity) return currentContext
32
+ val baseContext = currentContext.baseContext
33
+ if (baseContext === currentContext) return null
34
+ currentContext = baseContext
35
+ }
36
+ return currentContext as? FragmentActivity
37
+ }
38
+
25
39
  private class OneKeyImageHostView(context: ThemedReactContext) : ImageView(context) {
26
40
  private val fallbackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
27
41
  color = Color.rgb(110, 110, 120)
@@ -123,10 +137,12 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
123
137
  private enum class DisplayState { LOADING, IMAGE, ERROR, FALLBACK }
124
138
 
125
139
  private val hostView = OneKeyImageHostView(context)
126
- // Keep requests independent of ScreenStack Fragment teardown while React
127
- // retains the view.
128
- private val requestManager by lazy(LazyThreadSafetyMode.NONE) {
129
- Glide.with(context.applicationContext)
140
+ // The Activity outlives ScreenStack Fragments but still provides bounded
141
+ // background and destruction lifecycle handling for image requests.
142
+ private val activityRequestManager by lazy(LazyThreadSafetyMode.NONE) {
143
+ val activity = context.findFragmentActivity()
144
+ ?: (context.currentActivity as? FragmentActivity)
145
+ Glide.with(requireNotNull(activity) { "A FragmentActivity is required to load images" })
130
146
  }
131
147
  private var loadRunnable: Runnable? = null
132
148
  private var currentTarget: CustomViewTarget<OneKeyImageHostView, Drawable>? = null
@@ -140,6 +156,18 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
140
156
  private var pendingDisplayGeneration: Long? = null
141
157
  private var fallbackRunnable: Runnable? = null
142
158
 
159
+ /**
160
+ * Native reusable containers own their request cleanup explicitly, so their
161
+ * requests must not inherit a transient React Native screen Fragment lifecycle.
162
+ */
163
+ internal var usesApplicationRequestManager = false
164
+
165
+ private fun requestManager() = if (usesApplicationRequestManager) {
166
+ Glide.with(context.applicationContext)
167
+ } else {
168
+ activityRequestManager
169
+ }
170
+
143
171
  override val view: View = hostView
144
172
 
145
173
  override var sourceUri: String? = null
@@ -213,6 +241,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
213
241
  hostView.onReadyForRequest = { scheduleLoad() }
214
242
  hostView.onAttachmentChanged = { attached ->
215
243
  if (attached) {
244
+ scheduleLoad()
216
245
  schedulePendingDisplayIfNeeded()
217
246
  } else {
218
247
  displayRunnable?.let(hostView::removeCallbacks)
@@ -411,6 +440,8 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
411
440
 
412
441
  override fun onResourceCleared(placeholder: Drawable?) {
413
442
  if (requestGeneration == generation && currentTarget === this) {
443
+ currentTarget = null
444
+ lastSignature = null
414
445
  requestActive = false
415
446
  hostView.skeletonRequested = false
416
447
  hostView.setImageDrawable(null)
@@ -451,7 +482,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
451
482
  }
452
483
  }
453
484
  currentTarget = target
454
- requestManager
485
+ requestManager()
455
486
  .asDrawable()
456
487
  .load(OneKeyImageModel.build(requestUrl, headersJson))
457
488
  .apply(requestOptions(policy))
@@ -528,7 +559,7 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
528
559
  // Clear the field first so onResourceCleared from our own cancellation
529
560
  // cannot erase state belonging to the next generation/request.
530
561
  currentTarget = null
531
- requestManager.clear(target)
562
+ requestManager().clear(target)
532
563
  }
533
564
 
534
565
  private fun scheduleOnDisplay(requestGeneration: Long) {
@@ -0,0 +1,71 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import android.widget.FrameLayout
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+
6
+ /** A native-only OneKeyImage host for reusable container views such as list cells. */
7
+ class OneKeyImageReusableView(context: ThemedReactContext) : FrameLayout(context) {
8
+ private val image = HybridOneKeyImage(context).apply {
9
+ usesApplicationRequestManager = true
10
+ }
11
+
12
+ init {
13
+ addView(
14
+ image.view,
15
+ LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT),
16
+ )
17
+ clipChildren = true
18
+ }
19
+
20
+ fun configure(
21
+ sourceUri: String?,
22
+ sourceHeadersJson: String?,
23
+ variant: String,
24
+ contentFit: String,
25
+ cachePolicy: String,
26
+ autoplay: Boolean,
27
+ recyclingKey: String,
28
+ optimizeTos: Boolean,
29
+ overscan: Double,
30
+ loadingStrategy: String,
31
+ ) {
32
+ image.sourceHeadersJson = sourceHeadersJson
33
+ image.variant = when (variant) {
34
+ "token" -> OneKeyImageVariant.TOKEN
35
+ "network" -> OneKeyImageVariant.NETWORK
36
+ "avatar" -> OneKeyImageVariant.AVATAR
37
+ else -> OneKeyImageVariant.GENERIC
38
+ }
39
+ image.contentFit = when (contentFit) {
40
+ "contain" -> OneKeyImageContentFit.CONTAIN
41
+ "fill" -> OneKeyImageContentFit.FILL
42
+ "center" -> OneKeyImageContentFit.CENTER
43
+ else -> OneKeyImageContentFit.COVER
44
+ }
45
+ image.cachePolicy = when (cachePolicy) {
46
+ "memory" -> OneKeyImageCachePolicy.MEMORY
47
+ "disk" -> OneKeyImageCachePolicy.DISK
48
+ "none" -> OneKeyImageCachePolicy.NONE
49
+ else -> OneKeyImageCachePolicy.MEMORY_DISK
50
+ }
51
+ image.autoplay = autoplay
52
+ image.recyclingKey = recyclingKey
53
+ image.optimizeTos = optimizeTos
54
+ image.overscan = overscan
55
+ image.loadingStrategy = when (loadingStrategy) {
56
+ "skeleton" -> OneKeyImageLoadingStrategy.SKELETON
57
+ "none" -> OneKeyImageLoadingStrategy.NONE
58
+ else -> OneKeyImageLoadingStrategy.STATIC
59
+ }
60
+ image.sourceUri = sourceUri
61
+ image.afterUpdate()
62
+ }
63
+
64
+ fun prepareForReuse() {
65
+ image.prepareForRecycle()
66
+ }
67
+
68
+ fun dispose() {
69
+ image.onDropView()
70
+ }
71
+ }
@@ -0,0 +1,52 @@
1
+ import UIKit
2
+
3
+ /// A native-only OneKeyImage host for reusable container views such as list cells.
4
+ public final class OneKeyImageReusableView: UIView {
5
+ private let image = HybridOneKeyImage()
6
+
7
+ public override init(frame: CGRect) {
8
+ super.init(frame: frame)
9
+ let imageView = image.view
10
+ imageView.frame = bounds
11
+ imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
12
+ addSubview(imageView)
13
+ clipsToBounds = true
14
+ }
15
+
16
+ public required init?(coder: NSCoder) {
17
+ fatalError("init(coder:) has not been implemented")
18
+ }
19
+
20
+ public func configure(
21
+ sourceUri: String?,
22
+ sourceHeadersJson: String?,
23
+ variant: String,
24
+ contentFit: String,
25
+ cachePolicy: String,
26
+ autoplay: Bool,
27
+ recyclingKey: String,
28
+ optimizeTos: Bool,
29
+ overscan: Double,
30
+ loadingStrategy: String
31
+ ) {
32
+ image.sourceHeadersJson = sourceHeadersJson
33
+ image.variant = OneKeyImageVariant(fromString: variant) ?? .generic
34
+ image.contentFit = OneKeyImageContentFit(fromString: contentFit) ?? .cover
35
+ image.cachePolicy = OneKeyImageCachePolicy(fromString: cachePolicy) ?? .memoryDisk
36
+ image.autoplay = autoplay
37
+ image.recyclingKey = recyclingKey
38
+ image.optimizeTos = optimizeTos
39
+ image.overscan = overscan
40
+ image.loadingStrategy = OneKeyImageLoadingStrategy(fromString: loadingStrategy) ?? .static
41
+ image.sourceUri = sourceUri
42
+ image.afterUpdate()
43
+ }
44
+
45
+ public func prepareForReuse() {
46
+ image.prepareForRecycle()
47
+ }
48
+
49
+ deinit {
50
+ image.onDropView()
51
+ }
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-image",
3
- "version": "3.0.103",
3
+ "version": "3.0.105",
4
4
  "description": "High-performance native image view for OneKey",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -81,7 +81,7 @@
81
81
  "typescript": "^5.9.2"
82
82
  },
83
83
  "peerDependencies": {
84
- "@onekeyfe/react-native-skeleton": "3.0.103",
84
+ "@onekeyfe/react-native-skeleton": "3.0.105",
85
85
  "react": "*",
86
86
  "react-native": "*",
87
87
  "react-native-nitro-modules": "0.37.0"