@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
|
-
//
|
|
127
|
-
//
|
|
128
|
-
private val
|
|
129
|
-
|
|
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.
|
|
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.
|
|
84
|
+
"@onekeyfe/react-native-skeleton": "3.0.105",
|
|
85
85
|
"react": "*",
|
|
86
86
|
"react-native": "*",
|
|
87
87
|
"react-native-nitro-modules": "0.37.0"
|