Generate SSD action rows from typed Jerboa

ober

3be0f479b98e6fc42551dba080381efa2b2c910c

diff --git a/.build.yml b/.build.yml
index e3f414b..7cb6136 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,7 @@ packages:
   - make=4.4.1-r4
 sources:
   # Build dependency: full immutable commit, mirrored in dependencies.lock.json.
-  - "https://git.sr.ht/~lisp/jerboa#c0f2d60605a5153c1f82a08940234be86526f778"
+  - "https://git.sr.ht/~lisp/jerboa#d2d02fb5b44d6fbfeb2aa3842ad44522fad5187b"
   # The second source is the build subject selected by the SourceHut submitter.
   - https://git.sr.ht/~lisp/jerboa-android
 tasks:
@@ -14,6 +14,6 @@ tasks:
       test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2
       test "$(apk info -v git)" = git-2.54.0-r0
       test "$(apk info -v make)" = make-4.4.1-r4
-      test "$(git -C ../jerboa rev-parse HEAD)" = c0f2d60605a5153c1f82a08940234be86526f778
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 04c8e7d80a4abcd2a2de654be650cf4b30cc33a2
+      test "$(git -C ../jerboa rev-parse HEAD)" = d2d02fb5b44d6fbfeb2aa3842ad44522fad5187b
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 76a8eb714ebe1177b9c2ed7208e83d2133cf3d15
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index f00821c..c166bc4 100644
--- a/dependencies.lock.json
+++ b/dependencies.lock.json
@@ -11,8 +11,8 @@
   "generator_runtime": {
     "name": "jerboa",
     "repository": "https://git.sr.ht/~lisp/jerboa",
-    "commit": "c0f2d60605a5153c1f82a08940234be86526f778",
-    "tree": "04c8e7d80a4abcd2a2de654be650cf4b30cc33a2"
+    "commit": "d2d02fb5b44d6fbfeb2aa3842ad44522fad5187b",
+    "tree": "76a8eb714ebe1177b9c2ed7208e83d2133cf3d15"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 8b98fe2..b06d33a 100755
--- a/scripts/verify-supply-chain.sh
+++ b/scripts/verify-supply-chain.sh
@@ -3,8 +3,8 @@ set -eu
 
 repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
 lock="$repo/dependencies.lock.json"
-jerboa_commit=c0f2d60605a5153c1f82a08940234be86526f778 # gitsafe:ignore
-jerboa_tree=04c8e7d80a4abcd2a2de654be650cf4b30cc33a2 # gitsafe:ignore
+jerboa_commit=d2d02fb5b44d6fbfeb2aa3842ad44522fad5187b # gitsafe:ignore
+jerboa_tree=76a8eb714ebe1177b9c2ed7208e83d2133cf3d15 # gitsafe:ignore
 gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336
 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331
diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 1d95017..955ccba 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -4958,6 +4958,7 @@
                 autoCompleteInstallFiringArcVisibilityWatcher
                 autoCompleteInstallBoxTypeDialogBehavior
                 buttonSetOnClickAction
+                mainActivityActionRow
                 mainActivityAddActionRowButtons
                 permissionResultGranted downloadAccessBeforeRuntimePermissions
                 downloadAccessFromPermission activityResultRejected
@@ -5255,6 +5256,24 @@
                   (button : Button)
                   (listener : (-> View Unit))) : Unit
           (kotlin-member-call setOnClickListener))
+        (extern (linearLayoutForActivity
+                  (activity : MainActivity)) : LinearLayout
+          (kotlin-call LinearLayout))
+        (extern (linearLayoutOrientationSetRaw
+                  (layout : LinearLayout)
+                  (orientation : Int32)) : Unit
+          (kotlin-member-set orientation))
+        (extern (linearLayoutGravitySetRaw
+                  (layout : LinearLayout)
+                  (gravity : Int32)) : Unit
+          (kotlin-member-set gravity))
+        (extern (buttonArrayAsList
+                  (buttons : (Array Button))) : (List Button)
+          (kotlin-member-call asList))
+        (extern (mainActivityDpRaw
+                  (activity : MainActivity)
+                  (value : Int32)) : Int32
+          (kotlin-call mainActivityDp))
         (extern (linearLayoutWeightedWrapContentParams
                   (width : Int32)
                   (height : Int32)
@@ -7523,6 +7542,20 @@
             button
             (lambda ((unused : View))
               (invoke action))))
