diff --git a/tests/test-home/.androidide/init/model.jar b/tests/test-home/.androidide/init/model.jar index 354c0b41b..e91c759b5 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/include.xml b/tests/test-project/app/src/main/res/layout/include.xml index 3c92d4f98..95d4a9e89 100644 --- a/tests/test-project/app/src/main/res/layout/include.xml +++ b/tests/test-project/app/src/main/res/layout/include.xml @@ -4,6 +4,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + diff --git a/tests/test-project/app/src/main/res/layout/layout_include.xml b/tests/test-project/app/src/main/res/layout/layout_include.xml index 1b75662a9..0c4a7e97d 100644 --- a/tests/test-project/app/src/main/res/layout/layout_include.xml +++ b/tests/test-project/app/src/main/res/layout/layout_include.xml @@ -5,17 +5,6 @@ android:layout_height="match_parent" android:gravity="center"> - - - + diff --git a/tests/test-project/app/src/main/res/layout/layout_merge.xml b/tests/test-project/app/src/main/res/layout/layout_merge.xml new file mode 100644 index 000000000..d5a58431d --- /dev/null +++ b/tests/test-project/app/src/main/res/layout/layout_merge.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/tests/test-project/app/src/main/res/layout/merge.xml b/tests/test-project/app/src/main/res/layout/merge.xml new file mode 100644 index 000000000..4933d2438 --- /dev/null +++ b/tests/test-project/app/src/main/res/layout/merge.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/DesignerWorkspaceFragment.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/DesignerWorkspaceFragment.kt index ea865119d..c22155bcf 100644 --- a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/DesignerWorkspaceFragment.kt +++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/DesignerWorkspaceFragment.kt @@ -114,4 +114,4 @@ class DesignerWorkspaceFragment : BaseFragment() { viewModel.file = file } } -} +} \ No newline at end of file diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/ILayoutInflater.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/ILayoutInflater.kt index d95d1fbb2..fa00bdbba 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/ILayoutInflater.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/ILayoutInflater.kt @@ -37,7 +37,7 @@ abstract class ILayoutInflater { * @param file The file to inflate. * @param parent The parent view. */ - abstract fun inflate(file: File, parent: ViewGroup): IView? + abstract fun inflate(file: File, parent: ViewGroup): List companion object { @JvmStatic diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/inflationEvents.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/inflationEvents.kt index 9f0654c8e..5a05c5a21 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/inflationEvents.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/inflationEvents.kt @@ -47,7 +47,7 @@ class InflationStartEvent : IInflationEvent(Unit) * * @property view The inflated view. */ -class InflationFinishEvent(view: IView?) : IInflationEvent(view) +class InflationFinishEvent(views: List) : IInflationEvent>(views) /** * Indicates that the given view has been inflated. The view can be anywhere in the layout. 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 c920d4897..6df766186 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 @@ -21,13 +21,16 @@ import android.content.Context import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams +import com.android.SdkConstants.EXT_XML import com.android.aapt.Resources.SourcePosition +import com.android.aapt.Resources.XmlAttribute import com.android.aapt.Resources.XmlElement import com.android.aapt.Resources.XmlNode.NodeCase.ELEMENT import com.android.aaptcompiler.AaptResourceType.ID import com.android.aaptcompiler.AaptResourceType.LAYOUT import com.android.aaptcompiler.BlameLogger import com.android.aaptcompiler.ResourceFile +import com.android.aaptcompiler.ResourceFile.Type.ProtoXml import com.android.aaptcompiler.ResourceName import com.android.aaptcompiler.XmlProcessor import com.android.aaptcompiler.extractPathData @@ -42,6 +45,7 @@ import com.itsaky.androidide.inflater.InflationStartEvent import com.itsaky.androidide.inflater.OnApplyAttributeEvent import com.itsaky.androidide.inflater.OnInflateViewEvent import com.itsaky.androidide.inflater.internal.utils.IDTable +import com.itsaky.androidide.inflater.internal.utils.parseLayoutReference import com.itsaky.androidide.projects.ProjectManager import com.itsaky.androidide.projects.api.AndroidModule import com.itsaky.androidide.utils.ILogger @@ -68,7 +72,7 @@ open class LayoutInflaterImpl : ILayoutInflater() { protected val currentLayoutFile: LayoutFile get() = this._currentLayoutFile!! - override fun inflate(file: File, parent: ViewGroup): IView? { + override fun inflate(file: File, parent: ViewGroup): List { this._primaryInflatingFile = file IDTable.newRound() inflationEventListener?.onEvent(InflationStartEvent()) @@ -78,32 +82,8 @@ open class LayoutInflaterImpl : ILayoutInflater() { } } - protected open fun doInflate(file: File, parent: ViewGroup): IView? { - val pathData = extractPathData(file) - if (pathData.type != LAYOUT) { - throw InflateException("File is not a layout file.") - } - - if (ProjectManager.rootProject == null) { - throw InflateException("Project is not initialized!") - } - - val module = - ProjectManager.findModuleForFile(file) as? AndroidModule - ?: throw InflateException( - "Cannot find module for given file. Is the project initialized?" - ) - val resFile = - ResourceFile( - ResourceName(module.packageName, pathData.type!!, pathData.name), - pathData.config, - pathData.source, - ResourceFile.Type.ProtoXml - ) - - val processor = XmlProcessor(pathData.source, BlameLogger(IDELogger)) - processor.process(resFile, file.inputStream()) - + protected open fun doInflate(file: File, parent: ViewGroup): List { + val (processor, module) = processXmlFile(file) return doInflate(processor, parent, module) } @@ -111,7 +91,15 @@ open class LayoutInflaterImpl : ILayoutInflater() { processor: XmlProcessor, parent: ViewGroup, module: AndroidModule, - ): IView? { + ): List { + return doInflate(processor, module) { wrap(parent) } + } + + protected open fun doInflate( + processor: XmlProcessor, + module: AndroidModule, + parent: () -> IViewGroup, + ): List { // TODO(itsaky) : Add test for multiple view as root layout // The inflater should fail in such cases val (file, node) = @@ -132,11 +120,11 @@ open class LayoutInflaterImpl : ILayoutInflater() { .forEach { IDTable.set(currentLayoutFile.resName, it.name.entry!!, View.generateViewId()) } val element = node.element - val view = onCreateView(element, wrap(parent), module) + val views = onCreateView(element, parent(), module) this._currentLayoutFile = null - return view + return views } protected open fun onCreateView( @@ -146,16 +134,15 @@ open class LayoutInflaterImpl : ILayoutInflater() { widgets: WidgetTable = module.getWidgetTable() ?: throw IllegalStateException("No widget table found for module $module"), - ): IView { + ): List { val parentView = parent.view as ViewGroup - - if ("include" == element.name) { - return onCreateIncludedView(element, parentView, module, widgets) + if ("merge" == element.name) { + return onCreateMergedView(element, parent, module, widgets) } - if ("merge" == element.name) { - return onCreateMergedView(element, parentView, module, widgets) + if ("include" == element.name) { + return onCreateIncludedView(element, parent) } val widget = @@ -171,32 +158,8 @@ open class LayoutInflaterImpl : ILayoutInflater() { }) as ViewImpl - if (element.namespaceDeclarationCount > 0) { - for (xmlNamespace in element.namespaceDeclarationList) { - view.namespaceDecls[xmlNamespace.uri] = NamespaceImpl(xmlNamespace.prefix, xmlNamespace.uri) - } - } - - val adapter = - AttributeAdapterIndex.getAdapter(view.name) - ?: throw InflateException("No attribute adapter found for view ${view.name}") - - view.view.layoutParams = generateLayoutParams(parentView) - parent.addChild(view) - - adapter.applyBasic(view) - - if (element.attributeCount > 0) { - for (xmlAttribute in element.attributeList) { - val namespace = - view.findNamespaceByUri(xmlAttribute.namespaceUri) - ?: throw InflateException("Unknown namespace : ${xmlAttribute.namespaceUri}") - val attr = - AttributeImpl(namespace = namespace, name = xmlAttribute.name, value = xmlAttribute.value) - view.addAttribute(attr) - inflationEventListener?.onEvent(OnApplyAttributeEvent(view, attr)) - } - } + addNamespaceDecls(element, view) + applyAttributes(element, view, parent) if (element.childCount > 0 && view is IViewGroup) { for (child in element.childList) { @@ -206,28 +169,94 @@ open class LayoutInflaterImpl : ILayoutInflater() { onCreateView(element = child.element, parent = view, module = module, widgets = widgets) } } - + inflationEventListener?.onEvent(OnInflateViewEvent(view)) - return view + return listOf(view) + } + + @JvmOverloads + protected open fun applyAttributes( + element: XmlElement, + view: ViewImpl, + parent: IViewGroup, + attachToParent: Boolean = true, + checkAttr: (XmlAttribute) -> Boolean = { true } + ) { + val parentView = parent.view as ViewGroup + val adapter = + AttributeAdapterIndex.getAdapter(view.name) + ?: throw InflateException("No attribute adapter found for view ${view.name}") + + view.view.layoutParams = generateLayoutParams(parentView) + + if (attachToParent) { + parent.addChild(view) + } + + adapter.applyBasic(view) + + if (element.attributeCount > 0) { + for (xmlAttribute in element.attributeList) { + if (!checkAttr(xmlAttribute)) { + continue + } + val namespace = + view.findNamespaceByUri(xmlAttribute.namespaceUri) + ?: throw InflateException("Unknown namespace : ${xmlAttribute.namespaceUri}") + val attr = + AttributeImpl(namespace = namespace, name = xmlAttribute.name, value = xmlAttribute.value) + view.addAttribute(attr) + inflationEventListener?.onEvent(OnApplyAttributeEvent(view, attr)) + } + } } private fun onCreateMergedView( element: XmlElement, - parent: ViewGroup, + parent: IViewGroup, module: AndroidModule, widgets: WidgetTable, - ): IView { - TODO("Not yet implemented") + ): List { + val views = mutableListOf() + for (xmlNode in element.childList) { + if (xmlNode.nodeCase == ELEMENT) { + views.addAll(onCreateView(xmlNode.element, parent, module, widgets)) + } + } + + addNamespaceDecls(element, parent as ViewImpl) + + return views } - private fun onCreateIncludedView( - element: XmlElement, - parent: ViewGroup, - module: AndroidModule, - widgets: WidgetTable, - ): IView { - TODO("Not yet implemented") + private fun onCreateIncludedView(element: XmlElement, parent: IViewGroup): List { + val layout = + element.attributeList.find { it.namespaceUri.isNullOrBlank() && it.name == "layout" } + ?: throw InflateException(" tag must have 'layout' attribute") + val file = parseLayoutReference(layout.value) + if (file == null || !file.exists() || !file.isFile || file.extension != EXT_XML) { + throw InflateException("Invalid layout file reference; '${layout.value}'") + } + + val (processor, module) = processXmlFile(file) + val inflated = doInflate(processor, module) { parent } + if (inflated.isEmpty() || inflated.size > 1) { + // probably a merged view or no views at all + // no need to apply attributes + return inflated + } + + val view = inflated[0] as ViewImpl + addNamespaceDecls(element = element, view = view) + + // The inflated view is already attached to parent + // so we don't need to do it here + applyAttributes(element = element, view = view, parent = parent, attachToParent = false) { + !(it.namespaceUri.isNullOrBlank() && it.name == "layout") + } + + return inflated } protected open fun onCreatePlatformView( @@ -272,6 +301,40 @@ open class LayoutInflaterImpl : ILayoutInflater() { } } + protected open fun addNamespaceDecls(element: XmlElement, view: ViewImpl) { + if (element.namespaceDeclarationCount > 0) { + for (xmlNamespace in element.namespaceDeclarationList) { + view.namespaceDecls[xmlNamespace.uri] = NamespaceImpl(xmlNamespace.prefix, xmlNamespace.uri) + } + } + } + + protected open fun processXmlFile(file: File): Pair { + val pathData = extractPathData(file) + if (pathData.type != LAYOUT) { + throw InflateException("File is not a layout file.") + } + + if (ProjectManager.rootProject == null) { + throw InflateException("Project is not initialized!") + } + + val module = + ProjectManager.findModuleForFile(file) as? AndroidModule + ?: throw InflateException("Cannot find module for given file. Is the project initialized?") + val resFile = + ResourceFile( + ResourceName(module.packageName, pathData.type!!, pathData.name), + pathData.config, + pathData.source, + ProtoXml + ) + + val processor = XmlProcessor(pathData.source, BlameLogger(IDELogger)) + processor.process(resFile, file.inputStream()) + return processor to module + } + private fun createViewInstance(widget: Widget, parent: ViewGroup): View { val klass = javaClass.classLoader!!.loadClass(widget.qualifiedName) val constructor = klass.getConstructor(Context::class.java) diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewGroupImpl.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewGroupImpl.kt index 1a5f186ce..57ff82e96 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewGroupImpl.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ViewGroupImpl.kt @@ -18,10 +18,8 @@ package com.itsaky.androidide.inflater.internal import android.view.ViewGroup -import com.android.aaptcompiler.XmlResource import com.itsaky.androidide.inflater.IView import com.itsaky.androidide.inflater.IViewGroup -import java.io.File open class ViewGroupImpl(file: LayoutFile, name: String, view: ViewGroup) : ViewImpl(file = file, name = name, view = view), IViewGroup { @@ -68,4 +66,11 @@ open class ViewGroupImpl(file: LayoutFile, name: String, view: ViewGroup) : addChild(index, view) return existing } + + override fun printHierarchy(builder: StringBuilder, indent: Int) { + super.printHierarchy(builder, indent) + for (child in children) { + (child as ViewImpl).printHierarchy(builder, indent + 1) + } + } } 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 7d7c2683d..ef1a6f953 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 @@ -59,4 +59,14 @@ constructor( internal fun findNamespaceByUri(uri: String): INamespace? { return this.namespaceDecls[uri] ?: (parent as? ViewImpl)?.findNamespaceByUri(uri) } + + internal open fun printHierarchy() : String { + return StringBuilder().apply { printHierarchy(this, 0) }.toString() + } + + internal open fun printHierarchy(builder: StringBuilder, indent: Int) { + builder.append(" ".repeat(indent * 4)) + builder.append(name) + builder.append("\n") + } } 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 0b4eb93f5..9ec54d7a8 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 @@ -39,11 +39,6 @@ import com.itsaky.androidide.inflater.IView import com.itsaky.androidide.inflater.internal.AttributeImpl import com.itsaky.androidide.inflater.internal.LayoutFile import com.itsaky.androidide.inflater.internal.ViewImpl -import com.itsaky.androidide.inflater.internal.utils.endParse -import com.itsaky.androidide.inflater.internal.utils.parseId -import com.itsaky.androidide.inflater.internal.utils.startParse -import com.itsaky.androidide.projects.ProjectManager -import com.itsaky.androidide.projects.api.AndroidModule /** * Attribute adapter for [View]. @@ -275,10 +270,6 @@ open class ViewAttrAdapter : IAttributeAdapter() { @Suppress("UNCHECKED_CAST") return (view.view as T).let { val file = (view as ViewImpl).file - val module = - ProjectManager.findModuleForFile(file.file) as? AndroidModule - ?: throw IllegalStateException("Cannot find module for file: $file") - startParse(module) val applied = it.block( file, @@ -288,7 +279,6 @@ open class ViewAttrAdapter : IAttributeAdapter() { attribute.name, attribute.value ) - endParse() return@let applied } } @@ -312,7 +302,7 @@ open class ViewAttrAdapter : IAttributeAdapter() { protected open fun parsePorterDuffMode(mode: String): PorterDuff.Mode { return com.itsaky.androidide.inflater.internal.utils.parsePorterDuffMode(mode) } - + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q) protected open fun isApi29(): Boolean { return Build.VERSION.SDK_INT >= 29 diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/basicParseUtils.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/basicParseUtils.kt index 6263e49a3..8106dab16 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/basicParseUtils.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/basicParseUtils.kt @@ -24,6 +24,7 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.util.TypedValue.complexToDimension import android.view.Gravity +import android.view.InflateException import android.view.ViewGroup.LayoutParams import androidx.core.text.isDigitsOnly import com.android.SdkConstants.EXT_XML @@ -35,6 +36,7 @@ import com.android.aaptcompiler.AaptResourceType.COLOR import com.android.aaptcompiler.AaptResourceType.DIMEN import com.android.aaptcompiler.AaptResourceType.DRAWABLE import com.android.aaptcompiler.AaptResourceType.INTEGER +import com.android.aaptcompiler.AaptResourceType.LAYOUT import com.android.aaptcompiler.AaptResourceType.STRING import com.android.aaptcompiler.ArrayResource import com.android.aaptcompiler.AttributeResource @@ -200,6 +202,24 @@ fun parseDrawable(context: Context, value: String, def: Drawable = unknownDrawab return def } +fun parseLayoutReference(value: String): File? { + if (value[0] != '@') { + throw InflateException("Value must be a reference to a layout file") + } + + val layoutResolver: (Value?) -> File? = + fun(it): File? { + return if (it is FileReference) { + File(it.source.path) + } else null + } + val type = parseResourceReference(value)?.second ?: return null + if (type != LAYOUT) { + throwInvalidResType(LAYOUT, value) + } + return parseReference(value, type, null, layoutResolver) +} + @JvmOverloads fun parseColorDrawable(context: Context, value: String, def: Int = Color.TRANSPARENT): Drawable { return newColorDrawable(parseColor(context, value, def)) diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/module.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/module.kt index b5fd8469e..df8042535 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/module.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/module.kt @@ -26,11 +26,6 @@ val module: AndroidModule get() = currentModule ?: throw IllegalStateException("You must call startParse(AndroidModule) first") -fun startParseIfNeeded(file: File) { - if (currentModule != null) return - (ProjectManager.findModuleForFile(file) as? AndroidModule)?.also { startParse(it) } -} - fun startParse(m: AndroidModule) { currentModule = m } 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 cd6ca79cf..03ea27efe 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 @@ -44,8 +44,9 @@ class LayoutInflaterTest { val inflater = ILayoutInflater.newInflater() val inflated = inflater.inflate(file, parent) inflated.apply { - assertThat(this).isNotNull() - assertThat(this!!.view).isInstanceOf(View::class.java) + assertThat(this).isNotEmpty() + assertThat(this).hasSize(1) + forEach { assertThat(it.view).isInstanceOf(View::class.java) } } } } @@ -62,10 +63,9 @@ class LayoutInflaterTest { 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")) + val inflated = inflater.inflate(file, parent)[0] + assertThat(inflated.view).isInstanceOf(view.java) + assertThat(inflated.view.id).isEqualTo(IDTable.get(view.simpleName!!, "template_view")) (inflated as ViewImpl).attributes.apply { assertThat(this) @@ -81,6 +81,45 @@ class LayoutInflaterTest { } } + @Test + fun `verify included view hierarchy`() { + inflaterTest { module -> + requiresActivity { activity -> + val parent = LinearLayout(activity) + val inflater = ILayoutInflater.newInflater() + val inflated = inflater.inflate(layoutFile("include"), parent) + assertThat(inflated).hasSize(1) + + val view = inflated[0] as ViewImpl + assertThat(view.printHierarchy()) + .isEqualTo( + "android.widget.LinearLayout\n" + + " android.widget.RelativeLayout\n" + + " android.widget.TextView\n" + + " android.widget.TextView\n" + ) + } + } + } + + @Test + fun `verify merged view hierarchy`() { + inflaterTest { module -> + requiresActivity { activity -> + val parent = LinearLayout(activity) + val inflater = ILayoutInflater.newInflater() + val inflated = inflater.inflate(layoutFile("merge"), parent) + assertThat(inflated).hasSize(1) + + val view = inflated[0] as ViewImpl + assertThat(view.printHierarchy()) + .isEqualTo( + "android.widget.LinearLayout\n android.widget.TextView\n android.widget.TextView\n" + ) + } + } + } + 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") diff --git a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt index 063707b0e..31a783e46 100644 --- a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt +++ b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt @@ -19,13 +19,9 @@ package com.itsaky.androidide.inflater import android.graphics.Color import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.ClipDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.GradientDrawable -import android.graphics.drawable.LayerDrawable -import android.graphics.drawable.ShapeDrawable import com.google.common.truth.Truth.assertThat -import com.itsaky.androidide.inflater.internal.utils.endParse import com.itsaky.androidide.inflater.internal.utils.parseBoolean import com.itsaky.androidide.inflater.internal.utils.parseColor import com.itsaky.androidide.inflater.internal.utils.parseDimension @@ -34,7 +30,6 @@ import com.itsaky.androidide.inflater.internal.utils.parseInteger import com.itsaky.androidide.inflater.internal.utils.parseIntegerArray import com.itsaky.androidide.inflater.internal.utils.parseString import com.itsaky.androidide.inflater.internal.utils.parseStringArray -import com.itsaky.androidide.inflater.internal.utils.startParse import kotlin.math.roundToInt import org.junit.Test import org.junit.runner.RunWith @@ -47,7 +42,6 @@ class ValueParsersTest { fun `hardcoded dimension parser test`() { inflaterTest { module -> requiresActivity { activity -> - startParse(module) // Hardcoded dimensions val units = arrayOf("dp", "sp", "pt", "px", "in", "mm") @@ -84,7 +78,6 @@ class ValueParsersTest { assertThat(parseDimension(activity, "@dimen/test_dimen_1pt", def = 0f)).isEqualTo(1) assertThat(parseDimension(activity, "@dimen/test_dimen_1pt_ref", def = 0f)).isEqualTo(1) assertThat(parseDimension(activity, "@dimen/test_dimen_0pt_ref", def = 0f)).isEqualTo(0) - endParse() } } } @@ -93,7 +86,6 @@ class ValueParsersTest { fun `drawable parse test`() { inflaterTest { module -> requiresActivity { activity -> - startParse(module) parseDrawable(activity, "#ff0000").apply { assertThat(this).isNotNull() assertThat(this).isInstanceOf(ColorDrawable::class.java) @@ -107,7 +99,6 @@ class ValueParsersTest { assertThat(this).isNotNull() assertThat(this).isInstanceOf(GradientDrawable::class.java) } - endParse() } } } @@ -116,7 +107,6 @@ class ValueParsersTest { fun `boolean parser test`() { inflaterTest { module -> requiresActivity { - startParse(module) assertThat(parseBoolean("true")).isTrue() assertThat(parseBoolean("false", def = true)).isFalse() assertThat(parseBoolean("@android:bool/resolver_landscape_phone")).isTrue() @@ -125,7 +115,6 @@ class ValueParsersTest { assertThat(parseBoolean("@bool/test_bool_false", def = true)).isFalse() assertThat(parseBoolean("@bool/test_bool_true_ref")).isTrue() assertThat(parseBoolean("@bool/test_bool_false_ref", def = true)).isFalse() - endParse() } } } @@ -134,7 +123,6 @@ class ValueParsersTest { fun `integer parser test`() { inflaterTest { module -> requiresActivity { - startParse(module) assertThat(parseInteger("0", def = 1)).isEqualTo(0) assertThat(parseInteger("10")).isEqualTo(10) assertThat(parseInteger("110")).isEqualTo(110) @@ -147,7 +135,6 @@ class ValueParsersTest { assertThat(parseInteger("@integer/test_integer_1_ref")).isEqualTo(1) assertThat(parseInteger("@integer/test_integer_0", def = 1)).isEqualTo(0) assertThat(parseInteger("@integer/test_integer_0_ref", def = 1)).isEqualTo(0) - endParse() } } } @@ -155,7 +142,6 @@ class ValueParsersTest { @Test fun `string parser test`() { inflaterTest { - startParse(it) assertThat(parseString("Hello World!")).isEqualTo("Hello World!") assertThat(parseString("@android:string/ok")).isEqualTo("OK") assertThat(parseString("@android:string/cancel")).isEqualTo("Cancel") @@ -163,34 +149,29 @@ class ValueParsersTest { assertThat(parseString("@string/test_string_ref")).isEqualTo("I love Android!") assertThat(parseString("@string/test_string_styled")).isEqualTo("I love Android!") assertThat(parseString("@string/test_string_styled_ref")).isEqualTo("I love Android!") - endParse() } } @Test fun `string array parser test`() { inflaterTest { - startParse(it) assertThat(parseStringArray("@android:array/phoneTypes")) .isEqualTo( arrayOf("Home", "Mobile", "Work", "Work Fax", "Home Fax", "Pager", "Other", "Custom") ) assertThat(parseStringArray("@array/test_string_array")) .isEqualTo(arrayOf("I", "love", "Android")) - endParse() } } @Test fun `integer array parser test`() { inflaterTest { - startParse(it) assertThat( parseIntegerArray("@android:array/config_defaultImperceptibleKillingExemptionProcStates") ) .isEqualTo(intArrayOf(0, 1, 2, 4, 12)) assertThat(parseIntegerArray("@array/test_integer_array")).isEqualTo(intArrayOf(2, 4, 8)) - endParse() } } @@ -198,7 +179,6 @@ class ValueParsersTest { fun `color parser test`() { inflaterTest { requiresActivity { activity -> - startParse(it) assertThat(parseColor(activity, "@android:color/red")).isEqualTo(Color.RED) assertThat(parseColor(activity, "@android:color/black")).isEqualTo(Color.BLACK) assertThat(parseColor(activity, "@android:color/white")).isEqualTo(Color.WHITE) @@ -209,7 +189,6 @@ class ValueParsersTest { // TODO Implement color state list parser assertThat(parseColor(activity, "@color/test_selector")).isEqualTo(Color.TRANSPARENT) - endParse() } } } diff --git a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt index 1fdf358c0..f98bfc682 100644 --- a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt +++ b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt @@ -18,6 +18,8 @@ package com.itsaky.androidide.inflater import androidx.appcompat.app.AppCompatActivity +import com.itsaky.androidide.inflater.internal.utils.endParse +import com.itsaky.androidide.inflater.internal.utils.startParse import com.itsaky.androidide.lookup.Lookup import com.itsaky.androidide.projects.ProjectManager import com.itsaky.androidide.projects.api.AndroidModule @@ -51,7 +53,9 @@ object XmlInflaterTest { fun inflaterTest(block: (AndroidModule) -> Unit) { XmlInflaterTest.initIfNeeded() + startParse(ProjectManager.app!!) block(ProjectManager.app!!) + endParse() } fun requiresActivity(block: (AppCompatActivity) -> Unit) {