@onekeyfe/react-native-native-list 6.7.0-alpha.375 → 6.7.0-alpha.382
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/HybridNativeList.kt +14 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +240 -11
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityDetailsView.kt +7 -4
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +6 -3
- package/android/src/main/java/com/onekey/nativelist/NativeListButtonGroupRenderer.kt +102 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListContainerSlotAdapter.kt +12 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +87 -7
- package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +177 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +43 -14
- package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +25 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +33 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +22 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +2 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +2 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +3 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +69 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +55 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +1912 -107
- package/android/src/test/java/com/margelo/nitro/nativelist/NativeListPressPolicyTest.kt +1 -0
- package/docs/ROW_TEMPLATES.md +60 -9
- package/docs/SPEC.md +450 -5
- package/docs/STYLE_SPEC.md +6 -0
- package/ios/HybridNativeList.swift +21 -2
- package/ios/NativeListAccessoryStack.swift +227 -39
- package/ios/NativeListActivityDetailsView.swift +18 -8
- package/ios/NativeListActivityRenderer.swift +8 -2
- package/ios/NativeListButtonGroupRenderer.swift +89 -0
- package/ios/NativeListDataRowRenderer.swift +155 -27
- package/ios/NativeListDesignAssets.swift +3 -0
- package/ios/NativeListIdentityRenderer.swift +166 -30
- package/ios/NativeListLeadingVisual.swift +49 -14
- package/ios/NativeListMessageRenderer.swift +34 -1
- package/ios/NativeListModels.swift +42 -2
- package/ios/NativeListRendererRegistry.swift +1 -0
- package/ios/NativeListResolvedText.swift +2 -1
- package/ios/NativeListRowHost.swift +14 -0
- package/ios/NativeListSectionHeaderRenderer.swift +88 -0
- package/ios/RNCNativeListView.swift +1013 -55
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/icon.svg +4 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/icon.svg +3 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/icon.svg +3 -0
- package/ios/tests/NativeListActivityPresentationTests.swift +56 -0
- package/ios/tests/NativeListLifecycleTests.swift +2720 -3
- package/lib/module/NativeList.js +175 -28
- package/lib/module/NativeList.web.js +129 -30
- package/lib/module/index.js +1 -1
- package/lib/module/models.js +1 -0
- package/lib/module/selection.js +1 -1
- package/lib/module/validation.js +421 -8
- package/lib/module/web/NativeListWebEngine.js +507 -19
- package/lib/module/web/templates/ActivityRowRenderer.js +8 -7
- package/lib/module/web/templates/ButtonGroupRowRenderer.js +47 -0
- package/lib/module/web/templates/DataRowRenderer.js +40 -5
- package/lib/module/web/templates/IdentityRowRenderer.js +90 -4
- package/lib/module/web/templates/MessageRowRenderer.js +18 -2
- package/lib/module/web/templates/RowContainerStyle.js +1 -0
- package/lib/module/web/templates/RowRendererRegistry.js +3 -0
- package/lib/module/web/templates/SectionHeaderRowRenderer.js +36 -2
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +26 -0
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +20 -0
- package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +14 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +43 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/lib/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/lib/typescript/src/NativeList.d.ts +1 -1
- package/lib/typescript/src/NativeList.nitro.d.ts +2 -0
- package/lib/typescript/src/NativeList.types.d.ts +18 -0
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/models.d.ts +105 -9
- package/lib/typescript/src/validation.d.ts +12 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +32 -0
- package/lib/typescript/src/web/templates/ButtonGroupRowRenderer.d.ts +14 -0
- package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +1 -1
- package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +9 -3
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +26 -0
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +20 -0
- package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +14 -0
- package/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +43 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/package.json +3 -3
- package/react-native-native-list.podspec +1 -0
- package/src/NativeList.nitro.ts +2 -0
- package/src/NativeList.tsx +313 -44
- package/src/NativeList.types.ts +22 -0
- package/src/NativeList.web.tsx +204 -48
- package/src/index.ts +2 -0
- package/src/models.ts +117 -3
- package/src/selection.ts +1 -0
- package/src/validation.ts +852 -6
- package/src/web/NativeListWebEngine.ts +803 -20
- package/src/web/templates/ActivityRowRenderer.ts +14 -6
- package/src/web/templates/ButtonGroupRowRenderer.ts +58 -0
- package/src/web/templates/DataRowRenderer.ts +93 -20
- package/src/web/templates/IdentityRowRenderer.ts +150 -5
- package/src/web/templates/MessageRowRenderer.ts +23 -7
- package/src/web/templates/RowContainerStyle.ts +2 -0
- package/src/web/templates/RowRendererRegistry.ts +11 -1
- package/src/web/templates/SectionHeaderRowRenderer.ts +47 -4
|
@@ -22,6 +22,7 @@ internal class NativeListDataRowView(context: ThemedReactContext, fonts: NativeL
|
|
|
22
22
|
gravity = Gravity.CENTER_VERTICAL
|
|
23
23
|
}
|
|
24
24
|
private val cells = List(4) { NativeListTableColumnView(context, fonts) }
|
|
25
|
+
private val trailingAccessories = NativeListAccessoryStack(context, fonts)
|
|
25
26
|
|
|
26
27
|
override fun unselectedBackground(
|
|
27
28
|
item: NativeListItem,
|
|
@@ -54,16 +55,20 @@ internal class NativeListDataRowView(context: ThemedReactContext, fonts: NativeL
|
|
|
54
55
|
addView(visual)
|
|
55
56
|
addView(accessories)
|
|
56
57
|
addView(columns, LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f))
|
|
58
|
+
addView(trailingAccessories)
|
|
57
59
|
accessories.onAction = { key, view, slot, target ->
|
|
58
60
|
emitAction(key, view, "trailingAccessory", slot, target)
|
|
59
61
|
}
|
|
62
|
+
trailingAccessories.onAction = { key, view, slot, target ->
|
|
63
|
+
emitAction(key, view, "trailingAccessory", slot, target)
|
|
64
|
+
}
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
override fun defaultHeight(item: NativeListItem, layout: String): Int {
|
|
63
68
|
val values = item.json.optJSONArray("columns") ?: JSONArray()
|
|
64
69
|
val secondary =
|
|
65
70
|
(0 until values.length()).any {
|
|
66
|
-
values.getJSONObject(it)
|
|
71
|
+
nativeListDataColumnText(values.getJSONObject(it), "secondaryText").isNotEmpty()
|
|
67
72
|
}
|
|
68
73
|
return if (layout == "table") if (secondary) 60 else 52 else if (secondary) 64 else 56
|
|
69
74
|
}
|
|
@@ -127,6 +132,9 @@ internal class NativeListDataRowView(context: ThemedReactContext, fonts: NativeL
|
|
|
127
132
|
val accessoryStyle = JSONObject().put("value", style.optJSONObject("index"))
|
|
128
133
|
accessories.bind(item, descriptors, theme, accessoryStyle, sourceScale, checkboxState)
|
|
129
134
|
accessories.visibility = if (descriptors.length() == 0) GONE else VISIBLE
|
|
135
|
+
val trailing = data.optJSONArray("trailing") ?: JSONArray()
|
|
136
|
+
trailingAccessories.bind(item, trailing, theme, style, sourceScale, checkboxState)
|
|
137
|
+
trailingAccessories.visibility = if (trailing.length() == 0) GONE else VISIBLE
|
|
130
138
|
accessories.layoutParams =
|
|
131
139
|
LayoutParams(dp(32), LayoutParams.WRAP_CONTENT).apply { marginEnd = dp(10) }
|
|
132
140
|
val values = data.getJSONArray("columns")
|
|
@@ -159,6 +167,7 @@ internal class NativeListDataRowView(context: ThemedReactContext, fonts: NativeL
|
|
|
159
167
|
style,
|
|
160
168
|
fixedTableGeometry = unstyled,
|
|
161
169
|
)
|
|
170
|
+
cell.bindTitleIcons(value, theme, sourceScale)
|
|
162
171
|
// Legacy table default: every column is a fixed 40dp, top-aligned block.
|
|
163
172
|
cell.layoutParams =
|
|
164
173
|
LayoutParams(
|
|
@@ -194,17 +203,20 @@ internal class NativeListDataRowView(context: ThemedReactContext, fonts: NativeL
|
|
|
194
203
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
|
|
195
204
|
) {
|
|
196
205
|
accessories.updateSelection(item, checkboxState)
|
|
206
|
+
trailingAccessories.updateSelection(item, checkboxState)
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
override fun recycleContent() {
|
|
200
210
|
visual.recycle()
|
|
201
211
|
accessories.reset()
|
|
212
|
+
trailingAccessories.reset()
|
|
202
213
|
cells.forEach { it.reset() }
|
|
203
214
|
}
|
|
204
215
|
|
|
205
216
|
override fun disposeContent() {
|
|
206
217
|
visual.dispose()
|
|
207
218
|
accessories.reset()
|
|
219
|
+
trailingAccessories.reset()
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
private companion object {
|
|
@@ -215,6 +227,8 @@ internal class NativeListDataRowView(context: ThemedReactContext, fonts: NativeL
|
|
|
215
227
|
private class NativeListTableColumnView(context: android.content.Context, private val fonts: NativeListFonts) : LinearLayout(context) {
|
|
216
228
|
private val primaryLine = LinearLayout(context)
|
|
217
229
|
private val primary = NativeListTextView(context)
|
|
230
|
+
private val titleIcon = OneKeyIconView(context)
|
|
231
|
+
private val secondaryTitleIcon = OneKeyIconView(context)
|
|
218
232
|
private val badges = LinearLayout(context)
|
|
219
233
|
private val secondaryLine = LinearLayout(context)
|
|
220
234
|
private val secondaryLeading = NativeListTextView(context)
|
|
@@ -256,6 +270,13 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
256
270
|
},
|
|
257
271
|
)
|
|
258
272
|
primaryLine.addView(primary, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
|
|
273
|
+
for (icon in listOf(titleIcon, secondaryTitleIcon)) {
|
|
274
|
+
icon.visibility = GONE
|
|
275
|
+
primaryLine.addView(
|
|
276
|
+
icon,
|
|
277
|
+
LayoutParams(dp(20), dp(20)).apply { marginStart = dp(4) },
|
|
278
|
+
)
|
|
279
|
+
}
|
|
259
280
|
primaryLine.addView(
|
|
260
281
|
badges,
|
|
261
282
|
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
|
|
@@ -270,6 +291,13 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
270
291
|
}
|
|
271
292
|
|
|
272
293
|
fun reset() {
|
|
294
|
+
primaryLine.gravity = Gravity.CENTER_VERTICAL
|
|
295
|
+
for (icon in listOf(titleIcon, secondaryTitleIcon)) {
|
|
296
|
+
icon.visibility = GONE
|
|
297
|
+
icon.iconName = ""
|
|
298
|
+
icon.contentDescription = null
|
|
299
|
+
icon.setOnClickListener(null)
|
|
300
|
+
}
|
|
273
301
|
primaryLine.layoutParams.width = LayoutParams.WRAP_CONTENT
|
|
274
302
|
primaryLine.layoutParams.height = LayoutParams.WRAP_CONTENT
|
|
275
303
|
secondaryLine.layoutParams.height = LayoutParams.WRAP_CONTENT
|
|
@@ -292,6 +320,26 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
292
320
|
badges.visibility = GONE
|
|
293
321
|
}
|
|
294
322
|
|
|
323
|
+
fun bindTitleIcons(column: JSONObject, theme: JSONObject?, sourceScale: Boolean) {
|
|
324
|
+
for ((field, icon) in listOf(
|
|
325
|
+
"titleIcon" to titleIcon,
|
|
326
|
+
"secondaryTitleIcon" to secondaryTitleIcon,
|
|
327
|
+
)) {
|
|
328
|
+
val descriptor = column.optJSONObject(field) ?: continue
|
|
329
|
+
icon.iconName = descriptor.optString("name")
|
|
330
|
+
icon.glyphSizeDp = 20
|
|
331
|
+
icon.useSourceScale = sourceScale
|
|
332
|
+
icon.tintColor = parseNativeListColor(
|
|
333
|
+
descriptor.optString("tintColor").takeIf(String::isNotEmpty)
|
|
334
|
+
?: theme?.optString("iconSubdued", "#00000072") ?: "#00000072"
|
|
335
|
+
)
|
|
336
|
+
icon.contentDescription = descriptor.optString("accessibilityLabel").takeIf(String::isNotEmpty)
|
|
337
|
+
icon.visibility = VISIBLE
|
|
338
|
+
}
|
|
339
|
+
if (primary.maxLines > 1 && (titleIcon.visibility == VISIBLE || secondaryTitleIcon.visibility == VISIBLE))
|
|
340
|
+
primaryLine.gravity = Gravity.TOP
|
|
341
|
+
}
|
|
342
|
+
|
|
295
343
|
fun bind(
|
|
296
344
|
column: JSONObject,
|
|
297
345
|
rowBadges: JSONArray?,
|
|
@@ -326,7 +374,7 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
326
374
|
.bind(view)
|
|
327
375
|
if (resolved.optString("truncate") != "clip") view.ellipsize = TextUtils.TruncateAt.END
|
|
328
376
|
}
|
|
329
|
-
bindText(primary, column
|
|
377
|
+
bindText(primary, nativeListDataColumnText(column, "text"), "columns", primarySize, "medium", primaryColor, 20)
|
|
330
378
|
bindText(
|
|
331
379
|
secondaryLeading,
|
|
332
380
|
column.optString("secondaryLeadingText"),
|
|
@@ -338,13 +386,15 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
338
386
|
)
|
|
339
387
|
bindText(
|
|
340
388
|
secondary,
|
|
341
|
-
column
|
|
389
|
+
nativeListDataColumnText(column, "secondaryText"),
|
|
342
390
|
"columnSecondary",
|
|
343
391
|
12f,
|
|
344
392
|
"regular",
|
|
345
393
|
secondaryColor,
|
|
346
394
|
16,
|
|
347
395
|
)
|
|
396
|
+
primary.text = dataSegments(primary.text, column.optJSONArray("textSegments"), primary.textSize)
|
|
397
|
+
secondary.text = dataSegments(secondary.text, column.optJSONArray("secondaryTextSegments"), secondary.textSize)
|
|
348
398
|
if (rowBadges != null && rowBadges.length() > 0) {
|
|
349
399
|
badges.visibility = VISIBLE
|
|
350
400
|
for (index in 0 until minOf(2, rowBadges.length())) {
|
|
@@ -377,7 +427,7 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
377
427
|
secondaryLeading.visibility = VISIBLE
|
|
378
428
|
secondaryLine.visibility = VISIBLE
|
|
379
429
|
}
|
|
380
|
-
val secondaryText = column
|
|
430
|
+
val secondaryText = nativeListDataColumnText(column, "secondaryText")
|
|
381
431
|
if (secondaryText.isNotEmpty()) {
|
|
382
432
|
secondary.visibility = VISIBLE
|
|
383
433
|
secondaryLine.visibility = VISIBLE
|
|
@@ -404,7 +454,9 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
404
454
|
) {
|
|
405
455
|
reset()
|
|
406
456
|
gravity = Gravity.START
|
|
407
|
-
val value = android.text.SpannableStringBuilder(
|
|
457
|
+
val value = android.text.SpannableStringBuilder(dataSegments(
|
|
458
|
+
nativeListDataColumnText(column, "text"), column.optJSONArray("textSegments"),
|
|
459
|
+
sp(16f) * resources.displayMetrics.scaledDensity))
|
|
408
460
|
value.setSpan(
|
|
409
461
|
android.text.style.ForegroundColorSpan(primaryColor),
|
|
410
462
|
0,
|
|
@@ -428,10 +480,12 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
428
480
|
android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
429
481
|
)
|
|
430
482
|
}
|
|
431
|
-
val secondaryText = column
|
|
483
|
+
val secondaryText = nativeListDataColumnText(column, "secondaryText")
|
|
432
484
|
if (secondaryText.isNotEmpty()) {
|
|
433
485
|
val start = value.length
|
|
434
|
-
value.append("\n
|
|
486
|
+
value.append("\n")
|
|
487
|
+
value.append(dataSegments(secondaryText, column.optJSONArray("secondaryTextSegments"),
|
|
488
|
+
sp(16f) * resources.displayMetrics.scaledDensity))
|
|
435
489
|
value.setSpan(
|
|
436
490
|
android.text.style.ForegroundColorSpan(secondaryColor),
|
|
437
491
|
start,
|
|
@@ -461,6 +515,24 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
461
515
|
primary.visibility = VISIBLE
|
|
462
516
|
}
|
|
463
517
|
|
|
518
|
+
private fun dataSegments(fallback: CharSequence, segments: JSONArray?, fontSizePx: Float): CharSequence {
|
|
519
|
+
if (segments == null || segments.length() == 0) return fallback
|
|
520
|
+
val value = android.text.SpannableStringBuilder(fallback)
|
|
521
|
+
var offset = 0
|
|
522
|
+
for (index in 0 until segments.length()) {
|
|
523
|
+
val segment = segments.getJSONObject(index)
|
|
524
|
+
val end = offset + segment.optString("text").length
|
|
525
|
+
if (segment.optString("style") == "subscript" && end > offset) {
|
|
526
|
+
val density = resources.displayMetrics.density
|
|
527
|
+
val size = (kotlin.math.ceil(fontSizePx / density * 0.6f) * density).roundToInt()
|
|
528
|
+
value.setSpan(android.text.style.AbsoluteSizeSpan(size), offset, end,
|
|
529
|
+
android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
530
|
+
}
|
|
531
|
+
offset = end
|
|
532
|
+
}
|
|
533
|
+
return value
|
|
534
|
+
}
|
|
535
|
+
|
|
464
536
|
private fun applyStyle(style: JSONObject) {
|
|
465
537
|
style.optJSONObject("columns")?.let {
|
|
466
538
|
if (it.has("alignment")) {
|
|
@@ -485,3 +557,11 @@ private class NativeListTableColumnView(context: android.content.Context, privat
|
|
|
485
557
|
|
|
486
558
|
private fun sp(value: Float): Float = NativeListScale.font(resources, value)
|
|
487
559
|
}
|
|
560
|
+
|
|
561
|
+
private fun nativeListDataColumnText(column: JSONObject, field: String): String {
|
|
562
|
+
val segments = column.optJSONArray("${field}Segments")
|
|
563
|
+
if (segments == null || segments.length() == 0) return column.optString(field)
|
|
564
|
+
return buildString {
|
|
565
|
+
for (index in 0 until segments.length()) append(segments.getJSONObject(index).optString("text"))
|
|
566
|
+
}
|
|
567
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.margelo.nitro.nativelist
|
|
2
2
|
|
|
3
|
+
import android.graphics.PointF
|
|
3
4
|
import android.graphics.drawable.GradientDrawable
|
|
4
5
|
import android.text.Spannable
|
|
5
6
|
import android.text.SpannableStringBuilder
|
|
@@ -8,6 +9,7 @@ import android.text.style.AbsoluteSizeSpan
|
|
|
8
9
|
import android.text.style.ForegroundColorSpan
|
|
9
10
|
import android.util.TypedValue
|
|
10
11
|
import android.view.Gravity
|
|
12
|
+
import android.view.MotionEvent
|
|
11
13
|
import android.view.View
|
|
12
14
|
import android.view.ViewGroup
|
|
13
15
|
import android.widget.LinearLayout
|
|
@@ -28,17 +30,41 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
28
30
|
private val mainColumn = LinearLayout(context)
|
|
29
31
|
private val titleLine = PackedTitleLineLayout(context)
|
|
30
32
|
private val title = NativeListTextView(context)
|
|
33
|
+
private val titleIcon = OneKeyIconView(context)
|
|
31
34
|
private val subtitle = NativeListTextView(context)
|
|
32
35
|
private val tertiary = NativeListTextView(context)
|
|
33
36
|
private val badgeLine = NativeListTextView(context)
|
|
37
|
+
private val titleSuffixLabels = mutableListOf<NativeListTextView>()
|
|
38
|
+
private val titleSuffixMaxWidths = mutableListOf<Int>()
|
|
34
39
|
private val trailingColumn = NativeListAccessoryStack(context, fonts)
|
|
35
40
|
private val semanticBadgeLabels = mutableListOf<TextView>()
|
|
36
41
|
private val semanticSubtitleLabels = mutableListOf<TextView>()
|
|
37
42
|
private var walletBadgeLine: View? = null
|
|
38
43
|
private var currentItem: NativeListItem? = null
|
|
39
44
|
private var currentTheme: JSONObject? = null
|
|
45
|
+
private var longPressPoint: PointF? = null
|
|
46
|
+
private var longPressPointEpoch = -1L
|
|
40
47
|
private val paintDefaults = mutableMapOf<TextView, Int>()
|
|
41
48
|
|
|
49
|
+
override fun onRowTouch(event: MotionEvent) {
|
|
50
|
+
when (event.actionMasked) {
|
|
51
|
+
MotionEvent.ACTION_DOWN -> {
|
|
52
|
+
longPressPoint = if (currentItem?.isRowPressEnabled == true) PointF(event.x, event.y) else null
|
|
53
|
+
longPressPointEpoch = bindingEpoch
|
|
54
|
+
}
|
|
55
|
+
MotionEvent.ACTION_MOVE -> {
|
|
56
|
+
if (longPressPointEpoch == bindingEpoch && longPressPoint != null &&
|
|
57
|
+
event.x >= 0 && event.y >= 0 && event.x < width && event.y < height
|
|
58
|
+
) longPressPoint?.set(event.x, event.y)
|
|
59
|
+
else longPressPoint = null
|
|
60
|
+
}
|
|
61
|
+
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
62
|
+
longPressPoint = null
|
|
63
|
+
longPressPointEpoch = -1L
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
42
68
|
override fun backgroundGroupPosition(
|
|
43
69
|
item: NativeListItem,
|
|
44
70
|
layout: String,
|
|
@@ -83,6 +109,7 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
83
109
|
titleLine.orientation = HORIZONTAL
|
|
84
110
|
titleLine.gravity = Gravity.CENTER_VERTICAL
|
|
85
111
|
titleLine.addView(title, LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f))
|
|
112
|
+
titleLine.addView(titleIcon, LayoutParams(dp(20), dp(20)).apply { marginStart = dp(4) })
|
|
86
113
|
titleLine.addView(badgeLine)
|
|
87
114
|
trailingColumn.onAction = { key, view, slot, target ->
|
|
88
115
|
emitAction(key, view, "trailingAccessory", slot, target, trailingColumn.anchorInset(view))
|
|
@@ -111,7 +138,7 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
111
138
|
|
|
112
139
|
private fun showText(view: TextView, text: String, lines: Int) {
|
|
113
140
|
view.text = text
|
|
114
|
-
view.maxLines = lines
|
|
141
|
+
view.maxLines = if (lines == 0) Int.MAX_VALUE else lines
|
|
115
142
|
view.visibility = if (text.isEmpty()) GONE else VISIBLE
|
|
116
143
|
}
|
|
117
144
|
|
|
@@ -135,10 +162,42 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
135
162
|
layout: String,
|
|
136
163
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
|
|
137
164
|
) {
|
|
165
|
+
cancelLongPress()
|
|
166
|
+
longPressPoint = null
|
|
167
|
+
longPressPointEpoch = -1L
|
|
138
168
|
currentItem = item
|
|
169
|
+
val longPressActionKey = item.json.optString("longPressActionKey")
|
|
170
|
+
val boundEpoch = bindingEpoch
|
|
171
|
+
setOnLongClickListener(
|
|
172
|
+
if (longPressActionKey.isNotEmpty())
|
|
173
|
+
OnLongClickListener {
|
|
174
|
+
val point = longPressPoint
|
|
175
|
+
if (!isEnabled || !item.isRowPressEnabled || currentItem?.key != item.key ||
|
|
176
|
+
bindingEpoch != boundEpoch ||
|
|
177
|
+
(longPressPointEpoch == boundEpoch && point == null)) false
|
|
178
|
+
else {
|
|
179
|
+
val windowPoint = point?.takeIf { longPressPointEpoch == boundEpoch }?.let {
|
|
180
|
+
val location = IntArray(2)
|
|
181
|
+
getLocationInWindow(location)
|
|
182
|
+
PointF(location[0] + it.x, location[1] + it.y)
|
|
183
|
+
}
|
|
184
|
+
onAction?.invoke(item, longPressActionKey, null,
|
|
185
|
+
NativeListActionOrigin(this, this, bindingEpoch, "row",
|
|
186
|
+
windowPointPixels = windowPoint))
|
|
187
|
+
longPressPoint = null
|
|
188
|
+
longPressPointEpoch = -1L
|
|
189
|
+
true
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else null
|
|
193
|
+
)
|
|
194
|
+
isLongClickable = longPressActionKey.isNotEmpty()
|
|
139
195
|
currentTheme = theme
|
|
140
196
|
removeAllViews()
|
|
141
197
|
mainColumn.removeAllViews()
|
|
198
|
+
titleSuffixLabels.forEach(titleLine::removeView)
|
|
199
|
+
titleSuffixLabels.clear()
|
|
200
|
+
titleSuffixMaxWidths.clear()
|
|
142
201
|
semanticBadgeLabels.clear()
|
|
143
202
|
semanticSubtitleLabels.clear()
|
|
144
203
|
walletBadgeLine = null
|
|
@@ -157,8 +216,12 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
157
216
|
mainColumn.gravity = Gravity.CENTER_VERTICAL
|
|
158
217
|
titleLine.gravity = Gravity.CENTER_VERTICAL
|
|
159
218
|
titleLine.packsChildrenAtStart = false
|
|
219
|
+
titleLine.leadingTextMaxWidth = Int.MAX_VALUE
|
|
160
220
|
titleLine.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
|
161
221
|
title.layoutParams = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)
|
|
222
|
+
titleIcon.visibility = GONE
|
|
223
|
+
titleIcon.contentDescription = null
|
|
224
|
+
titleIcon.setOnClickListener(null)
|
|
162
225
|
for (view in listOf(title, subtitle, tertiary, badgeLine)) {
|
|
163
226
|
view.text = ""
|
|
164
227
|
view.visibility = GONE
|
|
@@ -294,6 +357,19 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
294
357
|
private fun addLeading(visual: JSONObject?, sizeDp: Int, spacingDp: Int = 12) {
|
|
295
358
|
// Legacy: a missing visual still reserves an empty, transparent leading slot.
|
|
296
359
|
val descriptor = visual ?: JSONObject().put("backgroundColor", "#00000000")
|
|
360
|
+
val overlays = descriptor.optJSONArray("overlays")
|
|
361
|
+
if (overlays != null && (0 until minOf(2, overlays.length())).any { index ->
|
|
362
|
+
val overlay = overlays.optJSONObject(index) ?: return@any false
|
|
363
|
+
overlay.optInt("offsetX", overlay.optInt("offset", 2)) > 0 ||
|
|
364
|
+
overlay.optInt("offsetY", overlay.optInt("offset", 2)) > 0
|
|
365
|
+
}) {
|
|
366
|
+
// The badge extends beyond the avatar and the row's padded content box.
|
|
367
|
+
clipChildren = false
|
|
368
|
+
clipToPadding = false
|
|
369
|
+
}
|
|
370
|
+
leadingFrame.bitmapBorderWidth =
|
|
371
|
+
if (descriptor.optString("borderColor").isNotEmpty() &&
|
|
372
|
+
descriptor.optString("borderStyle") != "dashed") dp(1) else 0
|
|
297
373
|
if (visual == null) leadingFrame.recycle()
|
|
298
374
|
val item = currentItem!!
|
|
299
375
|
val style = item.json.optJSONObject("style")?.optJSONObject("image") ?: JSONObject()
|
|
@@ -366,7 +442,58 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
366
442
|
}
|
|
367
443
|
|
|
368
444
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
445
|
+
val ratio = currentItem?.json?.optJSONObject("style")
|
|
446
|
+
?.optDouble("leadingContentMaxWidthRatio", Double.NaN) ?: Double.NaN
|
|
447
|
+
titleLine.leadingTextMaxWidth =
|
|
448
|
+
if (ratio.isFinite() && ratio > 0.0 && ratio <= 1.0 && leadingFrame.parent === this) {
|
|
449
|
+
val inner = (MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight)
|
|
450
|
+
.coerceAtLeast(0)
|
|
451
|
+
val leading = leadingFrame.layoutParams as LayoutParams
|
|
452
|
+
val icon = if (titleIcon.visibility == VISIBLE) dp(24) else 0
|
|
453
|
+
(inner * ratio).roundToInt()
|
|
454
|
+
.minus(leading.width + leading.marginEnd + icon)
|
|
455
|
+
.coerceAtLeast(0)
|
|
456
|
+
} else Int.MAX_VALUE
|
|
457
|
+
titleSuffixLabels.forEachIndexed { index, label ->
|
|
458
|
+
label.maxWidth = titleSuffixMaxWidths[index]
|
|
459
|
+
}
|
|
369
460
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
461
|
+
if (titleSuffixLabels.isNotEmpty()) {
|
|
462
|
+
val fixedWidth =
|
|
463
|
+
(1 until titleLine.childCount).sumOf { index ->
|
|
464
|
+
val view = titleLine.getChildAt(index)
|
|
465
|
+
if (view.visibility == GONE || titleSuffixLabels.any { it === view }) 0
|
|
466
|
+
else {
|
|
467
|
+
val margins = view.layoutParams as MarginLayoutParams
|
|
468
|
+
view.measuredWidth + margins.leftMargin + margins.rightMargin
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
val suffixMargins = titleSuffixLabels.sumOf {
|
|
472
|
+
val margins = it.layoutParams as MarginLayoutParams
|
|
473
|
+
margins.leftMargin + margins.rightMargin
|
|
474
|
+
}
|
|
475
|
+
val available =
|
|
476
|
+
(titleLine.measuredWidth - fixedWidth - suffixMargins - dp(80)).coerceAtLeast(0)
|
|
477
|
+
val desired = titleSuffixLabels.mapIndexed { index, label ->
|
|
478
|
+
label.paint.measureText(label.text.toString()).roundToInt()
|
|
479
|
+
.coerceAtMost(titleSuffixMaxWidths[index])
|
|
480
|
+
}
|
|
481
|
+
val total = desired.sum()
|
|
482
|
+
var remaining = available
|
|
483
|
+
var changed = false
|
|
484
|
+
titleSuffixLabels.forEachIndexed { index, label ->
|
|
485
|
+
val share =
|
|
486
|
+
if (total <= available) desired[index]
|
|
487
|
+
else if (index == titleSuffixLabels.lastIndex) remaining
|
|
488
|
+
else (desired[index].toLong() * available / total).toInt()
|
|
489
|
+
remaining -= share
|
|
490
|
+
if (label.maxWidth != share) {
|
|
491
|
+
label.maxWidth = share
|
|
492
|
+
changed = true
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (changed) super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
496
|
+
}
|
|
370
497
|
val item = currentItem ?: return
|
|
371
498
|
if (
|
|
372
499
|
item.hasExplicitHeight &&
|
|
@@ -424,6 +551,9 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
424
551
|
}
|
|
425
552
|
|
|
426
553
|
override fun recycleContent() {
|
|
554
|
+
cancelLongPress()
|
|
555
|
+
longPressPoint = null
|
|
556
|
+
longPressPointEpoch = -1L
|
|
427
557
|
leadingFrame.recycle()
|
|
428
558
|
trailingColumn.reset()
|
|
429
559
|
currentItem = null
|
|
@@ -592,6 +722,48 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
592
722
|
titleLine.packsChildrenAtStart = true
|
|
593
723
|
title.ellipsize = TextUtils.TruncateAt.END
|
|
594
724
|
showText(title, item.json.optString("title"), item.json.optInt("titleLines", 1))
|
|
725
|
+
item.json.optJSONArray("titleSuffixes")?.let { suffixes ->
|
|
726
|
+
for (index in 0 until suffixes.length()) {
|
|
727
|
+
val suffix = suffixes.optJSONObject(index) ?: continue
|
|
728
|
+
val label = NativeListTextView(context).apply {
|
|
729
|
+
text = suffix.optString("text")
|
|
730
|
+
typeface = fonts.regular(context)
|
|
731
|
+
textSize = sp(12f)
|
|
732
|
+
includeFontPadding = false
|
|
733
|
+
maxLines = 1
|
|
734
|
+
ellipsize = TextUtils.TruncateAt.END
|
|
735
|
+
setTextColor(
|
|
736
|
+
color(
|
|
737
|
+
theme,
|
|
738
|
+
if (suffix.optString("tone") == "disabled") "disabledText" else "secondaryText",
|
|
739
|
+
"#0000009B",
|
|
740
|
+
)
|
|
741
|
+
)
|
|
742
|
+
}
|
|
743
|
+
TextViewCompat.setLineHeight(label, dp(16))
|
|
744
|
+
val maxWidth =
|
|
745
|
+
if (suffix.has("maxWidth")) stylePx(suffix.optDouble("maxWidth")) else Int.MAX_VALUE
|
|
746
|
+
label.maxWidth = maxWidth
|
|
747
|
+
titleSuffixMaxWidths.add(maxWidth)
|
|
748
|
+
titleSuffixLabels.add(label)
|
|
749
|
+
titleLine.addView(
|
|
750
|
+
label,
|
|
751
|
+
titleLine.childCount - 1,
|
|
752
|
+
LayoutParams(LayoutParams.WRAP_CONTENT, dp(16)).apply { marginStart = dp(8) },
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
item.json.optJSONObject("titleIcon")?.let { icon ->
|
|
757
|
+
titleIcon.iconName = icon.optString("name")
|
|
758
|
+
titleIcon.glyphSizeDp = 20
|
|
759
|
+
titleIcon.useSourceScale = sourceScale
|
|
760
|
+
titleIcon.tintColor = safeColor(
|
|
761
|
+
icon.optString("tintColor").takeIf(String::isNotEmpty),
|
|
762
|
+
color(theme, "iconSubdued", "#00000072"),
|
|
763
|
+
)
|
|
764
|
+
titleIcon.contentDescription = icon.optString("accessibilityLabel").takeIf(String::isNotEmpty)
|
|
765
|
+
titleIcon.visibility = VISIBLE
|
|
766
|
+
}
|
|
595
767
|
if (item.json.optString("presentation") == "accountSelector") {
|
|
596
768
|
title.typeface = fonts.regular(context)
|
|
597
769
|
}
|
|
@@ -711,6 +883,10 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
711
883
|
.let { kinds ->
|
|
712
884
|
kinds.containsAll(listOf("value", "checkbox")) ||
|
|
713
885
|
kinds == listOf("valuePair", "menu") ||
|
|
886
|
+
kinds == listOf("valuePair", "chevron") ||
|
|
887
|
+
(kinds == listOf("icon", "chevron") &&
|
|
888
|
+
accessories.getJSONObject(0).optString("name") == "RefreshCcwOutline" &&
|
|
889
|
+
accessories.getJSONObject(0).optString("actionKey").isEmpty()) ||
|
|
714
890
|
(kinds == listOf("icon", "icon") &&
|
|
715
891
|
accessories.getJSONObject(0).optString("name") == "PencilOutline" &&
|
|
716
892
|
accessories.getJSONObject(1).optString("name") == "DragOutline")
|
|
@@ -27,6 +27,7 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
27
27
|
private val cornerBackdrop = View(context)
|
|
28
28
|
private val slots = mutableListOf<NativeListImageSlot>()
|
|
29
29
|
private val overlaySlots = mutableListOf<NativeListImageSlot>()
|
|
30
|
+
private val overlayFallbackIcons = mutableListOf<OneKeyIconView>()
|
|
30
31
|
private val overlays = mutableListOf<Pair<FrameLayout, JSONObject>>()
|
|
31
32
|
private var visual = JSONObject()
|
|
32
33
|
private var style = JSONObject()
|
|
@@ -159,6 +160,8 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
159
160
|
}
|
|
160
161
|
val placeholder = theme?.optString("strongBackground", "#0000000F") ?: "#0000000F"
|
|
161
162
|
val fallbackIcon = visual.optJSONObject("fallbackIcon")
|
|
163
|
+
val fallbackBackground = fallbackIcon?.optString("backgroundColor")
|
|
164
|
+
?.takeIf(String::isNotEmpty) ?: placeholder
|
|
162
165
|
val sourceFallback =
|
|
163
166
|
sources.firstOrNull()?.first?.optString("loadingStrategy", "none")?.let { it != "none" } ==
|
|
164
167
|
true && (visual.has("fallbackText") || fallbackIcon != null)
|
|
@@ -179,7 +182,7 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
179
182
|
val restored = fallbackKey?.let(NativeListSourceFallbackState::has) == true
|
|
180
183
|
fallback.textSize = if (sourceScale) 13f else NativeListScale.font(resources, 13f)
|
|
181
184
|
fallback.text = visual.optString("fallbackText").take(2)
|
|
182
|
-
fallback.setTextColor(color("#00000072"))
|
|
185
|
+
fallback.setTextColor(color(visual.optString("fallbackTextColor", "#00000072")))
|
|
183
186
|
fallback.visibility =
|
|
184
187
|
if (kind != "icon" && fallbackIcon == null && (sources.isEmpty() || restored)) VISIBLE
|
|
185
188
|
else GONE
|
|
@@ -193,7 +196,9 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
193
196
|
color(
|
|
194
197
|
visual.optString("backgroundColor", if (sources.isEmpty()) placeholder else "#00000000")
|
|
195
198
|
)
|
|
196
|
-
val backgroundColor =
|
|
199
|
+
val backgroundColor =
|
|
200
|
+
if (sources.isEmpty() && fallbackIcon?.has("backgroundColor") == true || restored) color(fallbackBackground)
|
|
201
|
+
else if (sourceFallback) color(placeholder) else visualBackground
|
|
197
202
|
background =
|
|
198
203
|
fill(backgroundColor, 0f).apply {
|
|
199
204
|
if (kind == "icon" && iconBorder) setStroke(1, color("#0000001F"))
|
|
@@ -210,7 +215,7 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
210
215
|
slot.recycle()
|
|
211
216
|
slot.view.visibility = GONE
|
|
212
217
|
} else {
|
|
213
|
-
slot.view.visibility = if (index > 0 ||
|
|
218
|
+
slot.view.visibility = if (index > 0 || restored) INVISIBLE else VISIBLE
|
|
214
219
|
slot.bind(
|
|
215
220
|
sources[index].first,
|
|
216
221
|
"$key:leading:$index",
|
|
@@ -229,7 +234,7 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
229
234
|
else NativeListSourceFallbackState.remember(it)
|
|
230
235
|
}
|
|
231
236
|
(background as? GradientDrawable)?.setColor(
|
|
232
|
-
if (loaded) visualBackground else color(
|
|
237
|
+
if (loaded) visualBackground else color(fallbackBackground)
|
|
233
238
|
)
|
|
234
239
|
fallback.visibility = if (!loaded && fallbackIcon == null) VISIBLE else GONE
|
|
235
240
|
icon.visibility = if (!loaded && fallbackIcon != null) VISIBLE else GONE
|
|
@@ -272,8 +277,18 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
272
277
|
overlayCount = descriptors.size,
|
|
273
278
|
)
|
|
274
279
|
while (overlaySlots.size < descriptors.size) overlaySlots.add(NativeListImageSlot(reactContext))
|
|
280
|
+
while (overlayFallbackIcons.size < descriptors.size)
|
|
281
|
+
overlayFallbackIcons.add(OneKeyIconView(context))
|
|
275
282
|
descriptors.forEachIndexed { index, data ->
|
|
276
283
|
val frame = previousOverlays.getOrNull(index)?.first ?: FrameLayout(context)
|
|
284
|
+
val image = data.optJSONObject("image")
|
|
285
|
+
val fallbackData = data.optJSONObject("fallbackIcon")
|
|
286
|
+
val fallbackGlyph = overlayFallbackIcons[index].apply {
|
|
287
|
+
visibility = GONE
|
|
288
|
+
iconName = fallbackData?.optString("name") ?: ""
|
|
289
|
+
tintColor = color(fallbackData?.optString("tintColor", "#0000009B") ?: "#0000009B")
|
|
290
|
+
useSourceScale = sourceScale
|
|
291
|
+
}
|
|
277
292
|
frame.background =
|
|
278
293
|
fill(
|
|
279
294
|
color(data.optString("backgroundColor", "#00000000")),
|
|
@@ -285,11 +300,15 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
285
300
|
frame.outlineProvider = ViewOutlineProvider.BACKGROUND
|
|
286
301
|
frame.clipToOutline = true
|
|
287
302
|
val child: View =
|
|
288
|
-
|
|
303
|
+
image?.let { source ->
|
|
289
304
|
val slot = overlaySlots[index]
|
|
290
305
|
slot.view.visibility = INVISIBLE
|
|
291
|
-
|
|
306
|
+
val circularImage = data.optInt("padding", 0) > 0 &&
|
|
307
|
+
data.optInt("width", data.optInt("size", 20)) == overlayHeight(data)
|
|
308
|
+
slot.bind(source, "$key:overlay:$index", placeholder = placeholder,
|
|
309
|
+
round = circularImage) {
|
|
292
310
|
slot.view.visibility = if (it) VISIBLE else INVISIBLE
|
|
311
|
+
fallbackGlyph.visibility = if (!it && fallbackData != null) VISIBLE else GONE
|
|
293
312
|
}
|
|
294
313
|
slot.view
|
|
295
314
|
}
|
|
@@ -306,8 +325,13 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
306
325
|
if (walletText) androidx.core.widget.TextViewCompat.setLineHeight(this, dp(16))
|
|
307
326
|
else setLineSpacing(0f, 1f)
|
|
308
327
|
typeface =
|
|
309
|
-
|
|
310
|
-
|
|
328
|
+
when (data.optString("fontWeight")) {
|
|
329
|
+
"regular" -> fonts.regular(context)
|
|
330
|
+
"medium" -> fonts.medium(context)
|
|
331
|
+
"semibold" -> fonts.semibold(context)
|
|
332
|
+
"bold" -> fonts.bold(context)
|
|
333
|
+
else -> if (isWalletText(data)) fonts.regular(context) else fonts.medium(context)
|
|
334
|
+
}
|
|
311
335
|
setTextColor(color(data.optString("tintColor", "#0000009B")))
|
|
312
336
|
}
|
|
313
337
|
} else {
|
|
@@ -321,11 +345,13 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
321
345
|
val inset = data.optInt("padding", 0)
|
|
322
346
|
if (isWalletText(data)) frame.setPadding(dp(2), 0, dp(2), 0)
|
|
323
347
|
else frame.setPadding(dp(inset), dp(inset), dp(inset), dp(inset))
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
348
|
+
frame.removeAllViews()
|
|
349
|
+
// A reused slot view may still belong to a frame dropped by an earlier bind.
|
|
350
|
+
(child.parent as? android.view.ViewGroup)?.removeView(child)
|
|
351
|
+
frame.addView(child, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
352
|
+
if (image != null && fallbackData != null) {
|
|
353
|
+
(fallbackGlyph.parent as? android.view.ViewGroup)?.removeView(fallbackGlyph)
|
|
354
|
+
frame.addView(fallbackGlyph, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
329
355
|
}
|
|
330
356
|
if (frame.parent == null) addView(frame)
|
|
331
357
|
overlays.add(frame to data)
|
|
@@ -383,8 +409,11 @@ internal class NativeListLeadingVisual(private val reactContext: ThemedReactCont
|
|
|
383
409
|
)
|
|
384
410
|
}
|
|
385
411
|
place(fallback, 0, 0, w, h)
|
|
412
|
+
val explicitFallbackSize = visual.optJSONObject("fallbackIcon")
|
|
413
|
+
?.optDouble("size", Double.NaN)?.takeIf { it.isFinite() && it > 0.0 }
|
|
386
414
|
val iconSize =
|
|
387
|
-
if (
|
|
415
|
+
if (explicitFallbackSize != null) dp(explicitFallbackSize.roundToInt())
|
|
416
|
+
else if (sources.isNotEmpty() && visual.optJSONObject("fallbackIcon") != null)
|
|
388
417
|
(minOf(w, h) *
|
|
389
418
|
if (visual.optJSONObject("fallbackIcon")?.optString("name") == "GlobusOutline") 1.2f
|
|
390
419
|
else 1f)
|