From d6686a3b8c15075448d52570e62756ebbcae06fb Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Thu, 14 May 2026 12:44:14 +0100 Subject: [PATCH 1/9] fix(ADFA-3375): Exclude child views from generic layout parameters --- .../codeonthego/layouteditor/tools/XmlLayoutParser.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutParser.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutParser.kt index 3482cd09a0..4e656a926d 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutParser.kt +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutParser.kt @@ -228,10 +228,12 @@ class XmlLayoutParser( } else { view = result as? View view?.let { - it.layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - ) + if (listViews.isEmpty()) { + it.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + ) + } listViews.add(it) } } From 06786b3a795cfd7b19f8debb68d0c39cfaa4cb90 Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Thu, 14 May 2026 14:18:16 +0100 Subject: [PATCH 2/9] feat(ADFA-3375): Support TextInputEditText in the layout editor --- .../main/assets/attributes/attributes.json | 66 +++++++++++++++++++ .../src/main/assets/widgetclasses.json | 3 +- .../callers/text/TextInputEditTextCaller.kt | 5 ++ .../palette/text/TextInputEditTextDesign.kt | 43 ++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputEditTextCaller.kt create mode 100644 layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputEditTextDesign.kt diff --git a/layouteditor/src/main/assets/attributes/attributes.json b/layouteditor/src/main/assets/attributes/attributes.json index b66fac0a0a..e43530f760 100644 --- a/layouteditor/src/main/assets/attributes/attributes.json +++ b/layouteditor/src/main/assets/attributes/attributes.json @@ -638,5 +638,71 @@ "attributeName": "android:measureAllChildren", "argumentType": "boolean" } + ], + "com.google.android.material.textfield.TextInputEditText": [ + { + "name": "android:hint", + "methodName": "setHint", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:hint", + "argumentType": "text|string" + }, + { + "name": "android:inputType", + "methodName": "setInputType", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:inputType", + "argumentType": "flag", + "arguments": [ + "date", + "datetime", + "none", + "number", + "numberDecimal", + "numberSigned", + "numberPassword", + "phone", + "text", + "textAutoComplete", + "textAutoCorrect", + "textCapCharacters", + "textCapSentences", + "textCapWords", + "textEmailAddress", + "textEmailSubject", + "textEnableTextConversionSuggestions", + "textFilter", + "textImeMultiLine", + "textLongMessage", + "textMultiLine", + "textNoSuggestions", + "textPassword", + "textPersonName", + "textPhonetic", + "textPostalAddress", + "textShortMessage", + "textUri", + "textVisiblePassword", + "textWebEditText", + "textWebEmailAddress", + "textWebPassword", + "time" + ], + "defaultValue": "-1" + }, + { + "name": "android:textColorHint", + "methodName": "setHintTextColor", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:textColorHint", + "argumentType": "color" + }, + { + "name": "android:singleLine", + "methodName": "setSingleLine", + "className": "text.TextInputEditTextCaller", + "attributeName": "android:singleLine", + "argumentType": "boolean" + } ] } \ No newline at end of file diff --git a/layouteditor/src/main/assets/widgetclasses.json b/layouteditor/src/main/assets/widgetclasses.json index 7507fe9f3a..04770f28ee 100644 --- a/layouteditor/src/main/assets/widgetclasses.json +++ b/layouteditor/src/main/assets/widgetclasses.json @@ -53,5 +53,6 @@ "RatingBar": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.RatingBarDesign", "SearchView": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SearchViewDesign", "TextureView": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.TextureViewDesign", - "SurfaceView": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SurfaceViewDesign" + "SurfaceView": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SurfaceViewDesign", + "TextInputEditText": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputEditTextDesign" } \ No newline at end of file diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputEditTextCaller.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputEditTextCaller.kt new file mode 100644 index 0000000000..04b1d92eb3 --- /dev/null +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputEditTextCaller.kt @@ -0,0 +1,5 @@ +package org.appdevforall.codeonthego.layouteditor.editor.callers.text + +open class TextInputEditTextCaller : EditTextCaller() { + // Inherits typical EditText attributes +} diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputEditTextDesign.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputEditTextDesign.kt new file mode 100644 index 0000000000..999fc58858 --- /dev/null +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputEditTextDesign.kt @@ -0,0 +1,43 @@ +package org.appdevforall.codeonthego.layouteditor.editor.palette.text + +import android.content.Context +import android.graphics.Canvas +import com.google.android.material.textfield.TextInputEditText +import org.appdevforall.codeonthego.layouteditor.utils.Constants +import org.appdevforall.codeonthego.layouteditor.utils.Utils + +open class TextInputEditTextDesign(context: Context) : TextInputEditText(context) { + + private var drawStrokeEnabled: Boolean = false + private var isBlueprint: Boolean = false + + override fun dispatchDraw(canvas: Canvas) { + super.dispatchDraw(canvas) + + if (drawStrokeEnabled) { + Utils.drawDashPathStroke( + this, + canvas, + if (isBlueprint) Constants.BLUEPRINT_DASH_COLOR else Constants.DESIGN_DASH_COLOR + ) + } + } + + fun setStrokeEnabled(enabled: Boolean) { + drawStrokeEnabled = enabled + invalidate() + } + + override fun draw(canvas: Canvas) { + if (isBlueprint) { + Utils.drawDashPathStroke(this, canvas, Constants.BLUEPRINT_DASH_COLOR) + } else { + super.draw(canvas) + } + } + + fun setBlueprint(isBlueprint: Boolean) { + this.isBlueprint = isBlueprint + invalidate() + } +} From 27f10b74dff19d2aa9b38050d5daacc35be46be6 Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Thu, 14 May 2026 14:18:38 +0100 Subject: [PATCH 3/9] feat(ADFA-3375): Support TextInputLayout in the layout editor --- .../main/assets/attributes/attributes.json | 30 +++++++++++++ .../src/main/assets/widgetclasses.json | 3 +- .../editor/callers/TextInputLayoutCaller.java | 4 -- .../callers/text/TextInputLayoutCaller.kt | 38 ++++++++++++++++ .../palette/text/TextInputLayoutDesign.kt | 43 +++++++++++++++++++ 5 files changed, 113 insertions(+), 5 deletions(-) delete mode 100644 layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/TextInputLayoutCaller.java create mode 100644 layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt create mode 100644 layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputLayoutDesign.kt diff --git a/layouteditor/src/main/assets/attributes/attributes.json b/layouteditor/src/main/assets/attributes/attributes.json index e43530f760..f72c8db266 100644 --- a/layouteditor/src/main/assets/attributes/attributes.json +++ b/layouteditor/src/main/assets/attributes/attributes.json @@ -704,5 +704,35 @@ "attributeName": "android:singleLine", "argumentType": "boolean" } + ], + "com.google.android.material.textfield.TextInputLayout": [ + { + "name": "android:hint", + "methodName": "setHint", + "className": "text.TextInputLayoutCaller", + "attributeName": "android:hint", + "argumentType": "text|string" + }, + { + "name": "app:hintEnabled", + "methodName": "setHintEnabled", + "className": "text.TextInputLayoutCaller", + "attributeName": "app:hintEnabled", + "argumentType": "boolean" + }, + { + "name": "app:errorEnabled", + "methodName": "setErrorEnabled", + "className": "text.TextInputLayoutCaller", + "attributeName": "app:errorEnabled", + "argumentType": "boolean" + }, + { + "name": "app:counterEnabled", + "methodName": "setCounterEnabled", + "className": "text.TextInputLayoutCaller", + "attributeName": "app:counterEnabled", + "argumentType": "boolean" + } ] } \ No newline at end of file diff --git a/layouteditor/src/main/assets/widgetclasses.json b/layouteditor/src/main/assets/widgetclasses.json index 04770f28ee..133eefd57f 100644 --- a/layouteditor/src/main/assets/widgetclasses.json +++ b/layouteditor/src/main/assets/widgetclasses.json @@ -54,5 +54,6 @@ "SearchView": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SearchViewDesign", "TextureView": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.TextureViewDesign", "SurfaceView": "org.appdevforall.codeonthego.layouteditor.editor.palette.widgets.SurfaceViewDesign", - "TextInputEditText": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputEditTextDesign" + "TextInputEditText": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputEditTextDesign", + "TextInputLayout": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputLayoutDesign" } \ No newline at end of file diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/TextInputLayoutCaller.java b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/TextInputLayoutCaller.java deleted file mode 100644 index eb42b1b032..0000000000 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/TextInputLayoutCaller.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.appdevforall.codeonthego.layouteditor.editor.callers; - -public class TextInputLayoutCaller { -} diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt new file mode 100644 index 0000000000..be3289a930 --- /dev/null +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt @@ -0,0 +1,38 @@ +package org.appdevforall.codeonthego.layouteditor.editor.callers.text + +import android.content.Context +import android.view.View +import com.google.android.material.textfield.TextInputLayout +import org.appdevforall.codeonthego.layouteditor.managers.ProjectManager +import org.appdevforall.codeonthego.layouteditor.managers.ValuesManager +import org.appdevforall.codeonthego.layouteditor.tools.ValuesResourceParser + +object TextInputLayoutCaller { + + @JvmStatic + fun setHint(target: View, value: String, context: Context) { + var finalValue = value + if (finalValue.startsWith("@string/")) { + val project = ProjectManager.instance.openedProject + finalValue = ValuesManager.getValueFromResources( + ValuesResourceParser.TAG_STRING, finalValue, project!!.stringsPath + ) + } + (target as TextInputLayout).hint = finalValue + } + + @JvmStatic + fun setHintEnabled(target: View, value: String, context: Context) { + (target as TextInputLayout).isHintEnabled = java.lang.Boolean.parseBoolean(value) + } + + @JvmStatic + fun setErrorEnabled(target: View, value: String, context: Context) { + (target as TextInputLayout).isErrorEnabled = java.lang.Boolean.parseBoolean(value) + } + + @JvmStatic + fun setCounterEnabled(target: View, value: String, context: Context) { + (target as TextInputLayout).isCounterEnabled = java.lang.Boolean.parseBoolean(value) + } +} diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputLayoutDesign.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputLayoutDesign.kt new file mode 100644 index 0000000000..03049bb20c --- /dev/null +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/palette/text/TextInputLayoutDesign.kt @@ -0,0 +1,43 @@ +package org.appdevforall.codeonthego.layouteditor.editor.palette.text + +import android.content.Context +import android.graphics.Canvas +import com.google.android.material.textfield.TextInputLayout +import org.appdevforall.codeonthego.layouteditor.utils.Constants +import org.appdevforall.codeonthego.layouteditor.utils.Utils + +open class TextInputLayoutDesign(context: Context) : TextInputLayout(context) { + + private var drawStrokeEnabled: Boolean = false + private var isBlueprint: Boolean = false + + override fun dispatchDraw(canvas: Canvas) { + super.dispatchDraw(canvas) + + if (drawStrokeEnabled) { + Utils.drawDashPathStroke( + this, + canvas, + if (isBlueprint) Constants.BLUEPRINT_DASH_COLOR else Constants.DESIGN_DASH_COLOR + ) + } + } + + fun setStrokeEnabled(enabled: Boolean) { + drawStrokeEnabled = enabled + invalidate() + } + + override fun draw(canvas: Canvas) { + if (isBlueprint) { + Utils.drawDashPathStroke(this, canvas, Constants.BLUEPRINT_DASH_COLOR) + } else { + super.draw(canvas) + } + } + + fun setBlueprint(isBlueprint: Boolean) { + this.isBlueprint = isBlueprint + invalidate() + } +} From ac37b572c965d6c4195af78314df9958f5e5bac8 Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Thu, 14 May 2026 14:31:08 +0100 Subject: [PATCH 4/9] fix(ADFA-3375): Prevent the parsing of internal widgets --- .../tools/XmlLayoutGenerator.java | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java index 631fa235c0..19660c4983 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; public class XmlLayoutGenerator { final StringBuilder builder = new StringBuilder(); @@ -35,19 +36,30 @@ public String generate(@NonNull DesignEditor editor, boolean useSuperclasses) { } builder.append("\n"); - return peek(editor.getChildAt(0), editor.getViewAttributeMap(), 0); + peek(editor.getChildAt(0), editor.getViewAttributeMap(), 0); + return builder.toString().trim(); } - private String peek(View view, HashMap attributeMap, int depth) { - if (attributeMap == null || view == null) return ""; + private void peek(View view, HashMap attributeMap, int depth) { + if (attributeMap == null || view == null) return; + + if (!attributeMap.containsKey(view)) { + if (view instanceof ViewGroup group) { + for (int i = 0; i < group.getChildCount(); i++) { + peek(group.getChildAt(i), attributeMap, depth); + } + } + return; + } + if (tryWriteInclude(view, attributeMap, depth)) { - return builder.toString(); + return; } if (tryWriteFragment(view, attributeMap, depth)) { - return builder.toString(); + return; } if (tryWriteMerge(view, attributeMap, depth)) { - return builder.toString(); + return; } String indent = getIndent(depth); int nextDepth = depth; @@ -55,12 +67,14 @@ private String peek(View view, HashMap attributeMap, int dep String className = getClassName(view, indent); List keys = - (attributeMap.get(view) != null) ? attributeMap.get(view).keySet() : new ArrayList<>(); + (attributeMap.get(view) != null) ? new ArrayList<>(Objects.requireNonNull(attributeMap.get(view)).keySet()) : new ArrayList<>(); for (String key : keys) { - builder.append(TAB).append(indent).append(key).append("=\"").append(StringEscapeUtils.escapeXml11(attributeMap.get(view).getValue(key))).append("\"\n"); + builder.append(TAB).append(indent).append(key).append("=\"").append(StringEscapeUtils.escapeXml11(Objects.requireNonNull(attributeMap.get(view)).getValue(key))).append("\"\n"); } - builder.deleteCharAt(builder.length() - 1); + if (builder.charAt(builder.length() - 1) == '\n') { + builder.deleteCharAt(builder.length() - 1); + } if (view instanceof ViewGroup group) { if (!(group instanceof CalendarView) @@ -72,12 +86,18 @@ private String peek(View view, HashMap attributeMap, int dep if (group.getChildCount() > 0) { builder.append(">\n\n"); + int beforeLen = builder.length(); for (int i = 0; i < group.getChildCount(); i++) { peek(group.getChildAt(i), attributeMap, nextDepth); } - builder.append(indent).append("\n\n"); + if (builder.length() == beforeLen) { + builder.setLength(beforeLen - 3); + builder.append(" />\n\n"); + } else { + builder.append(indent).append("\n\n"); + } } else { builder.append(" />\n\n"); } @@ -87,8 +107,6 @@ private String peek(View view, HashMap attributeMap, int dep } else { builder.append(" />\n\n"); } - - return builder.toString().trim(); } @NonNull From 21be9ede0f39bcf851875a5f7b35685003a1141b Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Thu, 14 May 2026 18:45:52 +0100 Subject: [PATCH 5/9] feat(ADFA-3375): Handle text inputs in palette and structure view --- .../src/main/assets/palette/text.json | 19 ++++++++ .../layouteditor/editor/DesignEditor.kt | 28 +++++++++-- .../layouteditor/views/StructureView.kt | 47 +++++++++++++++++-- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/layouteditor/src/main/assets/palette/text.json b/layouteditor/src/main/assets/palette/text.json index d26ae8c1ce..370e177b48 100644 --- a/layouteditor/src/main/assets/palette/text.json +++ b/layouteditor/src/main/assets/palette/text.json @@ -138,5 +138,24 @@ "defaultAttributes": { "android:text": "CheckedTextView" } + }, + { + "name": "TextInputLayout", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputLayoutDesign", + "iconName": "ic_palette_linear_layout_vert", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "wrap_content" + } + }, + { + "name": "TextInputEditText", + "className": "org.appdevforall.codeonthego.layouteditor.editor.palette.text.TextInputEditTextDesign", + "iconName": "ic_palette_edit_text", + "defaultAttributes": { + "android:layout_width": "match_parent", + "android:layout_height": "wrap_content", + "android:inputType": "text" } + } ] \ No newline at end of file diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt index 26dc132342..f6872f69e2 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt @@ -13,6 +13,7 @@ import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.EditText +import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.Spinner import android.widget.TextView @@ -363,11 +364,28 @@ class DesignEditor : LinearLayout { context, ) as View - newView.layoutParams = - ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - ) + newView.layoutParams = when (parent) { + is LinearLayout -> { + LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT, + ) + } + + is FrameLayout -> { + FrameLayout.LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT, + ) + } + + else -> { + ViewGroup.LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT, + ) + } + } rearrangeListeners(newView) if (newView is ViewGroup) { diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt index 96576b78d7..77a4c1f554 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt @@ -59,9 +59,11 @@ import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup import com.google.android.material.color.MaterialColors +import com.google.android.material.textfield.TextInputLayout import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.navigation.NavigationView import com.google.android.material.tabs.TabLayout +import com.google.android.material.textfield.TextInputEditText import org.appdevforall.codeonthego.layouteditor.R import org.appdevforall.codeonthego.layouteditor.databinding.LayoutStructureViewItemBinding import org.appdevforall.codeonthego.layouteditor.managers.IdManager.idMap @@ -143,7 +145,7 @@ class StructureView( viewId.visibility = VISIBLE viewId.text = idMap[view] } - if (view is LinearLayout && view !is RadioGroup) { + if (view is LinearLayout && view !is RadioGroup && view !is TextInputLayout) { val orientation = if (view.orientation == LinearLayout.HORIZONTAL) "horizontal" else "vertical" val imgResId = imgMap[LinearLayout::class.java.simpleName + orientation]!! @@ -178,7 +180,8 @@ class StructureView( group !is SearchView && group !is NavigationView && group !is BottomNavigationView && - group !is TabLayout + group !is TabLayout && + group !is TextInputLayout ) { nextDepth++ @@ -186,6 +189,12 @@ class StructureView( val child = group.getChildAt(i) peek(child, nextDepth) } + } else if (group is TextInputLayout) { + nextDepth++ + val editText = group.editText + if (editText != null) { + peek(editText, nextDepth) + } } } } @@ -207,7 +216,8 @@ class StructureView( group !is SearchView && group !is NavigationView && group !is BottomNavigationView && - group !is TabLayout + group !is TabLayout && + group !is TextInputLayout ) { canvas.drawRect( x - pointRadius, @@ -234,6 +244,35 @@ class StructureView( paint, ) } + } else if (group is TextInputLayout) { + canvas.drawRect( + x - pointRadius, + y - pointRadius, + x + pointRadius, + y + pointRadius, + paint, + ) + val editText = group.editText + if (editText != null) { + val current = viewTextMap[editText] + if (current != null) { + val currentParent = current.parent.parent as ViewGroup + canvas.drawLine( + parent.x, + parent.y + parent.height.toFloat() / 2, + parent.x, + currentParent.y + currentParent.height.toFloat() / 2, + paint, + ) + canvas.drawLine( + parent.x, + currentParent.y + currentParent.height.toFloat() / 2, + currentParent.x, + currentParent.y + currentParent.height.toFloat() / 2, + paint, + ) + } + } } else { canvas.drawCircle( parent.x, @@ -369,6 +408,8 @@ class StructureView( imgMap[CoordinatorLayout::class.java.simpleName] = R.mipmap.ic_palette_coordinator_layout imgMap[DrawerLayout::class.java.simpleName] = R.mipmap.ic_palette_drawer_layout + imgMap[TextInputLayout::class.java.simpleName] = R.mipmap.ic_palette_linear_layout_vert + imgMap[TextInputEditText::class.java.simpleName] = R.mipmap.ic_palette_edit_text } } } From 73806f9bc8c92a389b489de31c5947fbd657439c Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Fri, 15 May 2026 16:28:09 +0100 Subject: [PATCH 6/9] refactor(ADFA-3375): Refactor drawing function --- .../layouteditor/views/StructureView.kt | 177 +++++++++--------- 1 file changed, 85 insertions(+), 92 deletions(-) diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt index 77a4c1f554..8c84ea7a65 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/views/StructureView.kt @@ -199,98 +199,91 @@ class StructureView( } } - /** This method is called to draw rectangles, lines, and circles for each TextView in the view. */ - override fun dispatchDraw(canvas: Canvas) { - super.dispatchDraw(canvas) - - for (text in textViewMap.keys) { - val view = textViewMap[text] - val parent = text.parent.parent as ViewGroup - - if (view is ViewGroup && view.childCount > 0) { - val x = parent.x - val y = parent.y + parent.height.toFloat() / 2 - - val group = view - if (group !is CalendarView && - group !is SearchView && - group !is NavigationView && - group !is BottomNavigationView && - group !is TabLayout && - group !is TextInputLayout - ) { - canvas.drawRect( - x - pointRadius, - y - pointRadius, - x + pointRadius, - y + pointRadius, - paint, - ) - for (i in 0 until group.childCount) { - val current = viewTextMap[group.getChildAt(i)] - val currentParent = current!!.parent.parent as ViewGroup - canvas.drawLine( - parent.x, - parent.y + parent.height.toFloat() / 2, - parent.x, - currentParent.y + currentParent.height.toFloat() / 2, - paint, - ) - canvas.drawLine( - parent.x, - currentParent.y + currentParent.height.toFloat() / 2, - currentParent.x, - currentParent.y + currentParent.height.toFloat() / 2, - paint, - ) - } - } else if (group is TextInputLayout) { - canvas.drawRect( - x - pointRadius, - y - pointRadius, - x + pointRadius, - y + pointRadius, - paint, - ) - val editText = group.editText - if (editText != null) { - val current = viewTextMap[editText] - if (current != null) { - val currentParent = current.parent.parent as ViewGroup - canvas.drawLine( - parent.x, - parent.y + parent.height.toFloat() / 2, - parent.x, - currentParent.y + currentParent.height.toFloat() / 2, - paint, - ) - canvas.drawLine( - parent.x, - currentParent.y + currentParent.height.toFloat() / 2, - currentParent.x, - currentParent.y + currentParent.height.toFloat() / 2, - paint, - ) - } - } - } else { - canvas.drawCircle( - parent.x, - parent.y + parent.height.toFloat() / 2, - pointRadius.toFloat(), - paint, - ) - } - } else { - canvas.drawCircle( - parent.x, - parent.y + parent.height.toFloat() / 2, - pointRadius.toFloat(), - paint, - ) - } - } - } + /** This method is called to draw rectangles, lines, and circles for each TextView in the view. */ + override fun dispatchDraw(canvas: Canvas) { + super.dispatchDraw(canvas) + + for (text in textViewMap.keys) { + val view = textViewMap[text] + val parent = text.parent.parent as ViewGroup + + val centerX = parent.x + val centerY = parent.y + parent.height.toFloat() / 2 + + fun drawCircle() { + canvas.drawCircle( + centerX, + centerY, + pointRadius.toFloat(), + paint, + ) + } + + fun drawRectangle() { + canvas.drawRect( + centerX - pointRadius, + centerY - pointRadius, + centerX + pointRadius, + centerY + pointRadius, + paint, + ) + } + + fun drawLine(targetParent: ViewGroup) { + val targetY = targetParent.y + targetParent.height.toFloat() / 2 + + canvas.drawLine( + centerX, + centerY, + centerX, + targetY, + paint, + ) + + canvas.drawLine( + centerX, + targetY, + targetParent.x, + targetY, + paint, + ) + } + + if (view !is ViewGroup || view.childCount <= 0) { + drawCircle() + continue + } + + when (view) { + is CalendarView, + is SearchView, + is NavigationView, + is BottomNavigationView, + is TabLayout -> { drawCircle() } + is TextInputLayout -> { + drawRectangle() + + val editText = view.editText ?: continue + val current = viewTextMap[editText] ?: continue + val currentParent = current.parent.parent as ViewGroup + + drawLine(currentParent) + } + + else -> { + drawRectangle() + + for (i in 0 until view.childCount) { + val child = view.getChildAt(i) + val current = viewTextMap[child] ?: continue + val currentParent = current.parent.parent as ViewGroup + + drawLine(currentParent) + } + } + } + } + } /** * This method is called when a TextView is clicked, and it calls the OnItemClickListener's From 4b53170e9f6d79b7c16c311bad32149f55d2c2a9 Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Mon, 18 May 2026 14:06:46 +0100 Subject: [PATCH 7/9] refactor(ADFA-3375): Replace Java method with Kotlin --- .../editor/callers/text/TextInputLayoutCaller.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt index be3289a930..b744034621 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt @@ -23,16 +23,16 @@ object TextInputLayoutCaller { @JvmStatic fun setHintEnabled(target: View, value: String, context: Context) { - (target as TextInputLayout).isHintEnabled = java.lang.Boolean.parseBoolean(value) + (target as TextInputLayout).isHintEnabled = value.toBoolean() } @JvmStatic fun setErrorEnabled(target: View, value: String, context: Context) { - (target as TextInputLayout).isErrorEnabled = java.lang.Boolean.parseBoolean(value) + (target as TextInputLayout).isErrorEnabled = value.toBoolean() } @JvmStatic fun setCounterEnabled(target: View, value: String, context: Context) { - (target as TextInputLayout).isCounterEnabled = java.lang.Boolean.parseBoolean(value) + (target as TextInputLayout).isCounterEnabled = value.toBoolean() } } From 831165a32043d5c9185228fcb68cb19f4a617fa8 Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Mon, 18 May 2026 14:07:32 +0100 Subject: [PATCH 8/9] fix(ADFA-3375): Handle NPE defensively --- .../layouteditor/editor/callers/text/TextInputLayoutCaller.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt index b744034621..9e4946b194 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/callers/text/TextInputLayoutCaller.kt @@ -13,9 +13,9 @@ object TextInputLayoutCaller { fun setHint(target: View, value: String, context: Context) { var finalValue = value if (finalValue.startsWith("@string/")) { - val project = ProjectManager.instance.openedProject + val project = ProjectManager.instance.openedProject ?: return finalValue = ValuesManager.getValueFromResources( - ValuesResourceParser.TAG_STRING, finalValue, project!!.stringsPath + ValuesResourceParser.TAG_STRING, finalValue, project.stringsPath ) } (target as TextInputLayout).hint = finalValue From 10a0ec35a517e9950329d628ff9b5a47e07a6f78 Mon Sep 17 00:00:00 2001 From: Oluwadara Abijo Date: Mon, 18 May 2026 15:04:17 +0100 Subject: [PATCH 9/9] refactor(ADFA-3375): Flatten nested if branches --- .../tools/XmlLayoutGenerator.java | 62 ++++++++----------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java index 19660c4983..67342a8893 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java @@ -44,12 +44,12 @@ private void peek(View view, HashMap attributeMap, int depth if (attributeMap == null || view == null) return; if (!attributeMap.containsKey(view)) { - if (view instanceof ViewGroup group) { - for (int i = 0; i < group.getChildCount(); i++) { - peek(group.getChildAt(i), attributeMap, depth); - } - } - return; + if (!(view instanceof ViewGroup group)) return; + + for (int i = 0; i < group.getChildCount(); i++) { + peek(group.getChildAt(i), attributeMap, depth); + } + return; } if (tryWriteInclude(view, attributeMap, depth)) { @@ -62,7 +62,6 @@ private void peek(View view, HashMap attributeMap, int depth return; } String indent = getIndent(depth); - int nextDepth = depth; String className = getClassName(view, indent); @@ -76,36 +75,29 @@ private void peek(View view, HashMap attributeMap, int depth builder.deleteCharAt(builder.length() - 1); } - if (view instanceof ViewGroup group) { - if (!(group instanceof CalendarView) - && !(group instanceof SearchView) - && !(group instanceof NavigationView) - && !(group instanceof BottomNavigationView) - && !(group instanceof TabLayout)) { - nextDepth++; - - if (group.getChildCount() > 0) { - builder.append(">\n\n"); - int beforeLen = builder.length(); - - for (int i = 0; i < group.getChildCount(); i++) { - peek(group.getChildAt(i), attributeMap, nextDepth); - } - - if (builder.length() == beforeLen) { - builder.setLength(beforeLen - 3); - builder.append(" />\n\n"); - } else { - builder.append(indent).append("\n\n"); - } - } else { - builder.append(" />\n\n"); - } - } else { + if (!(view instanceof ViewGroup group) + || group instanceof CalendarView + || group instanceof SearchView + || group instanceof NavigationView + || group instanceof BottomNavigationView + || group instanceof TabLayout + || group.getChildCount() == 0) { + builder.append(" />\n\n"); + return; + } + + builder.append(">\n\n"); + int beforeLen = builder.length(); + + for (int i = 0; i < group.getChildCount(); i++) { + peek(group.getChildAt(i), attributeMap, depth + 1); + } + + if (builder.length() == beforeLen) { + builder.setLength(beforeLen - 3); builder.append(" />\n\n"); - } } else { - builder.append(" />\n\n"); + builder.append(indent).append("\n\n"); } }