diff --git a/tests/test-home/.androidide/init/model.jar b/tests/test-home/.androidide/init/model.jar
index 4601d2708..f97ab5f43 100644
Binary files a/tests/test-home/.androidide/init/model.jar and b/tests/test-home/.androidide/init/model.jar differ
diff --git a/tests/test-project/app/src/main/res/layout/AbsoluteLayout.xml b/tests/test-project/app/src/main/res/layout/AbsoluteLayout.xml
new file mode 100644
index 000000000..d54cfe847
--- /dev/null
+++ b/tests/test-project/app/src/main/res/layout/AbsoluteLayout.xml
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/LayoutInflaterImpl.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/LayoutInflaterImpl.kt
index 39205ffc6..982cc6e8a 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/LayoutInflaterImpl.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/LayoutInflaterImpl.kt
@@ -43,8 +43,10 @@ import com.itsaky.androidide.inflater.internal.utils.IDTable
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.projects.api.AndroidModule
import com.itsaky.androidide.utils.ILogger
+import com.itsaky.androidide.utils.VMUtils
import com.itsaky.androidide.xml.widgets.Widget
import com.itsaky.androidide.xml.widgets.WidgetTable
+import org.jetbrains.annotations.TestOnly
import java.io.File
import java.lang.reflect.Method
@@ -64,7 +66,7 @@ open class LayoutInflaterImpl : ILayoutInflater() {
get() = this._primaryInflatingFile!!
protected val currentLayoutFile: LayoutFile
- get() = _currentLayoutFile!!
+ get() = this._currentLayoutFile!!
override fun inflate(file: File, parent: ViewGroup): IView? {
this._primaryInflatingFile = file
@@ -167,11 +169,11 @@ open class LayoutInflaterImpl : ILayoutInflater() {
val adapter =
AttributeAdapterIndex.getAdapter(view.name)
?: throw InflateException("No attribute adapter found for view ${view.name}")
-
- adapter.applyBasic(view)
-
+
view.view.layoutParams = generateLayoutParams(parentView)
parent.addChild(view)
+
+ adapter.applyBasic(view)
if (element.childCount > 0 && view is IViewGroup) {
for (child in element.childList) {
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewImpl.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewImpl.kt
index 6bfcbafaa..36214fdf1 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewImpl.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewImpl.kt
@@ -35,7 +35,7 @@ constructor(
override val tag: String = name.tagName()
) : IView {
private val log = ILogger.newInstance(javaClass.simpleName)
- protected val attributes = mutableListOf()
+ internal val attributes = mutableListOf()
override var parent: IViewGroup? = null
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt
index c19a7e7b4..0b4eb93f5 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt
@@ -76,7 +76,7 @@ open class ViewAttrAdapter : IAttributeAdapter() {
"foregroundGravity" -> foregroundGravity = parseGravity(value)
"foregroundTint" -> foregroundTintList = parseColorStateList(context, value)
"foregroundTintMode" -> foregroundTintMode = parsePorterDuffMode(value)
- "id" -> parseId(file.resName, value)
+ "id" -> id = parseId(file.resName, value)
"minHeight" -> minimumWidth = parseDimension(context, value)
"minWidth" -> minimumHeight = parseDimension(context, value)
"padding" ->
diff --git a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/AdapterIndexText.kt b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/AdapterIndexText.kt
index c4669bf7c..ce963244f 100644
--- a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/AdapterIndexText.kt
+++ b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/AdapterIndexText.kt
@@ -51,59 +51,21 @@ import com.itsaky.androidide.inflater.internal.adapters.WebViewAttrAdapter
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
+import java.io.File
/** @author Akash Yadav */
@RunWith(RobolectricTestRunner::class)
class AdapterIndexText {
-
+
@Test
fun `verify instances of adapters`() {
- val expected =
- mapOf(
- AbsoluteLayout::class to AbsoluteLayoutAttrAdapter::class,
- AutoCompleteTextView::class to AutoCompleteTextViewAttrAdapter::class,
- Button::class to ButtonAttrAdapter::class,
- CheckBox::class to CheckBoxAttrAdapter::class,
- CheckedTextView::class to CheckedTextViewAttrAdapter::class,
- EditText::class to EditTextAttrAdapter::class,
- FrameLayout::class to FrameLayoutAttrAdapter::class,
- GestureOverlayView::class to GestureOverlayViewAttrAdapter::class,
- GridLayout::class to GridLayoutAttrAdapter::class,
- ImageButton::class to ImageButtonAttrAdapter::class,
- ImageView::class to ImageViewAttrAdapter::class,
- LinearLayout::class to LinearLayoutAttrAdapter::class,
- ListView::class to ListViewAttrAdapter::class,
- ProgressBar::class to ProgressBarAttrAdapter::class,
- RadioButton::class to RadioButtonAttrAdapter::class,
- RelativeLayout::class to RelativeLayoutAttrAdapter::class,
- SeekBar::class to SeekBarAttrAdapter::class,
- Spinner::class to SpinnerAttrAdapter::class,
- Switch::class to SwitchAttrAdapter::class,
- SurfaceView::class to SurfaceViewAttrAdapter::class,
- TextView::class to TextViewAttrAdapter::class,
- TextureView::class to TextureViewAttrAdapter::class,
- ToggleButton::class to ToggleButtonAttrAdapter::class,
- View::class to ViewAttrAdapter::class,
- WebView::class to WebViewAttrAdapter::class
- )
-
- val unexpected =
- listOf(
- AbsListView::class,
- AbsSeekBar::class,
- AbsSpinner::class,
- AdapterView::class,
- CompoundButton::class,
- ViewGroup::class
- )
-
- expected.forEach {
+ viewToAdapter.forEach {
AttributeAdapterIndex.getAdapter(it.key.qualifiedName).apply {
assertThat(this).isNotNull()
assertThat(this).isInstanceOf(it.value.java)
}
}
- unexpected.forEach { assertThat(AttributeAdapterIndex.getAdapter(it.qualifiedName)).isNull() }
+ abstractViews.forEach { assertThat(AttributeAdapterIndex.getAdapter(it.qualifiedName)).isNull() }
}
}
diff --git a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/LayoutInflaterTest.kt b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/LayoutInflaterTest.kt
index 2ebd6e74f..9f1b065c6 100644
--- a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/LayoutInflaterTest.kt
+++ b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/LayoutInflaterTest.kt
@@ -19,25 +19,22 @@ package com.itsaky.androidide.inflater
import android.view.View
import android.widget.LinearLayout
-import androidx.appcompat.app.AppCompatActivity
import com.google.common.truth.Truth.assertThat
-import com.itsaky.androidide.lookup.Lookup
+import com.itsaky.androidide.inflater.internal.AttributeImpl
+import com.itsaky.androidide.inflater.internal.LayoutInflaterImpl
+import com.itsaky.androidide.inflater.internal.ViewImpl
+import com.itsaky.androidide.inflater.internal.utils.IDTable
import com.itsaky.androidide.projects.ProjectManager
-import com.itsaky.androidide.projects.builder.BuildService
-import com.itsaky.androidide.tooling.api.messages.InitializeProjectMessage
-import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher
+import com.itsaky.androidide.projects.api.AndroidModule
import java.io.File
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
-import org.robolectric.android.controller.ActivityController
/** @author Akash Yadav */
@RunWith(RobolectricTestRunner::class)
class LayoutInflaterTest {
-
+
@Test
fun `test functionality`() {
inflaterTest {
@@ -53,7 +50,7 @@ class LayoutInflaterTest {
}
}
}
-
+
@Test
fun `test inlined layout`() {
inflaterTest {
@@ -70,8 +67,46 @@ class LayoutInflaterTest {
}
}
+ @Test
+ fun `test inflated view is of expected type`() {
+ inflaterTest { module ->
+ requiresActivity { activity ->
+ val parent = LinearLayout(activity)
+ val inflater = ILayoutInflater.newInflater() as LayoutInflaterImpl
+ viewToAdapter.keys.forEach { view ->
+ parent.removeAllViews()
+ module.createLayoutFile(view.simpleName!!) { file ->
+ file.writeText(viewDeclTemplate(view.simpleName!!))
+ val inflated = inflater.inflate(file, parent)
+ assertThat(inflated!!.view).isInstanceOf(view.java)
+ assertThat(inflated.view.id)
+ .isEqualTo(IDTable.get(view.simpleName!!, "template_view"))
+
+ (inflated as ViewImpl).attributes.apply {
+ assertThat(this)
+ .contains(AttributeImpl(INamespace.ANDROID, "id", "@+id/template_view"))
+ assertThat(this)
+ .contains(AttributeImpl(INamespace.ANDROID, "layout_height", "match_parent"))
+ assertThat(this)
+ .contains(AttributeImpl(INamespace.ANDROID, "layout_width", "match_parent"))
+ }
+ }
+ }
+ }
+ }
+ }
+
private fun layoutFile(name: String): File {
val app = ProjectManager.app ?: throw IllegalStateException("Project is not initialized")
return File(app.projectDir, "src/main/res/layout/$name.xml")
}
+
+ private fun AndroidModule.createLayoutFile(name: String, block: (File) -> Unit = {}): File {
+ return File(projectDir, "src/main/res/layout/$name.xml").apply {
+ if (exists()) {
+ delete()
+ }
+ block(this)
+ }
+ }
}
diff --git a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/adapterUtils.kt b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/adapterUtils.kt
new file mode 100644
index 000000000..5ce767aa7
--- /dev/null
+++ b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/adapterUtils.kt
@@ -0,0 +1,124 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.androidide.inflater
+
+import android.gesture.GestureOverlayView
+import android.view.SurfaceView
+import android.view.TextureView
+import android.view.View
+import android.view.ViewGroup
+import android.webkit.WebView
+import android.widget.AbsListView
+import android.widget.AbsSeekBar
+import android.widget.AbsSpinner
+import android.widget.AbsoluteLayout
+import android.widget.AdapterView
+import android.widget.AutoCompleteTextView
+import android.widget.Button
+import android.widget.CheckBox
+import android.widget.CheckedTextView
+import android.widget.CompoundButton
+import android.widget.EditText
+import android.widget.FrameLayout
+import android.widget.GridLayout
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.ListView
+import android.widget.ProgressBar
+import android.widget.RadioButton
+import android.widget.RelativeLayout
+import android.widget.SeekBar
+import android.widget.Spinner
+import android.widget.Switch
+import android.widget.TextView
+import android.widget.ToggleButton
+import com.itsaky.androidide.inflater.internal.adapters.AbsoluteLayoutAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.AutoCompleteTextViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.ButtonAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.CheckBoxAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.CheckedTextViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.EditTextAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.FrameLayoutAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.GestureOverlayViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.GridLayoutAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.ImageButtonAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.ImageViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.LinearLayoutAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.ListViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.ProgressBarAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.RadioButtonAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.RelativeLayoutAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.SeekBarAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.SpinnerAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.SurfaceViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.SwitchAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.TextViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.TextureViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.ToggleButtonAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.ViewAttrAdapter
+import com.itsaky.androidide.inflater.internal.adapters.WebViewAttrAdapter
+
+internal fun viewDeclTemplate(name: String) : String =
+ """
+ <$name
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/template_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+ """.trimIndent()
+
+internal val viewToAdapter =
+ mapOf(
+ AbsoluteLayout::class to AbsoluteLayoutAttrAdapter::class,
+ AutoCompleteTextView::class to AutoCompleteTextViewAttrAdapter::class,
+ Button::class to ButtonAttrAdapter::class,
+ CheckBox::class to CheckBoxAttrAdapter::class,
+ CheckedTextView::class to CheckedTextViewAttrAdapter::class,
+ EditText::class to EditTextAttrAdapter::class,
+ FrameLayout::class to FrameLayoutAttrAdapter::class,
+ GestureOverlayView::class to GestureOverlayViewAttrAdapter::class,
+ GridLayout::class to GridLayoutAttrAdapter::class,
+ ImageButton::class to ImageButtonAttrAdapter::class,
+ ImageView::class to ImageViewAttrAdapter::class,
+ LinearLayout::class to LinearLayoutAttrAdapter::class,
+ ListView::class to ListViewAttrAdapter::class,
+ ProgressBar::class to ProgressBarAttrAdapter::class,
+ RadioButton::class to RadioButtonAttrAdapter::class,
+ RelativeLayout::class to RelativeLayoutAttrAdapter::class,
+ SeekBar::class to SeekBarAttrAdapter::class,
+ Spinner::class to SpinnerAttrAdapter::class,
+ Switch::class to SwitchAttrAdapter::class,
+ SurfaceView::class to SurfaceViewAttrAdapter::class,
+ TextView::class to TextViewAttrAdapter::class,
+ TextureView::class to TextureViewAttrAdapter::class,
+ ToggleButton::class to ToggleButtonAttrAdapter::class,
+ View::class to ViewAttrAdapter::class,
+ WebView::class to WebViewAttrAdapter::class
+ )
+
+internal val abstractViews =
+ listOf(
+ AbsListView::class,
+ AbsSeekBar::class,
+ AbsSpinner::class,
+ AdapterView::class,
+ CompoundButton::class,
+ ViewGroup::class
+ )
\ No newline at end of file