@onekeyfe/react-native-native-list 3.0.115 → 3.0.117
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +30 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +309 -32
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +41 -1
- package/ios/NativeListCell.swift +191 -18
- package/ios/RNCNativeListView.swift +4 -0
- package/lib/module/validation.js +24 -1
- package/lib/module/web/NativeListWebEngine.js +11 -0
- package/lib/typescript/src/models.d.ts +30 -1
- package/package.json +3 -3
- package/src/models.ts +25 -0
- package/src/validation.ts +54 -0
- package/src/web/NativeListWebEngine.ts +18 -0
|
@@ -10,6 +10,8 @@ import com.facebook.react.uimanager.ThemedReactContext
|
|
|
10
10
|
import org.json.JSONObject
|
|
11
11
|
import java.util.Collections
|
|
12
12
|
import java.util.WeakHashMap
|
|
13
|
+
import kotlin.math.ceil
|
|
14
|
+
import kotlin.math.roundToInt
|
|
13
15
|
|
|
14
16
|
internal class NativeListAdapter(
|
|
15
17
|
private val context: ThemedReactContext,
|
|
@@ -39,6 +41,8 @@ internal class NativeListAdapter(
|
|
|
39
41
|
)
|
|
40
42
|
val currentList: List<NativeListItem>
|
|
41
43
|
get() = differ.currentList
|
|
44
|
+
private var marketSourceItems: List<NativeListItem>? = null
|
|
45
|
+
private var marketSourceEdges = DoubleArray(0)
|
|
42
46
|
private val createdRows = Collections.newSetFromMap(
|
|
43
47
|
WeakHashMap<NativeListRowView, Boolean>(),
|
|
44
48
|
)
|
|
@@ -122,6 +126,30 @@ internal class NativeListAdapter(
|
|
|
122
126
|
fun itemAt(position: Int): NativeListItem? =
|
|
123
127
|
reorderItems?.getOrNull(position) ?: differ.currentList.getOrNull(position)
|
|
124
128
|
|
|
129
|
+
// OneKey patch: Yoga rounds badge dimensions from unrounded cumulative row edges.
|
|
130
|
+
fun marketSourceEdgePx(position: Int): Double {
|
|
131
|
+
val items = reorderItems ?: differ.currentList
|
|
132
|
+
if (marketSourceItems !== items) {
|
|
133
|
+
val density = context.resources.displayMetrics.density.toDouble()
|
|
134
|
+
val edges = DoubleArray(items.size + 1)
|
|
135
|
+
items.forEachIndexed { index, item ->
|
|
136
|
+
val style = item.json.optJSONObject("style")
|
|
137
|
+
val height = item.json.optDouble("height", 0.0)
|
|
138
|
+
val sourceHeight = if (item.type == "market" && style != null) {
|
|
139
|
+
val titleHeight = ceil((style.optJSONObject("title")?.optDouble("lineHeight", 24.0) ?: 24.0) * density) / density
|
|
140
|
+
val subtitleHeight = if (item.json.optString("subtitle").isNotEmpty() || item.json.optJSONObject("subtitlePrefix") != null) {
|
|
141
|
+
ceil((style.optJSONObject("subtitle")?.optDouble("lineHeight", 20.0) ?: 20.0) * density) / density + style.optDouble("lineGap", 0.0)
|
|
142
|
+
} else 0.0
|
|
143
|
+
maxOf(height.roundToInt().toDouble(), titleHeight + subtitleHeight + style.optDouble("verticalPadding", 12.0) * 2).toFloat().toDouble()
|
|
144
|
+
} else height
|
|
145
|
+
edges[index + 1] = edges[index] + sourceHeight * density
|
|
146
|
+
}
|
|
147
|
+
marketSourceItems = items
|
|
148
|
+
marketSourceEdges = edges
|
|
149
|
+
}
|
|
150
|
+
return marketSourceEdges.getOrElse(position) { 0.0 }
|
|
151
|
+
}
|
|
152
|
+
|
|
125
153
|
fun positionOfKey(key: String): Int =
|
|
126
154
|
(reorderItems ?: differ.currentList).indexOfFirst { it.key == key }
|
|
127
155
|
|
|
@@ -169,6 +197,8 @@ internal class NativeListAdapter(
|
|
|
169
197
|
}
|
|
170
198
|
|
|
171
199
|
fun dispose() {
|
|
200
|
+
marketSourceItems = null
|
|
201
|
+
marketSourceEdges = DoubleArray(0)
|
|
172
202
|
reorderItems = null
|
|
173
203
|
suppressDifferUpdates = false
|
|
174
204
|
createdRows.forEach(NativeListRowView::dispose)
|
|
@@ -45,6 +45,7 @@ import com.facebook.react.uimanager.style.LogicalEdge
|
|
|
45
45
|
import com.margelo.nitro.onekeyimage.OneKeyImageReusableView
|
|
46
46
|
import androidx.core.graphics.PathParser
|
|
47
47
|
import androidx.core.widget.TextViewCompat
|
|
48
|
+
import androidx.recyclerview.widget.RecyclerView
|
|
48
49
|
import org.json.JSONArray
|
|
49
50
|
import org.json.JSONObject
|
|
50
51
|
import kotlin.math.roundToInt
|
|
@@ -119,6 +120,7 @@ internal data class NativeListActionOrigin(
|
|
|
119
120
|
val source: String,
|
|
120
121
|
val slot: Int? = null,
|
|
121
122
|
val anchorInsetPixels: Int = 0,
|
|
123
|
+
val windowPointPixels: android.graphics.PointF? = null,
|
|
122
124
|
)
|
|
123
125
|
|
|
124
126
|
/** React Native color strings use CSS #RRGGBBAA ordering; Android expects #AARRGGBB. */
|
|
@@ -209,6 +211,8 @@ private class DottedUnderlineTextView(context: android.content.Context) : TextVi
|
|
|
209
211
|
|
|
210
212
|
private class PackedTitleLineLayout(context: android.content.Context) : LinearLayout(context) {
|
|
211
213
|
var packsChildrenAtStart = false
|
|
214
|
+
// OneKey patch: optional cap for the Market subtitle's localized name.
|
|
215
|
+
var leadingTextMaxWidth = Int.MAX_VALUE
|
|
212
216
|
|
|
213
217
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
214
218
|
if (!packsChildrenAtStart || childCount < 2) {
|
|
@@ -236,7 +240,7 @@ private class PackedTitleLineLayout(context: android.content.Context) : LinearLa
|
|
|
236
240
|
val titleMargins = title.layoutParams as MarginLayoutParams
|
|
237
241
|
if (widthMode != MeasureSpec.UNSPECIFIED) {
|
|
238
242
|
(title as TextView).maxWidth = (widthSize - paddingLeft - paddingRight - accessoryWidth -
|
|
239
|
-
titleMargins.leftMargin - titleMargins.rightMargin).coerceAtLeast(0)
|
|
243
|
+
titleMargins.leftMargin - titleMargins.rightMargin).coerceAtLeast(0).coerceAtMost(leadingTextMaxWidth)
|
|
240
244
|
}
|
|
241
245
|
(title.layoutParams as LayoutParams).apply {
|
|
242
246
|
width = LayoutParams.WRAP_CONTENT
|
|
@@ -393,7 +397,16 @@ private class NativeListTableColumnView(context: android.content.Context) : Line
|
|
|
393
397
|
internal class NativeListRowView(
|
|
394
398
|
private val reactContext: ThemedReactContext,
|
|
395
399
|
) : LinearLayout(reactContext) {
|
|
396
|
-
private
|
|
400
|
+
private var marketLeadingUsesSourceClip = false
|
|
401
|
+
private val leadingFrame = object : FrameLayout(context) {
|
|
402
|
+
override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean {
|
|
403
|
+
if (!marketLeadingUsesSourceClip || child !== leadingImages[0]) return super.drawChild(canvas, child, drawingTime)
|
|
404
|
+
// OneKey patch: clip only the Market avatar to the source border's padding box.
|
|
405
|
+
val saved = canvas.save()
|
|
406
|
+
BackgroundStyleApplicator.clipToPaddingBox(this, canvas)
|
|
407
|
+
return try { super.drawChild(canvas, child, drawingTime) } finally { canvas.restoreToCount(saved) }
|
|
408
|
+
}
|
|
409
|
+
}
|
|
397
410
|
// OneKey patch: reset selector fragments and corner decorations on every bind.
|
|
398
411
|
private val selectorViews = mutableListOf<View>()
|
|
399
412
|
private var selectorUsesSourceScale = false
|
|
@@ -405,6 +418,7 @@ internal class NativeListRowView(
|
|
|
405
418
|
}
|
|
406
419
|
private val selectorOriginalFontFeatures = mutableMapOf<TextView, String?>()
|
|
407
420
|
private val selectorOriginalPaintFlags = mutableMapOf<TextView, Int>()
|
|
421
|
+
private val marketOriginalPaintFlags = mutableMapOf<TextView, Int>()
|
|
408
422
|
private val selectorLineHeights = mutableMapOf<TextView, Int>()
|
|
409
423
|
private val selectorFontSizes = mutableMapOf<TextView, Float>()
|
|
410
424
|
private val selectorImages = mutableListOf<OneKeyImageReusableView>()
|
|
@@ -427,6 +441,8 @@ internal class NativeListRowView(
|
|
|
427
441
|
private val titleLine = PackedTitleLineLayout(context)
|
|
428
442
|
private val title = DottedUnderlineTextView(context)
|
|
429
443
|
private val subtitle = TextView(context)
|
|
444
|
+
// OneKey patch: the first text shrinks while the volume retains its width.
|
|
445
|
+
private val marketSubtitleLine = PackedTitleLineLayout(context)
|
|
430
446
|
private val tertiary = TextView(context)
|
|
431
447
|
private val status = TextView(context)
|
|
432
448
|
private val metricSubtitle = TextView(context)
|
|
@@ -479,6 +495,8 @@ internal class NativeListRowView(
|
|
|
479
495
|
private var marketLongPressRunnable: Runnable? = null
|
|
480
496
|
private var marketTouchStartX = 0f
|
|
481
497
|
private var marketTouchStartY = 0f
|
|
498
|
+
private var marketTouchX = 0f
|
|
499
|
+
private var marketTouchY = 0f
|
|
482
500
|
private var marketLongPressFired = false
|
|
483
501
|
private var reorderActive = false
|
|
484
502
|
private var checkboxCheckedColor = Color.rgb(32, 32, 32)
|
|
@@ -610,6 +628,8 @@ internal class NativeListRowView(
|
|
|
610
628
|
if (item?.type == "market") {
|
|
611
629
|
marketTouchStartX = event.x
|
|
612
630
|
marketTouchStartY = event.y
|
|
631
|
+
marketTouchX = event.x
|
|
632
|
+
marketTouchY = event.y
|
|
613
633
|
item.json.optString("pressInActionKey").takeIf(String::isNotEmpty)?.let { actionKey ->
|
|
614
634
|
emitAction(item, actionKey, null, this, "row")
|
|
615
635
|
}
|
|
@@ -620,7 +640,11 @@ internal class NativeListRowView(
|
|
|
620
640
|
if (touchPressed && current?.key == expectedKey && current.type == "market") {
|
|
621
641
|
marketLongPressRunnable = null
|
|
622
642
|
marketLongPressFired = true
|
|
623
|
-
|
|
643
|
+
val location = IntArray(2)
|
|
644
|
+
getLocationInWindow(location)
|
|
645
|
+
onAction?.invoke(current, actionKey, null, actionOrigin(this, "row").copy(
|
|
646
|
+
windowPointPixels = android.graphics.PointF(location[0] + marketTouchX, location[1] + marketTouchY),
|
|
647
|
+
))
|
|
624
648
|
}
|
|
625
649
|
}
|
|
626
650
|
marketLongPressRunnable = runnable
|
|
@@ -634,6 +658,8 @@ internal class NativeListRowView(
|
|
|
634
658
|
}
|
|
635
659
|
}
|
|
636
660
|
MotionEvent.ACTION_MOVE -> {
|
|
661
|
+
marketTouchX = event.x
|
|
662
|
+
marketTouchY = event.y
|
|
637
663
|
val outside = event.x < 0 || event.y < 0 || event.x >= width || event.y >= height
|
|
638
664
|
val movedMarket = (tag as? NativeListItem)?.type == "market" &&
|
|
639
665
|
(kotlin.math.abs(event.x - marketTouchStartX) > dp(10) ||
|
|
@@ -713,6 +739,22 @@ internal class NativeListRowView(
|
|
|
713
739
|
}
|
|
714
740
|
|
|
715
741
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
742
|
+
val marketItem = (tag as? NativeListItem)?.takeIf { it.type == "market" }
|
|
743
|
+
val marketStyle = marketItem?.json?.optJSONObject("style")
|
|
744
|
+
if (marketStyle?.has("horizontalPadding") == true) {
|
|
745
|
+
// OneKey patch: Yoga rounds the two absolute edges, not both padding values.
|
|
746
|
+
val sourcePadding = marketStyle.optDouble("horizontalPadding") * resources.displayMetrics.density
|
|
747
|
+
val width = MeasureSpec.getSize(widthMeasureSpec)
|
|
748
|
+
val rowHeight = selectorHeight
|
|
749
|
+
val sourceVerticalPadding = marketStyle.takeIf { it.has("verticalPadding") && rowHeight != null }
|
|
750
|
+
?.optDouble("verticalPadding")?.times(resources.displayMetrics.density)
|
|
751
|
+
setPadding(
|
|
752
|
+
sourcePadding.roundToInt(),
|
|
753
|
+
sourceVerticalPadding?.roundToInt() ?: paddingTop,
|
|
754
|
+
width - (width - sourcePadding).roundToInt(),
|
|
755
|
+
if (sourceVerticalPadding != null && rowHeight != null) rowHeight - (rowHeight - sourceVerticalPadding).roundToInt() else paddingBottom,
|
|
756
|
+
)
|
|
757
|
+
}
|
|
716
758
|
if (isMediaTile) {
|
|
717
759
|
val availableWidth = (MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight)
|
|
718
760
|
.coerceAtLeast(0)
|
|
@@ -737,6 +779,63 @@ internal class NativeListRowView(
|
|
|
737
779
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
738
780
|
super.onLayout(changed, left, top, right, bottom)
|
|
739
781
|
val item = tag as? NativeListItem ?: return
|
|
782
|
+
if (item.type == "market" && item.json.optJSONObject("style") != null) {
|
|
783
|
+
// OneKey patch: preserve Yoga's half-pixel centering for Market columns and badges.
|
|
784
|
+
for (column in listOf(leadingFrame, mainColumn, trailingColumn)) {
|
|
785
|
+
if (column.parent === this && column.visibility != GONE) {
|
|
786
|
+
column.offsetTopAndBottom((height - column.height + 1) / 2 - column.top)
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
// OneKey patch: token avatars stay centered in the row when text rounding adds a pixel.
|
|
790
|
+
if (item.json.optString("variant") != "token" && leadingFrame.parent === this && mainColumn.parent === this) {
|
|
791
|
+
leadingFrame.offsetTopAndBottom(mainColumn.top + (mainColumn.height - leadingFrame.height + 1) / 2 - leadingFrame.top)
|
|
792
|
+
}
|
|
793
|
+
// OneKey patch: preserve Yoga's absolute-edge rounding for the Market network badge.
|
|
794
|
+
if (leadingOverlayBackground.visibility == VISIBLE && item.json.optString("variant") == "token") {
|
|
795
|
+
val recycler = parent as? RecyclerView
|
|
796
|
+
val adapter = recycler?.adapter as? NativeListAdapter
|
|
797
|
+
val position = recycler?.getChildAdapterPosition(this) ?: RecyclerView.NO_POSITION
|
|
798
|
+
if (recycler != null && adapter != null && position != RecyclerView.NO_POSITION && adapter.itemAt(position)?.key == item.key) {
|
|
799
|
+
val density = resources.displayMetrics.density.toDouble()
|
|
800
|
+
val rowTop = adapter.marketSourceEdgePx(position)
|
|
801
|
+
val rowHeight = adapter.marketSourceEdgePx(position + 1) - rowTop
|
|
802
|
+
val imageHeight = item.json.optJSONObject("style")?.optJSONObject("image")?.optDouble("height", 32.0) ?: 32.0
|
|
803
|
+
val contentTop = (recycler.paddingTop / density).roundToInt() * density
|
|
804
|
+
val badgeTop = contentTop + rowTop + (rowHeight - imageHeight * density) / 2 + (imageHeight - 16) * density
|
|
805
|
+
val badgeHeight = (badgeTop + 20 * density).roundToInt() - badgeTop.roundToInt()
|
|
806
|
+
val top = ((imageHeight - 16) * density).roundToInt()
|
|
807
|
+
leadingOverlayBackground.layout(leadingOverlayBackground.left, top, leadingOverlayBackground.right, top + badgeHeight)
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
for (badge in marketBadgeViews) {
|
|
811
|
+
if (badge.parent === titleLine && badge.visibility != GONE) {
|
|
812
|
+
badge.offsetTopAndBottom((titleLine.height - badge.height + 1) / 2 - badge.top)
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// OneKey patch: preserve fractional text advances and gaps until the final edge.
|
|
816
|
+
val style = item.json.optJSONObject("style")
|
|
817
|
+
for ((line, gap) in listOf(
|
|
818
|
+
titleLine to style.optDouble("titleBadgeGap", 4.0),
|
|
819
|
+
marketSubtitleLine to (item.json.optJSONObject("subtitlePrefix")?.optDouble("gap", 4.0) ?: 0.0),
|
|
820
|
+
)) {
|
|
821
|
+
var cursor = 0f
|
|
822
|
+
var hasPrevious = false
|
|
823
|
+
for (index in 0 until line.childCount) {
|
|
824
|
+
val child = line.getChildAt(index)
|
|
825
|
+
if (child.visibility == GONE) continue
|
|
826
|
+
if (hasPrevious) {
|
|
827
|
+
cursor += (gap * resources.displayMetrics.density).toFloat()
|
|
828
|
+
child.offsetLeftAndRight(cursor.roundToInt() - child.left)
|
|
829
|
+
}
|
|
830
|
+
val advance = if (index == 0 && child is TextView) {
|
|
831
|
+
val textWidth = if (line === titleLine) child.paint.measureText(child.text.toString()) else child.layout?.getLineWidth(0)
|
|
832
|
+
textWidth?.coerceAtMost(child.width.toFloat()) ?: child.width.toFloat()
|
|
833
|
+
} else child.width.toFloat()
|
|
834
|
+
cursor = if (hasPrevious) cursor + advance else child.left + advance
|
|
835
|
+
hasPrevious = true
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
740
839
|
val accessory = item.json.optJSONArray("trailing")?.optJSONObject(0)
|
|
741
840
|
if (item.type == "identity" && item.json.has("height") && item.json.optString("presentation") == "accountSelector" && accessory?.optString("kind") == "icon" && accessory.optString("name") == "PlusSmallOutline") {
|
|
742
841
|
// OneKey patch: the borderless Plus retains the source's fixed top18/negative7 slot.
|
|
@@ -785,7 +884,7 @@ internal class NativeListRowView(
|
|
|
785
884
|
touchPressed = shouldRestorePressed
|
|
786
885
|
currentLayout = layout
|
|
787
886
|
tag = item
|
|
788
|
-
selectorUsesSourceScale = item.type == "market" || item.usesSelectorSourceScale || useSourceScale
|
|
887
|
+
selectorUsesSourceScale = item.type == "market" || item.type == "system" && item.json.optString("presentation") == "market" && item.json.optString("variant") == "retry" || item.usesSelectorSourceScale || useSourceScale
|
|
789
888
|
reorderActive = false
|
|
790
889
|
leadingImages.forEach(OneKeyImageReusableView::prepareForReuse)
|
|
791
890
|
secondaryImage.prepareForReuse()
|
|
@@ -1079,6 +1178,8 @@ internal class NativeListRowView(
|
|
|
1079
1178
|
selectorOriginalFontFeatures.clear()
|
|
1080
1179
|
selectorOriginalPaintFlags.forEach { (view, flags) -> view.paintFlags = flags }
|
|
1081
1180
|
selectorOriginalPaintFlags.clear()
|
|
1181
|
+
marketOriginalPaintFlags.forEach { (view, flags) -> view.paintFlags = flags }
|
|
1182
|
+
marketOriginalPaintFlags.clear()
|
|
1082
1183
|
selectorLineHeights.clear()
|
|
1083
1184
|
selectorFontSizes.clear()
|
|
1084
1185
|
// OneKey patch: selector-only views cannot survive a recycled binding.
|
|
@@ -1095,7 +1196,17 @@ internal class NativeListRowView(
|
|
|
1095
1196
|
badge.setOnClickListener(null)
|
|
1096
1197
|
badge.contentDescription = null
|
|
1097
1198
|
marketBadgeLabels[index].apply {
|
|
1199
|
+
// OneKey patch: restore every optional Market badge typography property on reuse.
|
|
1098
1200
|
text = ""
|
|
1201
|
+
layoutParams = wrap()
|
|
1202
|
+
fontFeatureSettings = null
|
|
1203
|
+
textSize = sp(11f)
|
|
1204
|
+
typeface = NativeListFonts.medium(context)
|
|
1205
|
+
setLineSpacing(0f, 1f)
|
|
1206
|
+
includeFontPadding = false
|
|
1207
|
+
maxLines = 1
|
|
1208
|
+
ellipsize = TextUtils.TruncateAt.END
|
|
1209
|
+
gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
|
1099
1210
|
setTextColor(color(null, "secondaryText", "#0000009B"))
|
|
1100
1211
|
}
|
|
1101
1212
|
marketBadgeImages[index].prepareForReuse()
|
|
@@ -1140,6 +1251,9 @@ internal class NativeListRowView(
|
|
|
1140
1251
|
boundCheckboxData = null
|
|
1141
1252
|
mainColumn.orientation = VERTICAL
|
|
1142
1253
|
mainColumn.gravity = Gravity.CENTER_VERTICAL
|
|
1254
|
+
// OneKey patch: remove the Market group before restoring shared labels.
|
|
1255
|
+
marketSubtitleLine.removeAllViews()
|
|
1256
|
+
mainColumn.removeView(marketSubtitleLine)
|
|
1143
1257
|
mediaMetadataRow.removeView(subtitle)
|
|
1144
1258
|
mediaMetadataRow.removeView(mediaNetworkImage)
|
|
1145
1259
|
mainColumn.removeView(mediaMetadataRow)
|
|
@@ -1165,6 +1279,10 @@ internal class NativeListRowView(
|
|
|
1165
1279
|
badgeLine.text = ""
|
|
1166
1280
|
title.setLineSpacing(0f, 1f)
|
|
1167
1281
|
subtitle.setLineSpacing(0f, 1f)
|
|
1282
|
+
tertiary.setLineSpacing(0f, 1f)
|
|
1283
|
+
tertiary.maxWidth = Int.MAX_VALUE
|
|
1284
|
+
tertiary.ellipsize = TextUtils.TruncateAt.END
|
|
1285
|
+
tertiary.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
|
1168
1286
|
title.letterSpacing = 0f
|
|
1169
1287
|
title.showsDottedUnderline = false
|
|
1170
1288
|
title.setPadding(0, 0, 0, 0)
|
|
@@ -1218,6 +1336,7 @@ internal class NativeListRowView(
|
|
|
1218
1336
|
spinner.visibility = GONE
|
|
1219
1337
|
spinner.layoutParams = LayoutParams(dp(20), dp(20)).apply { gravity = Gravity.END }
|
|
1220
1338
|
spinner.alpha = 1f
|
|
1339
|
+
marketLeadingUsesSourceClip = false
|
|
1221
1340
|
leadingFrame.visibility = GONE
|
|
1222
1341
|
leadingFrame.background = null
|
|
1223
1342
|
leadingFrame.clipChildren = false
|
|
@@ -1248,6 +1367,8 @@ internal class NativeListRowView(
|
|
|
1248
1367
|
leadingActionIcon.iconName = ""
|
|
1249
1368
|
leadingActionIcon.glyphSizeDp = 24
|
|
1250
1369
|
leadingActionIcon.setOnClickListener(null)
|
|
1370
|
+
leadingActionIcon.setTag(com.facebook.react.R.id.react_test_id, null)
|
|
1371
|
+
leadingActionIcon.contentDescription = null
|
|
1251
1372
|
mainColumn.visibility = VISIBLE
|
|
1252
1373
|
dataContainer.visibility = GONE
|
|
1253
1374
|
dataColumns.forEach { it.visibility = GONE }
|
|
@@ -1932,6 +2053,25 @@ internal class NativeListRowView(
|
|
|
1932
2053
|
)
|
|
1933
2054
|
}
|
|
1934
2055
|
|
|
2056
|
+
// OneKey patch: opt-in Market text uses the same pixel rounding and line box as RN.
|
|
2057
|
+
private fun applyMarketTextMetrics(view: TextView, style: JSONObject?) {
|
|
2058
|
+
if (style == null || view.visibility != VISIBLE || view.text.isEmpty()) return
|
|
2059
|
+
marketOriginalPaintFlags.putIfAbsent(view, view.paintFlags)
|
|
2060
|
+
view.paintFlags = view.paintFlags or Paint.SUBPIXEL_TEXT_FLAG or Paint.LINEAR_TEXT_FLAG
|
|
2061
|
+
if (style.has("fontSize")) {
|
|
2062
|
+
val sourceSize = sp(style.optDouble("fontSize").toFloat()) * resources.displayMetrics.density
|
|
2063
|
+
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, kotlin.math.ceil(sourceSize.toDouble()).toFloat())
|
|
2064
|
+
}
|
|
2065
|
+
val text = SpannableStringBuilder(view.text)
|
|
2066
|
+
text.getSpans(0, text.length, SelectorLineHeightSpan::class.java).forEach(text::removeSpan)
|
|
2067
|
+
if (style.has("lineHeight")) {
|
|
2068
|
+
val lineHeight = kotlin.math.ceil(style.optDouble("lineHeight") * (if (selectorUsesSourceScale) 1f else NativeListScale.factor(resources)) * resources.displayMetrics.density).toInt()
|
|
2069
|
+
text.setSpan(SelectorLineHeightSpan(lineHeight), 0, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
2070
|
+
view.setLineSpacing(0f, 1f)
|
|
2071
|
+
}
|
|
2072
|
+
view.text = text
|
|
2073
|
+
}
|
|
2074
|
+
|
|
1935
2075
|
private fun marketText(value: String, segments: JSONArray?, fontSize: Float): CharSequence {
|
|
1936
2076
|
if (segments == null || segments.length() == 0) return value
|
|
1937
2077
|
val result = SpannableStringBuilder()
|
|
@@ -1968,6 +2108,33 @@ internal class NativeListRowView(
|
|
|
1968
2108
|
?: if (variant == "perp") 8 else 14
|
|
1969
2109
|
setPadding(dp(horizontalPadding), dp(verticalPadding), dp(horizontalPadding), dp(verticalPadding))
|
|
1970
2110
|
|
|
2111
|
+
item.json.optJSONObject("leadingAction")?.let { action ->
|
|
2112
|
+
leadingActionIcon.visibility = VISIBLE
|
|
2113
|
+
leadingActionIcon.iconName = action.optString("name")
|
|
2114
|
+
leadingActionIcon.glyphSizeDp = 24
|
|
2115
|
+
leadingActionIcon.tintColor = safeColor(
|
|
2116
|
+
action.optString("tintColor"),
|
|
2117
|
+
color(theme, "icon", "#0000009B"),
|
|
2118
|
+
)
|
|
2119
|
+
leadingActionIcon.isEnabled = !action.optBoolean("disabled", false)
|
|
2120
|
+
leadingActionIcon.alpha = if (leadingActionIcon.isEnabled) 1f else 0.4f
|
|
2121
|
+
leadingActionIcon.setTag(
|
|
2122
|
+
com.facebook.react.R.id.react_test_id,
|
|
2123
|
+
action.optString("testID").takeIf(String::isNotEmpty),
|
|
2124
|
+
)
|
|
2125
|
+
leadingActionIcon.contentDescription = action.optString("accessibilityLabel")
|
|
2126
|
+
leadingActionIcon.setOnClickListener {
|
|
2127
|
+
emitAction(
|
|
2128
|
+
item,
|
|
2129
|
+
action.optString("actionKey"),
|
|
2130
|
+
null,
|
|
2131
|
+
leadingActionIcon,
|
|
2132
|
+
"leadingAction",
|
|
2133
|
+
)
|
|
2134
|
+
}
|
|
2135
|
+
addView(leadingActionIcon, LayoutParams(dp(36), dp(36)).apply { marginEnd = dp(5) })
|
|
2136
|
+
}
|
|
2137
|
+
|
|
1971
2138
|
val leading = JSONObject(item.json.getJSONObject("leading").toString())
|
|
1972
2139
|
imageStyle?.optString("shape")?.takeIf(String::isNotEmpty)?.let { leading.put("shape", it) }
|
|
1973
2140
|
imageStyle?.optString("contentFit")?.takeIf(String::isNotEmpty)?.let { contentFit ->
|
|
@@ -1989,7 +2156,11 @@ internal class NativeListRowView(
|
|
|
1989
2156
|
cornerRadiusDp = cornerRadius,
|
|
1990
2157
|
)
|
|
1991
2158
|
|
|
1992
|
-
|
|
2159
|
+
// OneKey patch: retain the source gap without changing other templates.
|
|
2160
|
+
// addView(mainColumn, weighted())
|
|
2161
|
+
addView(mainColumn, weighted().apply {
|
|
2162
|
+
marginEnd = dp(style?.optDouble("contentTrailingGap", 0.0)?.roundToInt() ?: 0)
|
|
2163
|
+
})
|
|
1993
2164
|
titleLine.packsChildrenAtStart = true
|
|
1994
2165
|
showText(title, item.json.optString("title"), style?.optJSONObject("title")?.optInt("lines", 1) ?: 1)
|
|
1995
2166
|
applyMarketTextStyle(
|
|
@@ -2008,6 +2179,7 @@ internal class NativeListRowView(
|
|
|
2008
2179
|
val badge = badges.getJSONObject(index)
|
|
2009
2180
|
val badgeView = marketBadgeViews[index]
|
|
2010
2181
|
val badgeLabel = marketBadgeLabels[index]
|
|
2182
|
+
val badgeStyle = badge.optJSONObject("style")
|
|
2011
2183
|
val hasGlyph = badge.optString("iconName") == "verified"
|
|
2012
2184
|
val remoteIcon = badge.optJSONObject("icon")
|
|
2013
2185
|
val hasIcon = hasGlyph || remoteIcon != null
|
|
@@ -2022,15 +2194,28 @@ internal class NativeListRowView(
|
|
|
2022
2194
|
val foreground = safeColor(badge.optString("textColor"), toneColor)
|
|
2023
2195
|
badgeView.visibility = VISIBLE
|
|
2024
2196
|
val iconOnly = hasIcon && text.isEmpty()
|
|
2025
|
-
|
|
2197
|
+
// OneKey patch: Market callers can request the original badge padding.
|
|
2198
|
+
val padding = badgeStyle?.optDouble("horizontalPadding", 5.0)?.roundToInt() ?: 5
|
|
2199
|
+
val leftPadding = if (badgeStyle?.has("horizontalPadding") == true) padding else if (hasIcon) 2 else 5
|
|
2200
|
+
// badgeView.setPadding(dp(if (iconOnly) 0 else if (hasIcon) 2 else 5), 0, dp(if (iconOnly) 0 else 5), 0)
|
|
2201
|
+
badgeView.setPadding(dp(if (iconOnly) 0 else leftPadding), 0, dp(if (iconOnly) 0 else padding), 0)
|
|
2026
2202
|
badgeView.background = roundedFill(
|
|
2027
2203
|
safeColor(
|
|
2028
2204
|
badge.optString("backgroundColor"),
|
|
2029
2205
|
if (hasIcon && text.isEmpty()) Color.TRANSPARENT else color(theme, "strongBackground", "#0000000F"),
|
|
2030
2206
|
),
|
|
2031
2207
|
4f,
|
|
2032
|
-
)
|
|
2208
|
+
).apply {
|
|
2209
|
+
if (badgeStyle != null) this.cornerRadius = 4f * resources.displayMetrics.density
|
|
2210
|
+
}
|
|
2033
2211
|
badgeLabel.text = text
|
|
2212
|
+
// OneKey patch: match SizableText's tabular numerals only for explicit Market metrics.
|
|
2213
|
+
badgeLabel.fontFeatureSettings = if (badgeStyle != null) "tnum" else null
|
|
2214
|
+
badgeLabel.textSize = sp(badgeStyle?.optDouble("fontSize", 11.0)?.toFloat() ?: 11f)
|
|
2215
|
+
badgeLabel.typeface = marketTypeface(badgeStyle?.optString("fontWeight").orEmpty(), "medium")
|
|
2216
|
+
if (badgeStyle?.has("lineHeight") == true) {
|
|
2217
|
+
TextViewCompat.setLineHeight(badgeLabel, dp(badgeStyle.optDouble("lineHeight").roundToInt()))
|
|
2218
|
+
}
|
|
2034
2219
|
badgeLabel.setTextColor(foreground)
|
|
2035
2220
|
badgeLabel.visibility = if (text.isEmpty()) GONE else VISIBLE
|
|
2036
2221
|
if (hasGlyph) {
|
|
@@ -2056,7 +2241,9 @@ internal class NativeListRowView(
|
|
|
2056
2241
|
badgeView.contentDescription = badge.optString("accessibilityLabel", text)
|
|
2057
2242
|
titleLine.addView(
|
|
2058
2243
|
badgeView,
|
|
2059
|
-
|
|
2244
|
+
// OneKey patch: preserve 18dp unless the Market row opts into source metrics.
|
|
2245
|
+
// LayoutParams(LayoutParams.WRAP_CONTENT, dp(18)).apply { marginStart = dp(titleBadgeGap) },
|
|
2246
|
+
LayoutParams(LayoutParams.WRAP_CONTENT, dp(badgeStyle?.optDouble("height", 18.0)?.roundToInt() ?: 18)).apply { marginStart = dp(titleBadgeGap) },
|
|
2060
2247
|
)
|
|
2061
2248
|
}
|
|
2062
2249
|
}
|
|
@@ -2082,6 +2269,31 @@ internal class NativeListRowView(
|
|
|
2082
2269
|
style?.optJSONObject("subtitle")?.optDouble("fontSize", 14.0)?.toFloat() ?: 14f,
|
|
2083
2270
|
)
|
|
2084
2271
|
}
|
|
2272
|
+
// OneKey patch: preserve independent name metrics, truncation, and volume.
|
|
2273
|
+
val subtitlePrefix = item.json.optJSONObject("subtitlePrefix")
|
|
2274
|
+
val subtitlePadding = style?.optDouble("subtitleTrailingPadding", 0.0)?.roundToInt() ?: 0
|
|
2275
|
+
if (subtitlePrefix != null || subtitlePadding > 0) {
|
|
2276
|
+
mainColumn.removeView(subtitle)
|
|
2277
|
+
mainColumn.removeView(tertiary)
|
|
2278
|
+
marketSubtitleLine.orientation = HORIZONTAL
|
|
2279
|
+
marketSubtitleLine.gravity = Gravity.CENTER_VERTICAL
|
|
2280
|
+
marketSubtitleLine.packsChildrenAtStart = true
|
|
2281
|
+
marketSubtitleLine.leadingTextMaxWidth = subtitlePrefix?.takeIf { it.has("maxWidth") }
|
|
2282
|
+
?.optDouble("maxWidth")?.roundToInt()?.let(::dp) ?: Int.MAX_VALUE
|
|
2283
|
+
marketSubtitleLine.setPadding(0, 0, dp(subtitlePadding), 0)
|
|
2284
|
+
showText(tertiary, subtitlePrefix?.optString("text") ?: "", 1)
|
|
2285
|
+
applyMarketTextStyle(tertiary, subtitlePrefix?.optJSONObject("style"), 12f, 16, "regular", color(theme, "secondaryText", "#646464"), "start")
|
|
2286
|
+
marketSubtitleLine.addView(tertiary, wrap())
|
|
2287
|
+
marketSubtitleLine.addView(subtitle, wrap().apply {
|
|
2288
|
+
if (tertiary.visibility == VISIBLE && subtitle.visibility == VISIBLE) {
|
|
2289
|
+
marginStart = dp(subtitlePrefix?.optDouble("gap", 4.0)?.roundToInt() ?: 4)
|
|
2290
|
+
}
|
|
2291
|
+
})
|
|
2292
|
+
mainColumn.addView(marketSubtitleLine, 1, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply {
|
|
2293
|
+
topMargin = dp(style?.optDouble("lineGap", 0.0)?.roundToInt() ?: 0)
|
|
2294
|
+
})
|
|
2295
|
+
marketSubtitleLine.visibility = if (tertiary.visibility == VISIBLE || subtitle.visibility == VISIBLE) VISIBLE else GONE
|
|
2296
|
+
}
|
|
2085
2297
|
trailingColumn.orientation = HORIZONTAL
|
|
2086
2298
|
trailingColumn.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
|
2087
2299
|
addView(trailingColumn, wrap())
|
|
@@ -2150,6 +2362,24 @@ internal class NativeListRowView(
|
|
|
2150
2362
|
dp(style?.optDouble("changeWidth", 80.0)?.roundToInt() ?: 80),
|
|
2151
2363
|
dp(style?.optDouble("changeHeight", 32.0)?.roundToInt() ?: 32),
|
|
2152
2364
|
)
|
|
2365
|
+
applyMarketTextMetrics(title, style?.optJSONObject("title"))
|
|
2366
|
+
applyMarketTextMetrics(subtitle, style?.optJSONObject("subtitle"))
|
|
2367
|
+
applyMarketTextMetrics(tertiary, item.json.optJSONObject("subtitlePrefix")?.optJSONObject("style"))
|
|
2368
|
+
applyMarketTextMetrics(price, priceStyle)
|
|
2369
|
+
applyMarketTextMetrics(change, changeStyle)
|
|
2370
|
+
val badges = item.json.optJSONArray("badges")
|
|
2371
|
+
marketBadgeLabels.forEachIndexed { index, label ->
|
|
2372
|
+
val badge = badges?.optJSONObject(index)
|
|
2373
|
+
val badgeStyle = badge?.optJSONObject("style")
|
|
2374
|
+
applyMarketTextMetrics(label, badgeStyle)
|
|
2375
|
+
if (badge != null && label.visibility == VISIBLE && badgeStyle?.has("horizontalPadding") == true &&
|
|
2376
|
+
badge.optJSONObject("icon") == null && badge.optString("iconName").isEmpty()) {
|
|
2377
|
+
// OneKey patch: match RN's intrinsic text width and one rounded padding pair.
|
|
2378
|
+
label.layoutParams = wrap().apply { width = label.paint.measureText(label.text.toString()).roundToInt() }
|
|
2379
|
+
val padding = badgeStyle.optDouble("horizontalPadding") * resources.displayMetrics.density
|
|
2380
|
+
marketBadgeViews[index].setPadding(kotlin.math.floor(padding).toInt(), 0, (padding * 2).roundToInt() - kotlin.math.floor(padding).toInt(), 0)
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2153
2383
|
contentDescription = item.json.optString(
|
|
2154
2384
|
"accessibilityLabel",
|
|
2155
2385
|
listOf(
|
|
@@ -2817,16 +3047,41 @@ internal class NativeListRowView(
|
|
|
2817
3047
|
)
|
|
2818
3048
|
return
|
|
2819
3049
|
}
|
|
3050
|
+
if (isMarket && variant == "retry") {
|
|
3051
|
+
val message = item.json.optString("message")
|
|
3052
|
+
orientation = VERTICAL
|
|
3053
|
+
gravity = Gravity.CENTER
|
|
3054
|
+
setPadding(dp(32), dp(if (message.isEmpty()) 11 else 32), dp(32), dp(if (message.isEmpty()) 11 else 27))
|
|
3055
|
+
if (message.isNotEmpty()) {
|
|
3056
|
+
showText(title, message, 2)
|
|
3057
|
+
val style = JSONObject().put("fontSize", 16).put("lineHeight", 24)
|
|
3058
|
+
applyMarketTextStyle(title, style, 16f, 24, "regular", color(theme, "secondaryText", "#0000009B"), "center")
|
|
3059
|
+
applyMarketTextMetrics(title, style)
|
|
3060
|
+
addView(mainColumn, wrap().apply { bottomMargin = kotlin.math.ceil(7.0 * resources.displayMetrics.density).toInt() })
|
|
3061
|
+
}
|
|
3062
|
+
showTrailing(0, item.json.optString("actionText", "Retry"), true, item.json.optString("actionKey"))
|
|
3063
|
+
val button = trailingViews[0]
|
|
3064
|
+
val style = JSONObject().put("fontSize", 14).put("lineHeight", 20)
|
|
3065
|
+
applyMarketTextStyle(button, style, 14f, 20, "medium", color(theme, "secondaryText", "#0000009B"), "center")
|
|
3066
|
+
applyMarketTextMetrics(button, style)
|
|
3067
|
+
button.background = null
|
|
3068
|
+
// Match Yoga's text rounding before adding the tertiary Button's border/padding.
|
|
3069
|
+
val density = resources.displayMetrics.density
|
|
3070
|
+
val buttonWidth = (kotlin.math.ceil(button.paint.measureText(button.text.toString()).toDouble()) + 18 * density).roundToInt()
|
|
3071
|
+
val buttonHeight = kotlin.math.ceil(kotlin.math.ceil(20.0 * density) + 10 * density).toInt()
|
|
3072
|
+
button.setPadding(0, 0, 0, 0)
|
|
3073
|
+
button.layoutParams = LayoutParams(buttonWidth, buttonHeight)
|
|
3074
|
+
addView(trailingColumn, wrap())
|
|
3075
|
+
return
|
|
3076
|
+
}
|
|
2820
3077
|
if (isMarket && variant == "noMatch") {
|
|
2821
3078
|
gravity = Gravity.CENTER
|
|
2822
3079
|
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
3080
|
showText(title, item.json.optString("message"), 1)
|
|
2829
|
-
|
|
3081
|
+
val style = JSONObject().put("fontSize", 16).put("lineHeight", 24)
|
|
3082
|
+
applyMarketTextStyle(title, style, 16f, 24, "regular", color(theme, "secondaryText", "#0000009B"), "center")
|
|
3083
|
+
applyMarketTextMetrics(title, style)
|
|
3084
|
+
addView(mainColumn, wrap())
|
|
2830
3085
|
return
|
|
2831
3086
|
}
|
|
2832
3087
|
// OneKey patch: warning title/description wrap inside the actual scroll content.
|
|
@@ -2972,12 +3227,19 @@ internal class NativeListRowView(
|
|
|
2972
3227
|
if (tokenPair) {
|
|
2973
3228
|
leadingOverlayBackground.visibility = VISIBLE
|
|
2974
3229
|
leadingOverlayBackground.background = roundedFill(visualBackdropColor, 10f)
|
|
3230
|
+
val item = tag as? NativeListItem
|
|
3231
|
+
val marketPadding = if (item?.type == "market") item.json.optJSONObject("style")?.optDouble("horizontalPadding", 20.0) ?: 20.0 else null
|
|
3232
|
+
val density = resources.displayMetrics.density
|
|
3233
|
+
// Yoga rounds the absolute horizontal edges, including the avatar's fractional origin.
|
|
3234
|
+
val badgeRight = marketPadding?.let { ((it + sizeDp + 4) * density).roundToInt() }
|
|
3235
|
+
val badgeLeft = marketPadding?.let { ((it + sizeDp - 16) * density).roundToInt() }
|
|
3236
|
+
val avatarRight = marketPadding?.let { ((it + sizeDp) * density).roundToInt() }
|
|
2975
3237
|
leadingOverlayBackground.layoutParams = FrameLayout.LayoutParams(
|
|
2976
|
-
dp(20),
|
|
3238
|
+
if (badgeRight != null && badgeLeft != null) badgeRight - badgeLeft else dp(20),
|
|
2977
3239
|
dp(20),
|
|
2978
3240
|
Gravity.END or Gravity.BOTTOM,
|
|
2979
3241
|
).apply {
|
|
2980
|
-
marginEnd = -dp(4)
|
|
3242
|
+
marginEnd = if (badgeRight != null && avatarRight != null) avatarRight - badgeRight else -dp(4)
|
|
2981
3243
|
bottomMargin = -dp(4)
|
|
2982
3244
|
}
|
|
2983
3245
|
}
|
|
@@ -3019,21 +3281,26 @@ internal class NativeListRowView(
|
|
|
3019
3281
|
image.clipToOutline = true
|
|
3020
3282
|
if ((tag as? NativeListItem)?.type == "market" && index == 0 && visual.optString("borderColor").isNotEmpty()) {
|
|
3021
3283
|
val inset = dp(1)
|
|
3022
|
-
val
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3284
|
+
val density = resources.displayMetrics.density
|
|
3285
|
+
val sourceRadius = cornerRadiusDp ?: leadingCornerRadius(shape, minOf(sizeDp, heightDp))
|
|
3286
|
+
// OneKey patch: retain the source's fractional border and background rendering.
|
|
3287
|
+
leadingFrame.background = null
|
|
3288
|
+
BackgroundStyleApplicator.setBackgroundColor(leadingFrame, visualBackground)
|
|
3289
|
+
// Match the source's physical edges: React Native rasterizes ALL as a different stroke path.
|
|
3290
|
+
for (edge in arrayOf(LogicalEdge.LEFT, LogicalEdge.TOP, LogicalEdge.RIGHT, LogicalEdge.BOTTOM)) {
|
|
3291
|
+
BackgroundStyleApplicator.setBorderWidth(leadingFrame, edge, 1f)
|
|
3292
|
+
BackgroundStyleApplicator.setBorderColor(leadingFrame, edge, safeColor(visual.optString("borderColor"), Color.TRANSPARENT))
|
|
3027
3293
|
}
|
|
3028
|
-
|
|
3294
|
+
BackgroundStyleApplicator.setBorderRadius(leadingFrame, BorderRadiusProp.BORDER_RADIUS, LengthPercentage(sourceRadius, LengthPercentageType.POINT))
|
|
3295
|
+
// OneKey patch: round the source image's absolute edges inside its border.
|
|
3296
|
+
val sourceLeft = ((tag as? NativeListItem)?.json?.optJSONObject("style")?.optDouble("horizontalPadding", 20.0) ?: 20.0) * density
|
|
3297
|
+
val imageWidth = (sourceLeft + (sizeDp - 1) * density).roundToInt() - (sourceLeft + density).roundToInt()
|
|
3298
|
+
image.layoutParams = FrameLayout.LayoutParams(imageWidth, dp(heightDp - 2)).apply {
|
|
3029
3299
|
leftMargin = inset
|
|
3030
3300
|
topMargin = inset
|
|
3031
3301
|
}
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
outline.setRoundRect(-inset, -inset, view.width + inset, view.height + inset, radius)
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3302
|
+
marketLeadingUsesSourceClip = true
|
|
3303
|
+
image.clipToOutline = false
|
|
3037
3304
|
}
|
|
3038
3305
|
val fallbackIcon = if (index == 0) visual.optJSONObject("fallbackIcon") else null
|
|
3039
3306
|
val expectedEpoch = bindingEpoch
|
|
@@ -3497,6 +3764,13 @@ internal class NativeListRowView(
|
|
|
3497
3764
|
}
|
|
3498
3765
|
|
|
3499
3766
|
private fun applySize(item: NativeListItem) {
|
|
3767
|
+
if (item.type == "system" && item.json.optString("presentation") == "market" &&
|
|
3768
|
+
item.json.optString("variant") in setOf("retry", "noMatch")) {
|
|
3769
|
+
val defaultHeight = if (item.json.optString("variant") == "noMatch") 88.0 else if (item.json.optString("message").isEmpty()) 52.0 else 120.0
|
|
3770
|
+
minimumHeight = (item.json.optDouble("height", defaultHeight).toFloat() * resources.displayMetrics.density).roundToInt()
|
|
3771
|
+
selectorHeight = if (item.json.has("height")) minimumHeight else null
|
|
3772
|
+
return
|
|
3773
|
+
}
|
|
3500
3774
|
if (item.type == "market") {
|
|
3501
3775
|
val style = item.json.optJSONObject("style")
|
|
3502
3776
|
val imageHeight = style?.optJSONObject("image")?.optDouble(
|
|
@@ -3505,12 +3779,15 @@ internal class NativeListRowView(
|
|
|
3505
3779
|
) ?: if (item.json.optString("variant") == "stock") 40.0 else 32.0
|
|
3506
3780
|
val verticalPadding = style?.optDouble("verticalPadding", 12.0) ?: 12.0
|
|
3507
3781
|
val defaultHeight = if (item.json.optString("variant") == "stock") 72.0 else 68.0
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
).roundToInt(),
|
|
3782
|
+
// OneKey patch: preserve fractional DP until the final physical pixel edge.
|
|
3783
|
+
val rowHeight = item.json.optDouble(
|
|
3784
|
+
"height",
|
|
3785
|
+
maxOf(defaultHeight, imageHeight + verticalPadding * 2),
|
|
3513
3786
|
)
|
|
3787
|
+
val sourceScale = if (selectorUsesSourceScale) 1f else NativeListScale.factor(resources)
|
|
3788
|
+
minimumHeight = (rowHeight * sourceScale * resources.displayMetrics.density).roundToInt()
|
|
3789
|
+
// OneKey patch: explicit Market row heights must not grow after child pixel rounding.
|
|
3790
|
+
selectorHeight = if (item.json.has("height")) minimumHeight else null
|
|
3514
3791
|
return
|
|
3515
3792
|
}
|
|
3516
3793
|
val isWalletSidebar =
|