Move SSD box weapon wrapper to typed Kotlin

ober

e009bd75e38556c3807a950ffc8cc12d848d9188

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index e193b92..1c9a969 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -94,6 +94,26 @@
                           (findPartialBoxTypes items lowered normalized))
                         exact))))))))
 
+    (typed-kotlin-file "com/sfb/ssdreview/BoxTypeAndroid.kt"
+      (kotlin-imports (android content Context))
+      (typed-library (com sfb ssdreview)
+        (export boxTypeRawIsWeapon)
+        (type Context)
+        (extern (boxTypesResolve (context : Context) (raw : String)) : (Nullable BoxType)
+          (kotlin-call BoxTypes resolve))
+        (def (resolvedBoxTypeNameOr (boxType : (Nullable BoxType)) (fallback : String)) : String
+          (if (nullable-null? boxType)
+              fallback
+              (BoxType-name (nullable-get boxType))))
+        (def (boxTypeRawIsWeapon (context : Context) (raw : String)) : Bool
+          (let ((text (string-trim raw)))
+            (if (string-blank? text)
+                #f
+                (boxTypeIsWeapon
+                  (resolvedBoxTypeNameOr
+                    (boxTypesResolve context text)
+                    text)))))))
+
     (typed-kotlin-file "com/sfb/ssdreview/OcrWord.kt"
       (typed-library (com sfb ssdreview)
         (export make-OcrWord OcrWord? OcrWord-text OcrWord-conf OcrWord-bbox)
@@ -2878,13 +2898,6 @@
        "        return resolveBoxType(load(context), raw)"
        "    }"
        ""
-       "    fun isWeapon(context: Context, raw: String): Boolean {"
-       "        val text = raw.trim()"
-       "        if (text.isBlank()) return false"
-       "        val resolved = resolve(context, text)"
-       "        return boxTypeIsWeapon(resolved?.name ?: text)"
-       "    }"
-       ""
        "}"
        ))
     (kotlin-file-lines "com/sfb/ssdreview/MainActivity.kt"
@@ -3139,7 +3152,7 @@
        "    ) {"
        "        var previousBoxType = initialBoxType"
        "        fun update() {"
-       "            val visible = firingArc.text.isNotBlank() || BoxTypes.isWeapon(this, boxType.text.toString())"
+       "            val visible = firingArc.text.isNotBlank() || boxTypeRawIsWeapon(this, boxType.text.toString())"
        "            firingArc.visibility = if (visible) View.VISIBLE else View.GONE"
        "        }"
        "        boxType.addTextChangedListener(object : TextWatcher {"