@onekeyfe/react-native-native-list 3.0.113 → 3.0.114

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.
@@ -6,18 +6,12 @@ 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
11
9
  import android.graphics.Outline
12
10
  import android.graphics.Paint
13
11
  import android.graphics.Path
14
- import android.graphics.RectF
15
- import android.graphics.Shader
16
12
  import android.graphics.Typeface
17
13
  import android.graphics.drawable.Drawable
18
14
  import android.graphics.drawable.GradientDrawable
19
- import android.os.Handler
20
- import android.os.Looper
21
15
  import android.text.Spannable
22
16
  import android.text.SpannableStringBuilder
23
17
  import android.text.TextUtils
@@ -30,7 +24,6 @@ import android.view.MotionEvent
30
24
  import android.view.View
31
25
  import android.view.ViewGroup
32
26
  import android.view.ViewOutlineProvider
33
- import android.view.animation.LinearInterpolator
34
27
  import android.widget.FrameLayout
35
28
  import android.widget.LinearLayout
36
29
  import android.widget.ProgressBar
@@ -49,69 +42,6 @@ import org.json.JSONArray
49
42
  import org.json.JSONObject
50
43
  import kotlin.math.roundToInt
51
44
 
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
-
115
45
  internal data class NativeListActionOrigin(
116
46
  val sourceView: View,
117
47
  val ownerRowView: NativeListRowView,
@@ -217,25 +147,26 @@ private class PackedTitleLineLayout(context: android.content.Context) : LinearLa
217
147
  }
218
148
 
219
149
  val title = getChildAt(0)
150
+ val badge = getChildAt(1)
220
151
  val widthMode = MeasureSpec.getMode(widthMeasureSpec)
221
152
  val widthSize = MeasureSpec.getSize(widthMeasureSpec)
