Skip to content

ADFA-3615 | Group radio buttons together in XML generation#1219

Merged
jatezzz merged 5 commits intostagefrom
feat/ADFA-3615-radio-button-grouping-experimental
Apr 21, 2026
Merged

ADFA-3615 | Group radio buttons together in XML generation#1219
jatezzz merged 5 commits intostagefrom
feat/ADFA-3615-radio-button-grouping-experimental

Conversation

@jatezzz
Copy link
Copy Markdown
Collaborator

@jatezzz jatezzz commented Apr 20, 2026

Description

Added logic to group individual radio buttons detected by the Yolo model into standard Android <RadioGroup> components. Introduced a LayoutItem sealed interface to construct a layout tree during geometry processing. The LayoutGeometryProcessor now identifies sequences of adjacent radio buttons and clusters them horizontally or vertically. Finally, AndroidXmlGenerator translates these clusters into <RadioGroup> tags, automatically handling IDs, orientation, and the checkedButton attribute based on the parsed annotations.

Details

document_5129774364132116179.mp4

Ticket

ADFA-3615

@jatezzz jatezzz requested review from a team, Daniel-ADFA and avestaadfa April 20, 2026 20:31
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

📝 Walkthrough

Release Notes - ADFA-3615: Radio Button Grouping in XML Generation

Features & Improvements

  • Radio Button Grouping: Added logic to group individual radio buttons detected by the YOLO model into Android <RadioGroup> components with automatic orientation detection (horizontal/vertical)
  • Layout Tree Architecture: Introduced LayoutItem sealed interface to represent distinct layout element shapes (SimpleView, HorizontalRow, RadioGroup)
  • Enhanced Geometry Processing: LayoutGeometryProcessor now identifies sequences of adjacent radio buttons and clusters them with support for radio label merging
  • XML Generation Refactor:
    • New AndroidWidget sealed class hierarchy for structured widget rendering (TextBasedWidget, InputWidget, ImageWidget, GenericWidget)
    • New LayoutRenderer for pattern-matching layout items and emitting Android XML
    • New XmlContext utility providing XML output building and collision-free ID generation with character escaping
    • Refactored AndroidXmlGenerator to coordinate layout tree rendering with proper scroll container handling
  • Attribute Management: Supports parsing annotations, XML attribute escaping, and per-widget-type attribute emission (including tools:ignore, android:checked states, margin calculations, etc.)

Architecture Changes

  • Complete refactor of Android XML generation pipeline: replaced monolithic 217-line AndroidXmlGenerator (domain folder) with modular component system (~330 new lines across multiple files in xml subfolder)
  • Centralized XML ID generation with collision detection via XmlContext.nextId() and collision-free android:id resolution
  • Dependency injection: YoloToXmlConverter now delegates XML generation to injected AndroidXmlGenerator instance

⚠️ Risks & Best Practices Concerns

  • High Complexity Increase: Large architectural refactor introduces multiple new sealed classes and rendering pipelines—increased surface area for bugs
  • Complete Component Replacement: Old AndroidXmlGenerator entirely removed and replaced; comprehensive testing required to verify all previous XML generation functionality is preserved (row grouping, layout containers, spacing logic, widget-specific attributes)
  • New ID Collision System: XmlContext.nextId() uses label-based counters—verify that ID collision prevention works correctly under edge cases (duplicate labels, special characters in labels)
  • Radio Button Label Merging: mergeRadioLabels() logic copies adjacent text boxes to radio elements; potential data loss or layout distortion if text box positioning/attributes differ significantly from radio buttons
  • Lack of Backward Compatibility Evidence: No indication of migration path or deprecation warnings for code consuming the old AndroidXmlGenerator API
  • Untested Complex Paths: Radio group attribute parsing, per-radio checked state resolution, margin computation for horizontal radio layouts, and layout tree construction are new and require thorough validation

Walkthrough

Refactors Android XML generation into modular components: adds AndroidXmlGenerator, LayoutGeometryProcessor.buildLayoutTree(), LayoutRenderer, AndroidWidget, and XmlContext; replaces the previous monolithic AndroidXmlGenerator; and updates YoloToXmlConverter to accept and delegate to the new generator.

Changes

