@onekeyfe/react-native-native-list 3.0.112 → 3.0.113
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/README.md +57 -3
- package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +17 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +543 -26
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +26 -2
- package/ios/NativeListCell.swift +504 -20
- package/ios/NativeListDesignAssets.swift +8 -0
- package/ios/RNCNativeListView.swift +85 -7
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/Contents.json +1 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/icon.svg +1 -0
- package/lib/module/validation.js +129 -1
- package/lib/module/web/NativeListWebEngine.js +357 -5
- package/lib/typescript/src/models.d.ts +82 -2
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +26 -1
- package/package.json +2 -2
- package/src/models.ts +121 -3
- package/src/validation.ts +206 -0
- package/src/web/NativeListWebEngine.ts +578 -4
|
@@ -6,12 +6,18 @@ import android.animation.TimeInterpolator
|
|
|
6
6
|
import android.animation.ValueAnimator
|
|
7
7
|
import android.graphics.Color
|
|
8
8
|
import android.graphics.Canvas
|
|
9
|
+
import android.graphics.LinearGradient
|
|
10
|
+
import android.graphics.Matrix
|
|
9
11
|
import android.graphics.Outline
|
|
10
12
|
import android.graphics.Paint
|
|
11
13
|
import android.graphics.Path
|
|
14
|
+
import android.graphics.RectF
|
|
15
|
+
import android.graphics.Shader
|
|
12
16
|
import android.graphics.Typeface
|
|
13
17
|
import android.graphics.drawable.Drawable
|
|
14
18
|
import android.graphics.drawable.GradientDrawable
|
|
19
|
+
import android.os.Handler
|
|
20
|
+
import android.os.Looper
|
|
15
21
|
import android.text.Spannable
|
|
16
22
|
import android.text.SpannableStringBuilder
|
|
17
23
|
import android.text.TextUtils
|
|
@@ -24,6 +30,7 @@ import android.view.MotionEvent
|
|
|
24
30
|
import android.view.View
|
|
25
31
|
import android.view.ViewGroup
|
|
26
32
|
import android.view.ViewOutlineProvider
|
|
33
|
+
import android.view.animation.LinearInterpolator
|
|
27
34
|
import android.widget.FrameLayout
|
|
28
35
|
import android.widget.LinearLayout
|
|
29
36
|
import android.widget.ProgressBar
|
|
@@ -42,6 +49,69 @@ import org.json.JSONArray
|
|
|
42
49
|
import org.json.JSONObject
|
|
43
50
|
import kotlin.math.roundToInt
|
|
44
51
|
|
|
52
|
+
// Market/TokenListSkeleton: source geometry and the native Skeleton's 3s shimmer.
|
|
53
|
+
private class NativeListMarketSkeleton(context: android.content.Context, backgroundColor: Int) : View(context) {
|
|
54
|
+
private val marks = Array(5) { RectF() }
|
|
55
|
+
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
56
|
+
private val matrix = Matrix()
|
|
57
|
+
private val shaders = arrayOfNulls<LinearGradient>(5)
|
|
58
|
+
private val dark = Color.red(backgroundColor) * 0.299 + Color.green(backgroundColor) * 0.587 + Color.blue(backgroundColor) * 0.114 < 128
|
|
59
|
+
private val colors = intArrayOf(
|
|
60
|
+
Color.parseColor(if (dark) "#111111" else "#FAFAFA"),
|
|
61
|
+
Color.parseColor(if (dark) "#333333" else "#CDCDCD"),
|
|
62
|
+
Color.parseColor(if (dark) "#111111" else "#FAFAFA"),
|
|
63
|
+
)
|
|
64
|
+
private var phase = 0f
|
|
65
|
+
private val animator = ValueAnimator.ofFloat(0f, 1f).apply {
|
|
66
|
+
duration = 3000
|
|
67
|
+
repeatCount = ValueAnimator.INFINITE
|
|
68
|
+
interpolator = LinearInterpolator()
|
|
69
|
+
addUpdateListener { phase = it.animatedValue as Float; invalidate() }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private fun dp(value: Float) = NativeListScale.dp(resources, value)
|
|
73
|
+
|
|
74
|
+
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
75
|
+
super.onSizeChanged(w, h, oldw, oldh)
|
|
76
|
+
marks[0].set(0f, 0f, dp(32f), dp(32f))
|
|
77
|
+
marks[1].set(dp(44f), 0f, dp(124f), dp(16f))
|
|
78
|
+
marks[2].set(dp(44f), dp(20f), dp(104f), dp(32f))
|
|
79
|
+
marks[3].set(w - dp(168f), dp(7f), w - dp(88f), dp(25f))
|
|
80
|
+
marks[4].set(w - dp(80f), dp(7f), w.toFloat(), dp(25f))
|
|
81
|
+
marks.forEachIndexed { index, mark ->
|
|
82
|
+
shaders[index] = LinearGradient(0f, 0f, mark.width(), 0f, colors, floatArrayOf(0f, 0.5f, 1f), Shader.TileMode.CLAMP)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
override fun onDraw(canvas: Canvas) {
|
|
87
|
+
super.onDraw(canvas)
|
|
88
|
+
marks.forEachIndexed { index, mark ->
|
|
89
|
+
matrix.setTranslate(mark.left - mark.width() + phase * mark.width() * 3f, 0f)
|
|
90
|
+
shaders[index]?.setLocalMatrix(matrix)
|
|
91
|
+
paint.shader = shaders[index]
|
|
92
|
+
val radius = dp(if (index == 0) 16f else 8f)
|
|
93
|
+
canvas.drawRoundRect(mark, radius, radius, paint)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
override fun onAttachedToWindow() {
|
|
98
|
+
super.onAttachedToWindow()
|
|
99
|
+
if (windowVisibility == VISIBLE) animator.start()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
override fun onDetachedFromWindow() {
|
|
103
|
+
animator.cancel()
|
|
104
|
+
super.onDetachedFromWindow()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
override fun onWindowVisibilityChanged(visibility: Int) {
|
|
108
|
+
super.onWindowVisibilityChanged(visibility)
|
|
109
|
+
if (visibility == VISIBLE && isAttachedToWindow) {
|
|
110
|
+
if (!animator.isStarted) animator.start()
|
|
111
|
+
} else animator.cancel()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
45
115
|
internal data class NativeListActionOrigin(
|
|
46
116
|
val sourceView: View,
|
|
47
117
|
val ownerRowView: NativeListRowView,
|
|
@@ -147,26 +217,25 @@ private class PackedTitleLineLayout(context: android.content.Context) : LinearLa
|
|
|
147
217
|
}
|
|
148
218
|
|
|
149
219
|
val title = getChildAt(0)
|
|
150
|
-
val badge = getChildAt(1)
|
|
151
220
|
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
|
152
221
|
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
|
|
153
|
-
|
|
222
|
+
var accessoryWidth = 0
|
|
223
|
+
for (index in 1 until childCount) {
|
|
224
|
+
val child = getChildAt(index)
|
|
225
|
+
if (child.visibility == GONE) continue
|
|
154
226
|
measureChildWithMargins(
|
|
155
|
-
|
|
227
|
+
child,
|
|
156
228
|
widthMeasureSpec,
|
|
157
|
-
paddingLeft + paddingRight,
|
|
229
|
+
paddingLeft + paddingRight + accessoryWidth,
|
|
158
230
|
heightMeasureSpec,
|
|
159
231
|
paddingTop + paddingBottom,
|
|
160
232
|
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
val badgeMargins = badge.layoutParams as MarginLayoutParams
|
|
164
|
-
val badgeWidth = if (badge.visibility == GONE) 0 else {
|
|
165
|
-
badge.measuredWidth + badgeMargins.leftMargin + badgeMargins.rightMargin
|
|
233
|
+
val margins = child.layoutParams as MarginLayoutParams
|
|
234
|
+
accessoryWidth += child.measuredWidth + margins.leftMargin + margins.rightMargin
|
|
166
235
|
}
|
|
167
236
|
val titleMargins = title.layoutParams as MarginLayoutParams
|
|
168
237
|
if (widthMode != MeasureSpec.UNSPECIFIED) {
|
|
169
|
-
(title as TextView).maxWidth = (widthSize - paddingLeft - paddingRight -
|
|
238
|
+
(title as TextView).maxWidth = (widthSize - paddingLeft - paddingRight - accessoryWidth -
|
|
170
239
|
titleMargins.leftMargin - titleMargins.rightMargin).coerceAtLeast(0)
|
|
171
240
|
}
|
|
172
241
|
(title.layoutParams as LayoutParams).apply {
|
|
@@ -362,6 +431,10 @@ internal class NativeListRowView(
|
|
|
362
431
|
private val status = TextView(context)
|
|
363
432
|
private val metricSubtitle = TextView(context)
|
|
364
433
|
private val badgeLine = TextView(context)
|
|
434
|
+
private val marketBadgeViews = List(3) { LinearLayout(context) }
|
|
435
|
+
private val marketBadgeLabels = List(3) { TextView(context) }
|
|
436
|
+
private val marketBadgeImages = List(3) { OneKeyImageReusableView(reactContext) }
|
|
437
|
+
private val marketBadgeGlyphs = List(3) { OneKeyIconView(context) }
|
|
365
438
|
private val activityContentRow = LinearLayout(context)
|
|
366
439
|
private val actionLine = LinearLayout(context)
|
|
367
440
|
private val actionViews = List(3) { TextView(context) }
|
|
@@ -402,6 +475,11 @@ internal class NativeListRowView(
|
|
|
402
475
|
private var pressedRowBackground: Drawable? = null
|
|
403
476
|
// OneKey patch: preserve a held row independently from RecyclerView snapshot rebinding.
|
|
404
477
|
private var touchPressed = false
|
|
478
|
+
private val marketLongPressHandler = Handler(Looper.getMainLooper())
|
|
479
|
+
private var marketLongPressRunnable: Runnable? = null
|
|
480
|
+
private var marketTouchStartX = 0f
|
|
481
|
+
private var marketTouchStartY = 0f
|
|
482
|
+
private var marketLongPressFired = false
|
|
405
483
|
private var reorderActive = false
|
|
406
484
|
private var checkboxCheckedColor = Color.rgb(32, 32, 32)
|
|
407
485
|
private var checkboxUncheckedColor = Color.rgb(252, 252, 252)
|
|
@@ -466,6 +544,23 @@ internal class NativeListRowView(
|
|
|
466
544
|
titleLine.gravity = Gravity.CENTER_VERTICAL
|
|
467
545
|
titleLine.addView(title, LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f))
|
|
468
546
|
titleLine.addView(badgeLine, wrap())
|
|
547
|
+
marketBadgeViews.forEachIndexed { index, badge ->
|
|
548
|
+
badge.orientation = HORIZONTAL
|
|
549
|
+
badge.gravity = Gravity.CENTER_VERTICAL
|
|
550
|
+
badge.isClickable = true
|
|
551
|
+
marketBadgeGlyphs[index].visibility = GONE
|
|
552
|
+
marketBadgeImages[index].visibility = GONE
|
|
553
|
+
marketBadgeLabels[index].apply {
|
|
554
|
+
includeFontPadding = false
|
|
555
|
+
maxLines = 1
|
|
556
|
+
ellipsize = TextUtils.TruncateAt.END
|
|
557
|
+
textSize = sp(11f)
|
|
558
|
+
typeface = NativeListFonts.medium(context)
|
|
559
|
+
}
|
|
560
|
+
badge.addView(marketBadgeGlyphs[index], LayoutParams(dp(16), dp(16)))
|
|
561
|
+
badge.addView(marketBadgeImages[index], LayoutParams(dp(14), dp(14)))
|
|
562
|
+
badge.addView(marketBadgeLabels[index], wrap())
|
|
563
|
+
}
|
|
469
564
|
mainColumn.addView(titleLine)
|
|
470
565
|
mainColumn.addView(subtitle)
|
|
471
566
|
mainColumn.addView(tertiary)
|
|
@@ -510,19 +605,47 @@ internal class NativeListRowView(
|
|
|
510
605
|
when (event.actionMasked) {
|
|
511
606
|
MotionEvent.ACTION_DOWN -> if (isEnabled) {
|
|
512
607
|
touchPressed = true
|
|
608
|
+
marketLongPressFired = false
|
|
609
|
+
val item = tag as? NativeListItem
|
|
610
|
+
if (item?.type == "market") {
|
|
611
|
+
marketTouchStartX = event.x
|
|
612
|
+
marketTouchStartY = event.y
|
|
613
|
+
item.json.optString("pressInActionKey").takeIf(String::isNotEmpty)?.let { actionKey ->
|
|
614
|
+
emitAction(item, actionKey, null, this, "row")
|
|
615
|
+
}
|
|
616
|
+
item.json.optString("longPressActionKey").takeIf(String::isNotEmpty)?.let { actionKey ->
|
|
617
|
+
val expectedKey = item.key
|
|
618
|
+
val runnable = Runnable {
|
|
619
|
+
val current = tag as? NativeListItem
|
|
620
|
+
if (touchPressed && current?.key == expectedKey && current.type == "market") {
|
|
621
|
+
marketLongPressRunnable = null
|
|
622
|
+
marketLongPressFired = true
|
|
623
|
+
emitAction(current, actionKey, null, this, "row")
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
marketLongPressRunnable = runnable
|
|
627
|
+
marketLongPressHandler.postDelayed(runnable, 800L)
|
|
628
|
+
}
|
|
629
|
+
}
|
|
513
630
|
if ((tag as? NativeListItem)?.type == "mediaTile") {
|
|
514
631
|
leadingFrame.alpha = 0.8f
|
|
515
632
|
} else {
|
|
516
633
|
background = pressedRowBackground
|
|
517
634
|
}
|
|
518
635
|
}
|
|
519
|
-
MotionEvent.ACTION_MOVE ->
|
|
520
|
-
event.x < 0 || event.y < 0 || event.x >= width || event.y >= height
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
636
|
+
MotionEvent.ACTION_MOVE -> {
|
|
637
|
+
val outside = event.x < 0 || event.y < 0 || event.x >= width || event.y >= height
|
|
638
|
+
val movedMarket = (tag as? NativeListItem)?.type == "market" &&
|
|
639
|
+
(kotlin.math.abs(event.x - marketTouchStartX) > dp(10) ||
|
|
640
|
+
kotlin.math.abs(event.y - marketTouchStartY) > dp(10))
|
|
641
|
+
if (outside || movedMarket) {
|
|
642
|
+
cancelMarketLongPress()
|
|
643
|
+
touchPressed = false
|
|
644
|
+
restoreRestingBackground()
|
|
645
|
+
}
|
|
524
646
|
}
|
|
525
647
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
648
|
+
cancelMarketLongPress()
|
|
526
649
|
touchPressed = false
|
|
527
650
|
restoreRestingBackground()
|
|
528
651
|
}
|
|
@@ -531,6 +654,10 @@ internal class NativeListRowView(
|
|
|
531
654
|
}
|
|
532
655
|
setOnClickListener { view ->
|
|
533
656
|
(view.tag as? NativeListItem)?.let { item ->
|
|
657
|
+
if (item.type == "market" && marketLongPressFired) {
|
|
658
|
+
marketLongPressFired = false
|
|
659
|
+
return@let
|
|
660
|
+
}
|
|
534
661
|
// OneKey patch: allow create-address accessories when whole-row press is gated.
|
|
535
662
|
if (!item.json.optBoolean("pressDisabled", false)) onRowPress?.invoke(item, actionOrigin(view, "row"))
|
|
536
663
|
}
|
|
@@ -650,13 +777,15 @@ internal class NativeListRowView(
|
|
|
650
777
|
useSourceScale: Boolean = false,
|
|
651
778
|
) {
|
|
652
779
|
val shouldRestorePressed = touchPressed && boundKey == item.key
|
|
780
|
+
cancelMarketLongPress()
|
|
781
|
+
marketLongPressFired = false
|
|
653
782
|
invalidateCurrentBinding()
|
|
654
783
|
bindingEpoch += 1
|
|
655
784
|
boundKey = item.key
|
|
656
785
|
touchPressed = shouldRestorePressed
|
|
657
786
|
currentLayout = layout
|
|
658
787
|
tag = item
|
|
659
|
-
selectorUsesSourceScale = item.usesSelectorSourceScale || useSourceScale
|
|
788
|
+
selectorUsesSourceScale = item.type == "market" || item.usesSelectorSourceScale || useSourceScale
|
|
660
789
|
reorderActive = false
|
|
661
790
|
leadingImages.forEach(OneKeyImageReusableView::prepareForReuse)
|
|
662
791
|
secondaryImage.prepareForReuse()
|
|
@@ -742,6 +871,7 @@ internal class NativeListRowView(
|
|
|
742
871
|
"activity" -> bindActivity(item, theme)
|
|
743
872
|
"message" -> bindMessage(item, theme)
|
|
744
873
|
"dataRow" -> bindDataRow(item, theme, checkboxState)
|
|
874
|
+
"market" -> bindMarket(item, theme)
|
|
745
875
|
"mediaTile" -> bindMediaTile(item, theme)
|
|
746
876
|
"metricCard" -> bindMetricCard(item, theme)
|
|
747
877
|
"sectionHeader" -> bindSectionHeader(item, theme, checkboxState)
|
|
@@ -776,6 +906,8 @@ internal class NativeListRowView(
|
|
|
776
906
|
}
|
|
777
907
|
|
|
778
908
|
fun recycle() {
|
|
909
|
+
cancelMarketLongPress()
|
|
910
|
+
marketLongPressFired = false
|
|
779
911
|
touchPressed = false
|
|
780
912
|
restoreRestingBackground()
|
|
781
913
|
invalidateCurrentBinding()
|
|
@@ -866,6 +998,7 @@ internal class NativeListRowView(
|
|
|
866
998
|
}
|
|
867
999
|
|
|
868
1000
|
fun dispose() {
|
|
1001
|
+
cancelMarketLongPress()
|
|
869
1002
|
invalidateCurrentBinding()
|
|
870
1003
|
restoreRestingBackground()
|
|
871
1004
|
selectorImages.forEach(OneKeyImageReusableView::dispose)
|
|
@@ -874,6 +1007,7 @@ internal class NativeListRowView(
|
|
|
874
1007
|
secondaryImage.dispose()
|
|
875
1008
|
mediaNetworkImage.dispose()
|
|
876
1009
|
metricVisualImages.forEach(OneKeyImageReusableView::dispose)
|
|
1010
|
+
marketBadgeImages.forEach(OneKeyImageReusableView::dispose)
|
|
877
1011
|
walletGroupRows.forEach(NativeListRowView::dispose)
|
|
878
1012
|
}
|
|
879
1013
|
|
|
@@ -953,6 +1087,25 @@ internal class NativeListRowView(
|
|
|
953
1087
|
selectorImages.forEach(OneKeyImageReusableView::dispose)
|
|
954
1088
|
selectorImages.clear()
|
|
955
1089
|
selectorHeight = null
|
|
1090
|
+
marketBadgeViews.forEachIndexed { index, badge ->
|
|
1091
|
+
(badge.parent as? ViewGroup)?.removeView(badge)
|
|
1092
|
+
badge.visibility = GONE
|
|
1093
|
+
badge.background = null
|
|
1094
|
+
badge.setPadding(0, 0, 0, 0)
|
|
1095
|
+
badge.setOnClickListener(null)
|
|
1096
|
+
badge.contentDescription = null
|
|
1097
|
+
marketBadgeLabels[index].apply {
|
|
1098
|
+
text = ""
|
|
1099
|
+
setTextColor(color(null, "secondaryText", "#0000009B"))
|
|
1100
|
+
}
|
|
1101
|
+
marketBadgeImages[index].prepareForReuse()
|
|
1102
|
+
marketBadgeImages[index].visibility = GONE
|
|
1103
|
+
marketBadgeGlyphs[index].apply {
|
|
1104
|
+
visibility = GONE
|
|
1105
|
+
iconName = ""
|
|
1106
|
+
tintColor = color(null, "secondaryText", "#0000009B")
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
956
1109
|
title.setOnClickListener(null)
|
|
957
1110
|
title.isClickable = false
|
|
958
1111
|
walletGroupRows.forEach { it.invalidateCurrentBinding() }
|
|
@@ -1121,12 +1274,21 @@ internal class NativeListRowView(
|
|
|
1121
1274
|
mainColumn.removeView(skeletonSecondary)
|
|
1122
1275
|
setOnClickListener { view ->
|
|
1123
1276
|
(view.tag as? NativeListItem)?.let { item ->
|
|
1277
|
+
if (item.type == "market" && marketLongPressFired) {
|
|
1278
|
+
marketLongPressFired = false
|
|
1279
|
+
return@let
|
|
1280
|
+
}
|
|
1124
1281
|
// OneKey patch: allow create-address accessories when whole-row press is gated.
|
|
1125
1282
|
if (!item.json.optBoolean("pressDisabled", false)) onRowPress?.invoke(item, actionOrigin(view, "row"))
|
|
1126
1283
|
}
|
|
1127
1284
|
}
|
|
1128
1285
|
}
|
|
1129
1286
|
|
|
1287
|
+
private fun cancelMarketLongPress() {
|
|
1288
|
+
marketLongPressRunnable?.let(marketLongPressHandler::removeCallbacks)
|
|
1289
|
+
marketLongPressRunnable = null
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1130
1292
|
private fun restoreRestingBackground() {
|
|
1131
1293
|
background = if (reorderActive) pressedRowBackground else restingRowBackground
|
|
1132
1294
|
leadingFrame.alpha = 1f
|
|
@@ -1733,6 +1895,272 @@ internal class NativeListRowView(
|
|
|
1733
1895
|
addView(dataContainer, weighted())
|
|
1734
1896
|
}
|
|
1735
1897
|
|
|
1898
|
+
private fun marketTypeface(weight: String, fallback: String): Typeface = when (
|
|
1899
|
+
weight.ifEmpty { fallback }
|
|
1900
|
+
) {
|
|
1901
|
+
"regular" -> NativeListFonts.regular(context)
|
|
1902
|
+
"semibold" -> NativeListFonts.semibold(context)
|
|
1903
|
+
"bold" -> NativeListFonts.bold(context)
|
|
1904
|
+
else -> NativeListFonts.medium(context)
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
private fun applyMarketTextStyle(
|
|
1908
|
+
view: TextView,
|
|
1909
|
+
style: JSONObject?,
|
|
1910
|
+
defaultSize: Float,
|
|
1911
|
+
defaultLineHeight: Int,
|
|
1912
|
+
defaultWeight: String,
|
|
1913
|
+
defaultColor: Int,
|
|
1914
|
+
defaultAlignment: String,
|
|
1915
|
+
) {
|
|
1916
|
+
view.includeFontPadding = false
|
|
1917
|
+
view.fontFeatureSettings = "tnum"
|
|
1918
|
+
view.textSize = sp(style?.optDouble("fontSize", defaultSize.toDouble())?.toFloat() ?: defaultSize)
|
|
1919
|
+
view.typeface = marketTypeface(style?.optString("fontWeight").orEmpty(), defaultWeight)
|
|
1920
|
+
view.setTextColor(safeColor(style?.optString("color"), defaultColor))
|
|
1921
|
+
val alignment = style?.optString("alignment", defaultAlignment) ?: defaultAlignment
|
|
1922
|
+
view.gravity = Gravity.CENTER_VERTICAL or when (alignment) {
|
|
1923
|
+
"center" -> Gravity.CENTER_HORIZONTAL
|
|
1924
|
+
"end" -> Gravity.END
|
|
1925
|
+
else -> Gravity.START
|
|
1926
|
+
}
|
|
1927
|
+
view.maxLines = style?.optInt("lines", 1)?.coerceIn(1, 2) ?: 1
|
|
1928
|
+
view.ellipsize = TextUtils.TruncateAt.END
|
|
1929
|
+
TextViewCompat.setLineHeight(
|
|
1930
|
+
view,
|
|
1931
|
+
dp(style?.optDouble("lineHeight", defaultLineHeight.toDouble())?.roundToInt() ?: defaultLineHeight),
|
|
1932
|
+
)
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
private fun marketText(value: String, segments: JSONArray?, fontSize: Float): CharSequence {
|
|
1936
|
+
if (segments == null || segments.length() == 0) return value
|
|
1937
|
+
val result = SpannableStringBuilder()
|
|
1938
|
+
for (index in 0 until segments.length()) {
|
|
1939
|
+
val segment = segments.getJSONObject(index)
|
|
1940
|
+
val start = result.length
|
|
1941
|
+
result.append(segment.optString("text"))
|
|
1942
|
+
if (segment.optString("style") == "subscript") {
|
|
1943
|
+
result.setSpan(
|
|
1944
|
+
AbsoluteSizeSpan(sp(kotlin.math.ceil(fontSize * 0.6f)).roundToInt(), true),
|
|
1945
|
+
start,
|
|
1946
|
+
result.length,
|
|
1947
|
+
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
1948
|
+
)
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
return result
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
private fun bindMarket(item: NativeListItem, theme: JSONObject?) {
|
|
1955
|
+
// Network badges extend beyond the leading image's frame.
|
|
1956
|
+
clipChildren = false
|
|
1957
|
+
val variant = item.json.optString("variant")
|
|
1958
|
+
val style = item.json.optJSONObject("style")
|
|
1959
|
+
val imageStyle = style?.optJSONObject("image")
|
|
1960
|
+
val imageWidth = imageStyle?.optDouble("width", if (variant == "stock") 40.0 else 32.0)?.roundToInt()
|
|
1961
|
+
?: if (variant == "stock") 40 else 32
|
|
1962
|
+
val imageHeight = imageStyle?.optDouble("height", if (variant == "stock") 40.0 else 32.0)?.roundToInt()
|
|
1963
|
+
?: if (variant == "stock") 40 else 32
|
|
1964
|
+
val horizontalPadding = style?.optDouble("horizontalPadding", if (variant == "perp") 16.0 else 20.0)?.roundToInt()
|
|
1965
|
+
?: if (variant == "perp") 16 else 20
|
|
1966
|
+
val verticalPadding = style?.optDouble("verticalPadding", 12.0)?.roundToInt() ?: 12
|
|
1967
|
+
val leadingGap = style?.optDouble("leadingGap", if (variant == "perp") 8.0 else 14.0)?.roundToInt()
|
|
1968
|
+
?: if (variant == "perp") 8 else 14
|
|
1969
|
+
setPadding(dp(horizontalPadding), dp(verticalPadding), dp(horizontalPadding), dp(verticalPadding))
|
|
1970
|
+
|
|
1971
|
+
val leading = JSONObject(item.json.getJSONObject("leading").toString())
|
|
1972
|
+
imageStyle?.optString("shape")?.takeIf(String::isNotEmpty)?.let { leading.put("shape", it) }
|
|
1973
|
+
imageStyle?.optString("contentFit")?.takeIf(String::isNotEmpty)?.let { contentFit ->
|
|
1974
|
+
leading.optJSONObject("image")?.put("contentFit", contentFit)
|
|
1975
|
+
}
|
|
1976
|
+
val shape = imageStyle?.optString("shape", leading.optString("shape", "circle"))
|
|
1977
|
+
?: leading.optString("shape", "circle")
|
|
1978
|
+
val cornerRadius = imageStyle?.takeIf { it.has("cornerRadius") }?.optDouble("cornerRadius")?.toFloat()
|
|
1979
|
+
?: when (shape) {
|
|
1980
|
+
"square" -> 0f
|
|
1981
|
+
"rounded" -> 8f
|
|
1982
|
+
else -> minOf(imageWidth, imageHeight) / 2f
|
|
1983
|
+
}
|
|
1984
|
+
addLeading(
|
|
1985
|
+
leading,
|
|
1986
|
+
sizeDp = imageWidth,
|
|
1987
|
+
spacingDp = leadingGap,
|
|
1988
|
+
heightDp = imageHeight,
|
|
1989
|
+
cornerRadiusDp = cornerRadius,
|
|
1990
|
+
)
|
|
1991
|
+
|
|
1992
|
+
addView(mainColumn, weighted())
|
|
1993
|
+
titleLine.packsChildrenAtStart = true
|
|
1994
|
+
showText(title, item.json.optString("title"), style?.optJSONObject("title")?.optInt("lines", 1) ?: 1)
|
|
1995
|
+
applyMarketTextStyle(
|
|
1996
|
+
title,
|
|
1997
|
+
style?.optJSONObject("title"),
|
|
1998
|
+
16f,
|
|
1999
|
+
24,
|
|
2000
|
+
"medium",
|
|
2001
|
+
color(theme, "primaryText", "#202020"),
|
|
2002
|
+
"start",
|
|
2003
|
+
)
|
|
2004
|
+
val badges = item.json.optJSONArray("badges")
|
|
2005
|
+
val titleBadgeGap = style?.optDouble("titleBadgeGap", 4.0)?.roundToInt() ?: 4
|
|
2006
|
+
if (badges != null) {
|
|
2007
|
+
for (index in 0 until minOf(marketBadgeViews.size, badges.length())) {
|
|
2008
|
+
val badge = badges.getJSONObject(index)
|
|
2009
|
+
val badgeView = marketBadgeViews[index]
|
|
2010
|
+
val badgeLabel = marketBadgeLabels[index]
|
|
2011
|
+
val hasGlyph = badge.optString("iconName") == "verified"
|
|
2012
|
+
val remoteIcon = badge.optJSONObject("icon")
|
|
2013
|
+
val hasIcon = hasGlyph || remoteIcon != null
|
|
2014
|
+
val text = badge.optString("text")
|
|
2015
|
+
val toneColor = when (badge.optString("tone")) {
|
|
2016
|
+
"success" -> color(theme, "positive", "#218358")
|
|
2017
|
+
"danger" -> color(theme, "negative", "#CE2C31")
|
|
2018
|
+
"info" -> color(theme, "info", "#0D74CE")
|
|
2019
|
+
"warning" -> color(theme, "primaryText", "#202020")
|
|
2020
|
+
else -> color(theme, "secondaryText", "#646464")
|
|
2021
|
+
}
|
|
2022
|
+
val foreground = safeColor(badge.optString("textColor"), toneColor)
|
|
2023
|
+
badgeView.visibility = VISIBLE
|
|
2024
|
+
val iconOnly = hasIcon && text.isEmpty()
|
|
2025
|
+
badgeView.setPadding(dp(if (iconOnly) 0 else if (hasIcon) 2 else 5), 0, dp(if (iconOnly) 0 else 5), 0)
|
|
2026
|
+
badgeView.background = roundedFill(
|
|
2027
|
+
safeColor(
|
|
2028
|
+
badge.optString("backgroundColor"),
|
|
2029
|
+
if (hasIcon && text.isEmpty()) Color.TRANSPARENT else color(theme, "strongBackground", "#0000000F"),
|
|
2030
|
+
),
|
|
2031
|
+
4f,
|
|
2032
|
+
)
|
|
2033
|
+
badgeLabel.text = text
|
|
2034
|
+
badgeLabel.setTextColor(foreground)
|
|
2035
|
+
badgeLabel.visibility = if (text.isEmpty()) GONE else VISIBLE
|
|
2036
|
+
if (hasGlyph) {
|
|
2037
|
+
marketBadgeGlyphs[index].apply {
|
|
2038
|
+
iconName = "BadgeVerifiedSolid"
|
|
2039
|
+
tintColor = foreground
|
|
2040
|
+
visibility = VISIBLE
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
remoteIcon?.let { icon ->
|
|
2044
|
+
marketBadgeImages[index].visibility = VISIBLE
|
|
2045
|
+
marketBadgeImages[index].outlineProvider = circleOutlineProvider
|
|
2046
|
+
marketBadgeImages[index].clipToOutline = true
|
|
2047
|
+
bindImage(icon, marketBadgeImages[index], item.key, 20 + index, "generic")
|
|
2048
|
+
}
|
|
2049
|
+
val actionKey = badge.optString("actionKey")
|
|
2050
|
+
badgeView.isClickable = actionKey.isNotEmpty()
|
|
2051
|
+
if (actionKey.isNotEmpty()) {
|
|
2052
|
+
badgeView.setOnClickListener {
|
|
2053
|
+
emitAction(item, actionKey, null, badgeView, "marketBadge", index)
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
badgeView.contentDescription = badge.optString("accessibilityLabel", text)
|
|
2057
|
+
titleLine.addView(
|
|
2058
|
+
badgeView,
|
|
2059
|
+
LayoutParams(LayoutParams.WRAP_CONTENT, dp(18)).apply { marginStart = dp(titleBadgeGap) },
|
|
2060
|
+
)
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
subtitle.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply {
|
|
2064
|
+
topMargin = dp(style?.optDouble("lineGap", 0.0)?.roundToInt() ?: 0)
|
|
2065
|
+
}
|
|
2066
|
+
val subtitleText = item.json.optString("subtitle")
|
|
2067
|
+
val subtitleSegments = item.json.optJSONArray("subtitleSegments")
|
|
2068
|
+
if (subtitleText.isNotEmpty() || (subtitleSegments?.length() ?: 0) > 0) {
|
|
2069
|
+
subtitle.visibility = VISIBLE
|
|
2070
|
+
applyMarketTextStyle(
|
|
2071
|
+
subtitle,
|
|
2072
|
+
style?.optJSONObject("subtitle"),
|
|
2073
|
+
14f,
|
|
2074
|
+
20,
|
|
2075
|
+
"regular",
|
|
2076
|
+
color(theme, "secondaryText", "#646464"),
|
|
2077
|
+
"start",
|
|
2078
|
+
)
|
|
2079
|
+
subtitle.text = marketText(
|
|
2080
|
+
subtitleText,
|
|
2081
|
+
subtitleSegments,
|
|
2082
|
+
style?.optJSONObject("subtitle")?.optDouble("fontSize", 14.0)?.toFloat() ?: 14f,
|
|
2083
|
+
)
|
|
2084
|
+
}
|
|
2085
|
+
trailingColumn.orientation = HORIZONTAL
|
|
2086
|
+
trailingColumn.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
|
2087
|
+
addView(trailingColumn, wrap())
|
|
2088
|
+
bindMarketQuote(item, theme)
|
|
2089
|
+
item.json.optJSONObject("diagnostics")?.optString("imageBindActionKey")
|
|
2090
|
+
?.takeIf(String::isNotEmpty)
|
|
2091
|
+
?.takeIf { leading.optJSONObject("image") != null || leading.optJSONObject("networkImage") != null }
|
|
2092
|
+
?.let { actionKey -> onAction?.invoke(item, actionKey, null, null) }
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
fun bindMarketQuote(item: NativeListItem, theme: JSONObject?) {
|
|
2096
|
+
if (boundKey != item.key || item.type != "market") return
|
|
2097
|
+
tag = item
|
|
2098
|
+
val style = item.json.optJSONObject("style")
|
|
2099
|
+
val priceStyle = style?.optJSONObject("price")
|
|
2100
|
+
val price = trailingViews[0]
|
|
2101
|
+
price.isClickable = false
|
|
2102
|
+
price.isLongClickable = false
|
|
2103
|
+
price.visibility = VISIBLE
|
|
2104
|
+
price.maxWidth = dp(112)
|
|
2105
|
+
applyMarketTextStyle(
|
|
2106
|
+
price,
|
|
2107
|
+
priceStyle,
|
|
2108
|
+
16f,
|
|
2109
|
+
24,
|
|
2110
|
+
"medium",
|
|
2111
|
+
color(theme, "primaryText", "#202020"),
|
|
2112
|
+
"end",
|
|
2113
|
+
)
|
|
2114
|
+
price.text = marketText(
|
|
2115
|
+
item.json.optString("price"),
|
|
2116
|
+
item.json.optJSONArray("priceSegments"),
|
|
2117
|
+
priceStyle?.optDouble("fontSize", 16.0)?.toFloat() ?: 16f,
|
|
2118
|
+
)
|
|
2119
|
+
val trailingGap = style?.optDouble("trailingGap", 8.0)?.roundToInt() ?: 8
|
|
2120
|
+
price.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
|
|
2121
|
+
marginEnd = dp(trailingGap)
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
val changeData = item.json.getJSONObject("change")
|
|
2125
|
+
val changeStyle = style?.optJSONObject("change")
|
|
2126
|
+
val change = trailingViews[1]
|
|
2127
|
+
change.isClickable = false
|
|
2128
|
+
change.isLongClickable = false
|
|
2129
|
+
val toneColor = when (changeData.optString("tone")) {
|
|
2130
|
+
"positive" -> color(theme, "positive", "#218358")
|
|
2131
|
+
"negative" -> color(theme, "negative", "#CE2C31")
|
|
2132
|
+
else -> color(theme, "secondaryText", "#8D8D8D")
|
|
2133
|
+
}
|
|
2134
|
+
val textColor = safeColor(
|
|
2135
|
+
changeData.optString("textColor"),
|
|
2136
|
+
color(theme, "inverseText", "#FFFFFF"),
|
|
2137
|
+
)
|
|
2138
|
+
change.visibility = VISIBLE
|
|
2139
|
+
applyMarketTextStyle(change, changeStyle, 14f, 20, "medium", textColor, "center")
|
|
2140
|
+
change.text = marketText(
|
|
2141
|
+
changeData.optString("text"),
|
|
2142
|
+
changeData.optJSONArray("textSegments"),
|
|
2143
|
+
changeStyle?.optDouble("fontSize", 14.0)?.toFloat() ?: 14f,
|
|
2144
|
+
)
|
|
2145
|
+
change.background = roundedFill(
|
|
2146
|
+
safeColor(changeData.optString("backgroundColor"), toneColor),
|
|
2147
|
+
style?.optDouble("changeCornerRadius", 8.0)?.toFloat() ?: 8f,
|
|
2148
|
+
)
|
|
2149
|
+
change.layoutParams = LayoutParams(
|
|
2150
|
+
dp(style?.optDouble("changeWidth", 80.0)?.roundToInt() ?: 80),
|
|
2151
|
+
dp(style?.optDouble("changeHeight", 32.0)?.roundToInt() ?: 32),
|
|
2152
|
+
)
|
|
2153
|
+
contentDescription = item.json.optString(
|
|
2154
|
+
"accessibilityLabel",
|
|
2155
|
+
listOf(
|
|
2156
|
+
item.json.optString("title"),
|
|
2157
|
+
item.json.optString("subtitle"),
|
|
2158
|
+
item.json.optString("price"),
|
|
2159
|
+
changeData.optString("text"),
|
|
2160
|
+
).filter(String::isNotEmpty).joinToString(", "),
|
|
2161
|
+
)
|
|
2162
|
+
}
|
|
2163
|
+
|
|
1736
2164
|
private fun styledDataText(
|
|
1737
2165
|
column: JSONObject,
|
|
1738
2166
|
badges: JSONArray?,
|
|
@@ -2367,6 +2795,40 @@ internal class NativeListRowView(
|
|
|
2367
2795
|
|
|
2368
2796
|
private fun bindSystem(item: NativeListItem, theme: JSONObject?) {
|
|
2369
2797
|
val variant = item.json.optString("variant")
|
|
2798
|
+
val isMarket = item.json.optString("presentation") == "market"
|
|
2799
|
+
if (isMarket) setPadding(dp(20), dp(12), dp(20), dp(12))
|
|
2800
|
+
if (variant == "loading" && item.json.optString("loadingStyle") == "skeleton") {
|
|
2801
|
+
setPadding(dp(20), dp(12), dp(20), dp(12))
|
|
2802
|
+
addView(
|
|
2803
|
+
NativeListMarketSkeleton(context, color(theme, "background", "#FFFFFF")),
|
|
2804
|
+
LayoutParams(LayoutParams.MATCH_PARENT, dp(32)),
|
|
2805
|
+
)
|
|
2806
|
+
return
|
|
2807
|
+
}
|
|
2808
|
+
if (variant == "loading" && item.json.optString("loadingStyle") == "spinner") {
|
|
2809
|
+
gravity = Gravity.CENTER
|
|
2810
|
+
setPadding(0, dp(16), 0, dp(16))
|
|
2811
|
+
addView(
|
|
2812
|
+
ProgressBar(context, null, android.R.attr.progressBarStyleSmall).apply {
|
|
2813
|
+
isIndeterminate = true
|
|
2814
|
+
indeterminateTintList = android.content.res.ColorStateList.valueOf(color(theme, "icon", "#0000009B"))
|
|
2815
|
+
},
|
|
2816
|
+
LayoutParams(dp(20), dp(20)),
|
|
2817
|
+
)
|
|
2818
|
+
return
|
|
2819
|
+
}
|
|
2820
|
+
if (isMarket && variant == "noMatch") {
|
|
2821
|
+
gravity = Gravity.CENTER
|
|
2822
|
+
setPadding(dp(32), dp(32), dp(32), dp(32))
|
|
2823
|
+
title.textSize = sp(16f)
|
|
2824
|
+
title.typeface = NativeListFonts.regular(context)
|
|
2825
|
+
title.gravity = Gravity.CENTER
|
|
2826
|
+
title.setTextColor(color(theme, "secondaryText", "#0000009B"))
|
|
2827
|
+
TextViewCompat.setLineHeight(title, dp(24))
|
|
2828
|
+
showText(title, item.json.optString("message"), 1)
|
|
2829
|
+
addView(mainColumn, weighted())
|
|
2830
|
+
return
|
|
2831
|
+
}
|
|
2370
2832
|
// OneKey patch: warning title/description wrap inside the actual scroll content.
|
|
2371
2833
|
if (variant == "warning") {
|
|
2372
2834
|
setPadding(dp(12), dp(14), dp(12), dp(14))
|
|
@@ -2400,11 +2862,11 @@ internal class NativeListRowView(
|
|
|
2400
2862
|
}
|
|
2401
2863
|
gravity = Gravity.CENTER
|
|
2402
2864
|
if (variant == "loading") {
|
|
2403
|
-
addLeading(JSONObject().put("kind", "skeleton"), 40)
|
|
2865
|
+
addLeading(JSONObject().put("kind", "skeleton"), if (isMarket) 32 else 40)
|
|
2404
2866
|
leadingFallback.text = ""
|
|
2405
2867
|
leadingFallback.background = roundedFill(
|
|
2406
2868
|
color(theme, "strongBackground", "#0000000F"),
|
|
2407
|
-
20f,
|
|
2869
|
+
if (isMarket) 16f else 20f,
|
|
2408
2870
|
)
|
|
2409
2871
|
addSkeleton(skeletonPrimary, 120, 12, theme, bottomMarginDp = 8)
|
|
2410
2872
|
addSkeleton(skeletonSecondary, 80, 12, theme)
|
|
@@ -2445,15 +2907,17 @@ internal class NativeListRowView(
|
|
|
2445
2907
|
sizeDp: Int = 40,
|
|
2446
2908
|
secondaryVisual: JSONObject? = null,
|
|
2447
2909
|
spacingDp: Int = 12,
|
|
2910
|
+
heightDp: Int = sizeDp,
|
|
2911
|
+
cornerRadiusDp: Float? = null,
|
|
2448
2912
|
) {
|
|
2449
2913
|
leadingFrame.visibility = VISIBLE
|
|
2450
|
-
leadingFrame.layoutParams = LayoutParams(dp(sizeDp), dp(
|
|
2914
|
+
leadingFrame.layoutParams = LayoutParams(dp(sizeDp), dp(heightDp)).apply {
|
|
2451
2915
|
val item = tag as? NativeListItem
|
|
2452
2916
|
// OneKey patch: Yoga rounds cumulative selector edges, not each 12dp gap separately.
|
|
2453
2917
|
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)
|
|
2454
2918
|
}
|
|
2455
2919
|
addView(leadingFrame)
|
|
2456
|
-
leadingFallback.layoutParams = FrameLayout.LayoutParams(dp(sizeDp), dp(
|
|
2920
|
+
leadingFallback.layoutParams = FrameLayout.LayoutParams(dp(sizeDp), dp(heightDp))
|
|
2457
2921
|
if (visual == null) return
|
|
2458
2922
|
val kind = visual.optString("kind")
|
|
2459
2923
|
val shape = visual.optString(
|
|
@@ -2474,16 +2938,19 @@ internal class NativeListRowView(
|
|
|
2474
2938
|
if (!isIcon && visual.optString("backgroundColor").isNotEmpty()) {
|
|
2475
2939
|
leadingFrame.background = roundedFill(
|
|
2476
2940
|
visualBackground,
|
|
2477
|
-
leadingCornerRadius(shape, sizeDp),
|
|
2941
|
+
cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)),
|
|
2478
2942
|
)
|
|
2479
2943
|
}
|
|
2480
|
-
leadingFallback.background = roundedFill(
|
|
2944
|
+
leadingFallback.background = roundedFill(
|
|
2945
|
+
visualBackground,
|
|
2946
|
+
cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)),
|
|
2947
|
+
)
|
|
2481
2948
|
leadingFallback.visibility = if (!isIcon && sources.isEmpty()) VISIBLE else GONE
|
|
2482
2949
|
if (isIcon) {
|
|
2483
2950
|
leadingFrame.background = GradientDrawable().apply {
|
|
2484
2951
|
setColor(visualBackground)
|
|
2485
2952
|
setStroke(1, parseNativeListColor("#0000001F"))
|
|
2486
|
-
cornerRadius = scaledDp(leadingCornerRadius(shape, sizeDp))
|
|
2953
|
+
cornerRadius = scaledDp(cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)))
|
|
2487
2954
|
}
|
|
2488
2955
|
leadingIcon.iconName = visual.optString("name")
|
|
2489
2956
|
leadingIcon.tintColor = safeColor(
|
|
@@ -2535,13 +3002,33 @@ internal class NativeListRowView(
|
|
|
2535
3002
|
index = index,
|
|
2536
3003
|
count = visibleSources.size,
|
|
2537
3004
|
sizeDp = sizeDp,
|
|
3005
|
+
heightDp = heightDp,
|
|
2538
3006
|
tokenPair = tokenPair,
|
|
2539
3007
|
)
|
|
2540
3008
|
image.outlineProvider = when {
|
|
2541
3009
|
tokenPair && index == 1 -> circleOutlineProvider
|
|
3010
|
+
cornerRadiusDp != null -> roundedOutlineProvider(cornerRadiusDp)
|
|
2542
3011
|
else -> leadingOutlineProvider(shape)
|
|
2543
3012
|
}
|
|
2544
3013
|
image.clipToOutline = true
|
|
3014
|
+
if ((tag as? NativeListItem)?.type == "market" && index == 0 && visual.optString("borderColor").isNotEmpty()) {
|
|
3015
|
+
val inset = dp(1)
|
|
3016
|
+
val radius = scaledDp(cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)))
|
|
3017
|
+
leadingFrame.background = GradientDrawable().apply {
|
|
3018
|
+
setColor(visualBackground)
|
|
3019
|
+
setStroke(inset, safeColor(visual.optString("borderColor"), Color.TRANSPARENT))
|
|
3020
|
+
this.cornerRadius = radius
|
|
3021
|
+
}
|
|
3022
|
+
image.layoutParams = FrameLayout.LayoutParams(dp(sizeDp) - inset * 2, dp(heightDp) - inset * 2).apply {
|
|
3023
|
+
leftMargin = inset
|
|
3024
|
+
topMargin = inset
|
|
3025
|
+
}
|
|
3026
|
+
image.outlineProvider = object : ViewOutlineProvider() {
|
|
3027
|
+
override fun getOutline(view: View, outline: Outline) {
|
|
3028
|
+
outline.setRoundRect(-inset, -inset, view.width + inset, view.height + inset, radius)
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
2545
3032
|
val fallbackIcon = if (index == 0) visual.optJSONObject("fallbackIcon") else null
|
|
2546
3033
|
val expectedEpoch = bindingEpoch
|
|
2547
3034
|
bindImage(source, image, boundKey ?: "", index, variant,
|
|
@@ -2658,10 +3145,11 @@ internal class NativeListRowView(
|
|
|
2658
3145
|
index: Int,
|
|
2659
3146
|
count: Int,
|
|
2660
3147
|
sizeDp: Int,
|
|
3148
|
+
heightDp: Int,
|
|
2661
3149
|
tokenPair: Boolean,
|
|
2662
3150
|
): FrameLayout.LayoutParams {
|
|
2663
3151
|
if (count == 1 || tokenPair && index == 0) {
|
|
2664
|
-
return FrameLayout.LayoutParams(dp(sizeDp), dp(
|
|
3152
|
+
return FrameLayout.LayoutParams(dp(sizeDp), dp(heightDp))
|
|
2665
3153
|
}
|
|
2666
3154
|
if (tokenPair && index == 1) {
|
|
2667
3155
|
return FrameLayout.LayoutParams(dp(16), dp(16), Gravity.END or Gravity.BOTTOM).apply {
|
|
@@ -3003,6 +3491,22 @@ internal class NativeListRowView(
|
|
|
3003
3491
|
}
|
|
3004
3492
|
|
|
3005
3493
|
private fun applySize(item: NativeListItem) {
|
|
3494
|
+
if (item.type == "market") {
|
|
3495
|
+
val style = item.json.optJSONObject("style")
|
|
3496
|
+
val imageHeight = style?.optJSONObject("image")?.optDouble(
|
|
3497
|
+
"height",
|
|
3498
|
+
if (item.json.optString("variant") == "stock") 40.0 else 32.0,
|
|
3499
|
+
) ?: if (item.json.optString("variant") == "stock") 40.0 else 32.0
|
|
3500
|
+
val verticalPadding = style?.optDouble("verticalPadding", 12.0) ?: 12.0
|
|
3501
|
+
val defaultHeight = if (item.json.optString("variant") == "stock") 72.0 else 68.0
|
|
3502
|
+
minimumHeight = dp(
|
|
3503
|
+
item.json.optDouble(
|
|
3504
|
+
"height",
|
|
3505
|
+
maxOf(defaultHeight, imageHeight + verticalPadding * 2),
|
|
3506
|
+
).roundToInt(),
|
|
3507
|
+
)
|
|
3508
|
+
return
|
|
3509
|
+
}
|
|
3006
3510
|
val isWalletSidebar =
|
|
3007
3511
|
item.type == "identity" && item.json.optString("presentation") == "walletSidebar"
|
|
3008
3512
|
val isAccountSelectorIdentity =
|
|
@@ -3155,9 +3659,14 @@ internal class NativeListRowView(
|
|
|
3155
3659
|
else -> 36
|
|
3156
3660
|
}
|
|
3157
3661
|
"system" -> when (item.json.optString("variant")) {
|
|
3662
|
+
"loading" -> when (item.json.optString("loadingStyle")) {
|
|
3663
|
+
"skeleton" -> 56
|
|
3664
|
+
"spinner" -> 52
|
|
3665
|
+
else -> if (item.json.optString("presentation") == "market") 68 else 56
|
|
3666
|
+
}
|
|
3667
|
+
"noMatch", "retry" -> if (item.json.optString("presentation") == "market") 44 else if (item.json.optString("variant") == "noMatch") 36 else 44
|
|
3158
3668
|
"warning" -> 0
|
|
3159
|
-
"
|
|
3160
|
-
"retry" -> 44
|
|
3669
|
+
"end" -> if (item.json.optString("presentation") == "market") 44 else 36
|
|
3161
3670
|
else -> 56
|
|
3162
3671
|
}
|
|
3163
3672
|
"action" -> when {
|
|
@@ -3263,6 +3772,12 @@ internal class NativeListRowView(
|
|
|
3263
3772
|
}
|
|
3264
3773
|
}
|
|
3265
3774
|
|
|
3775
|
+
private fun roundedOutlineProvider(radiusDp: Float) = object : ViewOutlineProvider() {
|
|
3776
|
+
override fun getOutline(view: View, outline: Outline) {
|
|
3777
|
+
outline.setRoundRect(0, 0, view.width, view.height, scaledDp(radiusDp))
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3266
3781
|
private fun JSONArray?.hasAccessory(kind: String): Boolean {
|
|
3267
3782
|
if (this == null) return false
|
|
3268
3783
|
return (0 until length()).any { optJSONObject(it)?.optString("kind") == kind }
|
|
@@ -3459,6 +3974,7 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
3459
3974
|
"CrossedSmallSolid" to listOf(Path.FillType.WINDING),
|
|
3460
3975
|
"AccountErrorCustom" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
3461
3976
|
"Circle" to listOf(Path.FillType.WINDING),
|
|
3977
|
+
"BadgeVerifiedSolid" to listOf(Path.FillType.EVEN_ODD),
|
|
3462
3978
|
"ChevronRightSmallOutline" to listOf(Path.FillType.WINDING),
|
|
3463
3979
|
"MinusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
3464
3980
|
"PlusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
@@ -3486,6 +4002,7 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
3486
4002
|
"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"),
|
|
3487
4003
|
"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"),
|
|
3488
4004
|
"Circle" to listOf("M0 12a12 12 0 1 0 24 0a12 12 0 1 0 -24 0"),
|
|
4005
|
+
"BadgeVerifiedSolid" to listOf("M9.483 11.458v3.5h-1v-3.5z M10.467 2.698a2.03 2.03 0 0 1 3.065 0l1.358 1.564a.03.03 0 0 0 .028.01l2.046-.325a2.03 2.03 0 0 1 2.347 1.971l.037 2.07q0 .016.014.026l1.776 1.066a2.03 2.03 0 0 1 .532 3.019l-1.304 1.609a.03.03 0 0 0-.005.03l.675 1.956a2.03 2.03 0 0 1-1.533 2.656l-2.033.394a.03.03 0 0 0-.023.019l-.741 1.933a2.03 2.03 0 0 1-2.88 1.05l-1.811-1.006a.03.03 0 0 0-.03 0l-1.811 1.005a2.03 2.03 0 0 1-2.88-1.049l-.742-1.933a.03.03 0 0 0-.023-.019l-2.033-.394a2.03 2.03 0 0 1-1.532-2.656l.675-1.957a.03.03 0 0 0-.005-.029l-1.304-1.61a2.03 2.03 0 0 1 .532-3.018l1.776-1.066a.03.03 0 0 0 .014-.026l.035-2.07a2.03 2.03 0 0 1 2.349-1.97l2.045.324a.03.03 0 0 0 .028-.01zm1.516 3.76a.5.5 0 0 0-.447.276l-1.861 3.724H8.483a1 1 0 0 0-1 1v3.5a1 1 0 0 0 1 1h6.692a2 2 0 0 0 1.981-1.73l.341-2.5a2 2 0 0 0-1.982-2.27h-1.939l.197-1.269a1.5 1.5 0 0 0-1.481-1.731z"),
|
|
3489
4006
|
"ArrowBottomOutline" to listOf("m13 17.586 5-5L19.414 14 12 21.414 4.586 14 6 12.586l5 5V3h2z"),
|
|
3490
4007
|
"ArrowTopOutline" to listOf("M19.414 10 18 11.414l-5-5V21h-2V6.414l-5 5L4.586 10 12 2.586z"),
|
|
3491
4008
|
"ChartTrendingUpOutline" to listOf("M22 13h-2V9.414l-7 7-4-4-6 6L1.586 17 9 9.586l4 4L18.586 8H15V6h7z"),
|