222
- var accessoryWidth = 0
223
- for (index in 1 until childCount) {
224
- val child = getChildAt(index)
225
- if (child.visibility == GONE) continue
153
+ if (badge.visibility != GONE) {
226
154
  measureChildWithMargins(
227
- child,
155
+ badge,
228
156
  widthMeasureSpec,
229
- paddingLeft + paddingRight + accessoryWidth,
157
+ paddingLeft + paddingRight,
230
158
  heightMeasureSpec,
231
159
  paddingTop + paddingBottom,
232
160
  )
233
- val margins = child.layoutParams as MarginLayoutParams
234
- accessoryWidth += child.measuredWidth + margins.leftMargin + margins.rightMargin
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
235
166
  }
236
167
  val titleMargins = title.layoutParams as MarginLayoutParams
237
168
  if (widthMode != MeasureSpec.UNSPECIFIED) {
238
- (title as TextView).maxWidth = (widthSize - paddingLeft - paddingRight - accessoryWidth -
169
+ (title as TextView).maxWidth = (widthSize - paddingLeft - paddingRight - badgeWidth -
239
170
  titleMargins.leftMargin - titleMargins.rightMargin).coerceAtLeast(0)
240
171
  }
241
172
  (title.layoutParams as LayoutParams).apply {
@@ -431,10 +362,6 @@ internal class NativeListRowView(
431
362
  private val status = TextView(context)
432
363
  private val metricSubtitle = TextView(context)
433
364
  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) }
438
365
  private val activityContentRow = LinearLayout(context)
439
366
  private val actionLine = LinearLayout(context)
440
367
  private val actionViews = List(3) { TextView(context) }
@@ -475,11 +402,6 @@ internal class NativeListRowView(
475
402
  private var pressedRowBackground: Drawable? = null
476
403
  // OneKey patch: preserve a held row independently from RecyclerView snapshot rebinding.
477
404
  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
483
405
  private var reorderActive = false
484
406
  private var checkboxCheckedColor = Color.rgb(32, 32, 32)
485
407
  private var checkboxUncheckedColor = Color.rgb(252, 252, 252)
@@ -544,23 +466,6 @@ internal class NativeListRowView(
544
466
  titleLine.gravity = Gravity.CENTER_VERTICAL
545
467
  titleLine.addView(title, LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f))
546
468
  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
- }
564
469
  mainColumn.addView(titleLine)
565
470
  mainColumn.addView(subtitle)
566
471
  mainColumn.addView(tertiary)
@@ -605,47 +510,19 @@ internal class NativeListRowView(
605
510
  when (event.actionMasked) {
606
511
  MotionEvent.ACTION_DOWN -> if (isEnabled) {
607
512
  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
- }
630
513
  if ((tag as? NativeListItem)?.type == "mediaTile") {
631
514
  leadingFrame.alpha = 0.8f
632
515
  } else {
633
516
  background = pressedRowBackground
634
517
  }
635
518
  }
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
- }
519
+ MotionEvent.ACTION_MOVE -> if (
520
+ event.x < 0 || event.y < 0 || event.x >= width || event.y >= height
521
+ ) {
522
+ touchPressed = false
523
+ restoreRestingBackground()
646
524
  }
647
525
  MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
648
- cancelMarketLongPress()
649
526
  touchPressed = false
650
527
  restoreRestingBackground()
651
528
  }
@@ -654,10 +531,6 @@ internal class NativeListRowView(
654
531
  }
655
532
  setOnClickListener { view ->
656
533
  (view.tag as? NativeListItem)?.let { item ->
657
- if (item.type == "market" && marketLongPressFired) {
658
- marketLongPressFired = false
659
- return@let
660
- }
661
534
  // OneKey patch: allow create-address accessories when whole-row press is gated.
662
535
  if (!item.json.optBoolean("pressDisabled", false)) onRowPress?.invoke(item, actionOrigin(view, "row"))
663
536
  }
@@ -777,15 +650,13 @@ internal class NativeListRowView(
777
650
  useSourceScale: Boolean = false,
778
651
  ) {
779
652
  val shouldRestorePressed = touchPressed && boundKey == item.key
780
- cancelMarketLongPress()
781
- marketLongPressFired = false
782
653
  invalidateCurrentBinding()
783
654
  bindingEpoch += 1
784
655
  boundKey = item.key
785
656
  touchPressed = shouldRestorePressed
786
657
  currentLayout = layout
787
658
  tag = item
788
- selectorUsesSourceScale = item.type == "market" || item.usesSelectorSourceScale || useSourceScale
659
+ selectorUsesSourceScale = item.usesSelectorSourceScale || useSourceScale
789
660
  reorderActive = false
790
661
  leadingImages.forEach(OneKeyImageReusableView::prepareForReuse)
791
662
  secondaryImage.prepareForReuse()
@@ -871,7 +742,6 @@ internal class NativeListRowView(
871
742
  "activity" -> bindActivity(item, theme)
872
743
  "message" -> bindMessage(item, theme)
873
744
  "dataRow" -> bindDataRow(item, theme, checkboxState)
874
- "market" -> bindMarket(item, theme)
875
745
  "mediaTile" -> bindMediaTile(item, theme)
876
746
  "metricCard" -> bindMetricCard(item, theme)
877
747
  "sectionHeader" -> bindSectionHeader(item, theme, checkboxState)
@@ -906,8 +776,6 @@ internal class NativeListRowView(
906
776
  }
907
777
 
908
778
  fun recycle() {
909
- cancelMarketLongPress()
910
- marketLongPressFired = false
911
779
  touchPressed = false
912
780
  restoreRestingBackground()
913
781
  invalidateCurrentBinding()
@@ -998,7 +866,6 @@ internal class NativeListRowView(
998
866
  }
999
867
 
1000
868
  fun dispose() {
1001
- cancelMarketLongPress()
1002
869
  invalidateCurrentBinding()
1003
870
  restoreRestingBackground()
1004
871
  selectorImages.forEach(OneKeyImageReusableView::dispose)
@@ -1007,7 +874,6 @@ internal class NativeListRowView(
1007
874
  secondaryImage.dispose()
1008
875
  mediaNetworkImage.dispose()
1009
876
  metricVisualImages.forEach(OneKeyImageReusableView::dispose)
1010
- marketBadgeImages.forEach(OneKeyImageReusableView::dispose)
1011
877
  walletGroupRows.forEach(NativeListRowView::dispose)
1012
878
  }
1013
879
 
@@ -1087,25 +953,6 @@ internal class NativeListRowView(
1087
953
  selectorImages.forEach(OneKeyImageReusableView::dispose)
1088
954
  selectorImages.clear()
1089
955
  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
- }
1109
956
  title.setOnClickListener(null)
1110
957
  title.isClickable = false
1111
958
  walletGroupRows.forEach { it.invalidateCurrentBinding() }
@@ -1274,21 +1121,12 @@ internal class NativeListRowView(
1274
1121
  mainColumn.removeView(skeletonSecondary)
1275
1122
  setOnClickListener { view ->
1276
1123
  (view.tag as? NativeListItem)?.let { item ->
1277
- if (item.type == "market" && marketLongPressFired) {
1278
- marketLongPressFired = false
1279
- return@let
1280
- }
1281
1124
  // OneKey patch: allow create-address accessories when whole-row press is gated.
1282
1125
  if (!item.json.optBoolean("pressDisabled", false)) onRowPress?.invoke(item, actionOrigin(view, "row"))
1283
1126
  }
1284
1127
  }
1285
1128
  }
1286
1129
 
1287
- private fun cancelMarketLongPress() {
1288
- marketLongPressRunnable?.let(marketLongPressHandler::removeCallbacks)
1289
- marketLongPressRunnable = null
1290
- }
1291
-
1292
1130
  private fun restoreRestingBackground() {
1293
1131
  background = if (reorderActive) pressedRowBackground else restingRowBackground
1294
1132
  leadingFrame.alpha = 1f
@@ -1895,272 +1733,6 @@ internal class NativeListRowView(
1895
1733
  addView(dataContainer, weighted())
1896
1734
  }
1897
1735
 
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
-
2164
1736
  private fun styledDataText(
2165
1737
  column: JSONObject,
2166
1738
  badges: JSONArray?,
@@ -2795,40 +2367,6 @@ internal class NativeListRowView(
2795
2367
 
2796
2368
  private fun bindSystem(item: NativeListItem, theme: JSONObject?) {
2797
2369
  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
- }
2832
2370
  // OneKey patch: warning title/description wrap inside the actual scroll content.
2833
2371
  if (variant == "warning") {
2834
2372
  setPadding(dp(12), dp(14), dp(12), dp(14))
@@ -2862,11 +2400,11 @@ internal class NativeListRowView(
2862
2400
  }
2863
2401
  gravity = Gravity.CENTER
2864
2402
  if (variant == "loading") {
2865
- addLeading(JSONObject().put("kind", "skeleton"), if (isMarket) 32 else 40)
2403
+ addLeading(JSONObject().put("kind", "skeleton"), 40)
2866
2404
  leadingFallback.text = ""
2867
2405
  leadingFallback.background = roundedFill(
2868
2406
  color(theme, "strongBackground", "#0000000F"),
2869
- if (isMarket) 16f else 20f,
2407
+ 20f,
2870
2408
  )
2871
2409
  addSkeleton(skeletonPrimary, 120, 12, theme, bottomMarginDp = 8)
2872
2410
  addSkeleton(skeletonSecondary, 80, 12, theme)
@@ -2907,17 +2445,15 @@ internal class NativeListRowView(
2907
2445
  sizeDp: Int = 40,
2908
2446
  secondaryVisual: JSONObject? = null,
2909
2447
  spacingDp: Int = 12,
2910
- heightDp: Int = sizeDp,
2911
- cornerRadiusDp: Float? = null,
2912
2448
  ) {
2913
2449
  leadingFrame.visibility = VISIBLE
2914
- leadingFrame.layoutParams = LayoutParams(dp(sizeDp), dp(heightDp)).apply {
2450
+ leadingFrame.layoutParams = LayoutParams(dp(sizeDp), dp(sizeDp)).apply {
2915
2451
  val item = tag as? NativeListItem
2916
2452
  // OneKey patch: Yoga rounds cumulative selector edges, not each 12dp gap separately.
2917
2453
  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)
2918
2454
  }
2919
2455
  addView(leadingFrame)
2920
- leadingFallback.layoutParams = FrameLayout.LayoutParams(dp(sizeDp), dp(heightDp))
2456
+ leadingFallback.layoutParams = FrameLayout.LayoutParams(dp(sizeDp), dp(sizeDp))
2921
2457
  if (visual == null) return
2922
2458
  val kind = visual.optString("kind")
2923
2459
  val shape = visual.optString(
@@ -2938,19 +2474,16 @@ internal class NativeListRowView(
2938
2474
  if (!isIcon && visual.optString("backgroundColor").isNotEmpty()) {
2939
2475
  leadingFrame.background = roundedFill(
2940
2476
  visualBackground,
2941
- cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)),
2477
+ leadingCornerRadius(shape, sizeDp),
2942
2478
  )
2943
2479
  }
2944
- leadingFallback.background = roundedFill(
2945
- visualBackground,
2946
- cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)),
2947
- )
2480
+ leadingFallback.background = roundedFill(visualBackground, leadingCornerRadius(shape, sizeDp))
2948
2481
  leadingFallback.visibility = if (!isIcon && sources.isEmpty()) VISIBLE else GONE
2949
2482
  if (isIcon) {
2950
2483
  leadingFrame.background = GradientDrawable().apply {
2951
2484
  setColor(visualBackground)
2952
2485
  setStroke(1, parseNativeListColor("#0000001F"))
2953
- cornerRadius = scaledDp(cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp)))
2486
+ cornerRadius = scaledDp(leadingCornerRadius(shape, sizeDp))
2954
2487
  }
2955
2488
  leadingIcon.iconName = visual.optString("name")
2956
2489
  leadingIcon.tintColor = safeColor(
@@ -2961,9 +2494,15 @@ internal class NativeListRowView(
2961
2494
  }
2962
2495
  val visibleSources = sources.take(leadingImages.size)
2963
2496
  val tokenPair = kind == "token" && visibleSources.size > 1
2964
- if (tokenPair) {
2965
- // The network badge intentionally extends past the avatar frame.
2497
+ val hasAccountSelectorBadge =
2498
+ kind == "account" &&
2499
+ (tag as? NativeListItem)?.json?.optString("presentation") == "accountSelector" &&
2500
+ (visual.optJSONArray("overlays")?.length() ?: 0) > 0
2501
+ if (tokenPair || hasAccountSelectorBadge) {
2502
+ // Network badges intentionally extend past the avatar frame.
2966
2503
  clipChildren = false
2504
+ }
2505
+ if (tokenPair) {
2967
2506
  leadingOverlayBackground.visibility = VISIBLE
2968
2507
  leadingOverlayBackground.background = roundedFill(visualBackdropColor, 10f)
2969
2508
  leadingOverlayBackground.layoutParams = FrameLayout.LayoutParams(
@@ -3002,33 +2541,13 @@ internal class NativeListRowView(
3002
2541
  index = index,
3003
2542
  count = visibleSources.size,
3004
2543
  sizeDp = sizeDp,
3005
- heightDp = heightDp,
3006
2544
  tokenPair = tokenPair,
3007
2545
  )
3008
2546
  image.outlineProvider = when {
3009
2547
  tokenPair && index == 1 -> circleOutlineProvider
3010
- cornerRadiusDp != null -> roundedOutlineProvider(cornerRadiusDp)
3011
2548
  else -> leadingOutlineProvider(shape)
3012
2549
  }
3013
2550
  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
- }
3032
2551
  val fallbackIcon = if (index == 0) visual.optJSONObject("fallbackIcon") else null
3033
2552
  val expectedEpoch = bindingEpoch
3034
2553
  bindImage(source, image, boundKey ?: "", index, variant,
@@ -3145,11 +2664,10 @@ internal class NativeListRowView(
3145
2664
  index: Int,
3146
2665
  count: Int,
3147
2666
  sizeDp: Int,
3148
- heightDp: Int,
3149
2667
  tokenPair: Boolean,
3150
2668
  ): FrameLayout.LayoutParams {
3151
2669
  if (count == 1 || tokenPair && index == 0) {
3152
- return FrameLayout.LayoutParams(dp(sizeDp), dp(heightDp))
2670
+ return FrameLayout.LayoutParams(dp(sizeDp), dp(sizeDp))
3153
2671
  }
3154
2672
  if (tokenPair && index == 1) {
3155
2673
  return FrameLayout.LayoutParams(dp(16), dp(16), Gravity.END or Gravity.BOTTOM).apply {
@@ -3491,22 +3009,6 @@ internal class NativeListRowView(
3491
3009
  }
3492
3010
 
3493
3011
  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
- }
3510
3012
  val isWalletSidebar =
3511
3013
  item.type == "identity" && item.json.optString("presentation") == "walletSidebar"
3512
3014
  val isAccountSelectorIdentity =
@@ -3659,14 +3161,9 @@ internal class NativeListRowView(
3659
3161
  else -> 36
3660
3162
  }
3661
3163
  "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
3668
3164
  "warning" -> 0
3669
- "end" -> if (item.json.optString("presentation") == "market") 44 else 36
3165
+ "noMatch", "end" -> 36
3166
+ "retry" -> 44
3670
3167
  else -> 56
3671
3168
  }
3672
3169
  "action" -> when {
@@ -3772,12 +3269,6 @@ internal class NativeListRowView(
3772
3269
  }
3773
3270
  }
3774
3271
 
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
-
3781
3272
  private fun JSONArray?.hasAccessory(kind: String): Boolean {
3782
3273
  if (this == null) return false
3783
3274
  return (0 until length()).any { optJSONObject(it)?.optString("kind") == kind }
@@ -3974,7 +3465,6 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
3974
3465
  "CrossedSmallSolid" to listOf(Path.FillType.WINDING),
3975
3466
  "AccountErrorCustom" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
3976
3467
  "Circle" to listOf(Path.FillType.WINDING),
3977
- "BadgeVerifiedSolid" to listOf(Path.FillType.EVEN_ODD),
3978
3468
  "ChevronRightSmallOutline" to listOf(Path.FillType.WINDING),
3979
3469
  "MinusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
3980
3470
  "PlusCircleOutline" to listOf(Path.FillType.WINDING, Path.FillType.EVEN_ODD),
@@ -4002,7 +3492,6 @@ private class OneKeyIconView(context: android.content.Context) : View(context) {
4002
3492
  "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"),
4003
3493
  "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"),
4004
3494
  "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"),
4006
3495
  "ArrowBottomOutline" to listOf("m13 17.586 5-5L19.414 14 12 21.414 4.586 14 6 12.586l5 5V3h2z"),
4007
3496
  "ArrowTopOutline" to listOf("M19.414 10 18 11.414l-5-5V21h-2V6.414l-5 5L4.586 10 12 2.586z"),
4008
3497
  "ChartTrendingUpOutline" to listOf("M22 13h-2V9.414l-7 7-4-4-6 6L1.586 17 9 9.586l4 4L18.586 8H15V6h7z"),