Cohort / File(s) Summary
Layout model & processing
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/model/LayoutItem.kt, cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/LayoutGeometryProcessor.kt
Added sealed LayoutItem types (SimpleView, HorizontalRow, RadioGroup) and buildLayoutTree() which groups/scans boxes into rows and merges radio button labels, producing layout items.
Widget abstraction
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidWidget.kt
Introduced AndroidWidget sealed hierarchy and concrete widget classes to map box labels + parsed attributes to Android view tags and per-widget attributes/id resolution.
Rendering pipeline
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt
New LayoutRenderer that walks LayoutItem trees and emits Android XML for simple views, horizontal rows (computing margins), and radio groups (id allocation, checked handling).
XML output & IDs
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt
Added XmlContext for building XML strings, deterministic per-label id generation/registration (nextId, resolveId), and String.escapeXmlAttr() attribute escaping.
Generator & integration
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidXmlGenerator.kt, cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/YoloToXmlConverter.kt
Added new AndroidXmlGenerator that orchestrates prolog, optional ScrollView, layout-tree creation and rendering; updated YoloToXmlConverter to take an AndroidXmlGenerator and delegate XML generation.
Removed legacy
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/AndroidXmlGenerator.kt
Deleted previous monolithic AndroidXmlGenerator implementation (all XML construction helpers and procedural emission removed).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Yolo as YoloToXmlConverter
    participant Gen as AndroidXmlGenerator
    participant Geo as LayoutGeometryProcessor
    participant Renderer as LayoutRenderer
    participant Context as XmlContext

    Client->>Yolo: generateXmlLayout(detections, annotations, ...)
    Yolo->>Gen: buildXml(boxes, annotations, targetDpHeight, wrapInScroll)
    Gen->>Geo: buildLayoutTree(boxes)
    Geo-->>Gen: List<LayoutItem>
    Gen->>Renderer: render(item) [for each LayoutItem]
    Renderer->>Context: append elements, register ids
    Renderer-->>Gen: rendered fragments
    Gen->>Context: finalize wrapper tags
    Gen-->>Yolo: xml String
    Yolo-->>Client: xml String
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • Daniel-ADFA
  • itsaky-adfa
  • hal-eisen-adfa

Poem

🐇 I munched a layout into little parts,
Rows and radios, ids to chart,
Widgets spring from boxes neat,
XML hums a steady beat,
Hopping code that makes UIs smart.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: grouping radio buttons into RadioGroup components in XML generation, which directly aligns with the core objective of this PR.
Description check ✅ Passed The description is directly related to the changeset, providing context about the radio button grouping feature, the new LayoutItem interface, geometry processing changes, and AndroidXmlGenerator updates for RadioGroup generation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-3615-radio-button-grouping-experimental

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt (1)

24-29: escapeXmlAttr() silently trims — surprising side effect for a function named “escape”.

This extension is applied to every attribute value emitted by AndroidWidget.render (including android:text, android:hint, android:contentDescription). An escape helper should only perform the replacements; trimming changes semantics (legitimate leading/trailing whitespace in user annotations becomes unrecoverable) and is easy to miss when this is called on values the caller assumes are already normalized.

Recommend separating the concerns — callers that want to trim should trim explicitly before escaping.

