Move SSD detection truth checks to typed Kotlin

ober

3055bade02bf1647237543b3ef51c5f7adbdc8ae

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 8f5cb56..c80f53c 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -4164,7 +4164,8 @@
     (typed-kotlin-file "com/sfb/ssdreview/UiTextHelpers.kt"
       (kotlin-imports (java io File)
                       (android net Uri)
-                      (androidx documentfile provider DocumentFile))
+                      (androidx documentfile provider DocumentFile)
+                      (org json JSONObject))
       (typed-library (com sfb ssdreview)
         (export normalizeFiringArc normalizeLabel shouldReplaceGuessLabel
                 shouldReplaceBoxTypeLabel resolvedBoxTypeLabel
@@ -4231,7 +4232,9 @@
                 detectorSourceWithTruth detectionTruthNote detectionOcrNote
                 detectionLearnedNote shouldLoadLocalTruth
                 shouldUseExistingTruth shouldApplyExistingTruth
+                shouldUseExistingTruthObject shouldApplyExistingTruthObject
                 shouldApplyLearnedGuesses shouldMergeRemoteTruth
+                shouldMergeRemoteTruthObject detectionTruthNoteFor
                 reviewPrimaryColor reviewWhiteColor reviewLabelTextColor
                 reviewLabelBackgroundColor reviewCanvasBackgroundColor
                 reviewAccentColor reviewModeColor reviewDarkTextColor
@@ -4248,6 +4251,7 @@
         (type File)
         (type Uri)
         (type DocumentFile)
+        (type JSONObject)
         (extern (fileIsFile (file : File)) : Bool
           (kotlin-member-get isFile))
         (extern (fileName (file : File)) : String
@@ -4830,13 +4834,27 @@
                                      (forceDetect : Bool)) : Bool
           (and (> existingTruthGroupCount (int32 0))
                (not forceDetect)))
+        (def (shouldUseExistingTruthObject (truth : (Nullable JSONObject))
+                                           (existingTruthGroupCount : Int32)
+                                           (forceDetect : Bool)) : Bool
+          (and (truthPresent truth)
+               (shouldUseExistingTruth existingTruthGroupCount forceDetect)))
         (def (shouldApplyExistingTruth (existingTruthGroupCount : Int32)) : Bool
           (> existingTruthGroupCount (int32 0)))
+        (def (shouldApplyExistingTruthObject (truth : (Nullable JSONObject))
+                                             (existingTruthGroupCount : Int32)) : Bool
+          (and (truthPresent truth)
+               (shouldApplyExistingTruth existingTruthGroupCount)))
         (def (shouldApplyLearnedGuesses (existingTruthGroupCount : Int32)) : Bool
           (= existingTruthGroupCount (int32 0)))
         (def (shouldMergeRemoteTruth (hasExistingTruth : Bool)
                                      (forceDetect : Bool)) : Bool
           (and (not hasExistingTruth) (not forceDetect)))
+        (def (shouldMergeRemoteTruthObject (truth : (Nullable JSONObject))
+                                           (forceDetect : Bool)) : Bool
+          (shouldMergeRemoteTruth (truthPresent truth) forceDetect))
+        (def (detectionTruthNoteFor (truth : (Nullable JSONObject))) : String
+          (detectionTruthNote (truthPresent truth)))
         (def (reviewPrimaryColor) : Int32
           (androidColorRgb (int32 37) (int32 99) (int32 235)))
         (def (reviewWhiteColor) : Int32
@@ -6513,7 +6531,7 @@
        "                var detectorNote = \"\""
        "                var detectorSource = \"unknown\""
        ""
-	       "                if (truthPresent(existingTruth) && shouldUseExistingTruth(existingTruthGroupCount, forceDetect)) {"
+	       "                if (shouldUseExistingTruthObject(existingTruth, existingTruthGroupCount, forceDetect)) {"
 	       "                    detectorSource = \"local-truth\""
 	       "                    session.ssdArea = nullableTruthSsdAreaRect(existingTruth)"
 	       "                    truthStore.applyTruth(session)"
@@ -6534,7 +6552,7 @@
        "                    }"
        "                    ssdSessionAddDetectedCellsInsideArea(session, detectedCells)"
        "                    session.groups.addAll(initialGroupsLimited(session.cells, 120))"
-       "                    if (truthPresent(existingTruth) && shouldApplyExistingTruth(existingTruthGroupCount)) {"
+       "                    if (shouldApplyExistingTruthObject(existingTruth, existingTruthGroupCount)) {"
        "                        truthStore.applyTruth(session)"
        "                        ssdSessionPruneGroupsOutsideSsdArea(session)"
        "                    }"
@@ -6550,12 +6568,12 @@
 	       "                    reviewView.session = session"
 	       "                    reviewView.selectedGroupId = null"
 	       "                    reviewView.ssdArea = session.ssdArea"
-       "                    val truthNote = detectionTruthNote(truthPresent(existingTruth))"
+       "                    val truthNote = detectionTruthNoteFor(existingTruth)"
        "                    val ocrNote = detectionOcrNote(ocrGuesses, session.ocrWords.size)"
        "                    val learnedNote = detectionLearnedNote(learnedGuesses)"
        "                    setStatus(sessionCountsStatus(session, detectorNote + truthNote + ocrNote + learnedNote))"
        "                    logDetectResult(\"page-load\", session, detectorSource, learnedGuesses, ocrGuesses)"
-       "                    if (shouldMergeRemoteTruth(truthPresent(existingTruth), forceDetect)) mergeRemoteTruthAsync(session, expectedGeneration, uri, expectedPageIndex)"
+       "                    if (shouldMergeRemoteTruthObject(existingTruth, forceDetect)) mergeRemoteTruthAsync(session, expectedGeneration, uri, expectedPageIndex)"
        "                }"
        "            } catch (error: Exception) {"
        "                runOnUiThread {"