@onekeyfe/react-native-native-list 3.0.105 → 3.0.106
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/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +16 -3
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +6 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +671 -52
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +282 -54
- package/ios/HybridNativeList.swift +8 -2
- package/ios/NativeListCell.swift +630 -31
- package/ios/NativeListDesignAssets.swift +24 -1
- package/ios/RNCNativeListView.swift +216 -42
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/icon.svg +14 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/icon.svg +12 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/icon.svg +6 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/icon.svg +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/icon.svg +3 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/icon.svg +7 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/icon.svg +18 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/icon.svg +7 -0
- package/lib/module/NativeList.js +102 -8
- package/lib/module/NativeList.web.js +20 -2
- package/lib/module/avatarPrefetch.js +174 -0
- package/lib/module/validation.js +45 -2
- package/lib/module/web/NativeListAvatarWorker.js +542 -0
- package/lib/module/web/NativeListWebAvatarCache.js +267 -0
- package/lib/module/web/NativeListWebEngine.js +958 -47
- package/lib/typescript/src/NativeList.web.d.ts +2 -2
- package/lib/typescript/src/avatarPrefetch.d.ts +28 -0
- package/lib/typescript/src/models.d.ts +72 -4
- package/lib/typescript/src/web/NativeListWebAvatarCache.d.ts +6 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +10 -1
- package/package.json +2 -2
- package/src/NativeList.tsx +149 -13
- package/src/NativeList.web.tsx +30 -3
- package/src/avatarPrefetch.ts +236 -0
- package/src/models.ts +98 -7
- package/src/validation.ts +131 -2
- package/src/web/NativeListAvatarWorker.js +567 -0
- package/src/web/NativeListWebAvatarCache.ts +314 -0
- package/src/web/NativeListWebEngine.ts +1409 -60
|
@@ -29,6 +29,12 @@ import android.widget.LinearLayout
|
|
|
29
29
|
import android.widget.ProgressBar
|
|
30
30
|
import android.widget.TextView
|
|
31
31
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
32
|
+
// OneKey patch: selector checkboxes share the source React Native border/background renderer.
|
|
33
|
+
import com.facebook.react.uimanager.BackgroundStyleApplicator
|
|
34
|
+
import com.facebook.react.uimanager.LengthPercentage
|
|
35
|
+
import com.facebook.react.uimanager.LengthPercentageType
|
|
36
|
+
import com.facebook.react.uimanager.style.BorderRadiusProp
|
|
37
|
+
import com.facebook.react.uimanager.style.LogicalEdge
|
|
32
38
|
import com.margelo.nitro.onekeyimage.OneKeyImageReusableView
|
|
33
39
|
import androidx.core.graphics.PathParser
|
|
34
40
|
import androidx.core.widget.TextViewCompat
|
|
@@ -42,6 +48,7 @@ internal data class NativeListActionOrigin(
|
|
|
42
48
|
val bindingEpoch: Long,
|
|
43
49
|
val source: String,
|
|
44
50
|
val slot: Int? = null,
|
|
51
|
+
val anchorInsetPixels: Int = 0,
|
|
45
52
|
)
|
|
46
53
|
|
|
47
54
|
/** React Native color strings use CSS #RRGGBBAA ordering; Android expects #AARRGGBB. */
|
|
@@ -96,7 +103,20 @@ internal object NativeListFonts {
|
|
|
96
103
|
|
|
97
104
|
internal data class NativeSelectionTarget(val scope: String, val key: String?)
|
|
98
105
|
|
|
106
|
+
// OneKey patch: match React Native's CustomLineHeightSpan for first/last line bounds.
|
|
107
|
+
private class SelectorLineHeightSpan(private val lineHeight: Int) : android.text.style.LineHeightSpan {
|
|
108
|
+
override fun chooseHeight(text: CharSequence, start: Int, end: Int, spanstartv: Int, v: Int, fm: Paint.FontMetricsInt) {
|
|
109
|
+
val leading = lineHeight - (fm.descent - fm.ascent)
|
|
110
|
+
fm.ascent -= kotlin.math.ceil(leading / 2.0).toInt()
|
|
111
|
+
fm.descent += kotlin.math.floor(leading / 2.0).toInt()
|
|
112
|
+
if (start == 0) fm.top = fm.ascent
|
|
113
|
+
if (end == text.length) fm.bottom = fm.descent
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
99
117
|
private class DottedUnderlineTextView(context: android.content.Context) : TextView(context) {
|
|
118
|
+
var useSourceScale = false
|
|
119
|
+
private fun scaledDp(value: Float) = if (useSourceScale) value * resources.displayMetrics.density else NativeListScale.dp(resources, value)
|
|
100
120
|
var showsDottedUnderline = false
|
|
101
121
|
var dottedUnderlineColor = Color.TRANSPARENT
|
|
102
122
|
private val dottedPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
|
|
@@ -105,11 +125,11 @@ private class DottedUnderlineTextView(context: android.content.Context) : TextVi
|
|
|
105
125
|
super.onDraw(canvas)
|
|
106
126
|
if (!showsDottedUnderline || text.isEmpty()) return
|
|
107
127
|
dottedPaint.color = dottedUnderlineColor
|
|
108
|
-
val radius =
|
|
109
|
-
val spacing =
|
|
128
|
+
val radius = scaledDp(0.75f)
|
|
129
|
+
val spacing = scaledDp(4f)
|
|
110
130
|
val lineWidth = paint.measureText(text.toString()).coerceAtMost(width.toFloat())
|
|
111
131
|
val y = height - radius
|
|
112
|
-
var x =
|
|
132
|
+
var x = scaledDp(1f)
|
|
113
133
|
while (x <= lineWidth - radius) {
|
|
114
134
|
canvas.drawCircle(x, y, radius, dottedPaint)
|
|
115
135
|
x += spacing
|
|
@@ -160,6 +180,29 @@ private class PackedTitleLineLayout(context: android.content.Context) : LinearLa
|
|
|
160
180
|
}
|
|
161
181
|
}
|
|
162
182
|
|
|
183
|
+
// OneKey patch: fit subtitle segments at intrinsic width, shrinking text only when necessary.
|
|
184
|
+
private class SelectorSubtitleLayout(context: android.content.Context) : LinearLayout(context) {
|
|
185
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
186
|
+
val available = MeasureSpec.getSize(widthMeasureSpec)
|
|
187
|
+
val labels = mutableListOf<Pair<TextView, Int>>()
|
|
188
|
+
var fixedWidth = 0
|
|
189
|
+
for (index in 0 until childCount) {
|
|
190
|
+
val child = getChildAt(index)
|
|
191
|
+
val params = child.layoutParams as LayoutParams
|
|
192
|
+
if (child is TextView) {
|
|
193
|
+
child.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec)
|
|
194
|
+
labels.add(child to child.measuredWidth)
|
|
195
|
+
} else fixedWidth += params.width + params.leftMargin + params.rightMargin
|
|
196
|
+
}
|
|
197
|
+
val desired = labels.sumOf { it.second }
|
|
198
|
+
val textWidth = (available - fixedWidth).coerceAtLeast(0)
|
|
199
|
+
labels.forEach { (label, width) ->
|
|
200
|
+
(label.layoutParams as LayoutParams).width = if (desired <= textWidth) width else (width.toLong() * textWidth / desired.coerceAtLeast(1)).toInt()
|
|
201
|
+
}
|
|
202
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
163
206
|
private class NativeListTableColumnView(context: android.content.Context) : LinearLayout(context) {
|
|
164
207
|
private val primaryLine = LinearLayout(context)
|
|
165
208
|
private val primary = TextView(context)
|
|
@@ -282,6 +325,21 @@ internal class NativeListRowView(
|
|
|
282
325
|
private val reactContext: ThemedReactContext,
|
|
283
326
|
) : LinearLayout(reactContext) {
|
|
284
327
|
private val leadingFrame = FrameLayout(context)
|
|
328
|
+
// OneKey patch: reset selector fragments and corner decorations on every bind.
|
|
329
|
+
private val selectorViews = mutableListOf<View>()
|
|
330
|
+
private var selectorUsesSourceScale = false
|
|
331
|
+
private val selectorAccessibilityDelegate = object : View.AccessibilityDelegate() {
|
|
332
|
+
override fun onInitializeAccessibilityNodeInfo(host: View, info: android.view.accessibility.AccessibilityNodeInfo) {
|
|
333
|
+
super.onInitializeAccessibilityNodeInfo(host, info)
|
|
334
|
+
info.viewIdResourceName = host.getTag(com.facebook.react.R.id.react_test_id) as? String
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
private val selectorOriginalFontFeatures = mutableMapOf<TextView, String?>()
|
|
338
|
+
private val selectorOriginalPaintFlags = mutableMapOf<TextView, Int>()
|
|
339
|
+
private val selectorLineHeights = mutableMapOf<TextView, Int>()
|
|
340
|
+
private val selectorFontSizes = mutableMapOf<TextView, Float>()
|
|
341
|
+
private val selectorImages = mutableListOf<OneKeyImageReusableView>()
|
|
342
|
+
private var selectorHeight: Int? = null
|
|
285
343
|
private val leadingImages = List(3) { OneKeyImageReusableView(reactContext) }
|
|
286
344
|
private val leadingOverlayBackground = View(context)
|
|
287
345
|
private val leadingCornerIconFrame = FrameLayout(context)
|
|
@@ -334,16 +392,22 @@ internal class NativeListRowView(
|
|
|
334
392
|
private var walletGroupExpandAnimator: ValueAnimator? = null
|
|
335
393
|
private var isMediaTile = false
|
|
336
394
|
private var boundKey: String? = null
|
|
395
|
+
// OneKey patch: delayed retries cannot survive cell rebinding or recycling.
|
|
396
|
+
private val selectorImageRetries = mutableMapOf<OneKeyImageReusableView, Runnable>()
|
|
337
397
|
var bindingEpoch: Long = 0
|
|
338
398
|
private set
|
|
339
399
|
private var boundCheckboxData: JSONObject? = null
|
|
340
400
|
private var currentLayout = "linear"
|
|
341
401
|
private var restingRowBackground: Drawable? = null
|
|
342
402
|
private var pressedRowBackground: Drawable? = null
|
|
403
|
+
// OneKey patch: preserve a held row independently from RecyclerView snapshot rebinding.
|
|
404
|
+
private var touchPressed = false
|
|
343
405
|
private var reorderActive = false
|
|
344
406
|
private var checkboxCheckedColor = Color.rgb(32, 32, 32)
|
|
345
407
|
private var checkboxUncheckedColor = Color.rgb(252, 252, 252)
|
|
346
408
|
private var checkboxBorderColor = Color.rgb(206, 206, 206)
|
|
409
|
+
private var checkboxIconColor = Color.rgb(252, 252, 252)
|
|
410
|
+
private var checkboxUsesSelectorStyle = false
|
|
347
411
|
private var iconSubduedColor = Color.rgb(141, 141, 141)
|
|
348
412
|
private var visualBackdropColor = Color.WHITE
|
|
349
413
|
private val circleOutlineProvider = object : ViewOutlineProvider() {
|
|
@@ -445,6 +509,7 @@ internal class NativeListRowView(
|
|
|
445
509
|
setOnTouchListener { _, event ->
|
|
446
510
|
when (event.actionMasked) {
|
|
447
511
|
MotionEvent.ACTION_DOWN -> if (isEnabled) {
|
|
512
|
+
touchPressed = true
|
|
448
513
|
if ((tag as? NativeListItem)?.type == "mediaTile") {
|
|
449
514
|
leadingFrame.alpha = 0.8f
|
|
450
515
|
} else {
|
|
@@ -454,15 +519,20 @@ internal class NativeListRowView(
|
|
|
454
519
|
MotionEvent.ACTION_MOVE -> if (
|
|
455
520
|
event.x < 0 || event.y < 0 || event.x >= width || event.y >= height
|
|
456
521
|
) {
|
|
522
|
+
touchPressed = false
|
|
523
|
+
restoreRestingBackground()
|
|
524
|
+
}
|
|
525
|
+
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
526
|
+
touchPressed = false
|
|
457
527
|
restoreRestingBackground()
|
|
458
528
|
}
|
|
459
|
-
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> restoreRestingBackground()
|
|
460
529
|
}
|
|
461
530
|
false
|
|
462
531
|
}
|
|
463
532
|
setOnClickListener { view ->
|
|
464
533
|
(view.tag as? NativeListItem)?.let { item ->
|
|
465
|
-
|
|
534
|
+
// OneKey patch: allow create-address accessories when whole-row press is gated.
|
|
535
|
+
if (!item.json.optBoolean("pressDisabled", false)) onRowPress?.invoke(item, actionOrigin(view, "row"))
|
|
466
536
|
}
|
|
467
537
|
}
|
|
468
538
|
setWillNotDraw(false)
|
|
@@ -509,13 +579,64 @@ internal class NativeListRowView(
|
|
|
509
579
|
}
|
|
510
580
|
}
|
|
511
581
|
|
|
582
|
+
// OneKey patch: RecyclerView may replace item delegates during a selection update.
|
|
583
|
+
override fun onInitializeAccessibilityNodeInfo(info: android.view.accessibility.AccessibilityNodeInfo) {
|
|
584
|
+
super.onInitializeAccessibilityNodeInfo(info)
|
|
585
|
+
info.viewIdResourceName = getTag(com.facebook.react.R.id.react_test_id) as? String
|
|
586
|
+
}
|
|
587
|
+
|
|
512
588
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
513
589
|
if (isMediaTile) {
|
|
514
590
|
val availableWidth = (MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight)
|
|
515
591
|
.coerceAtLeast(0)
|
|
516
592
|
leadingFrame.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, availableWidth)
|
|
517
593
|
}
|
|
518
|
-
|
|
594
|
+
// OneKey patch: RecyclerView must use the adapter's measured selector height.
|
|
595
|
+
super.onMeasure(widthMeasureSpec, selectorHeight?.let { MeasureSpec.makeMeasureSpec(it, MeasureSpec.EXACTLY) } ?: heightMeasureSpec)
|
|
596
|
+
val item = tag as? NativeListItem ?: return
|
|
597
|
+
if (item.json.has("height") && item.json.optString("presentation") == "networkSelector" && item.type in setOf("identity", "sectionHeader") && checkbox.visibility == VISIBLE && trailingColumn.parent === this) {
|
|
598
|
+
// OneKey patch: Yoga snaps the compound accessory before its children; their rounded edges can overflow it.
|
|
599
|
+
val density = resources.displayMetrics.density
|
|
600
|
+
val visibleValues = trailingViews.filter { it.visibility == VISIBLE }
|
|
601
|
+
val sourceTrailingWidth = visibleValues.sumOf { it.measuredWidth } + (20 + 12 * visibleValues.size) * density
|
|
602
|
+
val sourceTrailingLeft = (measuredWidth - 12 * density - sourceTrailingWidth).roundToInt()
|
|
603
|
+
val measuredTrailingLeft = measuredWidth - paddingRight - trailingColumn.measuredWidth
|
|
604
|
+
val mainWidth = (mainColumn.measuredWidth + sourceTrailingLeft - measuredTrailingLeft).coerceAtLeast(0)
|
|
605
|
+
mainColumn.measure(MeasureSpec.makeMeasureSpec(mainWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mainColumn.measuredHeight, MeasureSpec.EXACTLY))
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// OneKey patch: Yoga rounds a half-pixel text center upward; LinearLayout truncates it.
|
|
610
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
611
|
+
super.onLayout(changed, left, top, right, bottom)
|
|
612
|
+
val item = tag as? NativeListItem ?: return
|
|
613
|
+
val accessory = item.json.optJSONArray("trailing")?.optJSONObject(0)
|
|
614
|
+
if (item.type == "identity" && item.json.has("height") && item.json.optString("presentation") == "accountSelector" && accessory?.optString("kind") == "icon" && accessory.optString("name") == "PlusSmallOutline") {
|
|
615
|
+
// OneKey patch: the borderless Plus retains the source's fixed top18/negative7 slot.
|
|
616
|
+
val icon = trailingIcons[0]
|
|
617
|
+
trailingColumn.offsetTopAndBottom(dp(18) - dp(7) - trailingColumn.top - icon.top)
|
|
618
|
+
}
|
|
619
|
+
if (item.type == "action" && item.json.has("height") && item.json.optString("presentation") == "accountSelector" && leadingIcon.visibility == VISIBLE) {
|
|
620
|
+
// OneKey patch: Yoga rounds the 4dp padding inside the 32dp Add account icon upward.
|
|
621
|
+
leadingIcon.offsetLeftAndRight((leadingFrame.width - leadingIcon.width + 1) / 2 - leadingIcon.left)
|
|
622
|
+
leadingIcon.offsetTopAndBottom((leadingFrame.height - leadingIcon.height + 1) / 2 - leadingIcon.top)
|
|
623
|
+
}
|
|
624
|
+
if (!item.json.has("height")) return
|
|
625
|
+
val isNetworkIdentity = item.type == "identity" && item.json.optString("presentation") == "networkSelector"
|
|
626
|
+
val isAccountAction = item.type == "action" && item.json.optString("presentation") == "accountSelector"
|
|
627
|
+
val isNetworkSummary = item.type == "sectionHeader" && item.json.optString("presentation") == "networkSelector" && item.json.optString("variant") == "summary"
|
|
628
|
+
val centeredColumns = when {
|
|
629
|
+
isNetworkIdentity || isAccountAction -> listOf(mainColumn, trailingColumn)
|
|
630
|
+
isNetworkSummary -> listOf(trailingColumn)
|
|
631
|
+
else -> return
|
|
632
|
+
}
|
|
633
|
+
for (column in centeredColumns) {
|
|
634
|
+
if (column.parent !== this || column.visibility == GONE) continue
|
|
635
|
+
val margins = column.layoutParams as MarginLayoutParams
|
|
636
|
+
val available = height - paddingTop - paddingBottom - margins.topMargin - margins.bottomMargin
|
|
637
|
+
val desiredTop = paddingTop + margins.topMargin + (available - column.height + 1) / 2
|
|
638
|
+
column.offsetTopAndBottom(desiredTop - column.top)
|
|
639
|
+
}
|
|
519
640
|
}
|
|
520
641
|
|
|
521
642
|
fun bind(
|
|
@@ -526,12 +647,16 @@ internal class NativeListRowView(
|
|
|
526
647
|
itemIndex: Int?,
|
|
527
648
|
selected: Boolean,
|
|
528
649
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
|
|
650
|
+
useSourceScale: Boolean = false,
|
|
529
651
|
) {
|
|
652
|
+
val shouldRestorePressed = touchPressed && boundKey == item.key
|
|
530
653
|
invalidateCurrentBinding()
|
|
531
654
|
bindingEpoch += 1
|
|
532
655
|
boundKey = item.key
|
|
656
|
+
touchPressed = shouldRestorePressed
|
|
533
657
|
currentLayout = layout
|
|
534
658
|
tag = item
|
|
659
|
+
selectorUsesSourceScale = item.usesSelectorSourceScale || useSourceScale
|
|
535
660
|
reorderActive = false
|
|
536
661
|
leadingImages.forEach(OneKeyImageReusableView::prepareForReuse)
|
|
537
662
|
secondaryImage.prepareForReuse()
|
|
@@ -544,12 +669,21 @@ internal class NativeListRowView(
|
|
|
544
669
|
val accent = color(theme, "accent", "#0D8200FC")
|
|
545
670
|
checkboxCheckedColor = primary
|
|
546
671
|
checkboxUncheckedColor = color(theme, "inverseText", "#FCFCFC")
|
|
672
|
+
checkboxIconColor = checkboxUncheckedColor
|
|
673
|
+
checkboxUsesSelectorStyle = item.json.optString("presentation") == "networkSelector"
|
|
547
674
|
checkboxBorderColor = Color.argb(
|
|
548
675
|
0x31,
|
|
549
676
|
0,
|
|
550
677
|
0,
|
|
551
678
|
0,
|
|
552
679
|
)
|
|
680
|
+
if (item.json.optString("presentation") == "networkSelector") {
|
|
681
|
+
checkboxCheckedColor = color(theme, "checkboxBackground", "#202020")
|
|
682
|
+
checkboxBorderColor = color(theme, "checkboxBorder", "#00000031")
|
|
683
|
+
checkboxIconColor = color(theme, "checkboxIcon", "#FFFFFF")
|
|
684
|
+
// OneKey patch: the V1 checkbox fills even its unchecked body with iconInverse.
|
|
685
|
+
checkboxUncheckedColor = checkboxIconColor
|
|
686
|
+
}
|
|
553
687
|
iconSubduedColor = color(theme, "iconSubdued", "#00000072")
|
|
554
688
|
visualBackdropColor = color(theme, "rowBackground", "#FFFFFF")
|
|
555
689
|
unreadDot.background = roundedFill(
|
|
@@ -569,7 +703,7 @@ internal class NativeListRowView(
|
|
|
569
703
|
if (item.type == "rail") "#0000000F" else "#00000017",
|
|
570
704
|
),
|
|
571
705
|
)
|
|
572
|
-
background = restingRowBackground
|
|
706
|
+
background = if (touchPressed || reorderActive) pressedRowBackground else restingRowBackground
|
|
573
707
|
if (layout == "table") {
|
|
574
708
|
if (item.type == "dataRow") {
|
|
575
709
|
setPadding(dp(20), dp(10), dp(20), dp(10))
|
|
@@ -592,8 +726,14 @@ internal class NativeListRowView(
|
|
|
592
726
|
invalidate()
|
|
593
727
|
trailingViews.forEach { it.setTextColor(primary) }
|
|
594
728
|
isEnabled = !item.json.optBoolean("disabled", false)
|
|
595
|
-
|
|
729
|
+
if (!isEnabled) touchPressed = false
|
|
730
|
+
background = if (touchPressed || reorderActive) pressedRowBackground else restingRowBackground
|
|
731
|
+
// OneKey patch: deprecation dims the row without disabling menu controls.
|
|
732
|
+
// alpha = if (isEnabled) 1f else 0.5f
|
|
733
|
+
alpha = item.json.optDouble("opacity", 1.0).toFloat() * (if (isEnabled) 1f else 0.5f)
|
|
596
734
|
contentDescription = item.json.optString("accessibilityLabel", item.json.optString("title"))
|
|
735
|
+
// OneKey patch: retain stable original selector test identifiers.
|
|
736
|
+
setTag(com.facebook.react.R.id.react_test_id, item.json.optString("testID").takeIf { it.isNotEmpty() })
|
|
597
737
|
|
|
598
738
|
when (item.type) {
|
|
599
739
|
"walletGroup" -> bindWalletGroup(item, theme, layout, listOrientation, checkboxState)
|
|
@@ -609,10 +749,34 @@ internal class NativeListRowView(
|
|
|
609
749
|
"system" -> bindSystem(item, theme)
|
|
610
750
|
}
|
|
611
751
|
applySize(item)
|
|
752
|
+
if (item.type == "system" && item.json.optString("variant") == "warning") {
|
|
753
|
+
title.typeface = NativeListFonts.medium(context)
|
|
754
|
+
title.textSize = sp(14f)
|
|
755
|
+
subtitle.textSize = sp(14f)
|
|
756
|
+
TextViewCompat.setLineHeight(title, dp(20))
|
|
757
|
+
TextViewCompat.setLineHeight(subtitle, dp(20))
|
|
758
|
+
minimumHeight = 0
|
|
759
|
+
}
|
|
612
760
|
applyListOrientation(item, listOrientation)
|
|
761
|
+
applySelectorTypography(item)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// OneKey patch: keep a small idle member pool after reuse or a direct rebind.
|
|
765
|
+
// The compact proxy/expansion keeps every member until it leaves that state.
|
|
766
|
+
private fun trimWalletGroupRows(required: Int) {
|
|
767
|
+
val retained = maxOf(8, required)
|
|
768
|
+
while (walletGroupRows.size > retained) {
|
|
769
|
+
val row = walletGroupRows.removeAt(walletGroupRows.lastIndex)
|
|
770
|
+
(row.parent as? ViewGroup)?.removeView(row)
|
|
771
|
+
row.dispose()
|
|
772
|
+
row.onRowPress = null
|
|
773
|
+
row.onAction = null
|
|
774
|
+
row.onBindingInvalidated = null
|
|
775
|
+
}
|
|
613
776
|
}
|
|
614
777
|
|
|
615
778
|
fun recycle() {
|
|
779
|
+
touchPressed = false
|
|
616
780
|
restoreRestingBackground()
|
|
617
781
|
invalidateCurrentBinding()
|
|
618
782
|
boundKey = null
|
|
@@ -625,6 +789,7 @@ internal class NativeListRowView(
|
|
|
625
789
|
row.alpha = 1f
|
|
626
790
|
row.recycle()
|
|
627
791
|
}
|
|
792
|
+
if (!reorderActive && walletGroupExpandAnimator?.isRunning != true) trimWalletGroupRows(0)
|
|
628
793
|
}
|
|
629
794
|
|
|
630
795
|
fun bindSelection(
|
|
@@ -636,6 +801,8 @@ internal class NativeListRowView(
|
|
|
636
801
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
|
|
637
802
|
) {
|
|
638
803
|
if (boundKey != item.key) return
|
|
804
|
+
// OneKey patch: keep row callbacks and checkbox fallback state current without resetting images.
|
|
805
|
+
tag = item
|
|
639
806
|
if (item.type == "walletGroup") {
|
|
640
807
|
val members = buildList {
|
|
641
808
|
add(item.json.getJSONObject("parent"))
|
|
@@ -669,7 +836,18 @@ internal class NativeListRowView(
|
|
|
669
836
|
),
|
|
670
837
|
)
|
|
671
838
|
}
|
|
672
|
-
|
|
839
|
+
// OneKey patch: the comparator permits state changes only on these existing checkbox slots.
|
|
840
|
+
// boundCheckboxData?.let { bindCheckbox(item, it, checkboxState) }
|
|
841
|
+
if (boundCheckboxData != null) {
|
|
842
|
+
val latestCheckbox = if (item.type == "identity") {
|
|
843
|
+
item.json.optJSONArray("trailing")?.let { trailing ->
|
|
844
|
+
(0 until trailing.length()).mapNotNull { trailing.optJSONObject(it) }
|
|
845
|
+
.lastOrNull { it.optString("kind") == "checkbox" }
|
|
846
|
+
}
|
|
847
|
+
} else item.json.optJSONObject("checkbox")
|
|
848
|
+
boundCheckboxData = latestCheckbox ?: boundCheckboxData
|
|
849
|
+
boundCheckboxData?.let { bindCheckbox(item, it, checkboxState) }
|
|
850
|
+
}
|
|
673
851
|
}
|
|
674
852
|
|
|
675
853
|
fun bindStableSummary(item: NativeListItem) {
|
|
@@ -684,10 +862,14 @@ internal class NativeListRowView(
|
|
|
684
862
|
contentDescription = item.json.optString("accessibilityLabel", item.json.optString("title"))
|
|
685
863
|
title.text = item.json.optString("title")
|
|
686
864
|
trailingViews[0].text = item.json.optString("value")
|
|
865
|
+
applySelectorTypography(item)
|
|
687
866
|
}
|
|
688
867
|
|
|
689
868
|
fun dispose() {
|
|
869
|
+
invalidateCurrentBinding()
|
|
690
870
|
restoreRestingBackground()
|
|
871
|
+
selectorImages.forEach(OneKeyImageReusableView::dispose)
|
|
872
|
+
selectorImages.clear()
|
|
691
873
|
leadingImages.forEach(OneKeyImageReusableView::dispose)
|
|
692
874
|
secondaryImage.dispose()
|
|
693
875
|
mediaNetworkImage.dispose()
|
|
@@ -699,7 +881,8 @@ internal class NativeListRowView(
|
|
|
699
881
|
sourceView: View,
|
|
700
882
|
source: String,
|
|
701
883
|
slot: Int? = null,
|
|
702
|
-
) = NativeListActionOrigin(sourceView, this, bindingEpoch, source, slot
|
|
884
|
+
) = NativeListActionOrigin(sourceView, this, bindingEpoch, source, slot,
|
|
885
|
+
if ((tag as? NativeListItem)?.json?.optString("presentation") == "accountSelector" && sourceView in trailingIcons && (sourceView.layoutParams as MarginLayoutParams).marginStart < 0) dp(7) else 0)
|
|
703
886
|
|
|
704
887
|
private fun emitAction(
|
|
705
888
|
item: NativeListItem,
|
|
@@ -712,13 +895,65 @@ internal class NativeListRowView(
|
|
|
712
895
|
onAction?.invoke(item, actionKey, target, actionOrigin(sourceView, source, slot))
|
|
713
896
|
}
|
|
714
897
|
|
|
898
|
+
// OneKey patch: match SizableText TABULAR_NUMS on dynamic labels without replacing their typeface.
|
|
899
|
+
private fun applySelectorTypography(item: NativeListItem) {
|
|
900
|
+
val usesSelectorTypography = item.json.optString("presentation") in setOf("accountSelector", "networkSelector", "walletSidebar") || item.type == "system" && item.json.optString("variant") == "warning"
|
|
901
|
+
fun visit(view: View) {
|
|
902
|
+
if (view is OneKeyIconView) view.useSourceScale = selectorUsesSourceScale
|
|
903
|
+
if (view is DottedUnderlineTextView) view.useSourceScale = selectorUsesSourceScale
|
|
904
|
+
if (view is TextView && usesSelectorTypography) {
|
|
905
|
+
val selectorLineHeight = selectorLineHeights.getOrPut(view) { view.lineHeight }
|
|
906
|
+
val original = view.fontFeatureSettings
|
|
907
|
+
if (!selectorOriginalFontFeatures.containsKey(view)) selectorOriginalFontFeatures[view] = original
|
|
908
|
+
view.fontFeatureSettings = if (original.isNullOrEmpty()) "tnum" else if (original.contains("tnum")) original else "$original, 'tnum' 1"
|
|
909
|
+
// OneKey patch: SizableText disables font scaling and rounds font sizes to whole pixels.
|
|
910
|
+
val sourceTypography = selectorUsesSourceScale || item.type == "system" && item.json.optString("variant") == "warning"
|
|
911
|
+
if (sourceTypography) {
|
|
912
|
+
// OneKey patch: React Native CustomStyleSpan disables hinting and preserves fractional advances.
|
|
913
|
+
selectorOriginalPaintFlags.putIfAbsent(view, view.paintFlags)
|
|
914
|
+
view.paintFlags = view.paintFlags or Paint.SUBPIXEL_TEXT_FLAG or Paint.LINEAR_TEXT_FLAG
|
|
915
|
+
val originalSize = selectorFontSizes.getOrPut(view) { view.textSize }
|
|
916
|
+
val sourceSize = originalSize * resources.displayMetrics.density / resources.displayMetrics.scaledDensity
|
|
917
|
+
view.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, kotlin.math.ceil(sourceSize.toDouble()).toFloat())
|
|
918
|
+
view.letterSpacing = 0f
|
|
919
|
+
}
|
|
920
|
+
if (sourceTypography && view.text.isNotEmpty()) {
|
|
921
|
+
val text = SpannableStringBuilder(view.text)
|
|
922
|
+
text.getSpans(0, text.length, SelectorLineHeightSpan::class.java).forEach(text::removeSpan)
|
|
923
|
+
text.setSpan(SelectorLineHeightSpan(selectorLineHeight), 0, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
924
|
+
view.setLineSpacing(0f, 1f)
|
|
925
|
+
view.text = text
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (view is ViewGroup) for (index in 0 until view.childCount) visit(view.getChildAt(index))
|
|
929
|
+
}
|
|
930
|
+
visit(this)
|
|
931
|
+
}
|
|
932
|
+
|
|
715
933
|
private fun invalidateCurrentBinding() {
|
|
934
|
+
selectorImageRetries.forEach { (image, retry) -> image.removeCallbacks(retry) }
|
|
935
|
+
selectorImageRetries.clear()
|
|
716
936
|
if (boundKey == null) return
|
|
717
937
|
onBindingInvalidated?.invoke(this, bindingEpoch)
|
|
718
938
|
bindingEpoch += 1
|
|
719
939
|
}
|
|
720
940
|
|
|
721
941
|
private fun resetViews() {
|
|
942
|
+
clipToPadding = true
|
|
943
|
+
selectorOriginalFontFeatures.forEach { (view, original) -> view.fontFeatureSettings = original }
|
|
944
|
+
selectorOriginalFontFeatures.clear()
|
|
945
|
+
selectorOriginalPaintFlags.forEach { (view, flags) -> view.paintFlags = flags }
|
|
946
|
+
selectorOriginalPaintFlags.clear()
|
|
947
|
+
selectorLineHeights.clear()
|
|
948
|
+
selectorFontSizes.clear()
|
|
949
|
+
// OneKey patch: selector-only views cannot survive a recycled binding.
|
|
950
|
+
selectorViews.forEach { (it.parent as? ViewGroup)?.removeView(it) }
|
|
951
|
+
selectorViews.clear()
|
|
952
|
+
selectorImages.forEach(OneKeyImageReusableView::dispose)
|
|
953
|
+
selectorImages.clear()
|
|
954
|
+
selectorHeight = null
|
|
955
|
+
title.setOnClickListener(null)
|
|
956
|
+
title.isClickable = false
|
|
722
957
|
walletGroupRows.forEach { it.invalidateCurrentBinding() }
|
|
723
958
|
walletGroupExpandAnimator?.removeAllListeners()
|
|
724
959
|
walletGroupExpandAnimator?.cancel()
|
|
@@ -736,6 +971,11 @@ internal class NativeListRowView(
|
|
|
736
971
|
activityContentRow.removeAllViews()
|
|
737
972
|
(actionLine.parent as? ViewGroup)?.removeView(actionLine)
|
|
738
973
|
removeAllViews()
|
|
974
|
+
// OneKey patch: the old animator is cancelled and old children are detached.
|
|
975
|
+
// Do not trim currently needed members when re-binding an expanded group.
|
|
976
|
+
val nextItem = tag as? NativeListItem
|
|
977
|
+
val required = if (nextItem?.type == "walletGroup") (nextItem.json.optJSONArray("children")?.length() ?: 0) + 1 else 0
|
|
978
|
+
trimWalletGroupRows(required)
|
|
739
979
|
orientation = HORIZONTAL
|
|
740
980
|
gravity = Gravity.CENTER_VERTICAL
|
|
741
981
|
minimumHeight = 0
|
|
@@ -801,6 +1041,7 @@ internal class NativeListRowView(
|
|
|
801
1041
|
trailingColumn.layoutParams = wrap()
|
|
802
1042
|
trailingViews.forEach {
|
|
803
1043
|
it.visibility = GONE
|
|
1044
|
+
it.setTag(com.facebook.react.R.id.react_test_id, null)
|
|
804
1045
|
it.gravity = Gravity.END
|
|
805
1046
|
it.maxLines = 1
|
|
806
1047
|
it.layoutParams = wrap()
|
|
@@ -879,7 +1120,8 @@ internal class NativeListRowView(
|
|
|
879
1120
|
mainColumn.removeView(skeletonSecondary)
|
|
880
1121
|
setOnClickListener { view ->
|
|
881
1122
|
(view.tag as? NativeListItem)?.let { item ->
|
|
882
|
-
|
|
1123
|
+
// OneKey patch: allow create-address accessories when whole-row press is gated.
|
|
1124
|
+
if (!item.json.optBoolean("pressDisabled", false)) onRowPress?.invoke(item, actionOrigin(view, "row"))
|
|
883
1125
|
}
|
|
884
1126
|
}
|
|
885
1127
|
}
|
|
@@ -1003,9 +1245,12 @@ internal class NativeListRowView(
|
|
|
1003
1245
|
members.add(children.getJSONObject(index))
|
|
1004
1246
|
}
|
|
1005
1247
|
}
|
|
1248
|
+
if (members.first().has("height")) setPadding(dp(1), dp(1), dp(1), dp(1))
|
|
1006
1249
|
walletGroupDragChildCount = members.size - 1
|
|
1007
1250
|
walletGroupExpandedHeightPx =
|
|
1008
|
-
|
|
1251
|
+
// OneKey patch: expanded groups include individual wallet badge heights.
|
|
1252
|
+
// dp(members.size * 68 + walletGroupDragChildCount * 12)
|
|
1253
|
+
dp(members.sumOf { it.optInt("height", if ((it.optJSONArray("badges")?.length() ?: 0) > 0) 92 else 68) } + walletGroupDragChildCount * 12 + if (members.first().has("height")) 2 else 0)
|
|
1009
1254
|
walletGroupDragBadgeBackgroundPaint.color = color(
|
|
1010
1255
|
theme,
|
|
1011
1256
|
"inverseBackground",
|
|
@@ -1048,8 +1293,11 @@ internal class NativeListRowView(
|
|
|
1048
1293
|
null,
|
|
1049
1294
|
memberJson.optBoolean("selected", false),
|
|
1050
1295
|
checkboxState,
|
|
1296
|
+
useSourceScale = selectorUsesSourceScale,
|
|
1051
1297
|
)
|
|
1052
|
-
|
|
1298
|
+
// OneKey patch: member geometry matches the outer group height calculation.
|
|
1299
|
+
// memberRow.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, dp(68)).apply {
|
|
1300
|
+
memberRow.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, dp(memberJson.optInt("height", if ((memberJson.optJSONArray("badges")?.length() ?: 0) > 0) 92 else 68))).apply {
|
|
1053
1301
|
if (index > 0) topMargin = dp(12)
|
|
1054
1302
|
}
|
|
1055
1303
|
addView(memberRow)
|
|
@@ -1082,8 +1330,11 @@ internal class NativeListRowView(
|
|
|
1082
1330
|
titleLine.gravity = Gravity.CENTER
|
|
1083
1331
|
titleLine.packsChildrenAtStart = false
|
|
1084
1332
|
titleLine.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
|
1085
|
-
title
|
|
1333
|
+
// OneKey patch: the original wallet title ellipsizes within the full inner row width.
|
|
1334
|
+
title.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
|
1086
1335
|
title.gravity = Gravity.CENTER
|
|
1336
|
+
// OneKey patch: this branch returns before the common identity ellipsis setup.
|
|
1337
|
+
if (item.json.has("height")) title.ellipsize = TextUtils.TruncateAt.END
|
|
1087
1338
|
showText(title, item.json.optString("title"), 1)
|
|
1088
1339
|
title.setTextColor(
|
|
1089
1340
|
color(
|
|
@@ -1095,13 +1346,41 @@ internal class NativeListRowView(
|
|
|
1095
1346
|
addView(
|
|
1096
1347
|
mainColumn,
|
|
1097
1348
|
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply {
|
|
1098
|
-
|
|
1349
|
+
// OneKey patch: snap the source 4 + 40 + 4 sequence once instead of rounding each gap.
|
|
1350
|
+
topMargin = if (item.json.has("height")) dp(48) - dp(44) else dp(4)
|
|
1099
1351
|
},
|
|
1100
1352
|
)
|
|
1353
|
+
// OneKey patch: wallet tags are a centered line below the wallet name.
|
|
1354
|
+
item.json.optJSONArray("badges")?.takeIf { it.length() > 0 }?.let { badges ->
|
|
1355
|
+
val isSelector = item.json.has("height")
|
|
1356
|
+
val badgeLineHeight = if (isSelector) 14 else 16
|
|
1357
|
+
val badgeHeight = badgeLineHeight + 4
|
|
1358
|
+
val line = LinearLayout(context).apply { orientation = HORIZONTAL; gravity = Gravity.CENTER }
|
|
1359
|
+
for (index in 0 until badges.length()) {
|
|
1360
|
+
val badge = TextView(context).apply {
|
|
1361
|
+
text = badges.getJSONObject(index).optString("text")
|
|
1362
|
+
textSize = sp(if (isSelector) 11f else 12f)
|
|
1363
|
+
typeface = NativeListFonts.regular(context)
|
|
1364
|
+
includeFontPadding = false
|
|
1365
|
+
maxLines = 1
|
|
1366
|
+
ellipsize = TextUtils.TruncateAt.END
|
|
1367
|
+
val warning = isSelector && badges.getJSONObject(index).optString("tone") == "warning"
|
|
1368
|
+
setTextColor(color(theme, if (warning) "caution" else "secondaryText", if (warning) "#AB6400" else "#0000009B"))
|
|
1369
|
+
background = roundedFill(color(theme, if (warning) "cautionBackground" else if (isSelector) "subduedBackground" else "strongBackground", if (warning) "#FFF4D5" else "#00000006"), 4f)
|
|
1370
|
+
setPadding(dp(if (isSelector) 6 else 4), dp(2), dp(if (isSelector) 6 else 4), dp(2))
|
|
1371
|
+
}
|
|
1372
|
+
TextViewCompat.setLineHeight(badge, dp(badgeLineHeight))
|
|
1373
|
+
line.addView(badge, LayoutParams(LayoutParams.WRAP_CONTENT, dp(badgeHeight)).apply { if (index > 0) marginStart = dp(4) })
|
|
1374
|
+
}
|
|
1375
|
+
mainColumn.addView(line, LayoutParams(LayoutParams.WRAP_CONTENT, dp(badgeHeight)).apply { topMargin = dp(4) })
|
|
1376
|
+
selectorViews.add(line)
|
|
1377
|
+
}
|
|
1101
1378
|
return
|
|
1102
1379
|
}
|
|
1103
1380
|
if (item.json.optString("presentation") == "networkSelector") {
|
|
1104
|
-
|
|
1381
|
+
// OneKey patch: the explicit 48-point row centers its 32-point network icon.
|
|
1382
|
+
// setPadding(dp(12), dp(7), dp(12), dp(8))
|
|
1383
|
+
setPadding(dp(12), dp(if (item.json.has("height")) 8 else 7), dp(12), dp(8))
|
|
1105
1384
|
}
|
|
1106
1385
|
val leading = item.json.optJSONObject("leading")
|
|
1107
1386
|
item.json.optJSONObject("leadingAction")?.let { action ->
|
|
@@ -1136,6 +1415,13 @@ internal class NativeListRowView(
|
|
|
1136
1415
|
item.json.optString("presentation") == "accountSelector"
|
|
1137
1416
|
) 32 else 40,
|
|
1138
1417
|
)
|
|
1418
|
+
// OneKey patch: custom network initials retain LetterAvatar typography.
|
|
1419
|
+
if (item.json.optString("presentation") == "networkSelector" && leading?.optJSONObject("image") == null && leading?.optJSONObject("fallbackIcon") == null && !leading?.optString("fallbackText").isNullOrEmpty()) {
|
|
1420
|
+
leadingFallback.textSize = sp(19f)
|
|
1421
|
+
leadingFallback.typeface = NativeListFonts.semibold(context)
|
|
1422
|
+
leadingFallback.setTextColor(color(theme, "inverseText", "#FCFCFC"))
|
|
1423
|
+
TextViewCompat.setLineHeight(leadingFallback, dp(27))
|
|
1424
|
+
}
|
|
1139
1425
|
addView(mainColumn, weighted())
|
|
1140
1426
|
titleLine.packsChildrenAtStart = true
|
|
1141
1427
|
title.ellipsize = TextUtils.TruncateAt.END
|
|
@@ -1144,6 +1430,43 @@ internal class NativeListRowView(
|
|
|
1144
1430
|
title.typeface = NativeListFonts.regular(context)
|
|
1145
1431
|
}
|
|
1146
1432
|
showText(subtitle, item.json.optString("subtitle"), item.json.optInt("subtitleLines", 2))
|
|
1433
|
+
// OneKey patch: use separate labels for independently truncated balance/address.
|
|
1434
|
+
item.json.optJSONArray("subtitleSegments")?.takeIf { it.length() > 0 }?.let { segments ->
|
|
1435
|
+
subtitle.visibility = GONE
|
|
1436
|
+
val line = SelectorSubtitleLayout(context).apply { orientation = HORIZONTAL; gravity = Gravity.CENTER_VERTICAL }
|
|
1437
|
+
for (index in 0 until segments.length()) {
|
|
1438
|
+
val segment = segments.getJSONObject(index)
|
|
1439
|
+
if (segment.optBoolean("separatorBefore", false)) {
|
|
1440
|
+
val dot = View(context).apply { background = roundedFill(color(theme, "disabledText", "#00000072"), 2f) }
|
|
1441
|
+
line.addView(dot, LayoutParams(dp(4), dp(4)).apply { marginStart = dp(6); marginEnd = dp(6) })
|
|
1442
|
+
}
|
|
1443
|
+
val label = TextView(context).apply {
|
|
1444
|
+
text = segment.optString("text")
|
|
1445
|
+
typeface = NativeListFonts.regular(context)
|
|
1446
|
+
textSize = sp(14f)
|
|
1447
|
+
includeFontPadding = false
|
|
1448
|
+
maxLines = 1
|
|
1449
|
+
ellipsize = TextUtils.TruncateAt.END
|
|
1450
|
+
val toneKey = when (segment.optString("tone")) { "primary" -> "primaryText"; "disabled" -> "disabledText"; "caution" -> "caution"; "positive" -> "positive"; "negative" -> "negative"; else -> "secondaryText" }
|
|
1451
|
+
setTextColor(color(theme, toneKey, if (toneKey == "caution") "#AB6400" else "#0000009B"))
|
|
1452
|
+
}
|
|
1453
|
+
TextViewCompat.setLineHeight(label, dp(20))
|
|
1454
|
+
applyValueSegments(label, segment.optJSONArray("textSegments"), 14, 20, false)
|
|
1455
|
+
line.addView(label, LayoutParams(LayoutParams.WRAP_CONTENT, dp(20)))
|
|
1456
|
+
}
|
|
1457
|
+
mainColumn.addView(line, 2, LayoutParams(LayoutParams.MATCH_PARENT, dp(20)))
|
|
1458
|
+
selectorViews.add(line)
|
|
1459
|
+
}
|
|
1460
|
+
item.json.optJSONArray("titleMatch")?.takeIf { it.length() > 0 }?.let { matches ->
|
|
1461
|
+
val highlighted = SpannableStringBuilder(title.text)
|
|
1462
|
+
for (index in 0 until matches.length()) {
|
|
1463
|
+
val match = matches.getJSONObject(index)
|
|
1464
|
+
val start = match.optInt("start")
|
|
1465
|
+
val end = match.optInt("end")
|
|
1466
|
+
if (start >= 0 && end > start && end <= highlighted.length) highlighted.setSpan(ForegroundColorSpan(color(theme, "info", "#0D74CE")), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
1467
|
+
}
|
|
1468
|
+
title.text = highlighted
|
|
1469
|
+
}
|
|
1147
1470
|
showText(tertiary, item.json.optString("tertiary"), 1)
|
|
1148
1471
|
tertiary.setTextColor(
|
|
1149
1472
|
color(
|
|
@@ -1164,6 +1487,10 @@ internal class NativeListRowView(
|
|
|
1164
1487
|
}
|
|
1165
1488
|
addView(trailingColumn, wrap())
|
|
1166
1489
|
val accessories = item.json.optJSONArray("trailing")
|
|
1490
|
+
if (item.json.has("height") && item.json.optString("presentation") == "networkSelector" && accessories.hasAccessory("checkbox")) {
|
|
1491
|
+
// OneKey patch: retain ListItem's title-to-accessory gap when measuring truncation.
|
|
1492
|
+
(mainColumn.layoutParams as LayoutParams).marginEnd = dp(12)
|
|
1493
|
+
}
|
|
1167
1494
|
if (accessories.hasAccessory("checkbox") && accessories.hasAccessory("value")) {
|
|
1168
1495
|
trailingColumn.orientation = HORIZONTAL
|
|
1169
1496
|
trailingColumn.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
|
@@ -1847,10 +2174,15 @@ internal class NativeListRowView(
|
|
|
1847
2174
|
val isSummary = variant == "summary"
|
|
1848
2175
|
val isGallery = variant == "gallery"
|
|
1849
2176
|
val isTable = currentLayout == "table"
|
|
2177
|
+
// OneKey patch: title help emits its own frame instead of toggling the section.
|
|
2178
|
+
item.json.optString("titleActionKey").takeIf { it.isNotEmpty() }?.let { key ->
|
|
2179
|
+
title.setOnClickListener { emitAction(item, key, null, title, "leadingAction") }
|
|
2180
|
+
}
|
|
1850
2181
|
val isNetworkSelector = item.json.optString("presentation") == "networkSelector"
|
|
1851
2182
|
val isHistory = variant == "history" || item.sectionKey?.startsWith("history-") == true
|
|
1852
2183
|
val isTokenManager = item.sectionKey in setOf("linear-tokens", "action-tokens")
|
|
1853
|
-
val
|
|
2184
|
+
val isExplicitNetworkHeader = isNetworkSelector && item.json.has("height")
|
|
2185
|
+
val hasDottedTitle = isSummary || (isNetworkSelector && (!isExplicitNetworkHeader || item.json.optString("titleActionKey").isNotEmpty())) ||
|
|
1854
2186
|
(item.json.optString("value").isNotEmpty() && item.json.optJSONObject("checkbox") != null)
|
|
1855
2187
|
// Linear/sectioned snapshots reserve the ListItem mx=8 at RecyclerView
|
|
1856
2188
|
// level, so header-local insets below are source px minus that outer inset.
|
|
@@ -1870,7 +2202,8 @@ internal class NativeListRowView(
|
|
|
1870
2202
|
),
|
|
1871
2203
|
)
|
|
1872
2204
|
if (isNetworkSelector) {
|
|
1873
|
-
|
|
2205
|
+
val verticalInset = if (isExplicitNetworkHeader && item.json.optString("titleActionKey").isEmpty()) 8 else 12
|
|
2206
|
+
setPadding(dp(headerHorizontalInset), dp(verticalInset), dp(headerHorizontalInset), dp(verticalInset))
|
|
1874
2207
|
title.textSize = sp(14f)
|
|
1875
2208
|
title.typeface = NativeListFonts.medium(context)
|
|
1876
2209
|
TextViewCompat.setLineHeight(title, dp(20))
|
|
@@ -1924,14 +2257,22 @@ internal class NativeListRowView(
|
|
|
1924
2257
|
item.json.optString("valueActionKey"),
|
|
1925
2258
|
color(theme, "secondaryText", "#0000009B"),
|
|
1926
2259
|
)
|
|
2260
|
+
trailingViews[0].setTag(com.facebook.react.R.id.react_test_id, item.json.optString("valueActionTestID").takeIf { it.isNotEmpty() })
|
|
2261
|
+
trailingViews[0].accessibilityDelegate = selectorAccessibilityDelegate
|
|
1927
2262
|
trailingViews[0].textSize = sp(16f)
|
|
1928
2263
|
trailingViews[0].typeface = NativeListFonts.medium(context)
|
|
1929
2264
|
TextViewCompat.setLineHeight(trailingViews[0], dp(24))
|
|
1930
|
-
|
|
2265
|
+
// OneKey patch: the migrated media button shares the original 24-point text box.
|
|
2266
|
+
if (isExplicitNetworkHeader) trailingViews[0].setPadding(0, 0, 0, 0)
|
|
2267
|
+
else trailingViews[0].setPadding(dp(14), dp(6), dp(14), dp(6))
|
|
1931
2268
|
trailingViews[0].layoutParams = wrap()
|
|
1932
2269
|
} else if (!isGallery && !isHistory && !isTokenManager) {
|
|
1933
2270
|
val value = item.json.optString("value")
|
|
1934
2271
|
val checkboxData = item.json.optJSONObject("checkbox")
|
|
2272
|
+
if (isExplicitNetworkHeader && checkboxData != null) {
|
|
2273
|
+
// OneKey patch: the asset section title reserves its original 8dp trailing margin.
|
|
2274
|
+
(mainColumn.layoutParams as LayoutParams).marginEnd = dp(8)
|
|
2275
|
+
}
|
|
1935
2276
|
if (checkboxData != null && value.isNotEmpty()) {
|
|
1936
2277
|
// Value and checkbox share the trailing edge as one compound accessory.
|
|
1937
2278
|
trailingColumn.orientation = HORIZONTAL
|
|
@@ -1966,6 +2307,27 @@ internal class NativeListRowView(
|
|
|
1966
2307
|
}
|
|
1967
2308
|
checkboxData?.let { bindCheckbox(item, it, checkboxState) }
|
|
1968
2309
|
}
|
|
2310
|
+
applyValueSegments(trailingViews[0], item.json.optJSONArray("valueSegments"))
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
// OneKey patch: preserve compact zero-count digits without changing their baseline.
|
|
2314
|
+
private fun applyValueSegments(view: TextView, segments: JSONArray?, fontSize: Int = 16, lineHeight: Int = 24, medium: Boolean = true) {
|
|
2315
|
+
if (segments == null || segments.length() == 0) return
|
|
2316
|
+
val value = SpannableStringBuilder()
|
|
2317
|
+
for (index in 0 until segments.length()) {
|
|
2318
|
+
val segment = segments.getJSONObject(index)
|
|
2319
|
+
val start = value.length
|
|
2320
|
+
value.append(segment.optString("text"))
|
|
2321
|
+
if (segment.optString("style") == "subscript") {
|
|
2322
|
+
val size = kotlin.math.ceil(fontSize * 0.6).toFloat()
|
|
2323
|
+
val span = if (selectorUsesSourceScale) AbsoluteSizeSpan(kotlin.math.ceil((size * resources.displayMetrics.density).toDouble()).toInt(), false) else AbsoluteSizeSpan(sp(size).roundToInt(), true)
|
|
2324
|
+
value.setSpan(span, start, value.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
view.textSize = sp(fontSize.toFloat())
|
|
2328
|
+
view.typeface = if (medium) NativeListFonts.medium(context) else NativeListFonts.regular(context)
|
|
2329
|
+
TextViewCompat.setLineHeight(view, dp(lineHeight))
|
|
2330
|
+
view.text = value
|
|
1969
2331
|
}
|
|
1970
2332
|
|
|
1971
2333
|
private fun bindAction(
|
|
@@ -1978,12 +2340,13 @@ internal class NativeListRowView(
|
|
|
1978
2340
|
addLeading(icon, if (isAccountSelector) 32 else 40)
|
|
1979
2341
|
leadingIcon.layoutParams = FrameLayout.LayoutParams(dp(24), dp(24), Gravity.CENTER)
|
|
1980
2342
|
if (!icon.has("backgroundColor")) leadingFrame.background = null
|
|
2343
|
+
if (isAccountSelector) leadingFrame.background = roundedFill(safeColor(icon.optString("backgroundColor"), color(theme, "strongBackground", "#0000000F")), 8f)
|
|
1981
2344
|
}
|
|
1982
2345
|
addView(mainColumn, weighted())
|
|
1983
2346
|
showText(title, item.json.optString("title"), 1)
|
|
1984
2347
|
if (isAccountSelector) {
|
|
1985
|
-
title.typeface = NativeListFonts.regular(context)
|
|
1986
|
-
title.setTextColor(color(theme, "secondaryText", "#0000009B"))
|
|
2348
|
+
title.typeface = if (item.json.has("icon")) NativeListFonts.medium(context) else NativeListFonts.regular(context)
|
|
2349
|
+
title.setTextColor(color(theme, if (item.json.optString("tone") == "primary") "primaryText" else "secondaryText", "#0000009B"))
|
|
1987
2350
|
} else if (item.json.optString("tone") == "danger") {
|
|
1988
2351
|
title.setTextColor(color(theme, "negative", "#C40006D3"))
|
|
1989
2352
|
}
|
|
@@ -2003,6 +2366,17 @@ internal class NativeListRowView(
|
|
|
2003
2366
|
|
|
2004
2367
|
private fun bindSystem(item: NativeListItem, theme: JSONObject?) {
|
|
2005
2368
|
val variant = item.json.optString("variant")
|
|
2369
|
+
// OneKey patch: warning title/description wrap inside the actual scroll content.
|
|
2370
|
+
if (variant == "warning") {
|
|
2371
|
+
setPadding(dp(12), dp(14), dp(12), dp(14))
|
|
2372
|
+
addView(mainColumn, weighted())
|
|
2373
|
+
showText(title, item.json.optString("title"), Int.MAX_VALUE)
|
|
2374
|
+
showText(subtitle, item.json.optString("message"), Int.MAX_VALUE)
|
|
2375
|
+
title.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
|
2376
|
+
subtitle.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { topMargin = dp(4) }
|
|
2377
|
+
titleLine.packsChildrenAtStart = false
|
|
2378
|
+
return
|
|
2379
|
+
}
|
|
2006
2380
|
if (variant == "spacer") {
|
|
2007
2381
|
minimumHeight = dp(item.json.optInt("height", 0))
|
|
2008
2382
|
return
|
|
@@ -2072,7 +2446,11 @@ internal class NativeListRowView(
|
|
|
2072
2446
|
spacingDp: Int = 12,
|
|
2073
2447
|
) {
|
|
2074
2448
|
leadingFrame.visibility = VISIBLE
|
|
2075
|
-
leadingFrame.layoutParams = LayoutParams(dp(sizeDp), dp(sizeDp)).apply {
|
|
2449
|
+
leadingFrame.layoutParams = LayoutParams(dp(sizeDp), dp(sizeDp)).apply {
|
|
2450
|
+
val item = tag as? NativeListItem
|
|
2451
|
+
// OneKey patch: Yoga rounds cumulative selector edges, not each 12dp gap separately.
|
|
2452
|
+
marginEnd = if (item?.json?.has("height") == true && item.json.optString("presentation") in setOf("accountSelector", "networkSelector")) dp(12 + sizeDp + spacingDp) - dp(12) - dp(sizeDp) else dp(spacingDp)
|
|
2453
|
+
}
|
|
2076
2454
|
addView(leadingFrame)
|
|
2077
2455
|
leadingFallback.layoutParams = FrameLayout.LayoutParams(dp(sizeDp), dp(sizeDp))
|
|
2078
2456
|
if (visual == null) return
|
|
@@ -2104,7 +2482,7 @@ internal class NativeListRowView(
|
|
|
2104
2482
|
leadingFrame.background = GradientDrawable().apply {
|
|
2105
2483
|
setColor(visualBackground)
|
|
2106
2484
|
setStroke(1, parseNativeListColor("#0000001F"))
|
|
2107
|
-
cornerRadius =
|
|
2485
|
+
cornerRadius = scaledDp(leadingCornerRadius(shape, sizeDp))
|
|
2108
2486
|
}
|
|
2109
2487
|
leadingIcon.iconName = visual.optString("name")
|
|
2110
2488
|
leadingIcon.tintColor = safeColor(
|
|
@@ -2161,7 +2539,89 @@ internal class NativeListRowView(
|
|
|
2161
2539
|
else -> leadingOutlineProvider(shape)
|
|
2162
2540
|
}
|
|
2163
2541
|
image.clipToOutline = true
|
|
2164
|
-
|
|
2542
|
+
val fallbackIcon = if (index == 0) visual.optJSONObject("fallbackIcon") else null
|
|
2543
|
+
val expectedEpoch = bindingEpoch
|
|
2544
|
+
bindImage(source, image, boundKey ?: "", index, variant,
|
|
2545
|
+
onLoad = if (fallbackIcon == null) null else ({
|
|
2546
|
+
if (bindingEpoch == expectedEpoch) { image.visibility = VISIBLE; leadingIcon.visibility = GONE }
|
|
2547
|
+
}),
|
|
2548
|
+
onError = if (fallbackIcon == null) null else ({
|
|
2549
|
+
if (bindingEpoch == expectedEpoch) {
|
|
2550
|
+
image.visibility = GONE
|
|
2551
|
+
leadingFallback.visibility = GONE
|
|
2552
|
+
leadingIcon.iconName = fallbackIcon.optString("name")
|
|
2553
|
+
leadingIcon.tintColor = safeColor(fallbackIcon.optString("tintColor"), parseNativeListColor("#0000009B"))
|
|
2554
|
+
leadingIcon.visibility = VISIBLE
|
|
2555
|
+
}
|
|
2556
|
+
}),
|
|
2557
|
+
)
|
|
2558
|
+
}
|
|
2559
|
+
// OneKey patch: wallet overlays retain source images, provider colors and QR text.
|
|
2560
|
+
visual.optJSONArray("overlays")?.let { overlays ->
|
|
2561
|
+
for (index in 0 until overlays.length()) {
|
|
2562
|
+
val overlay = overlays.getJSONObject(index)
|
|
2563
|
+
val size = overlay.optInt("size", 20)
|
|
2564
|
+
val inset = dp(overlay.optInt("padding", 0))
|
|
2565
|
+
val isWalletText = selectorUsesSourceScale && (tag as? NativeListItem)?.json?.optString("presentation") == "walletSidebar" && overlay.optString("text").isNotEmpty() && overlay.optJSONObject("image") == null && overlay.optString("name").isEmpty()
|
|
2566
|
+
val width = overlay.optInt("width", size)
|
|
2567
|
+
val height = overlay.optInt("height", if (isWalletText) 16 else size)
|
|
2568
|
+
val offsetX = dp(overlay.optInt("offsetX", overlay.optInt("offset", 2)))
|
|
2569
|
+
val offsetY = dp(overlay.optInt("offsetY", overlay.optInt("offset", 2)))
|
|
2570
|
+
val frame = FrameLayout(context).apply {
|
|
2571
|
+
setPadding(if (isWalletText) dp(2) else inset, if (isWalletText) 0 else inset, if (isWalletText) dp(2) else inset, if (isWalletText) 0 else inset)
|
|
2572
|
+
background = roundedFill(safeColor(overlay.optString("backgroundColor"), Color.TRANSPARENT), minOf(width, height) / 2f)
|
|
2573
|
+
outlineProvider = ViewOutlineProvider.BACKGROUND
|
|
2574
|
+
clipToOutline = true
|
|
2575
|
+
}
|
|
2576
|
+
val image = overlay.optJSONObject("image")
|
|
2577
|
+
val view = when {
|
|
2578
|
+
image != null -> OneKeyImageReusableView(reactContext).also {
|
|
2579
|
+
bindImage(image, it, boundKey ?: "", 10 + index, "generic")
|
|
2580
|
+
selectorImages.add(it)
|
|
2581
|
+
}
|
|
2582
|
+
overlay.optString("text").isNotEmpty() -> TextView(context).apply {
|
|
2583
|
+
text = overlay.optString("text")
|
|
2584
|
+
textSize = sp(if (isWalletText) 12f else 10f)
|
|
2585
|
+
typeface = if (isWalletText) NativeListFonts.regular(context) else NativeListFonts.medium(context)
|
|
2586
|
+
includeFontPadding = false
|
|
2587
|
+
gravity = Gravity.CENTER
|
|
2588
|
+
if (isWalletText) TextViewCompat.setLineHeight(this, dp(16))
|
|
2589
|
+
setTextColor(safeColor(overlay.optString("tintColor"), color(null, "secondaryText", "#0000009B")))
|
|
2590
|
+
}
|
|
2591
|
+
else -> OneKeyIconView(context).apply {
|
|
2592
|
+
iconName = overlay.optString("name")
|
|
2593
|
+
tintColor = safeColor(overlay.optString("tintColor"), parseNativeListColor("#0000009B"))
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
frame.addView(view, FrameLayout.LayoutParams(if (isWalletText && !overlay.has("width")) FrameLayout.LayoutParams.WRAP_CONTENT else FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT))
|
|
2597
|
+
val topLeft = overlay.optString("position") == "topLeft"
|
|
2598
|
+
leadingFrame.addView(frame, FrameLayout.LayoutParams(if (isWalletText && !overlay.has("width")) FrameLayout.LayoutParams.WRAP_CONTENT else dp(width), dp(height), if (topLeft) Gravity.START or Gravity.TOP else Gravity.END or Gravity.BOTTOM).apply {
|
|
2599
|
+
if (topLeft) { marginStart = -offsetX; topMargin = -offsetY } else { marginEnd = -offsetX; bottomMargin = -offsetY }
|
|
2600
|
+
})
|
|
2601
|
+
selectorViews.add(frame)
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
visual.optJSONObject("fallbackIcon")?.takeIf { sources.isEmpty() }?.let { fallbackIcon ->
|
|
2605
|
+
leadingFallback.visibility = GONE
|
|
2606
|
+
leadingIcon.visibility = VISIBLE
|
|
2607
|
+
leadingIcon.iconName = fallbackIcon.optString("name")
|
|
2608
|
+
leadingIcon.tintColor = safeColor(fallbackIcon.optString("tintColor"), parseNativeListColor("#0000009B"))
|
|
2609
|
+
if (selectorUsesSourceScale && (tag as? NativeListItem)?.json?.optString("presentation") == "walletSidebar" && leadingIcon.iconName == "PlusSmallOutline") {
|
|
2610
|
+
leadingIcon.layoutParams = FrameLayout.LayoutParams(dp(24), dp(24), Gravity.CENTER)
|
|
2611
|
+
leadingIcon.glyphSizeDp = 24
|
|
2612
|
+
}
|
|
2613
|
+
if (selectorUsesSourceScale && (tag as? NativeListItem)?.json?.optString("presentation") == "walletSidebar" && leadingIcon.iconName == "LockSolid") {
|
|
2614
|
+
leadingIcon.layoutParams = FrameLayout.LayoutParams(dp(40), dp(40), Gravity.CENTER)
|
|
2615
|
+
leadingIcon.glyphSizeDp = 40
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
if (visual.optString("borderStyle") == "dashed") {
|
|
2619
|
+
leadingFrame.background = GradientDrawable().apply {
|
|
2620
|
+
setColor(visualBackground)
|
|
2621
|
+
cornerRadius = dp(sizeDp / 2).toFloat()
|
|
2622
|
+
setStroke(dp(if (selectorUsesSourceScale && (tag as? NativeListItem)?.json?.optString("presentation") == "walletSidebar") 1 else 2), safeColor(visual.optString("borderColor"), parseNativeListColor("#00000072")), dp(4).toFloat(), dp(4).toFloat())
|
|
2623
|
+
}
|
|
2624
|
+
leadingFallback.background = null
|
|
2165
2625
|
}
|
|
2166
2626
|
}
|
|
2167
2627
|
|
|
@@ -2223,7 +2683,11 @@ internal class NativeListRowView(
|
|
|
2223
2683
|
for (index in 0 until minOf(2, accessories.length())) {
|
|
2224
2684
|
val accessory = accessories.getJSONObject(index)
|
|
2225
2685
|
when (accessory.optString("kind")) {
|
|
2226
|
-
"value" ->
|
|
2686
|
+
"value" -> {
|
|
2687
|
+
showTrailing(textIndex, accessory.optString("text"), !accessory.optBoolean("secondary", false))
|
|
2688
|
+
applyValueSegments(trailingViews[textIndex], accessory.optJSONArray("textSegments"))
|
|
2689
|
+
textIndex++
|
|
2690
|
+
}
|
|
2227
2691
|
"valuePair" -> showTrailingValuePair(textIndex++, accessory, theme)
|
|
2228
2692
|
"checkbox" -> bindCheckbox(item, accessory, checkboxState)
|
|
2229
2693
|
"radio" -> showTrailing(
|
|
@@ -2285,11 +2749,25 @@ internal class NativeListRowView(
|
|
|
2285
2749
|
return
|
|
2286
2750
|
}
|
|
2287
2751
|
checkbox.visibility = VISIBLE
|
|
2288
|
-
checkbox.setState(state,
|
|
2289
|
-
|
|
2290
|
-
|
|
2752
|
+
checkbox.setState(state, checkboxIconColor)
|
|
2753
|
+
val usesSourceCheckboxGeometry = checkboxUsesSelectorStyle && item.json.has("height")
|
|
2754
|
+
checkbox.usesSelectorGeometry = usesSourceCheckboxGeometry
|
|
2755
|
+
if (usesSourceCheckboxGeometry) {
|
|
2756
|
+
// OneKey patch: source Yoga children may round into padding; retain their complete border.
|
|
2757
|
+
clipToPadding = false
|
|
2758
|
+
// OneKey patch: even a transparent source border changes RN's background clipping path.
|
|
2759
|
+
checkbox.background = null
|
|
2760
|
+
BackgroundStyleApplicator.setBackgroundColor(checkbox, if (state == "unchecked") checkboxUncheckedColor else checkboxCheckedColor)
|
|
2761
|
+
BackgroundStyleApplicator.setBorderWidth(checkbox, LogicalEdge.ALL, 2f)
|
|
2762
|
+
BackgroundStyleApplicator.setBorderColor(checkbox, LogicalEdge.ALL, if (state == "unchecked") checkboxBorderColor else Color.TRANSPARENT)
|
|
2763
|
+
BackgroundStyleApplicator.setBorderRadius(checkbox, BorderRadiusProp.BORDER_RADIUS, LengthPercentage(4f, LengthPercentageType.POINT))
|
|
2291
2764
|
} else {
|
|
2292
|
-
|
|
2765
|
+
checkbox.background = if (state == "unchecked") {
|
|
2766
|
+
if (checkboxUsesSelectorStyle) GradientDrawable().apply { setColor(checkboxUncheckedColor); setStroke(dp(2), checkboxBorderColor); cornerRadius = scaledDp(4f) }
|
|
2767
|
+
else roundedStroke(checkboxBorderColor, checkboxUncheckedColor, 4f)
|
|
2768
|
+
} else {
|
|
2769
|
+
roundedFill(checkboxCheckedColor, 4f)
|
|
2770
|
+
}
|
|
2293
2771
|
}
|
|
2294
2772
|
// Row-level disabled opacity already applies to this child. Only apply a
|
|
2295
2773
|
// local 0.5 when the accessory alone is disabled, never 0.5 * 0.5.
|
|
@@ -2330,6 +2808,8 @@ internal class NativeListRowView(
|
|
|
2330
2808
|
}
|
|
2331
2809
|
val groupPosition = when {
|
|
2332
2810
|
item.type == "identity" && item.json.optString("presentation") == "walletSidebar" -> "single"
|
|
2811
|
+
// OneKey patch: explicit selector rows preserve the v1 ListItem corner radius.
|
|
2812
|
+
item.type == "identity" && item.json.optString("presentation") in setOf("accountSelector", "networkSelector") && item.json.has("height") -> "single"
|
|
2333
2813
|
else -> when (item.type) {
|
|
2334
2814
|
"metricCard" -> "single"
|
|
2335
2815
|
"rail" -> "rail"
|
|
@@ -2337,7 +2817,7 @@ internal class NativeListRowView(
|
|
|
2337
2817
|
}
|
|
2338
2818
|
}
|
|
2339
2819
|
var backgroundGroupPosition = if (item.type == "mediaTile") "mediaTile" else groupPosition
|
|
2340
|
-
if (layout == "sectioned") {
|
|
2820
|
+
if (layout == "sectioned" && !item.json.optBoolean("selected", false)) {
|
|
2341
2821
|
// Selection in sectioned lists is represented by the OneKey checkbox,
|
|
2342
2822
|
// matching iOS and the app-monorepo network selector.
|
|
2343
2823
|
rowBackground = color(theme, "rowBackground", "#FFFFFF")
|
|
@@ -2351,6 +2831,8 @@ internal class NativeListRowView(
|
|
|
2351
2831
|
rowBackground = color(theme, "subduedBackground", "#F9F9F9")
|
|
2352
2832
|
backgroundGroupPosition = ""
|
|
2353
2833
|
}
|
|
2834
|
+
// OneKey patch: section heading backgrounds are independent of list rows.
|
|
2835
|
+
if (item.json.has("backgroundColor")) rowBackground = safeColor(item.json.optString("backgroundColor"), rowBackground)
|
|
2354
2836
|
restingRowBackground = groupedBackground(backgroundGroupPosition, rowBackground)
|
|
2355
2837
|
background = restingRowBackground
|
|
2356
2838
|
}
|
|
@@ -2466,7 +2948,26 @@ internal class NativeListRowView(
|
|
|
2466
2948
|
val icon = trailingIcons[index]
|
|
2467
2949
|
icon.iconName = data.optString("name")
|
|
2468
2950
|
icon.tintColor = safeColor(data.optString("tintColor"), iconSubduedColor)
|
|
2469
|
-
|
|
2951
|
+
// OneKey patch: preserve the original 38dp press target around its 24dp layout slot.
|
|
2952
|
+
icon.setTag(com.facebook.react.R.id.react_test_id, data.optString("testID").takeIf { it.isNotEmpty() })
|
|
2953
|
+
icon.accessibilityDelegate = selectorAccessibilityDelegate
|
|
2954
|
+
icon.contentDescription = data.optString("accessibilityLabel").takeIf { it.isNotEmpty() }
|
|
2955
|
+
if (item.json.optString("presentation") == "accountSelector") {
|
|
2956
|
+
icon.glyphSizeDp = 24
|
|
2957
|
+
val isSourceMenu = item.json.has("height") && icon.iconName == "DotHorOutline"
|
|
2958
|
+
val size = if (isSourceMenu) 24 else if (item.json.has("height") && icon.iconName == "PlusSmallOutline") 36 else 38
|
|
2959
|
+
icon.layoutParams = LayoutParams(dp(size), dp(size)).apply {
|
|
2960
|
+
gravity = Gravity.CENTER_VERTICAL
|
|
2961
|
+
if (!isSourceMenu) {
|
|
2962
|
+
marginStart = -dp(7)
|
|
2963
|
+
marginEnd = -dp(7)
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
if (isSourceMenu) {
|
|
2967
|
+
// OneKey patch: the native ActionList trigger measures 24dp; Yoga rounds its trailing edge cumulatively.
|
|
2968
|
+
setPadding(paddingLeft, paddingTop, (12 * resources.displayMetrics.density).toInt(), paddingBottom)
|
|
2969
|
+
}
|
|
2970
|
+
} else if (icon.iconName == "ChevronRightSmallOutline") {
|
|
2470
2971
|
icon.glyphSizeDp = null
|
|
2471
2972
|
// ListItem.DrillIn is a 24dp icon with mx=-6, for a 12dp layout footprint.
|
|
2472
2973
|
icon.layoutParams = LayoutParams(dp(24), dp(24)).apply {
|
|
@@ -2516,7 +3017,7 @@ internal class NativeListRowView(
|
|
|
2516
3017
|
when (item.type) {
|
|
2517
3018
|
"message" -> 14f
|
|
2518
3019
|
"sectionHeader" -> when {
|
|
2519
|
-
isNetworkSelectorSection -> 14f
|
|
3020
|
+
isNetworkSelectorSection && item.json.optString("variant") != "summary" -> 14f
|
|
2520
3021
|
item.json.optString("variant") == "gallery" -> 18f
|
|
2521
3022
|
item.json.optString("variant") == "summary" -> 16f
|
|
2522
3023
|
currentLayout == "table" -> 11f
|
|
@@ -2530,11 +3031,14 @@ internal class NativeListRowView(
|
|
|
2530
3031
|
}
|
|
2531
3032
|
},
|
|
2532
3033
|
)
|
|
2533
|
-
title.typeface = if (
|
|
3034
|
+
title.typeface = if (isAccountSelectorAction && item.json.has("icon")) {
|
|
3035
|
+
NativeListFonts.medium(context)
|
|
3036
|
+
} else if (isWalletSidebar || isAccountSelectorIdentity || isAccountSelectorAction) {
|
|
2534
3037
|
NativeListFonts.regular(context)
|
|
2535
3038
|
} else {
|
|
2536
3039
|
when (item.type) {
|
|
2537
3040
|
"sectionHeader" -> when {
|
|
3041
|
+
isNetworkSelectorSection && item.json.has("height") && item.json.optString("variant") != "summary" && (item.json.optJSONObject("checkbox") != null || item.json.optString("titleActionKey").isEmpty()) -> NativeListFonts.semibold(context)
|
|
2538
3042
|
isNetworkSelectorSection -> NativeListFonts.medium(context)
|
|
2539
3043
|
currentLayout == "table" -> NativeListFonts.regular(context)
|
|
2540
3044
|
item.json.optString("variant") == "summary" -> NativeListFonts.medium(context)
|
|
@@ -2552,7 +3056,7 @@ internal class NativeListRowView(
|
|
|
2552
3056
|
else -> 14f
|
|
2553
3057
|
})
|
|
2554
3058
|
tertiary.textSize = sp(14f)
|
|
2555
|
-
if (isNetworkSelectorSection) {
|
|
3059
|
+
if (isNetworkSelectorSection && item.json.optString("variant") != "summary") {
|
|
2556
3060
|
TextViewCompat.setLineHeight(title, dp(20))
|
|
2557
3061
|
} else if (item.type == "sectionHeader" && item.json.optString("variant") == "gallery") {
|
|
2558
3062
|
TextViewCompat.setLineHeight(title, dp(24))
|
|
@@ -2589,11 +3093,42 @@ internal class NativeListRowView(
|
|
|
2589
3093
|
column.typeface = NativeListFonts.medium(context)
|
|
2590
3094
|
column.fontFeatureSettings = "tnum"
|
|
2591
3095
|
}
|
|
3096
|
+
// OneKey patch: exact selector row dimensions override template minimums.
|
|
3097
|
+
selectorHeight = if (item.json.has("height")) dp(item.json.optInt("height")) else null
|
|
3098
|
+
// OneKey patch: React Native's section spacers and letter blocks truncate physical heights.
|
|
3099
|
+
val isSelectorLetter = isNetworkSelectorSection && item.json.has("height") &&
|
|
3100
|
+
item.json.optString("variant") != "summary" && item.json.optString("titleActionKey").isEmpty() &&
|
|
3101
|
+
item.json.optJSONObject("checkbox") == null
|
|
3102
|
+
val isSelectorSectionSpacer = selectorUsesSourceScale && currentLayout == "sectioned" &&
|
|
3103
|
+
item.type == "system" && item.json.optString("variant") == "spacer"
|
|
3104
|
+
if (isSelectorLetter || isSelectorSectionSpacer) {
|
|
3105
|
+
selectorHeight = (item.json.optInt("height") * resources.displayMetrics.density).toInt()
|
|
3106
|
+
}
|
|
3107
|
+
if (isSelectorLetter) {
|
|
3108
|
+
val inset = (20 * resources.displayMetrics.density).toInt() - dp(8)
|
|
3109
|
+
// OneKey patch: SectionHeader has a fixed height and centered text, without vertical padding.
|
|
3110
|
+
setPadding(inset, 0, inset, 0)
|
|
3111
|
+
}
|
|
3112
|
+
if (item.json.has("height") && isNetworkSelectorSection && item.json.optString("variant") != "summary" && item.json.optString("titleActionKey").isNotEmpty() && item.json.optJSONObject("checkbox") == null) {
|
|
3113
|
+
// OneKey patch: the text-and-underline header's fractional measured height rounds up in React Native.
|
|
3114
|
+
selectorHeight = kotlin.math.ceil((item.json.optInt("height") * (if (selectorUsesSourceScale) 1f else NativeListScale.factor(resources)) * resources.displayMetrics.density).toDouble()).toInt()
|
|
3115
|
+
}
|
|
3116
|
+
// OneKey patch: an explicit per-row policy preserves each source list's measured heights.
|
|
3117
|
+
if (item.json.has("height")) {
|
|
3118
|
+
when (item.json.optString("heightRounding")) {
|
|
3119
|
+
"floor" -> selectorHeight = (item.json.optInt("height") * resources.displayMetrics.density).toInt()
|
|
3120
|
+
"nearest" -> selectorHeight = (item.json.optInt("height") * resources.displayMetrics.density).roundToInt()
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
2592
3123
|
val baseHeight = when {
|
|
3124
|
+
item.json.has("height") -> item.json.optInt("height")
|
|
2593
3125
|
item.type == "system" && item.json.optString("variant") == "spacer" -> item.json.optInt("height", 0)
|
|
2594
3126
|
item.type == "walletGroup" -> {
|
|
2595
3127
|
val childCount = item.json.optJSONArray("children")?.length() ?: 0
|
|
2596
|
-
|
|
3128
|
+
// OneKey patch: include badge heights in the group layout.
|
|
3129
|
+
// (childCount + 1) * 68 + childCount * 12
|
|
3130
|
+
val members = listOf(item.json.getJSONObject("parent")) + (0 until childCount).map { item.json.getJSONArray("children").getJSONObject(it) }
|
|
3131
|
+
members.sumOf { it.optInt("height", if ((it.optJSONArray("badges")?.length() ?: 0) > 0) 92 else 68) } + childCount * 12 + if (members.first().has("height")) 2 else 0
|
|
2597
3132
|
}
|
|
2598
3133
|
isNetworkSelectorIdentity -> 47
|
|
2599
3134
|
else -> when (item.type) {
|
|
@@ -2617,6 +3152,7 @@ internal class NativeListRowView(
|
|
|
2617
3152
|
else -> 36
|
|
2618
3153
|
}
|
|
2619
3154
|
"system" -> when (item.json.optString("variant")) {
|
|
3155
|
+
"warning" -> 0
|
|
2620
3156
|
"noMatch", "end" -> 36
|
|
2621
3157
|
"retry" -> 44
|
|
2622
3158
|
else -> 56
|
|
@@ -2636,14 +3172,14 @@ internal class NativeListRowView(
|
|
|
2636
3172
|
56
|
|
2637
3173
|
}
|
|
2638
3174
|
else -> when {
|
|
2639
|
-
item.type == "identity" && item.json.optString("presentation") == "walletSidebar" -> 68
|
|
3175
|
+
item.type == "identity" && item.json.optString("presentation") == "walletSidebar" -> if ((item.json.optJSONArray("badges")?.length() ?: 0) > 0) 92 else 68
|
|
2640
3176
|
item.type == "identity" && item.json.optString("tertiary").isNotEmpty() -> 72
|
|
2641
3177
|
item.type == "identity" && item.json.optString("subtitle").isNotEmpty() -> 60
|
|
2642
3178
|
else -> 56
|
|
2643
3179
|
}
|
|
2644
3180
|
}
|
|
2645
3181
|
}
|
|
2646
|
-
val modifier = if (isNetworkSelectorIdentity) {
|
|
3182
|
+
val modifier = if (item.json.has("height") || isNetworkSelectorIdentity) {
|
|
2647
3183
|
0
|
|
2648
3184
|
} else if (
|
|
2649
3185
|
item.type == "sectionHeader" && item.json.optString("variant") in listOf("summary", "gallery")
|
|
@@ -2713,7 +3249,12 @@ internal class NativeListRowView(
|
|
|
2713
3249
|
override fun getOutline(view: View, outline: Outline) {
|
|
2714
3250
|
when (shape) {
|
|
2715
3251
|
"square" -> outline.setRect(0, 0, view.width, view.height)
|
|
2716
|
-
"rounded" ->
|
|
3252
|
+
"rounded" -> {
|
|
3253
|
+
// OneKey patch: the account avatar has an 8-point radius at its 32-point size.
|
|
3254
|
+
// outline.setRoundRect(0, 0, view.width, view.height, dp(10).toFloat())
|
|
3255
|
+
val radius = if ((tag as? NativeListItem)?.json?.optString("presentation") == "accountSelector" && (tag as? NativeListItem)?.json?.has("height") == true) dp(8).toFloat() else dp(10).toFloat()
|
|
3256
|
+
outline.setRoundRect(0, 0, view.width, view.height, radius)
|
|
3257
|
+
}
|
|
2717
3258
|
else -> outline.setOval(0, 0, view.width, view.height)
|
|
2718
3259
|
}
|
|
2719
3260
|
}
|
|
@@ -2742,7 +3283,13 @@ internal class NativeListRowView(
|
|
|
2742
3283
|
token: String,
|
|
2743
3284
|
slot: Int,
|
|
2744
3285
|
variant: String,
|
|
3286
|
+
onLoad: (() -> Unit)? = null,
|
|
3287
|
+
onError: (() -> Unit)? = null,
|
|
3288
|
+
retryAttempt: Int = 0,
|
|
2745
3289
|
) {
|
|
3290
|
+
selectorImageRetries.remove(imageView)?.let(imageView::removeCallbacks)
|
|
3291
|
+
val expectedEpoch = bindingEpoch
|
|
3292
|
+
val retryLimit = source.optInt("retryTimes", 0).coerceAtLeast(0)
|
|
2746
3293
|
val uri = source.optString("uri").trim().takeIf(String::isNotEmpty)
|
|
2747
3294
|
imageView.configure(
|
|
2748
3295
|
sourceUri = uri,
|
|
@@ -2751,17 +3298,39 @@ internal class NativeListRowView(
|
|
|
2751
3298
|
contentFit = source.optString("contentFit", "cover"),
|
|
2752
3299
|
cachePolicy = source.optString("cachePolicy", "memory-disk"),
|
|
2753
3300
|
autoplay = source.optBoolean("autoplay", false),
|
|
2754
|
-
recyclingKey = "$token:$slot",
|
|
2755
|
-
optimizeTos = source.optBoolean("optimizeTos", true),
|
|
3301
|
+
recyclingKey = if (retryAttempt == 0) "$token:$slot" else "$token:$slot:retry:$retryAttempt",
|
|
3302
|
+
optimizeTos = retryAttempt == 0 && source.optBoolean("optimizeTos", true),
|
|
2756
3303
|
overscan = source.optDouble("overscan", 1.1),
|
|
2757
3304
|
loadingStrategy = source.optString("loadingStrategy", "static"),
|
|
3305
|
+
onLoad = if (retryLimit == 0) onLoad else ({
|
|
3306
|
+
if (bindingEpoch == expectedEpoch) {
|
|
3307
|
+
selectorImageRetries.remove(imageView)?.let(imageView::removeCallbacks)
|
|
3308
|
+
onLoad?.invoke()
|
|
3309
|
+
}
|
|
3310
|
+
}),
|
|
3311
|
+
onError = if (retryLimit == 0) onError else ({
|
|
3312
|
+
if (bindingEpoch == expectedEpoch) {
|
|
3313
|
+
if (retryAttempt >= retryLimit) onError?.invoke()
|
|
3314
|
+
else if (!selectorImageRetries.containsKey(imageView)) {
|
|
3315
|
+
val retry = Runnable {
|
|
3316
|
+
if (bindingEpoch == expectedEpoch) {
|
|
3317
|
+
selectorImageRetries.remove(imageView)
|
|
3318
|
+
bindImage(source, imageView, token, slot, variant, onLoad, onError, retryAttempt + 1)
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
selectorImageRetries[imageView] = retry
|
|
3322
|
+
imageView.postDelayed(retry, kotlin.random.Random.nextLong(3) * 1000L)
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
}),
|
|
2758
3326
|
)
|
|
2759
3327
|
}
|
|
2760
3328
|
|
|
2761
3329
|
private fun weighted() = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)
|
|
2762
3330
|
private fun wrap() = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
|
2763
|
-
private fun dp(value: Int): Int = NativeListScale.dp(resources, value)
|
|
2764
|
-
private fun
|
|
3331
|
+
private fun dp(value: Int): Int = if (selectorUsesSourceScale) (value * resources.displayMetrics.density).roundToInt() else NativeListScale.dp(resources, value)
|
|
3332
|
+
private fun scaledDp(value: Float): Float = if (selectorUsesSourceScale) (value * resources.displayMetrics.density).roundToInt().toFloat() else NativeListScale.dp(resources, value)
|
|
3333
|
+
private fun sp(value: Float): Float = if (selectorUsesSourceScale) value else NativeListScale.font(resources, value)
|
|
2765
3334
|
|
|
2766
3335
|
private fun color(theme: JSONObject?, key: String, fallback: String): Int =
|
|
2767
3336
|
safeColor(theme?.optString(key, fallback), parseNativeListColor(fallback))
|
|
@@ -2774,30 +3343,30 @@ internal class NativeListRowView(
|
|
|
2774
3343
|
|
|
2775
3344
|
private fun roundedFill(color: Int, radiusDp: Float) = GradientDrawable().apply {
|
|
2776
3345
|
setColor(color)
|
|
2777
|
-
cornerRadius =
|
|
3346
|
+
cornerRadius = scaledDp(radiusDp)
|
|
2778
3347
|
}
|
|
2779
3348
|
|
|
2780
3349
|
private fun roundedStroke(stroke: Int, fill: Int, radiusDp: Float) = GradientDrawable().apply {
|
|
2781
3350
|
setColor(fill)
|
|
2782
3351
|
setStroke(dp(2), stroke)
|
|
2783
|
-
cornerRadius =
|
|
3352
|
+
cornerRadius = scaledDp(radiusDp)
|
|
2784
3353
|
}
|
|
2785
3354
|
|
|
2786
3355
|
private fun roundedHairlineStroke(stroke: Int, radiusDp: Float) = GradientDrawable().apply {
|
|
2787
3356
|
setColor(Color.TRANSPARENT)
|
|
2788
3357
|
setStroke(1, stroke)
|
|
2789
|
-
cornerRadius =
|
|
3358
|
+
cornerRadius = scaledDp(radiusDp)
|
|
2790
3359
|
}
|
|
2791
3360
|
|
|
2792
3361
|
private fun groupedBackground(position: String, color: Int) = GradientDrawable().apply {
|
|
2793
3362
|
setColor(color)
|
|
2794
|
-
val radius =
|
|
3363
|
+
val radius = scaledDp(12f)
|
|
2795
3364
|
cornerRadii = when (position) {
|
|
2796
3365
|
"first" -> floatArrayOf(radius, radius, radius, radius, 0f, 0f, 0f, 0f)
|
|
2797
3366
|
"last" -> floatArrayOf(0f, 0f, 0f, 0f, radius, radius, radius, radius)
|
|
2798
3367
|
"single" -> FloatArray(8) { radius }
|
|
2799
|
-
"rail" -> FloatArray(8) {
|
|
2800
|
-
"mediaTile" -> FloatArray(8) {
|
|
3368
|
+
"rail" -> FloatArray(8) { scaledDp(8f) }
|
|
3369
|
+
"mediaTile" -> FloatArray(8) { scaledDp(16f) }
|
|
2801
3370
|
else -> FloatArray(8)
|
|
2802
3371
|
}
|
|
2803
3372
|
}
|
|
@@ -2805,6 +3374,7 @@ internal class NativeListRowView(
|
|
|
2805
3374
|
}
|
|
2806
3375
|
|
|
2807
3376
|
private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
3377
|
+
var useSourceScale = false
|
|
2808
3378
|
var iconName: String = ""
|
|
2809
3379
|
set(value) {
|
|
2810
3380
|
field = value
|
|
@@ -2828,9 +3398,11 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
2828
3398
|
val pathData = iconPaths[iconName] ?: return
|
|
2829
3399
|
val drawSize = minOf(
|
|
2830
3400
|
minOf(width, height).toFloat(),
|
|
2831
|
-
glyphSizeDp?.let { NativeListScale.dp(resources, it).toFloat() } ?: Float.MAX_VALUE,
|
|
3401
|
+
glyphSizeDp?.let { if (useSourceScale) (it * resources.displayMetrics.density).roundToInt().toFloat() else NativeListScale.dp(resources, it).toFloat() } ?: Float.MAX_VALUE,
|
|
2832
3402
|
)
|
|
2833
|
-
|
|
3403
|
+
// OneKey patch: custom account-error artwork uses an 18-point viewBox.
|
|
3404
|
+
// val scale = drawSize / 24f
|
|
3405
|
+
val scale = drawSize / (selectorIconViewBoxes[iconName] ?: 24f)
|
|
2834
3406
|
fill.color = tintColor
|
|
2835
3407
|
canvas.save()
|
|
2836
3408
|
canvas.translate((width - drawSize) / 2f, (height - drawSize) / 2f)
|
|
@@ -2839,6 +3411,8 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
2839
3411
|
pathData.forEachIndexed { index, data ->
|
|
2840
3412
|
PathParser.createPathFromPathData(data)?.let { path ->
|
|
2841
3413
|
path.fillType = sourceFillTypes?.getOrNull(index) ?: Path.FillType.EVEN_ODD
|
|
3414
|
+
// OneKey patch: provider illustration colors are part of their source asset.
|
|
3415
|
+
fill.color = selectorIconColors[iconName]?.getOrNull(index) ?: tintColor
|
|
2842
3416
|
canvas.drawPath(path, fill)
|
|
2843
3417
|
}
|
|
2844
3418
|
}
|
|
@@ -2846,10 +3420,42 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
2846
3420
|
}
|
|
2847
3421
|
|
|
2848
3422
|
companion object {
|
|
3423
|
+
// OneKey patch: preserve provider colors and non-24 viewBoxes.
|
|
3424
|
+
private val selectorIconColors: Map<String, List<Int?>> = mapOf(
|
|
3425
|
+
"GlobusOutline" to listOf(null),
|
|
3426
|
+
"LockSolid" to listOf(null),
|
|
3427
|
+
"GoogleIllus" to listOf(parseNativeListColor("#4285F4"), parseNativeListColor("#34A853"), parseNativeListColor("#FBBC05"), parseNativeListColor("#EA4335")),
|
|
3428
|
+
"AppleBrand" to listOf(null),
|
|
3429
|
+
"BotIllus" to listOf(parseNativeListColor("#8897A5"), parseNativeListColor("#3FA9F5"), parseNativeListColor("#8897A5"), parseNativeListColor("#8897A5"), parseNativeListColor("#10243E"), parseNativeListColor("#10243E"), parseNativeListColor("#10243E")),
|
|
3430
|
+
"AllNetworksSolid" to listOf(null, null),
|
|
3431
|
+
"CrossedSmallSolid" to listOf(null),
|
|
3432
|
+
"AccountErrorCustom" to listOf(Color.argb(0x72, 0, 0, 0), Color.argb(0x72, 0, 0, 0)),
|
|
3433
|
+
"Circle" to listOf(null),
|
|
3434
|
+
)
|
|
3435
|
+
private val selectorIconViewBoxes = mapOf(
|
|
3436
|
+
"GlobusOutline" to 24f,
|
|
3437
|
+
"LockSolid" to 24f,
|
|
3438
|
+
"GoogleIllus" to 24f,
|
|
3439
|
+
"AppleBrand" to 16f,
|
|
3440
|
+
"BotIllus" to 24f,
|
|
3441
|
+
"AllNetworksSolid" to 24f,
|
|
3442
|
+
"CrossedSmallSolid" to 24f,
|
|
3443
|
+
"AccountErrorCustom" to 18f,
|
|
3444
|
+
"Circle" to 24f,
|
|
3445
|
+
)
|
|
2849
3446
|
// Keep the SVG fill-rule used by each Action-row source path. React Native
|
|
2850
3447
|
// SVG defaults to nonzero (WINDING); only paths declaring fillRule="evenodd"
|
|
2851
3448
|
// use EVEN_ODD. Other existing icons retain their prior rendering behavior.
|
|
2852
3449
|
private val actionIconFillTypes = mapOf(
|
|
3450
|
+
"GlobusOutline" to listOf(Path.FillType.EVEN_ODD),
|
|
3451
|
+
"LockSolid" to listOf(Path.FillType.EVEN_ODD),
|
|
3452
|
+
"GoogleIllus" to listOf(Path.FillType.WINDING, Path.FillType.WINDING, Path.FillType.WINDING, Path.FillType.WINDING),
|
|
3453
|
+
"AppleBrand" to listOf(Path.FillType.WINDING),
|
|
3454
|
+
"BotIllus" to listOf(Path.FillType.WINDING, Path.FillType.WINDING, Path.FillType.WINDING, Path.FillType.WINDING, Path.FillType.WINDING, Path.FillType.WINDING, Path.FillType.WINDING),
|
|
3455
|
+
"AllNetworksSolid" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
3456
|
+
"CrossedSmallSolid" to listOf(Path.FillType.WINDING),
|
|
3457
|
+
"AccountErrorCustom" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
3458
|
+
"Circle" to listOf(Path.FillType.WINDING),
|
|
2853
3459
|
"ChevronRightSmallOutline" to listOf(Path.FillType.WINDING),
|
|
2854
3460
|
"MinusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
2855
3461
|
"PlusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
@@ -2867,6 +3473,16 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
2867
3473
|
|
|
2868
3474
|
// Exact 24x24 paths from app-monorepo packages/components Icon sources.
|
|
2869
3475
|
private val iconPaths = mapOf(
|
|
3476
|
+
// OneKey patch: official selector SVG path geometry.
|
|
3477
|
+
"GlobusOutline" to listOf("M12 2c5.185 0 9.448 3.947 9.95 9H22v2h-.05c-.502 5.053-4.765 9-9.95 9s-9.448-3.947-9.95-9H2v-2h.05C2.552 5.947 6.815 2 12 2M9.523 13c.09 1.982.438 3.726.934 5.002.29.746.612 1.282.917 1.614.304.331.517.384.626.384s.322-.053.626-.384c.305-.332.627-.868.917-1.614.496-1.276.845-3.02.934-5.002zm-5.459 0a8 8 0 0 0 4.8 6.36 10 10 0 0 1-.271-.633C7.994 17.187 7.61 15.189 7.52 13zm12.416 0c-.09 2.189-.474 4.187-1.073 5.727a10 10 0 0 1-.271.633 8 8 0 0 0 4.8-6.36zM8.863 4.639A8 8 0 0 0 4.064 11h3.457c.09-2.189.473-4.187 1.072-5.727q.127-.327.27-.634M12 4c-.109 0-.322.053-.626.384-.305.332-.627.868-.917 1.614-.496 1.276-.844 3.02-.934 5.002h4.954c-.09-1.982-.438-3.726-.934-5.002-.29-.746-.612-1.282-.917-1.614C12.322 4.053 12.109 4 12 4m3.136.639q.144.307.271.634c.599 1.54.982 3.538 1.073 5.727h3.456a8 8 0 0 0-4.8-6.361"),
|
|
3478
|
+
"LockSolid" to listOf("M12 2a5 5 0 0 1 5 5v2h3v13H4V9h3V7a5 5 0 0 1 5-5m-1 11v5h2v-5zm1-9a3 3 0 0 0-3 3v2h6V7a3 3 0 0 0-3-3"),
|
|
3479
|
+
"GoogleIllus" to listOf("M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09", "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23", "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22z", "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53"),
|
|
3480
|
+
"AppleBrand" to listOf("M11.67.834c.117 1.074-.315 2.153-.955 2.928-.64.773-1.692 1.378-2.718 1.298-.14-1.054.38-2.151.971-2.836C9.63 1.45 10.746.872 11.67.834M14.994 7.093c-.176.108-1.992 1.224-1.972 3.482.025 2.769 2.428 3.693 2.46 3.705l-.004.015a10.1 10.1 0 0 1-1.264 2.593c-.764 1.116-1.556 2.229-2.806 2.254-.598.011-1-.162-1.416-.343-.437-.19-.891-.386-1.609-.386-.751 0-1.226.203-1.683.398-.397.169-.78.333-1.32.354-1.208.047-2.124-1.207-2.895-2.32C.909 14.57-.294 10.414 1.322 7.612c.803-1.395 2.237-2.275 3.794-2.298.671-.014 1.32.244 1.89.47.434.172.821.326 1.135.326.282 0 .659-.149 1.099-.323.692-.273 1.539-.607 2.41-.518.599.026 2.276.24 3.354 1.818z"),
|
|
3481
|
+
"BotIllus" to listOf("M11 2a1 1 0 1 1 2 0v1.8l1.6 1.6a1 1 0 1 1-1.4 1.4L12 5.6l-1.2 1.2a1 1 0 0 1-1.4-1.4L11 3.8z", "M8.0 6.0h8.0a5.0 5.0 0 0 1 5.0 5.0v4.0a5.0 5.0 0 0 1 -5.0 5.0h-8.0a5.0 5.0 0 0 1 -5.0 -5.0v-4.0a5.0 5.0 0 0 1 5.0 -5.0z", "M3.0 10.0h0.0a1.5 1.5 0 0 1 1.5 1.5v3.0a1.5 1.5 0 0 1 -1.5 1.5h0.0a1.5 1.5 0 0 1 -1.5 -1.5v-3.0a1.5 1.5 0 0 1 1.5 -1.5z", "M21.0 10.0h0.0a1.5 1.5 0 0 1 1.5 1.5v3.0a1.5 1.5 0 0 1 -1.5 1.5h0.0a1.5 1.5 0 0 1 -1.5 -1.5v-3.0a1.5 1.5 0 0 1 1.5 -1.5z", "M7.5 12.0a1.5 1.5 0 1 0 3.0 0a1.5 1.5 0 1 0 -3.0 0", "M13.5 12.0a1.5 1.5 0 1 0 3.0 0a1.5 1.5 0 1 0 -3.0 0", "M8.5 15.4c.9.8 2.08 1.2 3.5 1.2s2.6-.4 3.5-1.2c.24-.2.6-.18.8.06.2.23.17.6-.06.8-1.14.98-2.58 1.46-4.24 1.46s-3.1-.48-4.24-1.46a.58.58 0 0 1-.06-.8c.2-.24.56-.26.8-.06"),
|
|
3482
|
+
"AllNetworksSolid" to listOf("M15.333 13.998a1.335 1.335 0 1 1 0 2.67 1.335 1.335 0 0 1 0-2.67", "M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0M8 12.668A2 2 0 0 0 6 14.666V16c0 1.103.895 1.997 1.998 1.998h1.334A2 2 0 0 0 11.33 16v-1.334a2 2 0 0 0-1.998-1.998zm7.333 0a2.665 2.665 0 1 0 0 5.33 2.665 2.665 0 0 0 0-5.33M7.999 6.001A2 2 0 0 0 6.001 8v1.334c0 1.103.895 1.998 1.998 1.998h1.334a2 2 0 0 0 1.998-1.998V7.999a2 2 0 0 0-1.998-1.998zm6.667 0A2 2 0 0 0 12.668 8v1.334c0 1.103.895 1.998 1.998 1.998H16a2 2 0 0 0 1.998-1.998V7.999A2 2 0 0 0 16 6.001z"),
|
|
3483
|
+
"CrossedSmallSolid" to listOf("M17.87 8.25 14.12 12l3.75 3.75-2.12 2.121-3.75-3.75-3.75 3.75-2.121-2.121L9.879 12l-3.75-3.75 2.12-2.121L12 9.879l3.75-3.75 2.122 2.121Z"),
|
|
3484
|
+
"AccountErrorCustom" to listOf("M12.5 12.75a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5", "M0 3.5A3.5 3.5 0 0 1 3.5 0h8.088A2.41 2.41 0 0 1 14 2.412V5h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H4a4 4 0 0 1-4-4zm2 3.163V14a2 2 0 0 0 2 2h11a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H3.5c-.537 0-1.045-.12-1.5-.337M2 3.5A1.5 1.5 0 0 0 3.5 5H12V2.412A.41.41 0 0 0 11.588 2H3.5A1.5 1.5 0 0 0 2 3.5"),
|
|
3485
|
+
"Circle" to listOf("M0 12a12 12 0 1 0 24 0a12 12 0 1 0 -24 0"),
|
|
2870
3486
|
"ArrowBottomOutline" to listOf("m13 17.586 5-5L19.414 14 12 21.414 4.586 14 6 12.586l5 5V3h2z"),
|
|
2871
3487
|
"ArrowTopOutline" to listOf("M19.414 10 18 11.414l-5-5V21h-2V6.414l-5 5L4.586 10 12 2.586z"),
|
|
2872
3488
|
"ChartTrendingUpOutline" to listOf("M22 13h-2V9.414l-7 7-4-4-6 6L1.586 17 9 9.586l4 4L18.586 8H15V6h7z"),
|
|
@@ -2910,6 +3526,7 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
2910
3526
|
}
|
|
2911
3527
|
|
|
2912
3528
|
private class OneKeyCheckboxView(context: android.content.Context) : View(context) {
|
|
3529
|
+
var usesSelectorGeometry = false
|
|
2913
3530
|
private val glyphPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
|
|
2914
3531
|
private var state = "unchecked"
|
|
2915
3532
|
|
|
@@ -2926,9 +3543,11 @@ private class OneKeyCheckboxView(context: android.content.Context) : View(contex
|
|
|
2926
3543
|
"indeterminate" -> "M4 8a1 1 0 0 1 1-1h6a1 1 0 0 1 0 2H5a1 1 0 0 1-1-1"
|
|
2927
3544
|
else -> return
|
|
2928
3545
|
}
|
|
2929
|
-
|
|
3546
|
+
// OneKey patch: the source icon is a 16dp child after the 2dp border, not 80% of a rounded frame.
|
|
3547
|
+
val drawSize = if (usesSelectorGeometry) (16 * resources.displayMetrics.density).roundToInt().toFloat() else minOf(width, height) * 0.8f
|
|
3548
|
+
val borderOffset = (2 * resources.displayMetrics.density).roundToInt().toFloat()
|
|
2930
3549
|
canvas.save()
|
|
2931
|
-
canvas.translate((width - drawSize) / 2f, (height - drawSize) / 2f)
|
|
3550
|
+
canvas.translate(if (usesSelectorGeometry) borderOffset else (width - drawSize) / 2f, if (usesSelectorGeometry) borderOffset else (height - drawSize) / 2f)
|
|
2932
3551
|
canvas.scale(drawSize / 16f, drawSize / 16f)
|
|
2933
3552
|
PathParser.createPathFromPathData(pathData)?.let { canvas.drawPath(it, glyphPaint) }
|
|
2934
3553
|
canvas.restore()
|