@craft-native/android 0.0.160 → 0.0.162
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -98,7 +98,11 @@ craft android build --native-bundle ../native-screen.js
|
|
|
98
98
|
|
|
99
99
|
Native mode maps STX `View`, `SafeAreaView`, `Text`, `Button`, `Link`, `Icon`,
|
|
100
100
|
`Pressable`, `TouchableOpacity`, `TouchableHighlight`, `Checkbox`, `TextInput`, `Picker`, `DatePicker`, `Modal`, `Switch`, `Slider`, `ActivityIndicator`, `Image`, `Video`, `ScrollView`,
|
|
101
|
-
and `FlatList` to Android views. `
|
|
101
|
+
`SectionList`, and `FlatList` to Android views. `SectionList` uses the same
|
|
102
|
+
native scroll host for ordinary `.stx` slot children; grouped section data
|
|
103
|
+
should use `FlatList` until the section-data protocol is available. The
|
|
104
|
+
generated host routes both list names through `CraftNativeScrollView` for this
|
|
105
|
+
slot-based contract. `Picker` renders `<select>`/`<option>` values
|
|
102
106
|
with `onValueChange` or `onChange`. `DatePicker` uses the platform `DatePicker`
|
|
103
107
|
and `TimePicker` controls for date, time, datetime, and countdown modes and
|
|
104
108
|
emits local `value`, ISO, and millisecond `timestamp` fields. `Modal` is parent-scoped and supports
|
|
@@ -131,7 +135,8 @@ When `flexShrink` is omitted, horizontal text labels and scroll hosts use the
|
|
|
131
135
|
same shrink defaults as the iOS renderer; buttons and editable fields retain
|
|
132
136
|
their intrinsic width unless explicitly configured.
|
|
133
137
|
Wrapped containers also honor `alignContent` (`center`, `flex-end`,
|
|
134
|
-
`space-between`, `space-around`, and `stretch`) consistently
|
|
138
|
+
`space-between`, `space-around`, `space-evenly`, and `stretch`) consistently
|
|
139
|
+
across hosts.
|
|
135
140
|
Shared wrapped-line IDs are accepted only when they are complete and monotonic;
|
|
136
141
|
invalid bridge data falls back to the host layout implementation.
|
|
137
142
|
Push, replace, and back preserve the
|
package/package.json
CHANGED
|
@@ -298,6 +298,43 @@ object CraftNative {
|
|
|
298
298
|
margins: FloatArray,
|
|
299
299
|
flags: FloatArray,
|
|
300
300
|
): FloatArray?
|
|
301
|
+
private external fun nativeGridAligned(
|
|
302
|
+
availableWidth: Float,
|
|
303
|
+
availableHeight: Float,
|
|
304
|
+
columns: Int,
|
|
305
|
+
columnGap: Float,
|
|
306
|
+
rowGap: Float,
|
|
307
|
+
alignContent: Int,
|
|
308
|
+
intrinsicWidth: FloatArray,
|
|
309
|
+
intrinsicHeight: FloatArray,
|
|
310
|
+
explicitWidth: FloatArray,
|
|
311
|
+
explicitHeight: FloatArray,
|
|
312
|
+
minWidth: FloatArray,
|
|
313
|
+
maxWidth: FloatArray,
|
|
314
|
+
minHeight: FloatArray,
|
|
315
|
+
maxHeight: FloatArray,
|
|
316
|
+
margins: FloatArray,
|
|
317
|
+
flags: FloatArray,
|
|
318
|
+
): FloatArray?
|
|
319
|
+
private external fun nativeGridAutoRowsAligned(
|
|
320
|
+
availableWidth: Float,
|
|
321
|
+
availableHeight: Float,
|
|
322
|
+
columns: Int,
|
|
323
|
+
columnGap: Float,
|
|
324
|
+
rowGap: Float,
|
|
325
|
+
alignContent: Int,
|
|
326
|
+
autoRowHeight: Float,
|
|
327
|
+
intrinsicWidth: FloatArray,
|
|
328
|
+
intrinsicHeight: FloatArray,
|
|
329
|
+
explicitWidth: FloatArray,
|
|
330
|
+
explicitHeight: FloatArray,
|
|
331
|
+
minWidth: FloatArray,
|
|
332
|
+
maxWidth: FloatArray,
|
|
333
|
+
minHeight: FloatArray,
|
|
334
|
+
maxHeight: FloatArray,
|
|
335
|
+
margins: FloatArray,
|
|
336
|
+
flags: FloatArray,
|
|
337
|
+
): FloatArray?
|
|
301
338
|
private external fun nativeClipboardRead(activity: Activity): String?
|
|
302
339
|
private external fun nativeClipboardWrite(activity: Activity, text: String): Boolean
|
|
303
340
|
private external fun nativeOpenUrl(activity: Activity, url: String): Boolean
|
|
@@ -532,13 +569,14 @@ object CraftNative {
|
|
|
532
569
|
margins: FloatArray,
|
|
533
570
|
flags: FloatArray,
|
|
534
571
|
autoRowHeight: Float? = null,
|
|
572
|
+
alignContent: Int = 0,
|
|
535
573
|
): FloatArray? {
|
|
536
574
|
if (!isAvailable) return null
|
|
537
575
|
return try {
|
|
538
576
|
if (autoRowHeight != null) {
|
|
539
|
-
|
|
577
|
+
nativeGridAutoRowsAligned(availableWidth, availableHeight, columns, columnGap, rowGap, alignContent, autoRowHeight, intrinsicWidth, intrinsicHeight, explicitWidth, explicitHeight, minWidth, maxWidth, minHeight, maxHeight, margins, flags)
|
|
540
578
|
} else {
|
|
541
|
-
|
|
579
|
+
nativeGridAligned(availableWidth, availableHeight, columns, columnGap, rowGap, alignContent, intrinsicWidth, intrinsicHeight, explicitWidth, explicitHeight, minWidth, maxWidth, minHeight, maxHeight, margins, flags)
|
|
542
580
|
}
|
|
543
581
|
?.takeIf { it.size >= intrinsicWidth.size * 4 && it.all { value -> value.isFinite() } }
|
|
544
582
|
} catch (e: UnsatisfiedLinkError) {
|
|
@@ -303,7 +303,8 @@ internal class CraftNativeCapabilities(
|
|
|
303
303
|
return FileResult.Multipart(body.toByteArray(), boundary)
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
private fun files(requestToken: String, method: String, args: JSONArray): CraftNativeCapabilityResult
|
|
306
|
+
private fun files(requestToken: String, method: String, args: JSONArray): CraftNativeCapabilityResult {
|
|
307
|
+
return when (method) {
|
|
307
308
|
"write" -> {
|
|
308
309
|
val name = args.optString(0).trim()
|
|
309
310
|
if (name.isBlank()) return failure("INVALID_ARGUMENT", "files.write needs a file name")
|
|
@@ -322,7 +323,8 @@ internal class CraftNativeCapabilities(
|
|
|
322
323
|
success(file.delete())
|
|
323
324
|
}
|
|
324
325
|
"download" -> download(requestToken, args)
|
|
325
|
-
|
|
326
|
+
else -> failure("UNKNOWN_ACTION", "Unsupported Files method")
|
|
327
|
+
}
|
|
326
328
|
}
|
|
327
329
|
|
|
328
330
|
private fun download(requestToken: String, args: JSONArray): CraftNativeCapabilityResult {
|
|
@@ -115,6 +115,7 @@ private data class CraftNativeLength(
|
|
|
115
115
|
private data class CraftNativeLayoutStyle(
|
|
116
116
|
val width: CraftNativeLength? = null,
|
|
117
117
|
val height: CraftNativeLength? = null,
|
|
118
|
+
val aspectRatio: Double? = null,
|
|
118
119
|
val minWidth: CraftNativeLength? = null,
|
|
119
120
|
val maxWidth: CraftNativeLength? = null,
|
|
120
121
|
val minHeight: CraftNativeLength? = null,
|
|
@@ -143,6 +144,7 @@ private data class CraftNativeLayoutStyle(
|
|
|
143
144
|
val flex = decimal("flex")
|
|
144
145
|
return CraftNativeLayoutStyle(
|
|
145
146
|
width = length("width"), height = length("height"),
|
|
147
|
+
aspectRatio = decimal("aspectRatio")?.takeIf { it.isFinite() && it > 0.0 },
|
|
146
148
|
minWidth = length("minWidth"), maxWidth = length("maxWidth"),
|
|
147
149
|
minHeight = length("minHeight"), maxHeight = length("maxHeight"),
|
|
148
150
|
position = raw.opt("position")
|
|
@@ -583,7 +585,8 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
|
|
|
583
585
|
// The STX style is authoritative. A reused View may still carry a
|
|
584
586
|
// previous fixed LayoutParams width after a reactive width is removed;
|
|
585
587
|
// using that value here prevents intrinsic sizing from being restored.
|
|
586
|
-
val
|
|
588
|
+
val authored = style.width?.resolve(base)
|
|
589
|
+
val explicit = authored ?: style.aspectRatio?.let { ratio -> style.height?.resolve(base)?.times(ratio)?.roundToInt() } ?: flexBasis
|
|
587
590
|
if (explicit != null) {
|
|
588
591
|
child.measure(
|
|
589
592
|
View.MeasureSpec.makeMeasureSpec(explicit, View.MeasureSpec.EXACTLY),
|
|
@@ -613,7 +616,8 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
|
|
|
613
616
|
val flexBasis = if (orientation == VERTICAL) style.flexBasis?.resolve(base) else null
|
|
614
617
|
// Match width measurement: do not let stale native LayoutParams keep
|
|
615
618
|
// a removed reactive height fixed.
|
|
616
|
-
val
|
|
619
|
+
val authored = style.height?.resolve(base)
|
|
620
|
+
val explicit = authored ?: style.aspectRatio?.let { ratio -> style.width?.resolve(base)?.div(ratio)?.roundToInt() } ?: flexBasis
|
|
617
621
|
val target = explicit ?: base
|
|
618
622
|
val spec = if (explicit != null) View.MeasureSpec.makeMeasureSpec(target, View.MeasureSpec.EXACTLY)
|
|
619
623
|
else View.MeasureSpec.makeMeasureSpec(base, View.MeasureSpec.AT_MOST)
|
|
@@ -649,6 +653,7 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
|
|
|
649
653
|
"space-between" -> 3
|
|
650
654
|
"space-around" -> 4
|
|
651
655
|
"stretch" -> 5
|
|
656
|
+
"space-evenly" -> 6
|
|
652
657
|
else -> 0
|
|
653
658
|
}
|
|
654
659
|
|
|
@@ -772,6 +777,7 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
|
|
|
772
777
|
listOf(if (align == "stretch") 1f else 0f, if (align == "center") 1f else if (align == "flex-end") 2f else 0f)
|
|
773
778
|
}.toFloatArray(),
|
|
774
779
|
gridAutoRows?.resolve(contentHeight)?.toFloat(),
|
|
780
|
+
alignContentCode(),
|
|
775
781
|
)
|
|
776
782
|
if (sharedFrames != null) {
|
|
777
783
|
for ((index, entry) in flow.withIndex()) {
|
|
@@ -783,9 +789,30 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
|
|
|
783
789
|
entry.view.layout(x, y, x + childWidth, y + childHeight)
|
|
784
790
|
}
|
|
785
791
|
} else {
|
|
786
|
-
|
|
792
|
+
val totalRows = layout.rowHeights.sum()
|
|
793
|
+
val rowGaps = max(0, layout.rowHeights.size - 1) * rowGap
|
|
794
|
+
val freeRows = max(0, contentHeight - totalRows - rowGaps)
|
|
795
|
+
val rowCount = max(1, layout.rowHeights.size)
|
|
796
|
+
var rowLeading = 0
|
|
797
|
+
var rowBetween = rowGap
|
|
798
|
+
var rowExtra = 0
|
|
799
|
+
when (alignContentCode()) {
|
|
800
|
+
1 -> rowLeading = freeRows / 2
|
|
801
|
+
2 -> rowLeading = freeRows
|
|
802
|
+
3 -> if (rowCount > 1) rowBetween += freeRows / (rowCount - 1)
|
|
803
|
+
4 -> {
|
|
804
|
+
rowLeading = freeRows / (rowCount * 2)
|
|
805
|
+
rowBetween += freeRows / rowCount
|
|
806
|
+
}
|
|
807
|
+
6 -> {
|
|
808
|
+
rowLeading = freeRows / (rowCount + 1)
|
|
809
|
+
rowBetween += freeRows / (rowCount + 1)
|
|
810
|
+
}
|
|
811
|
+
5 -> rowExtra = freeRows / rowCount
|
|
812
|
+
}
|
|
813
|
+
var rowTop = paddingTop + rowLeading
|
|
787
814
|
for ((rowIndex, row) in layout.rows.withIndex()) {
|
|
788
|
-
val rowHeight = layout.rowHeights[rowIndex]
|
|
815
|
+
val rowHeight = layout.rowHeights[rowIndex] + rowExtra
|
|
789
816
|
for ((column, entry) in row.withIndex()) {
|
|
790
817
|
val align = entry.style.alignSelf ?: alignItems
|
|
791
818
|
val lp = entry.view.layoutParams as? MarginLayoutParams
|
|
@@ -809,7 +836,7 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
|
|
|
809
836
|
}
|
|
810
837
|
entry.view.layout(x, y, x + childWidth, y + childHeight)
|
|
811
838
|
}
|
|
812
|
-
rowTop += rowHeight +
|
|
839
|
+
rowTop += rowHeight + rowBetween
|
|
813
840
|
}
|
|
814
841
|
}
|
|
815
842
|
for (entry in layout.absolute) {
|
|
@@ -849,6 +876,10 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
|
|
|
849
876
|
crossBetween = crossFree / lines.size + crossGap
|
|
850
877
|
crossLeading = crossFree / (lines.size * 2)
|
|
851
878
|
}
|
|
879
|
+
"space-evenly" -> {
|
|
880
|
+
crossBetween = crossFree / (lines.size + 1) + crossGap
|
|
881
|
+
crossLeading = crossFree / (lines.size + 1)
|
|
882
|
+
}
|
|
852
883
|
"stretch" -> {
|
|
853
884
|
val extra = crossFree / lines.size
|
|
854
885
|
lines.forEach { it.cross += extra }
|
|
@@ -2803,7 +2834,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
2803
2834
|
"Svg" -> (previous as? CraftNativeSvgView ?: CraftNativeSvgView(this)).apply {
|
|
2804
2835
|
drawing = node
|
|
2805
2836
|
}
|
|
2806
|
-
"ScrollView" -> (previous as? CraftNativeScrollView ?: CraftNativeScrollView(this)).apply {
|
|
2837
|
+
"ScrollView", "SectionList" -> (previous as? CraftNativeScrollView ?: CraftNativeScrollView(this)).apply {
|
|
2807
2838
|
val horizontal = props.optBoolean("horizontal", false) || style.optString("flexDirection") in setOf("row", "row-reverse")
|
|
2808
2839
|
setHorizontal(horizontal)
|
|
2809
2840
|
setNativeNestedScrollingEnabled(true)
|
|
@@ -3064,6 +3095,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
3064
3095
|
"video" -> "Video"
|
|
3065
3096
|
"icon" -> "Icon"
|
|
3066
3097
|
"scrollview" -> "ScrollView"
|
|
3098
|
+
"sectionlist" -> "SectionList"
|
|
3067
3099
|
"flatlist" -> "FlatList"
|
|
3068
3100
|
"svg" -> "Svg"
|
|
3069
3101
|
"touchableopacity" -> "TouchableOpacity"
|
|
@@ -3477,7 +3509,9 @@ class MainActivity : AppCompatActivity() {
|
|
|
3477
3509
|
else -> null
|
|
3478
3510
|
}
|
|
3479
3511
|
view.tooltipText = props.optString("accessibilityHint").takeIf { it.isNotBlank() }
|
|
3480
|
-
val role = props.optString("accessibilityRole").ifBlank {
|
|
3512
|
+
val role = props.optString("accessibilityRole").ifBlank {
|
|
3513
|
+
if (type == "Icon") "image" else type.lowercase()
|
|
3514
|
+
}
|
|
3481
3515
|
val state = props.optJSONObject("accessibilityState")
|
|
3482
3516
|
val disabled = props.optBoolean("disabled", false) || state?.optBoolean("disabled", false) == true
|
|
3483
3517
|
if (type !in setOf("Button", "Link", "Checkbox", "TextInput", "Picker", "DatePicker", "Modal", "Switch", "Slider", "ActivityIndicator")) {
|