+        (def (mainActivityActionRow
+               (activity : MainActivity)
+               (orientation : Int32)
+               (gravity : Int32)
+               (buttons : (Array Button) (modifiers vararg))) : LinearLayout
+          (let ((row (linearLayoutForActivity activity)))
+            (begin
+              (linearLayoutOrientationSetRaw row orientation)
+              (linearLayoutGravitySetRaw row gravity)
+              (mainActivityAddActionRowButtons
+                row
+                (buttonArrayAsList buttons)
+                (mainActivityDpRaw activity (int32 3)))
+              row)))
         (def (linearLayoutAddWeightedButton
                (layout : LinearLayout)
                (button : Button)
@@ -10382,37 +10415,37 @@
        "        ))"
        "        val controls = LinearLayout(this).apply {"
        "            orientation = LinearLayout.VERTICAL"
-       "            addView(actionRow("
+       "            addView(mainActivityActionRow(this@MainActivity, LinearLayout.HORIZONTAL, Gravity.CENTER_VERTICAL,"
 	       "                button(\"Open SSDs\", ButtonKind.PRIMARY) { openPdf() },"
 	       "                button(\"Detect\", ButtonKind.ACCENT) { detectCurrent(forceDetect = true) },"
 	       "                button(\"Set SSD Area\", ButtonKind.MODE) { mainActivityStartSsdAreaMode(this@MainActivity, reviewView, currentSession, selectedGroupId, HapticFeedbackConstants.CLOCK_TICK) },"
 	       "            ))"
-	       "            addView(actionRow("
+	       "            addView(mainActivityActionRow(this@MainActivity, LinearLayout.HORIZONTAL, Gravity.CENTER_VERTICAL,"
 	       "                button(\"Open Any\") { openAnyPdf() },"
 	       "                button(\"Rotate\") { mainActivityRotatePage(this@MainActivity, reviewView, loadGeneration, currentSession) },"
 	       "                button(\"Fit\") { reviewViewFitImage(reviewView) },"
 	       "            ))"
-	       "            addView(actionRow("
+	       "            addView(mainActivityActionRow(this@MainActivity, LinearLayout.HORIZONTAL, Gravity.CENTER_VERTICAL,"
 	       "                button(\"Prev Page\") { mainActivityChangePage(this@MainActivity, pdfUri, pageCount, pageIndex, -1) },"
 	       "                button(\"Page #\") { showPageDialog() },"
 	       "                button(\"Next Page\") { mainActivityChangePage(this@MainActivity, pdfUri, pageCount, pageIndex, 1) },"
 	       "            ))"
-	       "            addView(actionRow("
+	       "            addView(mainActivityActionRow(this@MainActivity, LinearLayout.HORIZONTAL, Gravity.CENTER_VERTICAL,"
 	       "                button(\"Pan\", ButtonKind.NEUTRAL) { mainActivityApplySelectMode(this@MainActivity, reviewView, ssdAreaMode, appendMode, false) },"
 	       "                button(\"Select Area\", ButtonKind.PRIMARY) { mainActivityApplySelectMode(this@MainActivity, reviewView, ssdAreaMode, appendMode, true) },"
 	       "                button(\"Add\", ButtonKind.ACCENT) { mainActivityStartAppendMode(this@MainActivity, reviewView, currentSession, selectedGroupId) },"
 	       "            ))"
-	       "            addView(actionRow("
+	       "            addView(mainActivityActionRow(this@MainActivity, LinearLayout.HORIZONTAL, Gravity.CENTER_VERTICAL,"
 	       "                button(\"Prev\", ButtonKind.NAV) { mainActivitySelectRelativeGroup(this@MainActivity, reviewView, currentSession, selectedGroupId, -1, HapticFeedbackConstants.CLOCK_TICK) },"
 	       "                button(\"Next\", ButtonKind.PRIMARY) { mainActivitySelectRelativeGroup(this@MainActivity, reviewView, currentSession, selectedGroupId, 1, HapticFeedbackConstants.CLOCK_TICK) },"
 	       "                button(\"Edit\") { editSelectedGroup() },"
 	       "            ))"
-	       "            addView(actionRow("
+	       "            addView(mainActivityActionRow(this@MainActivity, LinearLayout.HORIZONTAL, Gravity.CENTER_VERTICAL,"
 	       "                button(\"Approve\", ButtonKind.ACCENT) { mainActivityApproveSelectedGroup(this@MainActivity, reviewView, truthStore, currentSession, selectedGroupId, HapticFeedbackConstants.CONFIRM) },"
 	       "                button(\"Delete\", ButtonKind.DANGER) { mainActivityDeleteSelectedGroup(this@MainActivity, reviewView, truthStore, currentSession, selectedGroupId, HapticFeedbackConstants.REJECT) },"
 	       "                button(\"Save\") { mainActivitySaveTruth(this@MainActivity, truthStore, currentSession) },"
 	       "            ))"
-       "            addView(actionRow("
+       "            addView(mainActivityActionRow(this@MainActivity, LinearLayout.HORIZONTAL, Gravity.CENTER_VERTICAL,"
        "                button(\"Pull Truth\") { mainActivityPullTruthDumpAsync(this@MainActivity, truthStore) },"
        "                button(\"Push Truth\") { mainActivityPushTruthDumpAsync(this@MainActivity, truthStore) },"
        "                button(\"Sync Folder\") { syncTruthFolder() },"
@@ -10443,13 +10476,6 @@
        "            buttonSetOnClickAction(this, action)"
 	       "        }"
        ""
-       "    private fun actionRow(vararg buttons: Button): LinearLayout ="
-       "        LinearLayout(this).apply {"
-       "            orientation = LinearLayout.HORIZONTAL"
-       "            gravity = Gravity.CENTER_VERTICAL"
-       "            mainActivityAddActionRowButtons(this, buttons.asList(), mainActivityDp(this@MainActivity, 3))"
-       "        }"
-       ""
        "    private fun boxTypeInput(): AutoCompleteTextView ="
        "        AutoCompleteTextView(this).apply {"
        "            inputType = InputType.TYPE_CLASS_TEXT"