@craft-native/android 0.0.160 → 0.0.161

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. `Picker` renders `<select>`/`<option>` values
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craft-native/android",
3
- "version": "0.0.160",
3
+ "version": "0.0.161",
4
4
  "homepage": "https://github.com/craft-native/craft/tree/main/packages/android#readme",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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
- nativeGridAutoRows(availableWidth, availableHeight, columns, columnGap, rowGap, autoRowHeight, intrinsicWidth, intrinsicHeight, explicitWidth, explicitHeight, minWidth, maxWidth, minHeight, maxHeight, margins, flags)
577
+ nativeGridAutoRowsAligned(availableWidth, availableHeight, columns, columnGap, rowGap, alignContent, autoRowHeight, intrinsicWidth, intrinsicHeight, explicitWidth, explicitHeight, minWidth, maxWidth, minHeight, maxHeight, margins, flags)
540
578
  } else {
541
- nativeGrid(availableWidth, availableHeight, columns, columnGap, rowGap, intrinsicWidth, intrinsicHeight, explicitWidth, explicitHeight, minWidth, maxWidth, minHeight, maxHeight, margins, flags)
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 = when (method) {
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
- else -> failure("UNKNOWN_ACTION", "Unsupported Files method")
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 explicit = style.width?.resolve(base) ?: flexBasis
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 explicit = style.height?.resolve(base) ?: flexBasis
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)
@@ -772,6 +776,7 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
772
776
  listOf(if (align == "stretch") 1f else 0f, if (align == "center") 1f else if (align == "flex-end") 2f else 0f)
773
777
  }.toFloatArray(),
774
778
  gridAutoRows?.resolve(contentHeight)?.toFloat(),
779
+ alignContentCode(),
775
780
  )
776
781
  if (sharedFrames != null) {
777
782
  for ((index, entry) in flow.withIndex()) {
@@ -783,9 +788,26 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
783
788
  entry.view.layout(x, y, x + childWidth, y + childHeight)
784
789
  }
785
790
  } else {
786
- var rowTop = paddingTop
791
+ val totalRows = layout.rowHeights.sum()
792
+ val rowGaps = max(0, layout.rowHeights.size - 1) * rowGap
793
+ val freeRows = max(0, contentHeight - totalRows - rowGaps)
794
+ val rowCount = max(1, layout.rowHeights.size)
795
+ var rowLeading = 0
796
+ var rowBetween = rowGap
797
+ var rowExtra = 0
798
+ when (alignContentCode()) {
799
+ 1 -> rowLeading = freeRows / 2
800
+ 2 -> rowLeading = freeRows
801
+ 3 -> if (rowCount > 1) rowBetween += freeRows / (rowCount - 1)
802
+ 4 -> {
803
+ rowLeading = freeRows / (rowCount * 2)
804
+ rowBetween += freeRows / rowCount
805
+ }
806
+ 5 -> rowExtra = freeRows / rowCount
807
+ }
808
+ var rowTop = paddingTop + rowLeading
787
809
  for ((rowIndex, row) in layout.rows.withIndex()) {
788
- val rowHeight = layout.rowHeights[rowIndex]
810
+ val rowHeight = layout.rowHeights[rowIndex] + rowExtra
789
811
  for ((column, entry) in row.withIndex()) {
790
812
  val align = entry.style.alignSelf ?: alignItems
791
813
  val lp = entry.view.layoutParams as? MarginLayoutParams
@@ -809,7 +831,7 @@ private class CraftNativeFlexLayout(context: Context) : LinearLayout(context) {
809
831
  }
810
832
  entry.view.layout(x, y, x + childWidth, y + childHeight)
811
833
  }
812
- rowTop += rowHeight + rowGap
834
+ rowTop += rowHeight + rowBetween
813
835
  }
814
836
  }
815
837
  for (entry in layout.absolute) {
@@ -2803,7 +2825,7 @@ class MainActivity : AppCompatActivity() {
2803
2825
  "Svg" -> (previous as? CraftNativeSvgView ?: CraftNativeSvgView(this)).apply {
2804
2826
  drawing = node
2805
2827
  }
2806
- "ScrollView" -> (previous as? CraftNativeScrollView ?: CraftNativeScrollView(this)).apply {
2828
+ "ScrollView", "SectionList" -> (previous as? CraftNativeScrollView ?: CraftNativeScrollView(this)).apply {
2807
2829
  val horizontal = props.optBoolean("horizontal", false) || style.optString("flexDirection") in setOf("row", "row-reverse")
2808
2830
  setHorizontal(horizontal)
2809
2831
  setNativeNestedScrollingEnabled(true)
@@ -3064,6 +3086,7 @@ class MainActivity : AppCompatActivity() {
3064
3086
  "video" -> "Video"
3065
3087
  "icon" -> "Icon"
3066
3088
  "scrollview" -> "ScrollView"
3089
+ "sectionlist" -> "SectionList"
3067
3090
  "flatlist" -> "FlatList"
3068
3091
  "svg" -> "Svg"
3069
3092
  "touchableopacity" -> "TouchableOpacity"
@@ -3477,7 +3500,9 @@ class MainActivity : AppCompatActivity() {
3477
3500
  else -> null
3478
3501
  }
3479
3502
  view.tooltipText = props.optString("accessibilityHint").takeIf { it.isNotBlank() }
3480
- val role = props.optString("accessibilityRole").ifBlank { type.lowercase() }
3503
+ val role = props.optString("accessibilityRole").ifBlank {
3504
+ if (type == "Icon") "image" else type.lowercase()
3505
+ }
3481
3506
  val state = props.optJSONObject("accessibilityState")
3482
3507
  val disabled = props.optBoolean("disabled", false) || state?.optBoolean("disabled", false) == true
3483
3508
  if (type !in setOf("Button", "Link", "Checkbox", "TextInput", "Picker", "DatePicker", "Modal", "Switch", "Slider", "ActivityIndicator")) {