diff --git a/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt b/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt
index b60c03a0b..1d524f68b 100644
--- a/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt
+++ b/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt
@@ -19,32 +19,27 @@ package com.itsaky.androidide.actions
import android.content.Context
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.itsaky.androidide.utils.DialogUtils
import io.github.rosemoe.sora.widget.CodeEditor
import java.io.File
import java.nio.file.Path
-fun ActionItem.requireContext(data: ActionData): Context {
- return data.get(Context::class.java)
- ?: throw IllegalArgumentException("No context instance provided")
-}
-
-fun ActionItem.requireFile(data: ActionData): File {
+fun requireFile(data: ActionData): File {
return data.get(File::class.java) ?: throw IllegalArgumentException("No file instance provided")
}
-fun ActionItem.requirePath(data: ActionData): Path {
+fun requirePath(data: ActionData): Path {
return requireFile(data).toPath()
}
-fun ActionItem.requireEditor(data: ActionData): CodeEditor {
+fun requireEditor(data: ActionData): CodeEditor {
return data.get(CodeEditor::class.java)
?: throw IllegalArgumentException("An editor instance is required but none was provided")
}
-fun ActionItem.newDialogBuilder(data: ActionData): MaterialAlertDialogBuilder {
- val klass = Class.forName("com.itsaky.androidide.utils.DialogUtils")
- val method = klass.getDeclaredMethod("newMaterialDialogBuilder", Context::class.java)
- return method.invoke(null, data.get(Context::class.java)!!) as MaterialAlertDialogBuilder
+fun newDialogBuilder(data: ActionData): MaterialAlertDialogBuilder {
+ val context = data.get(Context::class.java)!!
+ return DialogUtils.newMaterialDialogBuilder(context)
}
/**
@@ -54,7 +49,7 @@ fun ActionItem.newDialogBuilder(data: ActionData): MaterialAlertDialogBuilder {
* @param types The type of objects to look for.
* @return `true` if the [data] has the given [types], `false` otherwise.
*/
-fun ActionItem.hasRequiredData(data: ActionData, vararg types: Class<*>): Boolean {
+fun hasRequiredData(data: ActionData, vararg types: Class<*>): Boolean {
for (type in types) {
data.get(type) ?: return false
}
diff --git a/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java b/common/src/main/java/com/itsaky/androidide/utils/DialogUtils.kt
similarity index 52%
rename from app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java
rename to common/src/main/java/com/itsaky/androidide/utils/DialogUtils.kt
index af6135bfc..59b6efc54 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java
+++ b/common/src/main/java/com/itsaky/androidide/utils/DialogUtils.kt
@@ -14,51 +14,48 @@
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see .
*/
+package com.itsaky.androidide.utils
-package com.itsaky.androidide.utils;
-
-import android.content.Context;
-import android.content.DialogInterface;
-
-import androidx.annotation.NonNull;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.itsaky.androidide.resources.R;
-
-import org.jetbrains.annotations.Contract;
+import android.content.Context
+import android.content.DialogInterface.OnClickListener
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.itsaky.androidide.resources.R.string
+import com.itsaky.androidide.resources.R.style
+import org.jetbrains.annotations.Contract
/**
* Utility class for creating dialogs.
*
* @author Akash Yadav
*/
-public class DialogUtils {
-
+object DialogUtils {
+
/**
* Create a new alert dialog with two buttons: Yes and No. This method
- * simply calls {@link #newYesNoDialog(Context, String, String, DialogInterface.OnClickListener,
- * DialogInterface.OnClickListener)} with default values for title and message.
+ * simply calls [.newYesNoDialog] with default values for title and message.
*
* @param context The context for the dialog.
* @param positiveClickListener A listener that will be invoked on the Yes button
- * click.
+ * click.
* @param negativeClickListener A listener that will be invoked on the No button
- * click.
+ * click.
* @return The newly created dialog.
*/
- @NonNull
- public static MaterialAlertDialogBuilder newYesNoDialog(
- Context context,
- DialogInterface.OnClickListener positiveClickListener,
- DialogInterface.OnClickListener negativeClickListener) {
+ @JvmStatic
+ @JvmOverloads
+ fun newYesNoDialog(
+ context: Context,
+ positiveClickListener: OnClickListener? = null,
+ negativeClickListener: OnClickListener? = null
+ ): MaterialAlertDialogBuilder {
return newYesNoDialog(
- context,
- context.getString(R.string.msg_yesno_def_title),
- context.getString(R.string.msg_yesno_def_message),
- positiveClickListener,
- negativeClickListener);
+ context,
+ context.getString(string.msg_yesno_def_title),
+ context.getString(string.msg_yesno_def_message),
+ positiveClickListener,
+ negativeClickListener)
}
-
+
/**
* Create a new alert dialog with two buttons: Yes and No.
*
@@ -66,36 +63,37 @@ public class DialogUtils {
* @param title The title of the dialog.
* @param message The message of the dialog.
* @param positiveClickListener A listener that will be invoked on the Yes button
- * click.
+ * click.
* @param negativeClickListener A listener that will be invoked on the No button
- * click.
+ * click.
* @return The newly created dialog instance.
*/
- @NonNull
- public static MaterialAlertDialogBuilder newYesNoDialog(
- Context context,
- String title,
- String message,
- DialogInterface.OnClickListener positiveClickListener,
- DialogInterface.OnClickListener negativeClickListener) {
- final var builder = DialogUtils.newMaterialDialogBuilder(context);
- builder.setTitle(title);
- builder.setMessage(message);
- builder.setPositiveButton(R.string.yes, positiveClickListener);
- builder.setNegativeButton(R.string.no, negativeClickListener);
-
- return builder;
+ @JvmStatic
+ @JvmOverloads
+ fun newYesNoDialog(
+ context: Context,
+ title: String,
+ message: String? = null,
+ positiveClickListener: OnClickListener? = null,
+ negativeClickListener: OnClickListener? = null
+ ): MaterialAlertDialogBuilder {
+ val builder = newMaterialDialogBuilder(context)
+ builder.setTitle(title)
+ builder.setMessage(message)
+ builder.setPositiveButton(string.yes, positiveClickListener)
+ builder.setNegativeButton(string.no, negativeClickListener)
+ return builder
}
-
+
/**
* Creates a new MaterialAlertDialogBuilder with the app's default style.
*
* @param context The context for the dialog builder.
* @return The new MaterialAlertDialogBuilder instance.
*/
- @NonNull
+ @JvmStatic
@Contract("_ -> new")
- public static MaterialAlertDialogBuilder newMaterialDialogBuilder(Context context) {
- return new MaterialAlertDialogBuilder(context, R.style.AppTheme_MaterialAlertDialog);
+ fun newMaterialDialogBuilder(context: Context): MaterialAlertDialogBuilder {
+ return MaterialAlertDialogBuilder(context, style.AppTheme_MaterialAlertDialog)
}
-}
+}
\ No newline at end of file
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt
index 28f28cef5..fef561221 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/BaseJavaCodeAction.kt
@@ -31,7 +31,6 @@ import com.itsaky.androidide.lsp.java.JavaLanguageServer
import com.itsaky.androidide.lsp.java.rewrite.Rewrite
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.utils.DocumentUtils
-import com.itsaky.androidide.utils.ILogger
import java.io.File
/**
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/CommentAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/CommentAction.kt
index 5d9f56f5d..88639994f 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/CommentAction.kt
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/CommentAction.kt
@@ -20,7 +20,6 @@ import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.requireEditor
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction
-import com.itsaky.androidide.utils.ILogger
/** @author Akash Yadav */
class CommentAction : BaseJavaCodeAction() {
diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/UncommentAction.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/UncommentAction.kt
index 9cd615464..1894bf2c9 100644
--- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/UncommentAction.kt
+++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/common/UncommentAction.kt
@@ -20,7 +20,6 @@ import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.requireEditor
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.lsp.java.actions.BaseJavaCodeAction
-import com.itsaky.androidide.utils.ILogger
/** @author Akash Yadav */
class UncommentAction : BaseJavaCodeAction() {
diff --git a/tests/test-home/.androidide/init/model.jar b/tests/test-home/.androidide/init/model.jar
index a99019d50..5f4ec9982 100644
Binary files a/tests/test-home/.androidide/init/model.jar and b/tests/test-home/.androidide/init/model.jar differ
diff --git a/uidesigner/build.gradle b/uidesigner/build.gradle
index 77d211d3d..cc687e38b 100644
--- a/uidesigner/build.gradle
+++ b/uidesigner/build.gradle
@@ -46,6 +46,7 @@ dependencies {
implementation project(path: ':common')
implementation project(path: ':logger')
+ implementation project(path: ':lookup')
implementation project(path: ':lsp:api')
implementation project(path: ':lsp:xml')
implementation project(path: ':resources')
diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/adapters/ViewAttrListAdapter.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/adapters/ViewAttrListAdapter.kt
index 64a9bae5c..2ce2dcf3f 100644
--- a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/adapters/ViewAttrListAdapter.kt
+++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/adapters/ViewAttrListAdapter.kt
@@ -18,7 +18,9 @@
package com.itsaky.androidide.uidesigner.adapters
import android.annotation.SuppressLint
+import android.content.Context
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
@@ -26,9 +28,12 @@ import androidx.recyclerview.widget.RecyclerView
import com.itsaky.androidide.inflater.IAttribute
import com.itsaky.androidide.inflater.IView
import com.itsaky.androidide.inflater.IView.SingleAttributeChangeListener
+import com.itsaky.androidide.uidesigner.R
import com.itsaky.androidide.uidesigner.adapters.ViewAttrListAdapter.VH
import com.itsaky.androidide.uidesigner.databinding.LayoutViewattrItemBinding
+import com.itsaky.androidide.uidesigner.models.UiAttribute
import com.itsaky.androidide.uidesigner.viewmodel.WorkspaceViewModel
+import com.itsaky.androidide.utils.DialogUtils
/**
* A [RecyclerView.Adapter] which shows the list of attributes of the selected view in the UI
@@ -36,11 +41,15 @@ import com.itsaky.androidide.uidesigner.viewmodel.WorkspaceViewModel
*
* @author Akash Yadav
*/
-class ViewAttrListAdapter(attributes: List, private val viewModel: WorkspaceViewModel?, private val onClick: (IAttribute) -> Unit) :
- RecyclerView.Adapter() {
-
- private val attributes = attributes.sortedBy { it.name }
-
+class ViewAttrListAdapter(
+ attributes: List,
+ private val viewModel: WorkspaceViewModel?,
+ private val onDeleteAttr: (IAttribute) -> Boolean,
+ private val onClick: (IAttribute) -> Unit
+) : RecyclerView.Adapter() {
+
+ private val attributes = attributes.sortedBy { it.name }.toMutableList()
+
class VH(val binding: LayoutViewattrItemBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
@@ -54,28 +63,56 @@ class ViewAttrListAdapter(attributes: List, private val viewModel: W
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: VH, position: Int) {
val binding = holder.binding
- val attr = this.attributes[position]
-
+ val attr = this.attributes[position] as UiAttribute
+
binding.attrName.text = "${attr.namespace.prefix}:${attr.name}"
binding.attrValue.text = attr.value
-
+
+ if (!attr.isRequired) {
+ binding.deleteAttr.visibility = View.VISIBLE
+ binding.deleteAttr.setOnClickListener {
+ confirmDeleteAttr(binding.deleteAttr.context, attr, position)
+ }
+ } else binding.deleteAttr.visibility = View.INVISIBLE
+
binding.root.setOnClickListener {
onClick(attr)
val viewModel = this.viewModel ?: return@setOnClickListener
- val attrUpdateListener = object : SingleAttributeChangeListener() {
- override fun onAttributeUpdated(view: IView, attribute: IAttribute, oldValue: String) {
- binding.attrValue.text = attribute.value
- }
- }
- val viewInfoScreenObserver = object : Observer {
- override fun onChanged(t: Int?) {
- if (t == WorkspaceViewModel.SCREEN_VIEW_INFO) {
- viewModel._viewInfoScreen.removeObserver(this)
+ val attrUpdateListener =
+ object : SingleAttributeChangeListener() {
+ override fun onAttributeUpdated(view: IView, attribute: IAttribute, oldValue: String) {
+ binding.attrValue.text = attribute.value
}
}
- }
- viewModel._viewInfoScreen.observe(binding.root.context as LifecycleOwner, viewInfoScreenObserver)
+ val viewInfoScreenObserver =
+ object : Observer {
+ override fun onChanged(t: Int?) {
+ if (t == WorkspaceViewModel.SCREEN_VIEW_INFO) {
+ viewModel._viewInfoScreen.removeObserver(this)
+ }
+ }
+ }
+ viewModel._viewInfoScreen.observe(
+ binding.root.context as LifecycleOwner,
+ viewInfoScreenObserver
+ )
viewModel.view?.registerAttributeChangeListener(attrUpdateListener)
}
}
+
+ private fun confirmDeleteAttr(context: Context, attribute: UiAttribute, position: Int) {
+ DialogUtils.newYesNoDialog(
+ context = context,
+ title = context.getString(R.string.title_confirm_delete),
+ message = context.getString(R.string.msg_confirm_delete),
+ positiveClickListener = { dialog, _ ->
+ dialog.dismiss()
+ if (onDeleteAttr(attribute)) {
+ this.attributes.removeAt(position)
+ notifyItemRemoved(position)
+ }
+ }
+ )
+ .show()
+ }
}
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 7b529f99a..5ea04a527 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
@@ -27,15 +27,15 @@ import androidx.transition.ChangeBounds
import androidx.transition.TransitionManager
import com.blankj.utilcode.util.SizeUtils
import com.itsaky.androidide.fragments.BaseFragment
-import com.itsaky.androidide.inflater.IInflateEventsListener
-import com.itsaky.androidide.inflater.IInflationEvent
import com.itsaky.androidide.inflater.ILayoutInflater
import com.itsaky.androidide.inflater.IView
import com.itsaky.androidide.inflater.IViewGroup
import com.itsaky.androidide.inflater.IViewGroup.SingleOnHierarchyChangeListener
-import com.itsaky.androidide.inflater.InflationFinishEvent
-import com.itsaky.androidide.inflater.InflationStartEvent
-import com.itsaky.androidide.inflater.OnInflateViewEvent
+import com.itsaky.androidide.inflater.events.IInflateEventsListener
+import com.itsaky.androidide.inflater.events.IInflationEvent
+import com.itsaky.androidide.inflater.events.InflationFinishEvent
+import com.itsaky.androidide.inflater.events.InflationStartEvent
+import com.itsaky.androidide.inflater.events.OnInflateViewEvent
import com.itsaky.androidide.inflater.internal.LayoutFile
import com.itsaky.androidide.inflater.internal.ViewImpl
import com.itsaky.androidide.inflater.viewGroup
@@ -47,8 +47,8 @@ import com.itsaky.androidide.uidesigner.drag.WidgetTouchListener
import com.itsaky.androidide.uidesigner.drawable.UiViewLayeredForeground
import com.itsaky.androidide.uidesigner.fragments.ViewInfoFragment.Companion.TAG
import com.itsaky.androidide.uidesigner.models.PlaceholderView
-import com.itsaky.androidide.uidesigner.models.UiView
import com.itsaky.androidide.uidesigner.models.UiViewGroup
+import com.itsaky.androidide.uidesigner.utils.UiLayoutInflater
import com.itsaky.androidide.uidesigner.utils.bgDesignerView
import com.itsaky.androidide.uidesigner.utils.layeredForeground
import com.itsaky.androidide.uidesigner.viewmodel.WorkspaceViewModel
@@ -70,6 +70,7 @@ class DesignerWorkspaceFragment : BaseFragment() {
private var isInflating = false
private val viewInfo by lazy { ViewInfoSheet() }
+ private val inflater by lazy { UiLayoutInflater() }
private val workspaceView by lazy {
UiViewGroup(LayoutFile(File(""), ""), LinearLayout::class.qualifiedName!!, binding!!.workspace)
}
@@ -152,14 +153,6 @@ class DesignerWorkspaceFragment : BaseFragment() {
placeholder.file = file
}
}
-
- override fun onInterceptCreateView(view: IView): IView {
- view as ViewImpl
- if (view is IViewGroup) {
- return UiViewGroup(view.file, view.name, view.viewGroup)
- }
- return UiView(view.file, view.name, view.view)
- }
}
}
@@ -177,8 +170,7 @@ class DesignerWorkspaceFragment : BaseFragment() {
viewModel._workspaceScreen.observe(viewLifecycleOwner) { binding?.flipper?.displayedChild = it }
viewModel._errText.observe(viewLifecycleOwner) { binding?.errText?.text = it }
-
- val inflater = ILayoutInflater.newInflater()
+
inflater.inflationEventListener = this.inflateListener
var hasError = false
val inflated =
@@ -199,6 +191,7 @@ class DesignerWorkspaceFragment : BaseFragment() {
override fun onDestroyView() {
super.onDestroyView()
this.binding = null
+ this.inflater.close()
}
private fun setupView(view: IView) {
diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/ViewInfoFragment.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/ViewInfoFragment.kt
index dd291d303..a51b87005 100644
--- a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/ViewInfoFragment.kt
+++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/ViewInfoFragment.kt
@@ -24,9 +24,6 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.itsaky.androidide.inflater.IAttribute
-import com.itsaky.androidide.inflater.IView
-import com.itsaky.androidide.inflater.IView.SingleAttributeChangeListener
import com.itsaky.androidide.uidesigner.adapters.ViewAttrListAdapter
import com.itsaky.androidide.uidesigner.databinding.LayoutViewInfoBinding
import com.itsaky.androidide.uidesigner.databinding.LayoutViewInfoHeaderBinding
@@ -52,7 +49,7 @@ class ViewInfoFragment : Fragment() {
field = value
header = LayoutViewInfoHeaderBinding.bind(value.root)
}
-
+
private var header: LayoutViewInfoHeaderBinding? = null
companion object {
@@ -86,7 +83,10 @@ class ViewInfoFragment : Fragment() {
header.name.text = view.simpleName
header.desc.text = view.name
binding.attrList.adapter =
- ViewAttrListAdapter(view.attributes, viewModel) {
+ ViewAttrListAdapter(attributes = view.attributes, viewModel = viewModel, onDeleteAttr = {
+ view.removeAttribute(it)
+ true
+ }) {
viewModel.selectedAttr = it
viewModel.viewInfoScreen = SCREEN_VALUE_EDITOR
}
diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/models/UiAttribute.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/models/UiAttribute.kt
new file mode 100644
index 000000000..7cde2a732
--- /dev/null
+++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/models/UiAttribute.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.uidesigner.models
+
+import com.itsaky.androidide.inflater.IAttribute
+import com.itsaky.androidide.inflater.INamespace
+import com.itsaky.androidide.inflater.IView
+import com.itsaky.androidide.inflater.internal.AttributeImpl
+import com.itsaky.androidide.inflater.internal.ViewAdapterIndex
+
+/**
+ * UI Designer specific implementation of [IAttribute].
+ *
+ * @author Akash Yadav
+ */
+internal data class UiAttribute
+@JvmOverloads
+constructor(
+ override val namespace: INamespace = INamespace.ANDROID,
+ override val name: String,
+ override var value: String
+) : AttributeImpl(namespace, name, value) {
+
+ constructor(src: IAttribute) : this(namespace = src.namespace, name = src.name, value = src.value)
+
+ /**
+ * Whether this attribute is required or not. Required attributes cannot be removed from a view
+ * once applied.
+ */
+ internal var isRequired = false
+
+ companion object {
+ @JvmStatic
+ fun isRequired(view: IView, attribute: IAttribute): Boolean {
+ val adapter = ViewAdapterIndex.getAdapter(view.name) ?: return false
+ return adapter.isRequiredAttribute(attribute)
+ }
+ }
+}
diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/UiInflaterComponentFactory.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/UiInflaterComponentFactory.kt
new file mode 100644
index 000000000..e26986d67
--- /dev/null
+++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/UiInflaterComponentFactory.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.uidesigner.utils
+
+import android.view.View
+import android.view.ViewGroup
+import com.itsaky.androidide.inflater.IAttribute
+import com.itsaky.androidide.inflater.INamespace
+import com.itsaky.androidide.inflater.IView
+import com.itsaky.androidide.inflater.IComponentFactory
+import com.itsaky.androidide.inflater.internal.LayoutFile
+import com.itsaky.androidide.uidesigner.models.UiAttribute
+import com.itsaky.androidide.uidesigner.models.UiView
+import com.itsaky.androidide.uidesigner.models.UiViewGroup
+
+/**
+ * Creates layout inflater components for UI Designer.
+ *
+ * @author Akash Yadav
+ */
+open class UiInflaterComponentFactory : IComponentFactory {
+
+ override fun createView(file: LayoutFile, name: String, view: View): IView {
+ if (view is ViewGroup) {
+ return UiViewGroup(file, name, view)
+ }
+ return UiView(file, name, view)
+ }
+
+ override fun createAttr(
+ view: IView,
+ namespace: INamespace,
+ name: String,
+ value: String
+ ): IAttribute {
+ return UiAttribute(namespace = namespace, name = name, value = value).apply {
+ isRequired = UiAttribute.isRequired(view, this)
+ }
+ }
+}
diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/UiLayoutInflater.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/UiLayoutInflater.kt
new file mode 100644
index 000000000..9577a24cc
--- /dev/null
+++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/UiLayoutInflater.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.uidesigner.utils
+
+import com.itsaky.androidide.inflater.ILayoutInflater
+import com.itsaky.androidide.inflater.internal.LayoutInflaterImpl
+import com.itsaky.androidide.lookup.Lookup
+
+/**
+ * Layout inflater implmentation for the UI designer.
+ *
+ * @author Akash Yadav
+ */
+class UiLayoutInflater : LayoutInflaterImpl() {
+
+ init {
+ this.componentFactory = UiInflaterComponentFactory()
+ Lookup.DEFAULT.update(ILayoutInflater.LOOKUP_KEY, this)
+ }
+}
diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/ValueCompletionProvider.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/ValueCompletionProvider.kt
index 4f58496de..4abf65b85 100644
--- a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/ValueCompletionProvider.kt
+++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/ValueCompletionProvider.kt
@@ -65,7 +65,7 @@ internal class ValueCompletionProvider(
this.start()
}
- val copy = (attribute as AttributeImpl).copy()
+ val copy = (attribute as AttributeImpl).copyAttr(view = view)
copy.value = value
view.updateAttribute(copy)
}
diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/Widgets.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/Widgets.kt
index a17169cb2..dfdc4e6bc 100644
--- a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/Widgets.kt
+++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/Widgets.kt
@@ -45,6 +45,7 @@ import com.itsaky.androidide.inflater.internal.AttributeImpl
import com.itsaky.androidide.inflater.internal.LayoutFile
import com.itsaky.androidide.uidesigner.R.drawable
import com.itsaky.androidide.uidesigner.R.string
+import com.itsaky.androidide.uidesigner.models.UiAttribute
import com.itsaky.androidide.uidesigner.models.UiWidget
import com.itsaky.androidide.uidesigner.models.UiWidgetCategory
@@ -167,7 +168,7 @@ internal object Widgets {
) {
override fun createView(context: Context, parent: ViewGroup, layoutFile: LayoutFile): IView {
return super.createView(context, parent, layoutFile).apply {
- addAttribute(AttributeImpl(name = "orientation", value = "horizontal"))
+ addAttribute(UiAttribute(name = "orientation", value = "horizontal"))
}
}
}
@@ -181,7 +182,7 @@ internal object Widgets {
) {
override fun createView(context: Context, parent: ViewGroup, layoutFile: LayoutFile): IView {
return super.createView(context, parent, layoutFile).apply {
- addAttribute(AttributeImpl(name = "orientation", value = "vertical"))
+ addAttribute(UiAttribute(name = "orientation", value = "vertical"))
}
}
}
diff --git a/uidesigner/src/main/res/layout/layout_viewattr_item.xml b/uidesigner/src/main/res/layout/layout_viewattr_item.xml
index 4b28e5e02..ee0f81fd5 100644
--- a/uidesigner/src/main/res/layout/layout_viewattr_item.xml
+++ b/uidesigner/src/main/res/layout/layout_viewattr_item.xml
@@ -34,9 +34,9 @@
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/delete_attr"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toTopOf="@id/delete_attr"
tools:text="android:layout_width" />
+
+
\ No newline at end of file
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/DefaultComponentFactory.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/DefaultComponentFactory.kt
new file mode 100644
index 000000000..e5eae8c79
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/DefaultComponentFactory.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.view.View
+import android.view.ViewGroup
+import com.itsaky.androidide.inflater.internal.AttributeImpl
+import com.itsaky.androidide.inflater.internal.LayoutFile
+import com.itsaky.androidide.inflater.internal.ViewGroupImpl
+import com.itsaky.androidide.inflater.internal.ViewImpl
+
+/** Default implementation of [IComponentFactory]. */
+open class DefaultComponentFactory : IComponentFactory {
+
+ override fun createView(file: LayoutFile, name: String, view: View): IView {
+ return if (view is ViewGroup) {
+ ViewGroupImpl(file, name, view)
+ } else ViewImpl(file, name, view)
+ }
+
+ override fun createAttr(
+ view: IView,
+ namespace: INamespace,
+ name: String,
+ value: String
+ ): IAttribute {
+ return AttributeImpl(namespace = namespace, name = name, value = value)
+ }
+}
\ No newline at end of file
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/IComponentFactory.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/IComponentFactory.kt
new file mode 100644
index 000000000..71b629121
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/IComponentFactory.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.view.View
+import com.itsaky.androidide.inflater.internal.LayoutFile
+import com.itsaky.androidide.lookup.Lookup.Key
+
+/** Creates instances of views and attributes. */
+interface IComponentFactory {
+
+ /**
+ * Creates a new instance of [IView].
+ *
+ * @param file The layout file.
+ * @param name The fully qualified name of the view being created.
+ * @param view The Android view object.
+ * @return The [IView] instance.
+ */
+ fun createView(file: LayoutFile, name: String, view: View): IView
+
+ /**
+ * Creates a new [IAttribute] instance.
+ *
+ * @param view The view to which the attribute will be applied.
+ * @param namespace The namespace of the attribute.
+ * @param name The name of the attribute.
+ * @param value The value of the attribute.
+ * @return The [IAttribute] instance.
+ */
+ fun createAttr(view: IView, namespace: INamespace, name: String, value: String): IAttribute
+
+ companion object {
+ @JvmField val LAYOUT_INFLATER_COMPONENT_FACTORY_KEY = Key()
+ }
+}
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 fa00bdbba..a27c7fa1c 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
@@ -18,7 +18,11 @@
package com.itsaky.androidide.inflater
import android.view.ViewGroup
+import com.itsaky.androidide.inflater.events.IInflateEventsListener
import com.itsaky.androidide.inflater.internal.LayoutInflaterImpl
+import com.itsaky.androidide.lookup.Lookup.Key
+import com.itsaky.androidide.projects.api.AndroidModule
+import java.io.Closeable
import java.io.File
/**
@@ -26,23 +30,61 @@ import java.io.File
*
* @author Akash Yadav
*/
-abstract class ILayoutInflater {
+interface ILayoutInflater : Closeable {
/** The listener which listens to layout inflation events. */
- abstract var inflationEventListener: IInflateEventsListener?
+ var inflationEventListener: IInflateEventsListener?
+
+ /** The [IComponentFactory] which is used to create components of the layout inflater. */
+ var componentFactory: IComponentFactory
+
+ /** The [AndroidModule] which is used to resolve resource references. */
+ var module: AndroidModule?
/**
* Inflate the given raw XML file.
*
+ * **NOTE** : The [startParse][com.itsaky.androidide.inflater.utils.startParse] method is called
+ * if it hasn't been called yet. However, the [endParse]
+ * [com.itsaky.androidide.inflater.utils.endParse] method is not called after the parse is done.
+ * The caller is expected to call [close] when this inflater instance is no longer needed. The
+ * [close] method calls the [endParse] [com.itsaky.androidide.inflater.utils.endParse].
+ *
* @param file The file to inflate.
* @param parent The parent view.
*/
- abstract fun inflate(file: File, parent: ViewGroup): List
+ fun inflate(file: File, parent: ViewGroup): List
+
+ fun closeSilently() {
+ try {
+ close()
+ } catch (err: Throwable) {
+ // ignored
+ }
+ }
companion object {
+
+ /** The [Key] that can be used to lookup the layout inflater service. */
+ @JvmField val LOOKUP_KEY = Key()
+
+ /**
+ * Creates a new [ILayoutInflater] instance.
+ *
+ * @param module The [AndroidModule] that will be used to resolve resource references.
+ * @param componentFactory The [IComponentFactory] that will be used to create [ILayoutInflater]
+ * components.
+ */
@JvmStatic
- fun newInflater(): ILayoutInflater {
- return LayoutInflaterImpl()
+ @JvmOverloads
+ fun newInflater(
+ module: AndroidModule? = null,
+ componentFactory: IComponentFactory = DefaultComponentFactory()
+ ): ILayoutInflater {
+ return LayoutInflaterImpl().also {
+ it.module = module
+ it.componentFactory = componentFactory
+ }
}
}
}
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/IViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/IViewAdapter.kt
index 39774cfba..cc79c4213 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/IViewAdapter.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/IViewAdapter.kt
@@ -39,4 +39,13 @@ abstract class IViewAdapter : AbstractParser() {
* @param view The view to apply basic attributes to.
*/
abstract fun applyBasic(view: IView)
+
+ /**
+ * Called to check whether the given attribute required for the view. A required view cannot be
+ * removed by the user.
+ *
+ * @param attribute The attribute to check.
+ * @return `true` if the attribute is required, `false` otherwise.
+ */
+ abstract fun isRequiredAttribute(attribute: IAttribute): Boolean
}
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/IInflateEventsListener.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/IInflateEventsListener.kt
new file mode 100644
index 000000000..ea8b20333
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/IInflateEventsListener.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.events
+
+/**
+ * A listener which listens for XML layout inflation events.
+ *
+ * @author Akash Yadav
+ */
+interface IInflateEventsListener {
+
+ /**
+ * Called when an event occurs. The [event] parameter contains more information about the event.
+ *
+ * @param event The event.
+ */
+ fun onEvent(event: IInflationEvent<*>)
+}
\ No newline at end of file
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/IInflationEvent.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/IInflationEvent.kt
new file mode 100644
index 000000000..ed30465a5
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/IInflationEvent.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.events
+
+/**
+ * An inflation event.
+ *
+ * @author Akash Yadav
+ */
+abstract class IInflationEvent(val data: T)
\ No newline at end of file
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/inflationEvents.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/inflationEvents.kt
similarity index 63%
rename from xml-inflater/src/main/java/com/itsaky/androidide/inflater/inflationEvents.kt
rename to xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/inflationEvents.kt
index 27ab1b48e..7a3912c4a 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/inflationEvents.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/events/inflationEvents.kt
@@ -15,38 +15,10 @@
* along with AndroidIDE. If not, see .
*/
-package com.itsaky.androidide.inflater
+package com.itsaky.androidide.inflater.events
-/**
- * A listener which listens for XML layout inflation events.
- *
- * @author Akash Yadav
- */
-interface IInflateEventsListener {
-
- /**
- * Called when an event occurs. The [event] parameter contains more information about the event.
- *
- * @param event The event.
- */
- fun onEvent(event: IInflationEvent<*>)
-
- /**
- * Allows the listener to intercept the view creation event and return view instance of their own.
- *
- * @param view The view that was created. The properties of this view is not expected to be
- * modified (see [OnInflateViewEvent] instead).
- * @return The view that should replace the [view] instance.
- */
- fun onInterceptCreateView(view: IView): IView = view
-}
-
-/**
- * An inflation event.
- *
- * @author Akash Yadav
- */
-abstract class IInflationEvent(val data: T)
+import com.itsaky.androidide.inflater.IAttribute
+import com.itsaky.androidide.inflater.IView
/** Indicates start of layout inflation. */
class InflationStartEvent : IInflationEvent(Unit)
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/AttributeImpl.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/AttributeImpl.kt
index 622a607a3..0501131a8 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/AttributeImpl.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/AttributeImpl.kt
@@ -20,9 +20,50 @@ package com.itsaky.androidide.inflater.internal
import com.itsaky.androidide.inflater.IAttribute
import com.itsaky.androidide.inflater.INamespace
import com.itsaky.androidide.inflater.INamespace.Companion.ANDROID
+import com.itsaky.androidide.inflater.IView
+import com.itsaky.androidide.inflater.newAttribute
-data class AttributeImpl @JvmOverloads constructor(
+open class AttributeImpl
+@JvmOverloads
+constructor(
override val namespace: INamespace = ANDROID,
override val name: String,
override var value: String
-) : IAttribute
+) : IAttribute {
+
+ @JvmOverloads
+ fun copyAttr(
+ view: IView? = null,
+ namespace: INamespace = this.namespace,
+ name: String = this.name,
+ value: String = this.value
+ ): IAttribute {
+ return newAttribute(view = view, namespace = namespace, name = name, value = value)
+ }
+
+ internal fun immutableCopy(): IAttribute {
+ return ImmutableAttributeImpl(this)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is AttributeImpl) return false
+
+ if (namespace != other.namespace) return false
+ if (name != other.name) return false
+ if (value != other.value) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = namespace.hashCode()
+ result = 31 * result + name.hashCode()
+ result = 31 * result + value.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "AttributeImpl(namespace=$namespace, name='$name', value='$value')"
+ }
+}
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableAttributeImpl.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableAttributeImpl.kt
new file mode 100644
index 000000000..52a3d6f59
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableAttributeImpl.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.internal
+
+import com.itsaky.androidide.inflater.IAttribute
+
+/**
+ * Immutable implementation of [IAttribute].
+ *
+ * @author Akash Yadav
+ */
+internal class ImmutableAttributeImpl(private val src: AttributeImpl) : IAttribute by src {
+ override var value: String
+ get() = src.value
+ set(value) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+}
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableViewGroupImpl.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableViewGroupImpl.kt
new file mode 100644
index 000000000..fba86e458
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableViewGroupImpl.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.internal
+
+import com.itsaky.androidide.inflater.IView
+import com.itsaky.androidide.inflater.IViewGroup
+import com.itsaky.androidide.inflater.IViewGroup.OnHierarchyChangeListener
+
+/**
+ * Immutable implementation of [IViewGroup].
+ *
+ * @author Akash Yadav
+ */
+class ImmutableViewGroupImpl(private val src: IViewGroup) : IViewGroup by src {
+
+ override fun addChild(view: IView) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun addChild(index: Int, view: IView) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun removeChild(view: IView) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun removeChild(index: Int) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun set(index: Int, view: IView): IView {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun addOnHierarchyChangeListener(listener: OnHierarchyChangeListener) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun removeOnHierarchyChangeListener(listener: OnHierarchyChangeListener) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+}
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableViewImpl.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableViewImpl.kt
new file mode 100644
index 000000000..629b46990
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/ImmutableViewImpl.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.internal
+
+import android.view.View
+import com.itsaky.androidide.inflater.IAttribute
+import com.itsaky.androidide.inflater.IView
+import com.itsaky.androidide.inflater.IView.AttributeChangeListener
+import com.itsaky.androidide.inflater.IViewGroup
+
+/**
+ * An immutable implementation of [IView].
+ *
+ * @author Akash Yadav
+ */
+internal class ImmutableViewImpl(private val src: ViewImpl) : IView by src {
+
+ override fun addAttribute(attribute: IAttribute, update: Boolean) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun removeAttribute(attribute: IAttribute) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun updateAttribute(attribute: IAttribute) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun onHighlightStateUpdated(highlight: Boolean) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun registerAttributeChangeListener(listener: AttributeChangeListener) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+
+ override fun unregisterAttributeChangeListener(listener: AttributeChangeListener) {
+ throw UnsupportedOperationException("Immutable!")
+ }
+}
\ 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 69a773721..493ec8eb1 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
@@ -33,25 +33,30 @@ import com.android.aaptcompiler.ResourceName
import com.android.aaptcompiler.XmlProcessor
import com.android.aaptcompiler.extractPathData
import com.itsaky.androidide.aapt.logging.IDELogger
-import com.itsaky.androidide.inflater.IInflateEventsListener
+import com.itsaky.androidide.inflater.DefaultComponentFactory
+import com.itsaky.androidide.inflater.IAttribute
+import com.itsaky.androidide.inflater.IComponentFactory
+import com.itsaky.androidide.inflater.IComponentFactory.Companion.LAYOUT_INFLATER_COMPONENT_FACTORY_KEY
import com.itsaky.androidide.inflater.ILayoutInflater
+import com.itsaky.androidide.inflater.INamespace
import com.itsaky.androidide.inflater.IView
import com.itsaky.androidide.inflater.IViewGroup
import com.itsaky.androidide.inflater.InflateException
-import com.itsaky.androidide.inflater.InflationFinishEvent
-import com.itsaky.androidide.inflater.InflationStartEvent
-import com.itsaky.androidide.inflater.OnApplyAttributeEvent
-import com.itsaky.androidide.inflater.OnInflateViewEvent
+import com.itsaky.androidide.inflater.events.IInflateEventsListener
+import com.itsaky.androidide.inflater.events.InflationFinishEvent
+import com.itsaky.androidide.inflater.events.InflationStartEvent
+import com.itsaky.androidide.inflater.events.OnApplyAttributeEvent
+import com.itsaky.androidide.inflater.events.OnInflateViewEvent
import com.itsaky.androidide.inflater.internal.utils.IDTable
import com.itsaky.androidide.inflater.internal.utils.ViewFactory.createViewInstance
import com.itsaky.androidide.inflater.internal.utils.ViewFactory.generateLayoutParams
-import com.itsaky.androidide.inflater.internal.utils.endParse
-import com.itsaky.androidide.inflater.internal.utils.isParsing
+import com.itsaky.androidide.inflater.utils.isParsing
import com.itsaky.androidide.inflater.internal.utils.parseLayoutReference
-import com.itsaky.androidide.inflater.internal.utils.startParse
+import com.itsaky.androidide.inflater.utils.endParse
+import com.itsaky.androidide.inflater.utils.startParse
+import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.projects.api.AndroidModule
-import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.xml.widgets.Widget
import com.itsaky.androidide.xml.widgets.WidgetTable
import java.io.File
@@ -61,13 +66,19 @@ import java.io.File
*
* @author Akash Yadav
*/
-open class LayoutInflaterImpl : ILayoutInflater() {
+open class LayoutInflaterImpl : ILayoutInflater {
override var inflationEventListener: IInflateEventsListener? = null
- private val log = ILogger.newInstance("LayoutInflaterImpl")
+ override var module: AndroidModule? = null
private var _primaryInflatingFile: File? = null
private var _currentLayoutFile: LayoutFile? = null
+ override var componentFactory: IComponentFactory = DefaultComponentFactory()
+ set(value) {
+ field = value
+ Lookup.DEFAULT.update(LAYOUT_INFLATER_COMPONENT_FACTORY_KEY, value)
+ }
+
protected val primaryInflatingFile: File
get() = this._primaryInflatingFile!!
@@ -78,15 +89,27 @@ open class LayoutInflaterImpl : ILayoutInflater() {
this._primaryInflatingFile = file
IDTable.newRound()
if (!isParsing) {
- startParse(file)
+ if (this.module == null) {
+ startParse(file)
+ this.module = com.itsaky.androidide.inflater.utils.module
+ } else {
+ startParse(this.module!!)
+ }
}
inflationEventListener?.onEvent(InflationStartEvent())
return doInflate(file, parent).apply {
inflationEventListener?.onEvent(InflationFinishEvent(this))
_primaryInflatingFile = null
- endParse()
}
}
+
+ override fun close() {
+ this.module = null
+ this.inflationEventListener = null
+ this._primaryInflatingFile = null
+ this._currentLayoutFile = null
+ endParse()
+ }
protected open fun doInflate(file: File, parent: ViewGroup): List {
val (processor, module) = processXmlFile(file)
@@ -210,8 +233,7 @@ open class LayoutInflaterImpl : ILayoutInflater() {
val namespace =
view.findNamespaceByUri(xmlAttribute.namespaceUri)
?: throw InflateException("Unknown namespace : ${xmlAttribute.namespaceUri}")
- val attr =
- AttributeImpl(namespace = namespace, name = xmlAttribute.name, value = xmlAttribute.value)
+ val attr = onCreateAttribute(view, namespace, xmlAttribute.name, xmlAttribute.value)
view.addAttribute(attribute = attr, update = true)
inflationEventListener?.onEvent(OnApplyAttributeEvent(view, attr))
}
@@ -275,16 +297,26 @@ open class LayoutInflaterImpl : ILayoutInflater() {
): IView {
return try {
val v = createViewInstance(widget.qualifiedName, parent.context)
- val view =
- if (v is ViewGroup) {
- ViewGroupImpl(currentLayoutFile, widget.qualifiedName, v)
- } else ViewImpl(currentLayoutFile, widget.qualifiedName, v)
- return inflationEventListener?.onInterceptCreateView(view) ?: view
+ return componentFactory.createView(currentLayoutFile, widget.qualifiedName, v)
} catch (err: Throwable) {
onCreateUnsupportedView("Unable to create view for widget ${widget.qualifiedName}", parent)
}
}
+ protected open fun onCreateAttribute(
+ view: ViewImpl,
+ namespace: INamespace,
+ name: String,
+ value: String
+ ): IAttribute {
+ return componentFactory.createAttr(
+ view = view,
+ namespace = namespace,
+ name = name,
+ value = value
+ )
+ }
+
protected open fun addNamespaceDecls(element: XmlElement, view: ViewImpl) {
if (element.namespaceDeclarationCount > 0) {
for (xmlNamespace in element.namespaceDeclarationList) {
@@ -324,7 +356,8 @@ open class LayoutInflaterImpl : ILayoutInflater() {
}
private fun wrap(parent: ViewGroup): IViewGroup {
- return ViewGroupImpl(currentLayoutFile, parent.javaClass.name, parent)
+ return componentFactory.createView(currentLayoutFile, parent.javaClass.name, parent)
+ as IViewGroup
}
private fun SourcePosition.lineCol(): String {
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 83d85b150..d18f213b9 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
@@ -103,4 +103,8 @@ open class ViewGroupImpl(file: LayoutFile, name: String, view: ViewGroup) :
protected open fun notifyOnViewRemoved(child: IView) {
this.hierarchyChangeListeners.forEach { it.onViewRemoved(this, child) }
}
+
+ override fun immutableCopy(): IViewGroup {
+ return ImmutableViewGroupImpl(this)
+ }
}
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 93d6fcabf..f17b3e268 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
@@ -66,22 +66,19 @@ constructor(
}
override fun removeAttribute(attribute: IAttribute) {
+
+ // reset the value of the attribute to its default value
+ attribute.value = ""
+ updateAttributeInternal(attribute = attribute, notify = false)
+
this._attributes.remove(attribute)
notifyAttrRemoved(attribute)
- // TODO(itsaky): Should attribute adapters handle this as well?
}
override fun updateAttribute(attribute: IAttribute) {
- val existing =
- findAttribute(attribute)
- ?: throw IllegalArgumentException("Attribute '${attribute.name}' not found")
-
- val oldVal = existing.value
- existing.value = attribute.value
- applyAttribute(existing)
- notifyAttrUpdated(attribute, oldVal)
+ updateAttributeInternal(attribute)
}
-
+
override fun findAttribute(namespaceUri: String, name: String): IAttribute? {
return this.attributes.find { it.namespace.uri == namespaceUri && it.name == name }
}
@@ -144,6 +141,24 @@ constructor(
builder.append("\n")
}
+ internal open fun immutableCopy() : IView {
+ return ImmutableViewImpl(this)
+ }
+
+ private fun updateAttributeInternal(attribute: IAttribute, notify: Boolean = true) {
+ val existing =
+ findAttribute(attribute)
+ ?: throw IllegalArgumentException("Attribute '${attribute.name}' not found")
+
+ val oldVal = existing.value
+ existing.value = attribute.value
+ applyAttribute(existing)
+
+ if (notify) {
+ notifyAttrUpdated(attribute, oldVal)
+ }
+ }
+
private fun notifyAttrAdded(attribute: IAttribute) {
for (listener in this.attrChangeListeners) {
listener.onAttributeAdded(this, attribute)
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAttrAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAttrAdapter.kt
index cde934269..cd846b544 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAttrAdapter.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAttrAdapter.kt
@@ -29,8 +29,8 @@ import com.itsaky.androidide.annotations.inflater.ViewAdapter
import com.itsaky.androidide.inflater.IAttribute
import com.itsaky.androidide.inflater.INamespace
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.newAttribute
import java.util.regex.Pattern
/**
@@ -43,12 +43,12 @@ open class TextViewAttrAdapter : ViewAttrAdapter() {
override fun apply(view: IView, attribute: IAttribute): Boolean {
return doApply(view, attribute) {
- _: LayoutFile,
- context: Context,
- _: LayoutParams,
- _: INamespace,
- name: String,
- value: String ->
+ _: LayoutFile,
+ context: Context,
+ _: LayoutParams,
+ _: INamespace,
+ name: String,
+ value: String ->
val drawables = compoundDrawables
val drawablesRelative = compoundDrawablesRelative
var applied = true
@@ -130,7 +130,7 @@ open class TextViewAttrAdapter : ViewAttrAdapter() {
override fun applyBasic(view: IView) {
super.applyBasic(view)
- view.addAttribute(AttributeImpl(name = "text", value = "AndroidIDE"))
+ view.addAttribute(newAttribute(view = view, name = "text", value = "AndroidIDE"))
}
protected open fun parseTextStyle(value: String): Int {
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 2584bbf6d..7cd2ed41d 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
@@ -36,9 +36,9 @@ import com.itsaky.androidide.inflater.IAttribute
import com.itsaky.androidide.inflater.INamespace
import com.itsaky.androidide.inflater.IView
import com.itsaky.androidide.inflater.IViewAdapter
-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.newAttribute
/**
* Attribute adapter for [View].
@@ -132,8 +132,20 @@ open class ViewAttrAdapter : IViewAdapter() {
}
override fun applyBasic(view: IView) {
- view.addAttribute(AttributeImpl(name = "layout_height", value = "wrap_content"))
- view.addAttribute(AttributeImpl(name = "layout_width", value = "wrap_content"))
+ view.addAttribute(newAttribute(view = view, name = "layout_height", value = "wrap_content"))
+ view.addAttribute(newAttribute(view = view, name = "layout_width", value = "wrap_content"))
+ }
+
+ override fun isRequiredAttribute(attribute: IAttribute): Boolean {
+ if (attribute.namespace.uri != INamespace.ANDROID.uri) {
+ return false
+ }
+
+ return when (attribute.name) {
+ "layout_width",
+ "layout_height" -> true
+ else -> false
+ }
}
protected open fun applyLayoutParams(
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 8106dab16..ed9d3cffc 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
@@ -59,6 +59,7 @@ import com.android.aaptcompiler.tryParseFlagSymbol
import com.android.aaptcompiler.tryParseInt
import com.android.aaptcompiler.tryParseReference
import com.itsaky.androidide.inflater.drawable.DrawableParserFactory
+import com.itsaky.androidide.inflater.utils.module
import com.itsaky.androidide.utils.ILogger
import java.io.File
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/lookup.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/lookup.kt
index f34d35a25..885fa2981 100644
--- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/lookup.kt
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/lookup.kt
@@ -26,6 +26,7 @@ import com.android.aaptcompiler.ResourceGroup
import com.android.aaptcompiler.ResourceName
import com.android.aaptcompiler.ResourceTable
import com.android.aaptcompiler.ResourceTablePackage
+import com.itsaky.androidide.inflater.utils.module
import com.itsaky.androidide.utils.ILogger
private val log: ILogger = ILogger.newInstance("ParseLookupUtils")
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/inflaterUtils.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/inflaterUtils.kt
new file mode 100644
index 000000000..b15024928
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/inflaterUtils.kt
@@ -0,0 +1,45 @@
+/*
+ * 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 com.itsaky.androidide.inflater.internal.AttributeImpl
+import com.itsaky.androidide.lookup.Lookup
+
+/** Get the [ILayoutInflater] registered with [Lookup]. */
+fun lookupLayoutInflater(): ILayoutInflater? {
+ return Lookup.DEFAULT.lookup(ILayoutInflater.LOOKUP_KEY)
+}
+
+/** Get the [IComponentFactory] registered with [Lookup]. */
+fun lookupComponentFactory(): IComponentFactory? {
+ return Lookup.DEFAULT.lookup(IComponentFactory.LAYOUT_INFLATER_COMPONENT_FACTORY_KEY)
+}
+
+@JvmOverloads
+fun newAttribute(
+ view: IView? = null,
+ namespace: INamespace = INamespace.ANDROID,
+ name: String,
+ value: String
+): IAttribute {
+ val componentFactory = lookupComponentFactory()
+ if (componentFactory != null && view != null) {
+ return componentFactory.createAttr(view, namespace, name, value)
+ }
+ return AttributeImpl(namespace, name, value)
+}
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/utils/module.kt
similarity index 96%
rename from xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/module.kt
rename to xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/module.kt
index 2a67e35e0..c18b0eb00 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/utils/module.kt
@@ -15,7 +15,7 @@
* along with AndroidIDE. If not, see .
*/
-package com.itsaky.androidide.inflater.internal.utils
+package com.itsaky.androidide.inflater.utils
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.projects.api.AndroidModule
diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/viewUtils.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/viewUtils.kt
similarity index 100%
rename from xml-inflater/src/main/java/com/itsaky/androidide/inflater/viewUtils.kt
rename to xml-inflater/src/main/java/com/itsaky/androidide/inflater/utils/viewUtils.kt
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 8ece4a4a1..c7641ee90 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
@@ -20,17 +20,16 @@ package com.itsaky.androidide.inflater
import android.view.View
import android.widget.LinearLayout
import com.google.common.truth.Truth.assertThat
-import com.itsaky.androidide.inflater.internal.AttributeImpl
import com.itsaky.androidide.inflater.internal.LayoutInflaterImpl
import com.itsaky.androidide.inflater.internal.ViewGroupImpl
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.api.AndroidModule
-import java.io.File
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
+import java.io.File
/** @author Akash Yadav */
@RunWith(RobolectricTestRunner::class)
@@ -70,11 +69,11 @@ class LayoutInflaterTest {
(inflated as ViewImpl).attributes.apply {
assertThat(this)
- .contains(AttributeImpl(INamespace.ANDROID, "id", "@+id/template_view"))
+ .contains(newAttribute(inflated, INamespace.ANDROID, "id", "@+id/template_view"))
assertThat(this)
- .contains(AttributeImpl(INamespace.ANDROID, "layout_height", "match_parent"))
+ .contains(newAttribute(inflated, INamespace.ANDROID, "layout_height", "match_parent"))
assertThat(this)
- .contains(AttributeImpl(INamespace.ANDROID, "layout_width", "match_parent"))
+ .contains(newAttribute(inflated, INamespace.ANDROID, "layout_width", "match_parent"))
}
}
}
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 f98bfc682..e567c12e3 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,8 +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.inflater.utils.endParse
+import com.itsaky.androidide.inflater.utils.startParse
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.projects.api.AndroidModule