♻️ Proposed change
-fun String.escapeXmlAttr(): String = this.trim()
-    .replace("&", "&amp;")
+fun String.escapeXmlAttr(): String = this
+    .replace("&", "&amp;")
     .replace("<", "&lt;")
     .replace(">", "&gt;")
     .replace("\"", "&quot;")
     .replace("'", "&apos;")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt`
around lines 24 - 29, The String.escapeXmlAttr() function silently trims input
which changes semantics; remove the .trim() call so escapeXmlAttr only performs
XML-escaping and does not alter leading/trailing whitespace, and update callers
(e.g., AndroidWidget.render usage sites that pass values for android:text,
android:hint, android:contentDescription) to explicitly call .trim() where
trimming is desired before calling escapeXmlAttr; ensure tests (if any) reflect
the new behavior.
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt (1)

61-75: Prefer a named type over an anonymous object {} for per-radio state.

The anonymous object expression works but is unidiomatic Kotlin — the properties are only usable within this local scope and tools/readers lose a type name for them. A small local data class (or even a Triple/Pair of records) would be clearer and would make follow-on changes (e.g. the fix for the multi-checked issue above) easier.

♻️ Proposed refactor
+    private data class RadioEntry(
+        val box: ScaledBox,
+        val attrs: Map<String, String>,
+        val id: String,
+        val extra: Map<String, String>,
+        val checked: Boolean
+    )
+
     private fun renderRadioGroup(boxes: List<ScaledBox>, orientation: String, indent: String) {
         ...
-            object { val box = box; val attrs = parsedAttrs; val id = id; val extra = extraAttrs; val checked = isChecked }
+            RadioEntry(box, parsedAttrs, id, extraAttrs, isChecked)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt`
around lines 61 - 75, The local radios list is built using an anonymous object
expression (object { val box = box; val attrs = parsedAttrs; ... }) which is
unidiomatic and hides the type; replace that anonymous object with a small local
named data class (e.g., RadioState with properties box, attrs, id, extra,
checked) defined in LayoutRenderer.kt near the radios construction, then
construct instances of RadioState in the mapIndexed lambda (use
FuzzyAttributeParser.parse and context.nextId as before) so downstream code like
radios.firstOrNull { it.checked }?.id can use a clear, named type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidWidget.kt`:
- Line 20: The render logic currently uses idOverride or
parsedAttrs["android:id"] directly and only calls context.nextId(box.label) when
absent, which allows user-supplied ids to bypass the id counter and cause
collisions; change render() so that whenever you derive an id from idOverride or
parsedAttrs["android:id"] you also reserve/register it with the XmlContext
counter (e.g. call a method like XmlContext.reserveId(id) or add to
context.counters/usedIds) before returning, and if reservation detects an
existing reservation handle it (e.g. pick a unique fallback via context.nextId
or normalize) so context.nextId(box.label) will never later generate the same
id; update handling for both idOverride and parsedAttrs extraction code paths
(symbols: idOverride, parsedAttrs["android:id"], context.nextId(box.label),
XmlContext.reserveId / context.counters / usedIds).

In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt`:
- Around line 58-98: renderRadioGroup can emit multiple children with
android:checked="true" causing contradictory XML; when calling renderSimpleView
for each radio, ensure non-selected radios are forced to have
"android:checked"="false" by adding that key to the parsedAttrsOverride map for
radios whose id != checkedId (leave the selected radio's parsedAttrs unchanged);
reference renderRadioGroup, the radios local objects, renderSimpleView call, and
TextBasedWidget.specificAttributes/parsedAttrsOverride so the change is made
where parsedAttrsOverride is passed through.

---

Nitpick comments:
In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt`:
- Around line 61-75: The local radios list is built using an anonymous object
expression (object { val box = box; val attrs = parsedAttrs; ... }) which is
unidiomatic and hides the type; replace that anonymous object with a small local
named data class (e.g., RadioState with properties box, attrs, id, extra,
checked) defined in LayoutRenderer.kt near the radios construction, then
construct instances of RadioState in the mapIndexed lambda (use
FuzzyAttributeParser.parse and context.nextId as before) so downstream code like
radios.firstOrNull { it.checked }?.id can use a clear, named type.

In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt`:
- Around line 24-29: The String.escapeXmlAttr() function silently trims input
which changes semantics; remove the .trim() call so escapeXmlAttr only performs
XML-escaping and does not alter leading/trailing whitespace, and update callers
(e.g., AndroidWidget.render usage sites that pass values for android:text,
android:hint, android:contentDescription) to explicitly call .trim() where
trimming is desired before calling escapeXmlAttr; ensure tests (if any) reflect
the new behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 35c4e640-aa9f-46af-9a66-7ab1d799d947

📥 Commits

Reviewing files that changed from the base of the PR and between d449e99 and a63b146.

📒 Files selected for processing (8)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/AndroidXmlGenerator.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/LayoutGeometryProcessor.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/YoloToXmlConverter.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/model/LayoutItem.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidWidget.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidXmlGenerator.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt
💤 Files with no reviewable changes (1)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/AndroidXmlGenerator.kt

@jatezzz jatezzz force-pushed the feat/ADFA-3615-radio-button-grouping-experimental branch from d369f50 to 55df292 Compare April 21, 2026 14:41
@jatezzz jatezzz requested a review from Daniel-ADFA April 21, 2026 16:08
@jatezzz jatezzz force-pushed the feat/ADFA-3615-radio-button-grouping-experimental branch from 6c2b43d to 7601466 Compare April 21, 2026 16:22
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidWidget.kt (1)

20-25: ⚠️ Potential issue | 🟡 Minor

Keep parsed IDs collision-safe when registering them.

registerId() only records the ID; it does not reject or rewrite duplicates. If a parsed android:id equals an earlier generated/manual ID, this still emits duplicate @+id/... values. Split idOverride from parsed IDs and make parsed-ID registration detect duplicates or fall back to nextId(...).

🛡️ Sketch of the safer flow
-        val manualId = idOverride ?: parsedAttrs["android:id"]?.substringAfterLast('/')
-        val id = if (manualId != null) {
-            context.registerId(manualId)
-            manualId
-        } else {
-            context.nextId(box.label)
-        }
+        val parsedId = parsedAttrs["android:id"]?.substringAfterLast('/')
+        val id = when {
+            idOverride != null -> idOverride
+            parsedId != null -> context.registerIdOrNull(parsedId) ?: context.nextId(box.label)
+            else -> context.nextId(box.label)
+        }

Supporting XmlContext change:

-    fun registerId(id: String) {
-        usedIds.add(id)
-    }
+    fun registerIdOrNull(id: String): String? =
+        id.takeIf { usedIds.add(it) }
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/LayoutGeometryProcessor.kt`:
- Around line 136-149: Rows containing a radio circle plus adjacent label (e.g.,
[radio_button_unchecked, text]) are currently treated as generic HorizontalRow
because the branch only checks row.all { isRadioButton(it) }; update the logic
in LayoutGeometryProcessor to detect "radio option rows" before falling back to
HorizontalRow by recognizing rows with exactly one radio element and one
neighboring text/label element, attach/merge the label into the radio item's
model (use isRadioButton and the verticalRadioRun handling), then emit
LayoutItem.RadioGroup (horizontal or vertical using the existing
verticalRadioRun/flushVerticalRadioRun flow) instead of
LayoutItem.HorizontalRow; adjust the branches around rows.forEach to check for
single-radio+label patterns and handle them accordingly while preserving
existing vertical radio run logic.

In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt`:
- Around line 61-75: When building radios, don't always call
context.nextId(box.label) first; instead check parsedAttrs for "android:id" and
resolve that into a collision-safe id using the same resolution logic
AndroidWidget uses (i.e. pass the parsed id through the same
context/id-normalization/collision-safe path), assign that resolved id to the
radio object (replace usages of the raw context.nextId result) and use the same
resolved id when computing checkedId (android:checkedButton) and when passing
idOverride later; apply the same change to the similar block around the other
occurrence (lines ~95-101) so annotated android:id values are preserved and
collision-safe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f14a6f82-e87a-4882-bf52-7aa0afb9274b

📥 Commits

Reviewing files that changed from the base of the PR and between a63b146 and 7601466.

📒 Files selected for processing (8)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/AndroidXmlGenerator.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/LayoutGeometryProcessor.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/YoloToXmlConverter.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/model/LayoutItem.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidWidget.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidXmlGenerator.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt
💤 Files with no reviewable changes (1)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/AndroidXmlGenerator.kt
✅ Files skipped from review due to trivial changes (1)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/model/LayoutItem.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidXmlGenerator.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt

@jatezzz jatezzz force-pushed the feat/ADFA-3615-radio-button-grouping-experimental branch from 7601466 to f1986eb Compare April 21, 2026 16:57
jatezzz added 5 commits April 21, 2026 15:10
Updated layout geometry processor to build a layout tree that detects contiguous radio buttons and generates corresponding `<RadioGroup>` XML tags.
…e radio labels for proper RadioGroup parsing
@jatezzz jatezzz force-pushed the feat/ADFA-3615-radio-button-grouping-experimental branch from f1986eb to 0b28785 Compare April 21, 2026 20:10
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt (1)

61-76: Replace the anonymous object { ... } with a local data class for readability.

The object { val box = box; val attrs = parsedAttrs; ... } pattern relies on Kotlin anonymous-object type inference inside a private function. It works, but a named local data class is clearer, produces proper equals/hashCode, avoids shadowing identifiers via val box = box, and makes the radio record easier to refactor/extend later.

♻️ Suggested refactor
-    private fun renderRadioGroup(boxes: List<ScaledBox>, orientation: String, indent: String) {
+    private data class RadioChild(
+        val box: ScaledBox,
+        val attrs: Map<String, String>,
+        val id: String,
+        val extra: Map<String, String>,
+        val checked: Boolean,
+    )
+
+    private fun renderRadioGroup(boxes: List<ScaledBox>, orientation: String, indent: String) {
         val groupId = context.nextId("radio_group")
 
         val radios = boxes.mapIndexed { index, box ->
             val parsedAttrs = FuzzyAttributeParser.parse(annotations[box], "RadioButton")
 
             val requestedId = parsedAttrs["android:id"]?.substringAfterLast('/')
             val id = context.resolveId(requestedId, box.label)
 
             val extraAttrs = if (orientation == "horizontal" && index < boxes.lastIndex) {
                 val gap = maxOf(0, boxes[index + 1].x - (box.x + box.w))
                 mapOf("android:layout_marginEnd" to "${gap}dp")
             } else emptyMap()
 
             val isChecked = box.label == "radio_button_checked" ||
                     parsedAttrs["android:checked"]?.equals("true", ignoreCase = true) == true
 
-            object { val box = box; val attrs = parsedAttrs; val id = id; val extra = extraAttrs; val checked = isChecked }
+            RadioChild(box, parsedAttrs, id, extraAttrs, isChecked)
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt`
around lines 61 - 76, Replace the anonymous object creation inside the radios
mapIndexed with a small local data class (e.g., data class RadioItem(val box:
BoxType, val attrs: Map<String,String>, val id: Int?, val extra:
Map<String,String>, val checked: Boolean)) declared inside the same function in
LayoutRenderer.kt; construct and return instances of that data class instead of
object { ... } (use the existing parsedAttrs, id, extraAttrs and isChecked
variables), which removes the val box = box shadowing and gives proper
equals/hashCode, clearer code, and easier future refactors for the radios
collection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/LayoutGeometryProcessor.kt`:
- Around line 149-182: The buildLayoutTree currently emits a RadioGroup for a
single radio because verticalRadioRun is always converted to a RadioGroup;
change the logic so flushVerticalRadioRun() only creates LayoutItem.RadioGroup
when verticalRadioRun.size >= 2 and for a single entry it instead adds
LayoutItem.SimpleView(verticalRadioRun.first()), then clear the run; keep the
rest of buildLayoutTree and the places that add to verticalRadioRun (the branch
row.all { isRadioButton(it) } && row.size == 1) unchanged so accumulation still
works but lone radios are emitted as SimpleView rather than a 1-button
RadioGroup.

In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidWidget.kt`:
- Around line 75-93: In specificAttributes(), avoid leaking internal box.label
into android:text when parsedAttrs["android:text"] is missing and box.text is
empty or equals box.label; instead, when tag is not in widgetTags set the
android:text to a neutral value (empty string or the tag name) or omit the
android:text entry entirely; update the logic around parsedAttrs, box.text,
box.label and tag in specificAttributes() so the fallback does not assign
box.label to attrs["android:text"] (keep tools:ignore if you still emit
android:text).

---

Nitpick comments:
In
`@cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt`:
- Around line 61-76: Replace the anonymous object creation inside the radios
mapIndexed with a small local data class (e.g., data class RadioItem(val box:
BoxType, val attrs: Map<String,String>, val id: Int?, val extra:
Map<String,String>, val checked: Boolean)) declared inside the same function in
LayoutRenderer.kt; construct and return instances of that data class instead of
object { ... } (use the existing parsedAttrs, id, extraAttrs and isChecked
variables), which removes the val box = box shadowing and gives proper
equals/hashCode, clearer code, and easier future refactors for the radios
collection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0a43a52b-33b9-4c12-b01d-66f7155b6806

📥 Commits

Reviewing files that changed from the base of the PR and between 7601466 and 0b28785.

📒 Files selected for processing (8)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/AndroidXmlGenerator.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/LayoutGeometryProcessor.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/YoloToXmlConverter.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/model/LayoutItem.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidWidget.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidXmlGenerator.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/LayoutRenderer.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt
💤 Files with no reviewable changes (1)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/AndroidXmlGenerator.kt
✅ Files skipped from review due to trivial changes (2)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/model/LayoutItem.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/AndroidXmlGenerator.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/YoloToXmlConverter.kt
  • cv-image-to-xml/src/main/java/org/appdevforall/codeonthego/computervision/domain/xml/XmlContext.kt

@jatezzz jatezzz merged commit 782c8cc into stage Apr 21, 2026
2 checks passed
@jatezzz jatezzz deleted the feat/ADFA-3615-radio-button-grouping-experimental branch April 21, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants