@onekeyfe/react-native-native-list 3.0.114 → 3.0.115
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/build.gradle +1 -0
- 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 +404 -90
- package/ios/NativeListCell.swift +506 -20
- package/ios/NativeListDesignAssets.swift +8 -0
- package/ios/RNCNativeListView.swift +271 -42
- 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 +461 -51
- package/lib/typescript/src/models.d.ts +82 -2
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +37 -3
- package/package.json +4 -2
- package/src/models.ts +121 -3
- package/src/validation.ts +206 -0
- package/src/web/NativeListWebEngine.ts +766 -91
|
@@ -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(
|
|
@@ -2541,13 +3008,33 @@ internal class NativeListRowView(
|
|
|
2541
3008
|
index = index,
|
|
2542
3009
|
count = visibleSources.size,
|
|
2543
3010
|
sizeDp = sizeDp,
|
|
3011
|
+
heightDp = heightDp,
|
|
2544
3012
|
tokenPair = tokenPair,
|
|
2545
3013
|
)
|
|
2546
3014
|
image.outlineProvider = when {
|
|
2547
3015
|
tokenPair && index == 1 -> circleOutlineProvider
|
|
3016
|
+
cornerRadiusDp != null -> roundedOutlineProvider(cornerRadiusDp)
|
|
2548
3017
|
else -> leadingOutlineProvider(shape)
|
|
2549
3018
|
}
|
|
2550
3019
|
image.clipToOutline = true
|
|
3020
|
+
if ((tag as? NativeListItem)?.type == "market" && index == 0 && visual.optString("borderColor").isNotEmpty()) {
|
|
3021
|
+
val inset = dp(1)
|
|
3022
|
+
val radius = scaledDp(cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)))
|
|
3023
|
+
leadingFrame.background = GradientDrawable().apply {
|
|
3024
|
+
setColor(visualBackground)
|
|
3025
|
+
setStroke(inset, safeColor(visual.optString("borderColor"), Color.TRANSPARENT))
|
|
3026
|
+
this.cornerRadius = radius
|
|
3027
|
+
}
|
|
3028
|
+
image.layoutParams = FrameLayout.LayoutParams(dp(sizeDp) - inset * 2, dp(heightDp) - inset * 2).apply {
|
|
3029
|
+
leftMargin = inset
|
|
3030
|
+
topMargin = inset
|
|
3031
|
+
}
|
|
3032
|
+
image.outlineProvider = object : ViewOutlineProvider() {
|
|
3033
|
+
override fun getOutline(view: View, outline: Outline) {
|
|
3034
|
+
outline.setRoundRect(-inset, -inset, view.width + inset, view.height + inset, radius)
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
2551
3038
|
val fallbackIcon = if (index == 0) visual.optJSONObject("fallbackIcon") else null
|
|
2552
3039
|
val expectedEpoch = bindingEpoch
|
|
2553
3040
|
bindImage(source, image, boundKey ?: "", index, variant,
|
|
@@ -2664,10 +3151,11 @@ internal class NativeListRowView(
|
|
|
2664
3151
|
index: Int,
|
|
2665
3152
|
count: Int,
|
|
2666
3153
|
sizeDp: Int,
|
|
3154
|
+
heightDp: Int,
|
|
2667
3155
|
tokenPair: Boolean,
|
|
2668
3156
|
): FrameLayout.LayoutParams {
|
|
2669
3157
|
if (count == 1 || tokenPair && index == 0) {
|
|
2670
|
-
return FrameLayout.LayoutParams(dp(sizeDp), dp(
|
|
3158
|
+
return FrameLayout.LayoutParams(dp(sizeDp), dp(heightDp))
|
|
2671
3159
|
}
|
|
2672
3160
|
if (tokenPair && index == 1) {
|
|
2673
3161
|
return FrameLayout.LayoutParams(dp(16), dp(16), Gravity.END or Gravity.BOTTOM).apply {
|
|
@@ -3009,6 +3497,22 @@ internal class NativeListRowView(
|
|
|
3009
3497
|
}
|
|
3010
3498
|
|
|
3011
3499
|
private fun applySize(item: NativeListItem) {
|
|
3500
|
+
if (item.type == "market") {
|
|
3501
|
+
val style = item.json.optJSONObject("style")
|
|
3502
|
+
val imageHeight = style?.optJSONObject("image")?.optDouble(
|
|
3503
|
+
"height",
|
|
3504
|
+
if (item.json.optString("variant") == "stock") 40.0 else 32.0,
|
|
3505
|
+
) ?: if (item.json.optString("variant") == "stock") 40.0 else 32.0
|
|
3506
|
+
val verticalPadding = style?.optDouble("verticalPadding", 12.0) ?: 12.0
|
|
3507
|
+
val defaultHeight = if (item.json.optString("variant") == "stock") 72.0 else 68.0
|
|
3508
|
+
minimumHeight = dp(
|
|
3509
|
+
item.json.optDouble(
|
|
3510
|
+
"height",
|
|
3511
|
+
maxOf(defaultHeight, imageHeight + verticalPadding * 2),
|
|
3512
|
+
).roundToInt(),
|
|
3513
|
+
)
|
|
3514
|
+
return
|
|
3515
|
+
}
|
|
3012
3516
|
val isWalletSidebar =
|
|
3013
3517
|
item.type == "identity" && item.json.optString("presentation") == "walletSidebar"
|
|
3014
3518
|
val isAccountSelectorIdentity =
|
|
@@ -3161,9 +3665,14 @@ internal class NativeListRowView(
|
|
|
3161
3665
|
else -> 36
|
|
3162
3666
|
}
|
|
3163
3667
|
"system" -> when (item.json.optString("variant")) {
|
|
3668
|
+
"loading" -> when (item.json.optString("loadingStyle")) {
|
|
3669
|
+
"skeleton" -> 56
|
|
3670
|
+
"spinner" -> 52
|
|
3671
|
+
else -> if (item.json.optString("presentation") == "market") 68 else 56
|
|
3672
|
+
}
|
|
3673
|
+
"noMatch", "retry" -> if (item.json.optString("presentation") == "market") 44 else if (item.json.optString("variant") == "noMatch") 36 else 44
|
|
3164
3674
|
"warning" -> 0
|
|
3165
|
-
"
|
|
3166
|
-
"retry" -> 44
|
|
3675
|
+
"end" -> if (item.json.optString("presentation") == "market") 44 else 36
|
|
3167
3676
|
else -> 56
|
|
3168
3677
|
}
|
|
3169
3678
|
"action" -> when {
|
|
@@ -3269,6 +3778,12 @@ internal class NativeListRowView(
|
|
|
3269
3778
|
}
|
|
3270
3779
|
}
|
|
3271
3780
|
|
|
3781
|
+
private fun roundedOutlineProvider(radiusDp: Float) = object : ViewOutlineProvider() {
|
|
3782
|
+
override fun getOutline(view: View, outline: Outline) {
|
|
3783
|
+
outline.setRoundRect(0, 0, view.width, view.height, scaledDp(radiusDp))
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3272
3787
|
private fun JSONArray?.hasAccessory(kind: String): Boolean {
|
|
3273
3788
|
if (this == null) return false
|
|
3274
3789
|
return (0 until length()).any { optJSONObject(it)?.optString("kind") == kind }
|
|
@@ -3465,6 +3980,7 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
3465
3980
|
"CrossedSmallSolid" to listOf(Path.FillType.WINDING),
|
|
3466
3981
|
"AccountErrorCustom" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
3467
3982
|
"Circle" to listOf(Path.FillType.WINDING),
|
|
3983
|
+
"BadgeVerifiedSolid" to listOf(Path.FillType.EVEN_ODD),
|
|
3468
3984
|
"ChevronRightSmallOutline" to listOf(Path.FillType.WINDING),
|
|
3469
3985
|
"MinusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
3470
3986
|
"PlusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
|
|
@@ -3492,6 +4008,7 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
|
|
|
3492
4008
|
"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"),
|
|
3493
4009
|
"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"),
|
|
3494
4010
|
"Circle" to listOf("M0 12a12 12 0 1 0 24 0a12 12 0 1 0 -24 0"),
|
|
4011
|
+
"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"),
|
|
3495
4012
|
"ArrowBottomOutline" to listOf("m13 17.586 5-5L19.414 14 12 21.414 4.586 14 6 12.586l5 5V3h2z"),
|
|
3496
4013
|
"ArrowTopOutline" to listOf("M19.414 10 18 11.414l-5-5V21h-2V6.414l-5 5L4.586 10 12 2.586z"),
|
|
3497
4014
|
"ChartTrendingUpOutline" to listOf("M22 13h-2V9.414l-7 7-4-4-6 6L1.586 17 9 9.586l4 4L18.586 8H15V6h7z"),
|