diff --git a/annotation-processors/src/main/java/com/itsaky/androidide/annotations/inflater/ViewAdapterAnnotationProcessor.kt b/annotation-processors/src/main/java/com/itsaky/androidide/annotations/inflater/ViewAdapterAnnotationProcessor.kt deleted file mode 100644 index f8b5f7919..000000000 --- a/annotation-processors/src/main/java/com/itsaky/androidide/annotations/inflater/ViewAdapterAnnotationProcessor.kt +++ /dev/null @@ -1,287 +0,0 @@ -/* - * 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.annotations.inflater - -import com.google.auto.service.AutoService -import com.squareup.javapoet.ClassName -import com.squareup.javapoet.CodeBlock -import com.squareup.javapoet.FieldSpec -import com.squareup.javapoet.JavaFile -import com.squareup.javapoet.MethodSpec -import com.squareup.javapoet.ParameterizedTypeName -import com.squareup.javapoet.TypeName -import com.squareup.javapoet.TypeSpec -import java.io.File -import javax.annotation.processing.AbstractProcessor -import javax.annotation.processing.Processor -import javax.annotation.processing.RoundEnvironment -import javax.lang.model.SourceVersion -import javax.lang.model.element.Element -import javax.lang.model.element.ElementKind.CLASS -import javax.lang.model.element.ElementKind.PACKAGE -import javax.lang.model.element.ExecutableElement -import javax.lang.model.element.Modifier.FINAL -import javax.lang.model.element.Modifier.PRIVATE -import javax.lang.model.element.Modifier.PUBLIC -import javax.lang.model.element.Modifier.STATIC -import javax.lang.model.element.TypeElement -import javax.lang.model.type.MirroredTypeException -import javax.tools.Diagnostic.Kind.ERROR - -@Suppress("unused") -@AutoService(Processor::class) -class ViewAdapterAnnotationProcessor : AbstractProcessor() { - - private val adapterElement by lazy { - processingEnv.typeUtils.erasure( - processingEnv.elementUtils.getTypeElement(ADAPTER_BASE_CLASS).asType() - ) - } - - private val viewElement by lazy { - processingEnv.typeUtils.erasure( - processingEnv.elementUtils.getTypeElement("android.view.View").asType() - ) - } - - private val indexClassBuilder = - TypeSpec.classBuilder(INDEX_CLASS_NAME).addModifiers(PUBLIC, FINAL) - private val indexAddStatements = CodeBlock.builder() - - private val listOfString: TypeName = - ParameterizedTypeName.get(java.util.ArrayList::class.java, String::class.java) - - init { - addIndexClassMembers() - } - - companion object { - const val ADAPTER_BASE_CLASS_PCK = "com.itsaky.androidide.inflater" - const val ADAPTER_BASE_CLASS_NAME = "IViewAdapter" - const val ADAPTER_BASE_CLASS = "$ADAPTER_BASE_CLASS_PCK.$ADAPTER_BASE_CLASS_NAME" - - const val INDEX_PACKAGE_NAME = "com.itsaky.androidide.inflater.internal" - const val INDEX_CLASS_NAME = "ViewAdapterIndex" - const val INDEX_MAP_FIELD = "adapterMap" - - const val METHOD_SET_SUPERCLASS_HIERARCHY = "setSuperclassHierarchy" - const val METHOD_SET_MODULE = "setModuleNamespace" - } - - override fun getSupportedAnnotationTypes(): MutableSet { - return mutableSetOf(ViewAdapter::class.java.name) - } - - override fun getSupportedSourceVersion(): SourceVersion { - return SourceVersion.latestSupported() - } - - override fun process( - annotations: MutableSet?, - roundEnv: RoundEnvironment - ): Boolean { - if (roundEnv.processingOver()) { - return true - } - roundEnv.getElementsAnnotatedWith(ViewAdapter::class.java).forEach { - if (it.kind != CLASS) { - processingEnv.messager.printMessage( - ERROR, - "@ViewAdapter can only be used with classes. Used on $it" - ) - return true - } - process(it) - } - - indexClassBuilder.addStaticBlock(indexAddStatements.build()) - val file = JavaFile.builder(INDEX_PACKAGE_NAME, indexClassBuilder.build()) - val generatedDir = - File( - processingEnv.options[KAPT_KOTLIN_GENERATED] - ?: throw IllegalStateException("Cannot find kapt output directory path") - ) - file.build().writeTo(generatedDir) - return false - } - - private fun process(element: Element) { - if (element.enclosingElement?.kind != PACKAGE || element !is TypeElement) { - processingEnv.messager.printMessage( - ERROR, - "@ViewAdapter can only be used on top-level classes. Used on $element" - ) - return - } - - val block = CodeBlock.builder() - - val constructors = - processingEnv.elementUtils.getAllMembers(element).filter { - "".contentEquals(it.simpleName) - } - if (constructors.size != 1) { - processingEnv.messager.printMessage( - ERROR, - "A view adapter must have only default constructor" - ) - return - } - - if ((constructors.first() as ExecutableElement).parameters.isNotEmpty()) { - processingEnv.messager.printMessage( - ERROR, - "View adapter constructor should not have any parameters" - ) - return - } - - if ( - !processingEnv.typeUtils.isAssignable( - processingEnv.typeUtils.erasure(element.asType()), - adapterElement - ) - ) { - processingEnv.messager.printMessage(ERROR, "Class must be a subtype of $ADAPTER_BASE_CLASS $element $adapterElement") - return - } - - if (element.typeParameters.size != 1) { - processingEnv.messager.printMessage( - ERROR, - "A view adapter must have exactly one type parameter" - ) - } - - val typeParam = element.typeParameters[0] - if (!processingEnv.typeUtils.isAssignable(typeParam.asType(), viewElement)) { - processingEnv.messager.printMessage( - ERROR, - "The type parameter of view adapter must be assignable from android.view.View" - ) - } - - val annotation = element.getAnnotation(ViewAdapter::class.java) ?: throw IllegalStateException() - val viewName = getViewName(annotation) - - val superclasses = getViewSuperclasses(annotation) - - block.addStatement("final var superclasses = new \$T()", listOfString) - for (superclass in superclasses) { - block.addStatement("superclasses.add(\$S)", superclass) - } - - val adapterType = ClassName.get(element) - val paramType = getViewTypeName(annotation) - - block.add("\n") - block.addStatement("final var adapter = new \$T()", ParameterizedTypeName.get(adapterType, paramType)) - block.addStatement("adapter.\$L(superclasses)", METHOD_SET_SUPERCLASS_HIERARCHY) - block.addStatement("adapter.\$L(\$S)", METHOD_SET_MODULE, annotation.moduleNamespace) - block.addStatement("\$L.put(\$S, adapter)", INDEX_MAP_FIELD, viewName) - - indexAddStatements.add("{\n") - indexAddStatements.indent() - indexAddStatements.add(block.build()) - indexAddStatements.unindent() - indexAddStatements.add("}\n") - } - - private fun addIndexClassMembers() { - val adpaterType = ClassName.get(ADAPTER_BASE_CLASS_PCK, ADAPTER_BASE_CLASS_NAME) - val type = - ParameterizedTypeName.get( - ClassName.get(Map::class.java), - ClassName.get(String::class.java), - adpaterType - ) - indexClassBuilder.addField( - FieldSpec.builder(type, INDEX_MAP_FIELD, PRIVATE, STATIC, FINAL) - .initializer("new \$T<>()", HashMap::class.java) - .build() - ) - - addUninstantiableConstructor(indexClassBuilder) - - indexClassBuilder.addMethod( - MethodSpec.methodBuilder("getAdapter") - .addAnnotation(ClassName.get("androidx.annotation", "Nullable")) - .addModifiers(PUBLIC, STATIC) - .addParameter(String::class.java, "view", FINAL) - .returns(adpaterType) - .addStatement("return \$L.get(\$L)", INDEX_MAP_FIELD, "view") - .build() - ) - } - - private fun addUninstantiableConstructor(builder: TypeSpec.Builder) { - builder.addMethod( - MethodSpec.constructorBuilder() - .addModifiers(PRIVATE) - .addStatement( - "throw new \$T(\$S)", - UnsupportedOperationException::class.java, - "This class cannot be instantiated." - ) - .build() - ) - } - - private fun getViewTypeName(annotation: ViewAdapter) : TypeName { - return try { - ClassName.get(annotation.forView.java) - ?: throw IllegalStateException("Cannot find type of $annotation") - } catch (err: MirroredTypeException) { - (processingEnv.typeUtils.asElement(err.typeMirror) as TypeElement).let { ClassName.get(it) } - } - } - - private fun getViewName(annotation: ViewAdapter): String { - return try { - annotation.forView.qualifiedName - ?: throw IllegalStateException("Cannot find type of $annotation") - } catch (err: MirroredTypeException) { - (processingEnv.typeUtils.asElement(err.typeMirror) as TypeElement).qualifiedName.toString() - } - } - - private fun getViewSuperclasses(annotation: ViewAdapter): List { - val result = mutableListOf() - try { - val view = annotation.forView - var superclass = view.java.superclass - while (superclass != null) { - result.add(superclass.name) - superclass = superclass.superclass - } - } catch (err: MirroredTypeException) { - val typeElement = processingEnv.typeUtils.asElement(err.typeMirror) as TypeElement - result.add(typeElement.qualifiedName.toString()) - - var superclass = typeElement.superclass - while (superclass != null) { - val superType = processingEnv.typeUtils.asElement(superclass) as? TypeElement? - superType ?: break - result.add(superType.qualifiedName.toString()) - superclass = superType.superclass - } - } - - return result - } -} diff --git a/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/ViewAdapterAnnotationProcessor.kt b/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/ViewAdapterAnnotationProcessor.kt new file mode 100644 index 000000000..62f7056a2 --- /dev/null +++ b/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/ViewAdapterAnnotationProcessor.kt @@ -0,0 +1,139 @@ +/* + * 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.annotations.processor + +import com.google.auto.service.AutoService +import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import javax.annotation.processing.AbstractProcessor +import javax.annotation.processing.Processor +import javax.annotation.processing.RoundEnvironment +import javax.lang.model.SourceVersion +import javax.lang.model.element.Element +import javax.lang.model.element.ElementKind.CLASS +import javax.lang.model.element.ElementKind.PACKAGE +import javax.lang.model.element.ExecutableElement +import javax.lang.model.element.TypeElement +import javax.tools.Diagnostic.Kind.ERROR + +@Suppress("unused") +@AutoService(Processor::class) +class ViewAdapterAnnotationProcessor : AbstractProcessor() { + + private val adapterElement by lazy { processingEnv.erasure(processingEnv.adapterElement()) } + private val viewElement by lazy { processingEnv.erasure(processingEnv.viewElement()) } + + private val generator = ViewAdapterIndexGenerator() + + override fun getSupportedAnnotationTypes(): MutableSet { + return mutableSetOf(ViewAdapter::class.java.name, IncludeInDesigner::class.java.name) + } + + override fun getSupportedSourceVersion(): SourceVersion { + return SourceVersion.latestSupported() + } + + override fun process( + annotations: MutableSet?, + roundEnv: RoundEnvironment + ): Boolean { + if (roundEnv.processingOver()) { + return true + } + + generator.init(processingEnv) + + roundEnv.getElementsAnnotatedWith(ViewAdapter::class.java).forEach { + if (it.kind != CLASS) { + processingEnv.messager.printMessage( + ERROR, + "@ViewAdapter can only be used with classes. Used on $it" + ) + return true + } + process(it) + } + + generator.generate( + processingEnv.options[KAPT_KOTLIN_GENERATED] + ?: throw IllegalStateException("Cannot find kapt output directory path") + ) + + return false + } + + private fun process(element: Element) { + if (element.enclosingElement?.kind != PACKAGE || element !is TypeElement) { + processingEnv.messager.printMessage( + ERROR, + "@ViewAdapter can only be used on top-level classes. Used on $element" + ) + return + } + + val constructors = + processingEnv.elementUtils.getAllMembers(element).filter { + "".contentEquals(it.simpleName) + } + if (constructors.size != 1) { + processingEnv.messager.printMessage( + ERROR, + "A view adapter must have only default constructor" + ) + return + } + + if ((constructors.first() as ExecutableElement).parameters.isNotEmpty()) { + processingEnv.messager.printMessage( + ERROR, + "View adapter constructor should not have any parameters" + ) + return + } + + if ( + !processingEnv.typeUtils.isAssignable( + processingEnv.typeUtils.erasure(element.asType()), + adapterElement + ) + ) { + processingEnv.messager.printMessage( + ERROR, + "Class must be a subtype of $ADAPTER_BASE_CLASS $element $adapterElement" + ) + return + } + + if (element.typeParameters.size != 1) { + processingEnv.messager.printMessage( + ERROR, + "A view adapter must have exactly one type parameter" + ) + } + + val typeParam = element.typeParameters[0] + if (!processingEnv.typeUtils.isAssignable(typeParam.asType(), viewElement)) { + processingEnv.messager.printMessage( + ERROR, + "The type parameter of view adapter must be assignable from android.view.View" + ) + } + + generator.addViewAdapter(processingEnv, element) + } +} diff --git a/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/ViewAdapterIndexGenerator.kt b/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/ViewAdapterIndexGenerator.kt new file mode 100644 index 000000000..89a6632f4 --- /dev/null +++ b/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/ViewAdapterIndexGenerator.kt @@ -0,0 +1,274 @@ +/* + * 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.annotations.processor + +import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.CodeBlock +import com.squareup.javapoet.FieldSpec +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterSpec +import com.squareup.javapoet.ParameterizedTypeName +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec +import com.squareup.javapoet.WildcardTypeName +import java.io.File +import java.util.Collections +import javax.annotation.processing.ProcessingEnvironment +import javax.lang.model.element.ExecutableElement +import javax.lang.model.element.Modifier.FINAL +import javax.lang.model.element.Modifier.PRIVATE +import javax.lang.model.element.Modifier.PUBLIC +import javax.lang.model.element.Modifier.STATIC +import javax.lang.model.element.TypeElement +import javax.lang.model.type.MirroredTypeException +import javax.tools.Diagnostic.Kind.ERROR + +/** + * Generates the ViewAdapterIndex class. + * + * @author Akash Yadav + */ +class ViewAdapterIndexGenerator { + + private val indexClassBuilder = + TypeSpec.classBuilder(INDEX_CLASS_NAME).addModifiers(PUBLIC, FINAL) + private val indexAddStatements = CodeBlock.builder() + + fun init(env: ProcessingEnvironment) { + val adapterType = + ParameterizedTypeName.get( + ClassName.get(ADAPTER_BASE_CLASS_PCK, ADAPTER_BASE_CLASS_NAME), + WildcardTypeName.subtypeOf(ClassName.get(env.viewElement())) + ) + val mapOfStringToViewAdapter = + ParameterizedTypeName.get( + ClassName.get(Map::class.java), + ClassName.get(String::class.java), + adapterType + ) + indexClassBuilder.addField( + FieldSpec.builder(mapOfStringToViewAdapter, INDEX_MAP_FIELD, PRIVATE, STATIC, FINAL).build() + ) + + indexAddStatements.addStatement( + "final var adapters = new \$T()", + ParameterizedTypeName.get( + ClassName.get(HashMap::class.java), + ClassName.get(String::class.java), + adapterType + ) + ) + + addUninstantiableConstructor(indexClassBuilder) + + indexClassBuilder.addMethod( + MethodSpec.methodBuilder("getAdapter") + .addAnnotation(ClassName.get("androidx.annotation", "Nullable")) + .addModifiers(PUBLIC, STATIC) + .addParameter(String::class.java, "view", FINAL) + .returns(adapterType) + .addStatement("return \$L.get(\$L)", INDEX_MAP_FIELD, "view") + .build() + ) + + val listOfViewAdapters = + ParameterizedTypeName.get(ClassName.get(java.util.List::class.java), adapterType) + val mapOfGroupToListOfViewAdapter = + ParameterizedTypeName.get( + ClassName.get(Map::class.java), + ClassName.get(IncludeInDesigner.Group::class.java), + listOfViewAdapters + ) + + indexClassBuilder.addField( + FieldSpec.builder( + mapOfGroupToListOfViewAdapter, + INDEX_PROVIDER_MAP_FIELD, + PRIVATE, + STATIC, + FINAL + ) + .build() + ) + + indexAddStatements.addStatement( + "final var providers = new \$T()", + ParameterizedTypeName.get( + ClassName.get(HashMap::class.java), + ClassName.get(IncludeInDesigner.Group::class.java), + listOfViewAdapters + ) + ) + + val nonNull = ClassName.get("androidx.annotation", "NonNull") + val groupParam = + ParameterSpec.builder(IncludeInDesigner.Group::class.java, "group", FINAL).build() + + indexClassBuilder.addMethod( + MethodSpec.methodBuilder("getWidgetProviders") + .addAnnotation(nonNull) + .addModifiers(PUBLIC, STATIC) + .returns(listOfViewAdapters) + .addParameter(groupParam) + .addStatement("return \$L.get(group)", INDEX_PROVIDER_MAP_FIELD) + .build() + ) + } + + private val listOfString: TypeName = + ParameterizedTypeName.get(java.util.ArrayList::class.java, String::class.java) + + fun addViewAdapter(processingEnv: ProcessingEnvironment, element: TypeElement) { + val block = CodeBlock.builder() + + val viewAdapter = + element.getAnnotation(ViewAdapter::class.java) ?: throw IllegalStateException() + val includeInDesigner = element.getAnnotation(IncludeInDesigner::class.java) + + val viewName = getViewName(processingEnv, viewAdapter) + val superclasses = getViewSuperclasses(processingEnv, viewAdapter) + + block.addStatement("final var superclasses = new \$T()", listOfString) + for (superclass in superclasses) { + block.addStatement("superclasses.add(\$S)", superclass) + } + + val adapterType = ClassName.get(element) + val paramType = getViewTypeName(processingEnv, viewAdapter) + + block.add("\n") + block.addStatement( + "final var adapter = new \$T()", + ParameterizedTypeName.get(adapterType, paramType) + ) + block.addStatement("adapter.\$L(superclasses)", METHOD_SET_SUPERCLASS_HIERARCHY) + block.addStatement("adapter.\$L(\$S)", METHOD_SET_MODULE, viewAdapter.moduleNamespace) + block.addStatement("adapters.put(\$S, adapter)", viewName) + + if (includeInDesigner != null) { + assertOverridesCreateWidget(processingEnv, element) + block.addStatement( + "providers.computeIfAbsent(\$T.\$L, g -> new ArrayList<>()).add(adapter)", + ClassName.get(IncludeInDesigner.Group::class.java), + includeInDesigner.group + ) + } + + indexAddStatements.add("{\n") + indexAddStatements.indent() + indexAddStatements.add(block.build()) + indexAddStatements.unindent() + indexAddStatements.add("}\n") + } + + private fun assertOverridesCreateWidget(env: ProcessingEnvironment, element: TypeElement) { + element.enclosedElements.filterIsInstance().find { + ADAPTER_FUNC_CREATE_WIDGET == it.simpleName.toString() + } + ?: env.messager.printMessage( + ERROR, + "${element.qualifiedName} annotated with ${IncludeInDesigner::class.java.simpleName} must override $ADAPTER_FUNC_CREATE_WIDGET" + ) + } + + fun generate(outDir: String) { + val javaCollections = ClassName.get(Collections::class.java) + + indexAddStatements.addStatement( + "\$L = \$T.unmodifiableMap(adapters)", + INDEX_MAP_FIELD, + javaCollections + ) + + indexAddStatements.addStatement( + "\$L = \$T.unmodifiableMap(providers)", + INDEX_PROVIDER_MAP_FIELD, + javaCollections + ) + + indexClassBuilder.addStaticBlock(indexAddStatements.build()) + val file = JavaFile.builder(INDEX_PACKAGE_NAME, indexClassBuilder.build()) + val generatedDir = File(outDir) + file.build().writeTo(generatedDir) + } + + private fun addUninstantiableConstructor(builder: TypeSpec.Builder) { + builder.addMethod( + MethodSpec.constructorBuilder() + .addModifiers(PRIVATE) + .addStatement( + "throw new \$T(\$S)", + UnsupportedOperationException::class.java, + "This class cannot be instantiated." + ) + .build() + ) + } + + private fun getViewTypeName( + processingEnv: ProcessingEnvironment, + annotation: ViewAdapter + ): TypeName { + return try { + ClassName.get(annotation.forView.java) + ?: throw IllegalStateException("Cannot find type of $annotation") + } catch (err: MirroredTypeException) { + (processingEnv.typeUtils.asElement(err.typeMirror) as TypeElement).let { ClassName.get(it) } + } + } + + private fun getViewName(processingEnv: ProcessingEnvironment, annotation: ViewAdapter): String { + return try { + annotation.forView.qualifiedName + ?: throw IllegalStateException("Cannot find type of $annotation") + } catch (err: MirroredTypeException) { + (processingEnv.typeUtils.asElement(err.typeMirror) as TypeElement).qualifiedName.toString() + } + } + + private fun getViewSuperclasses( + processingEnv: ProcessingEnvironment, + annotation: ViewAdapter + ): List { + val result = mutableListOf() + try { + val view = annotation.forView + var superclass = view.java.superclass + while (superclass != null) { + result.add(superclass.name) + superclass = superclass.superclass + } + } catch (err: MirroredTypeException) { + val typeElement = processingEnv.typeUtils.asElement(err.typeMirror) as TypeElement + result.add(typeElement.qualifiedName.toString()) + + var superclass = typeElement.superclass + while (superclass != null) { + val superType = processingEnv.typeUtils.asElement(superclass) as? TypeElement? + superType ?: break + result.add(superType.qualifiedName.toString()) + superclass = superType.superclass + } + } + + return result + } +} diff --git a/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/utils.kt b/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/utils.kt new file mode 100644 index 000000000..b373d9ddf --- /dev/null +++ b/annotation-processors/src/main/java/com/itsaky/androidide/annotations/processor/utils.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.annotations.processor + +import javax.annotation.processing.ProcessingEnvironment +import javax.lang.model.element.TypeElement +import javax.lang.model.type.TypeMirror + +const val KAPT_KOTLIN_GENERATED = "kapt.kotlin.generated" + +const val VIEW_CLASS = "android.view.View" + +const val ADAPTER_BASE_CLASS_PCK = "com.itsaky.androidide.inflater" +const val ADAPTER_BASE_CLASS_NAME = "IViewAdapter" +const val ADAPTER_BASE_CLASS = "$ADAPTER_BASE_CLASS_PCK.$ADAPTER_BASE_CLASS_NAME" +const val ADAPTER_FUNC_CREATE_WIDGET = "createUiWidgets" + +const val INDEX_PACKAGE_NAME = "com.itsaky.androidide.inflater.internal" +const val INDEX_CLASS_NAME = "ViewAdapterIndex" +const val INDEX_MAP_FIELD = "adapterMap" +const val INDEX_PROVIDER_MAP_FIELD = "widgetProviders" + +const val METHOD_SET_SUPERCLASS_HIERARCHY = "setSuperclassHierarchy" +const val METHOD_SET_MODULE = "setModuleNamespace" + +private var adapterElement: TypeElement? = null +private var viewElement: TypeElement? = null + +fun ProcessingEnvironment.adapterElement(): TypeElement { + return adapterElement + ?: elementUtils.getTypeElement(ADAPTER_BASE_CLASS)!!.also { adapterElement = it } +} + +fun ProcessingEnvironment.viewElement(): TypeElement { + return viewElement ?: elementUtils.getTypeElement(VIEW_CLASS)!!.also { viewElement = it } +} + +fun ProcessingEnvironment.erasure(typeElement: TypeElement): TypeMirror? { + return typeUtils.erasure(typeElement.asType()) +} diff --git a/annotation-processors/src/main/java/com/itsaky/androidide/annotations/inflater/contants.kt b/annotations/src/main/java/com/itsaky/androidide/annotations/uidesigner/IncludeInDesigner.kt similarity index 56% rename from annotation-processors/src/main/java/com/itsaky/androidide/annotations/inflater/contants.kt rename to annotations/src/main/java/com/itsaky/androidide/annotations/uidesigner/IncludeInDesigner.kt index 715d24ee2..e5299e3b2 100644 --- a/annotation-processors/src/main/java/com/itsaky/androidide/annotations/inflater/contants.kt +++ b/annotations/src/main/java/com/itsaky/androidide/annotations/uidesigner/IncludeInDesigner.kt @@ -15,10 +15,23 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.annotations.inflater +package com.itsaky.androidide.annotations.uidesigner /** + * Indicates that the view (whose view adapter is annotated with [ViewAdapter] + * [com.itsaky.androidide.annotations.inflater.ViewAdapter] and [IncludeInDesigner]) should be + * included in the UI Designer and made accessible to the user. + * * @author Akash Yadav */ +annotation class IncludeInDesigner(val group: Group) { -const val KAPT_KOTLIN_GENERATED = "kapt.kotlin.generated" \ No newline at end of file + /** The widget groups that are available in the UI Designer. */ + enum class Group { + /** Includes the Android platform widgets. */ + WIDGETS, + + /** Includes the Android platform layouts. */ + LAYOUTS + } +} diff --git a/editor/src/main/res/layout/layout_completion_item.xml b/editor/src/main/res/layout/layout_completion_item.xml index 824088194..b9abfe5fc 100755 --- a/editor/src/main/res/layout/layout_completion_item.xml +++ b/editor/src/main/res/layout/layout_completion_item.xml @@ -1,4 +1,21 @@ + + = mutableListOf(), var isExpanded: Boolean = false) \ No newline at end of file +data class UiWidgetCategory(@StringRes val label: Int, var widgets: List = emptyList(), var isExpanded: Boolean = false) \ No newline at end of file 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 dfdc4e6bc..9a4f8de49 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 @@ -17,36 +17,11 @@ package com.itsaky.androidide.uidesigner.utils -import android.content.Context -import android.view.SurfaceView -import android.view.TextureView -import android.view.ViewGroup -import android.webkit.WebView -import android.widget.AutoCompleteTextView -import android.widget.Button -import android.widget.CheckBox -import android.widget.CheckedTextView -import android.widget.EditText -import android.widget.FrameLayout -import android.widget.GridLayout -import android.widget.ImageButton -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.ListView -import android.widget.ProgressBar -import android.widget.RadioButton -import android.widget.RelativeLayout -import android.widget.SeekBar -import android.widget.Switch -import android.widget.ToggleButton -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.uidesigner.R.drawable +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.LAYOUTS +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.internal.ViewAdapterIndex +import com.itsaky.androidide.inflater.internal.utils.simpleName 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 internal object Widgets { @@ -69,72 +44,10 @@ internal object Widgets { if (widgets.isNotEmpty()) { return@apply } - widgets.apply { - add( - UiWidget( - AutoCompleteTextView::class.java, - string.widget_auto_complete_textview, - drawable.ic_widget_auto_complete_textview - ) - ) - add(UiWidget(Button::class.java, string.widget_button, drawable.ic_widget_button)) - add(UiWidget(CheckBox::class.java, string.widget_checkbox, drawable.ic_widget_checkbox)) - add( - UiWidget( - CheckedTextView::class.java, - string.widget_checked_textview, - drawable.ic_widget_checked_textview - ) - ) - add(UiWidget(EditText::class.java, string.widget_edittext, drawable.ic_widget_edittext)) - add( - UiWidget( - ImageButton::class.java, - string.widget_image_button, - drawable.ic_widget_imagebutton - ) - ) - add(UiWidget(ImageView::class.java, string.widget_image_view, drawable.ic_widget_imageview)) - add(UiWidget(ListView::class.java, string.widget_listview, drawable.ic_widget_list_view)) - add( - UiWidget( - ProgressBar::class.java, - string.widget_progressbar, - drawable.ic_widget_progress_bar - ) - ) - add( - UiWidget( - RadioButton::class.java, - string.widget_radio_button, - drawable.ic_widget_radio_button - ) - ) - add(UiWidget(SeekBar::class.java, string.widget_seekbar, drawable.ic_widget_seek_bar)) - add( - UiWidget( - SurfaceView::class.java, - string.widget_surfaceview, - drawable.ic_widget_surface_view - ) - ) - add(UiWidget(Switch::class.java, string.widget_switch, drawable.ic_widget_switch)) - add( - UiWidget( - TextureView::class.java, - string.widget_textureview, - drawable.ic_widget_textureview - ) - ) - add( - UiWidget( - ToggleButton::class.java, - string.widget_togglebutton, - drawable.ic_widget_toggle_button - ) - ) - add(UiWidget(WebView::class.java, string.widget_webview, drawable.ic_widget_webview)) - } + this.widgets = + ViewAdapterIndex.getWidgetProviders(WIDGETS) + .flatMap { it.getUiWidgets() } + .sortedBy { it.name.simpleName() } } } @@ -144,57 +57,10 @@ internal object Widgets { return@apply } - widgets.apply { - add( - UiWidget( - FrameLayout::class.java, - string.widget_frame_layout, - drawable.ic_widget_frame_layout - ) - ) - add( - UiWidget( - GridLayout::class.java, - string.widget_grid_layout, - drawable.ic_widget_grid_layout - ) - ) - add( - object : - UiWidget( - LinearLayout::class.java, - string.widget_linear_layout_horz, - drawable.ic_widget_linear_layout_horz - ) { - override fun createView(context: Context, parent: ViewGroup, layoutFile: LayoutFile): IView { - return super.createView(context, parent, layoutFile).apply { - addAttribute(UiAttribute(name = "orientation", value = "horizontal")) - } - } - } - ) - add( - object : - UiWidget( - LinearLayout::class.java, - string.widget_linear_layout_vert, - drawable.ic_widget_linear_layout_vert - ) { - override fun createView(context: Context, parent: ViewGroup, layoutFile: LayoutFile): IView { - return super.createView(context, parent, layoutFile).apply { - addAttribute(UiAttribute(name = "orientation", value = "vertical")) - } - } - } - ) - add( - UiWidget( - RelativeLayout::class.java, - string.widget_relative_layout, - drawable.ic_widget_relative_layout - ) - ) - } + this.widgets = + ViewAdapterIndex.getWidgetProviders(LAYOUTS) + .flatMap { it.getUiWidgets() } + .sortedBy { it.name.simpleName() } } } } 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 d62b66494..4b6aedf28 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 @@ -21,6 +21,7 @@ import android.view.View import com.android.SdkConstants import com.itsaky.androidide.inflater.internal.AttributeImpl import com.itsaky.androidide.inflater.internal.ViewImpl +import com.itsaky.androidide.inflater.models.UiWidget /** * Handles logic for applying attributes to a view. @@ -67,6 +68,23 @@ abstract class IViewAdapter : AbstractParser() { return@lazy handlers } + private val widget by lazy { createUiWidgets() } + + /** + * Get the [UiWidget] model that can be used to list this adapter's view in the UI designer. + * + * @throws UnsupportedOperationException If this view adapter does not adapt a UI designer widget. + */ + fun getUiWidgets(): List { + return widget + } + + protected open fun createUiWidgets(): List { + throw UnsupportedOperationException( + "${javaClass.simpleName} is not a UI Designer widget adapter" + ) + } + /** * Apply the given attribute to the given view. * diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsListViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsListViewAdapter.kt index 563c63290..eb1d7e7d4 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsListViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsListViewAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.widget.AbsListView import com.itsaky.androidide.inflater.AttributeHandlerScope diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSeekBarAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSeekBarAdapter.kt index 8d763dbe8..2fa337b8e 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSeekBarAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSeekBarAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.widget.AbsSeekBar import com.itsaky.androidide.inflater.AttributeHandlerScope diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSpinnerAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSpinnerAdapter.kt index 9da0e8d60..13fddf69f 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSpinnerAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsSpinnerAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.R.layout import android.widget.AbsSpinner import com.itsaky.androidide.inflater.AttributeHandlerScope diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsoluteLayoutAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsoluteLayoutAdapter.kt index cf30e4ebb..106b9bda6 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsoluteLayoutAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AbsoluteLayoutAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.widget.AbsoluteLayout import com.itsaky.androidide.annotations.inflater.ViewAdapter diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AdapterViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AdapterViewAdapter.kt index 863c339be..776d7f967 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AdapterViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AdapterViewAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.R.layout import android.content.Context import android.widget.AdapterView diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AutoCompleteTextViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AutoCompleteTextViewAdapter.kt index 475f2680d..a8a8dd7a7 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AutoCompleteTextViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/AutoCompleteTextViewAdapter.kt @@ -17,9 +17,14 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.widget.AutoCompleteTextView import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget /** * Attribute adapter for [AutoCompleteTextView]. @@ -27,6 +32,7 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(AutoCompleteTextView::class) +@IncludeInDesigner(group = WIDGETS) open class AutoCompleteTextViewAdapter : EditTextAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { @@ -37,4 +43,12 @@ open class AutoCompleteTextViewAdapter : EditTextAdapt create("dropDownWidth") { view.dropDownWidth = parseDimension(context, value) } create("dropDownHeight") { view.dropDownHeight = parseDimension(context, value) } } + + override fun createUiWidgets(): List { + return listOf(UiWidget( + AutoCompleteTextView::class.java, + string.widget_auto_complete_textview, + drawable.ic_widget_auto_complete_textview + )) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ButtonAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ButtonAdapter.kt index 7a317d866..26d3e40b1 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ButtonAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ButtonAdapter.kt @@ -19,10 +19,21 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.Button import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [Button]. * * @author Akash Yadav */ -@ViewAdapter(Button::class) open class ButtonAdapter : TextViewAdapter() +@ViewAdapter(Button::class) +@IncludeInDesigner(group = WIDGETS) +open class ButtonAdapter : TextViewAdapter() { + override fun createUiWidgets(): List { + return listOf(UiWidget(Button::class.java, string.widget_button, drawable.ic_widget_button)) + } +} diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckBoxAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckBoxAdapter.kt index 3b0a0be92..f5c58275a 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckBoxAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckBoxAdapter.kt @@ -19,10 +19,23 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.CheckBox import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [CheckBox]. * * @author Akash Yadav */ -@ViewAdapter(CheckBox::class) open class CheckBoxAdapter : CompoundButtonAdapter() \ No newline at end of file +@ViewAdapter(CheckBox::class) +@IncludeInDesigner(group = WIDGETS) +open class CheckBoxAdapter : CompoundButtonAdapter() { + override fun createUiWidgets(): List { + return listOf( + UiWidget(CheckBox::class.java, string.widget_checkbox, drawable.ic_widget_checkbox) + ) + } +} diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckedTextViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckedTextViewAdapter.kt index b24e79643..02faabbd2 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckedTextViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CheckedTextViewAdapter.kt @@ -19,7 +19,12 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.CheckedTextView import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [CheckedTextView]. @@ -27,6 +32,7 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(CheckedTextView::class) +@IncludeInDesigner(group = WIDGETS) open class CheckedTextViewAdapter : TextViewAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { @@ -34,4 +40,14 @@ open class CheckedTextViewAdapter : TextViewAdapter() { create("checkMarkTintMode") { view.checkMarkTintMode = parsePorterDuffMode(value) } create("checkMarkTint") { view.checkMarkTintList = parseColorStateList(context, value) } } + + override fun createUiWidgets(): List { + return listOf( + UiWidget( + CheckedTextView::class.java, + string.widget_checked_textview, + drawable.ic_widget_checked_textview + ) + ) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CompoundButtonAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CompoundButtonAdapter.kt index eca985a7f..545988b0e 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CompoundButtonAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/CompoundButtonAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.widget.CompoundButton import com.itsaky.androidide.inflater.AttributeHandlerScope import com.itsaky.androidide.inflater.IView diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/EditTextAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/EditTextAdapter.kt index 1e9c6d5d5..ff5b4c47f 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/EditTextAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/EditTextAdapter.kt @@ -19,6 +19,11 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.EditText import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [EditText]. @@ -26,4 +31,11 @@ import com.itsaky.androidide.annotations.inflater.ViewAdapter * @author Akash Yadav */ @ViewAdapter(EditText::class) -open class EditTextAdapter : TextViewAdapter() +@IncludeInDesigner(group = WIDGETS) +open class EditTextAdapter : TextViewAdapter() { + override fun createUiWidgets(): List { + return listOf( + UiWidget(EditText::class.java, string.widget_edittext, drawable.ic_widget_edittext) + ) + } +} diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/FrameLayoutAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/FrameLayoutAdapter.kt index 8c132f355..b5665c23d 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/FrameLayoutAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/FrameLayoutAdapter.kt @@ -19,7 +19,12 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.FrameLayout import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.LAYOUTS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [FrameLayout]. @@ -27,11 +32,18 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(FrameLayout::class) +@IncludeInDesigner(group = LAYOUTS) open class FrameLayoutAdapter : ViewGroupAdapter() { - + override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { super.createAttrHandlers(create) create("foregroundGravity") { view.foregroundGravity = parseGravity(value) } create("measureAllChildren") { view.measureAllChildren = parseBoolean(value) } } + + override fun createUiWidgets(): List { + return listOf( + UiWidget(FrameLayout::class.java, string.widget_frame_layout, drawable.ic_widget_frame_layout) + ) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GestureOverlayViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GestureOverlayViewAdapter.kt index 5e677b4bc..029799463 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GestureOverlayViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GestureOverlayViewAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.gesture.GestureOverlayView import com.blankj.utilcode.util.ReflectUtils import com.itsaky.androidide.annotations.inflater.ViewAdapter diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GridLayoutAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GridLayoutAdapter.kt index 5c911c3b6..e41e3486d 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GridLayoutAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GridLayoutAdapter.kt @@ -19,7 +19,12 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.GridLayout import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.LAYOUTS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [GridLayout]. @@ -27,6 +32,7 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(GridLayout::class) +@IncludeInDesigner(group = LAYOUTS) open class GridLayoutAdapter : ViewGroupAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { super.createAttrHandlers(create) @@ -39,6 +45,12 @@ open class GridLayoutAdapter : ViewGroupAdapter() { create("useDefaultMargins") { view.useDefaultMargins = parseBoolean(value) } } + override fun createUiWidgets(): List { + return listOf( + UiWidget(GridLayout::class.java, string.widget_grid_layout, drawable.ic_widget_grid_layout) + ) + } + protected open fun parseOrientation(value: String): Int { return if ("vertical" == value) { GridLayout.VERTICAL diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageButtonAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageButtonAdapter.kt index 57f2447ac..e7670383a 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageButtonAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageButtonAdapter.kt @@ -19,6 +19,11 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.ImageButton import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [ImageButton]. @@ -26,4 +31,11 @@ import com.itsaky.androidide.annotations.inflater.ViewAdapter * @author Akash Yadav */ @ViewAdapter(ImageButton::class) -open class ImageButtonAdapter : ImageViewAdapter() +@IncludeInDesigner(group = WIDGETS) +open class ImageButtonAdapter : ImageViewAdapter() { + override fun createUiWidgets(): List { + return listOf( + UiWidget(ImageButton::class.java, string.widget_image_button, drawable.ic_widget_imagebutton) + ) + } +} diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageViewAdapter.kt index 246ea1717..4a042a240 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ImageViewAdapter.kt @@ -27,9 +27,14 @@ import android.widget.ImageView.ScaleType.FIT_END import android.widget.ImageView.ScaleType.FIT_START import android.widget.ImageView.ScaleType.FIT_XY import android.widget.ImageView.ScaleType.MATRIX +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS import com.itsaky.androidide.inflater.AttributeHandlerScope import com.itsaky.androidide.inflater.IView import com.itsaky.androidide.inflater.R +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adpater [ImageView]s. @@ -37,6 +42,7 @@ import com.itsaky.androidide.inflater.R * @author Akash Yadav */ @com.itsaky.androidide.annotations.inflater.ViewAdapter(ImageView::class) +@IncludeInDesigner(group = WIDGETS) open class ImageViewAdapter : ViewAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { @@ -53,6 +59,12 @@ open class ImageViewAdapter : ViewAdapter() { create("tintMode") { view.imageTintMode = parsePorterDuffMode(value) } } + override fun createUiWidgets(): List { + return listOf( + UiWidget(ImageView::class.java, string.widget_image_view, drawable.ic_widget_imageview) + ) + } + override fun applyBasic(view: IView) { super.applyBasic(view) (view.view as ImageView).setImageResource(R.drawable.ic_android) diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/LinearLayoutAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/LinearLayoutAdapter.kt index 22a535f3c..5c93e13fa 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/LinearLayoutAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/LinearLayoutAdapter.kt @@ -17,14 +17,26 @@ package com.itsaky.androidide.inflater.internal.adapters +import android.content.Context +import android.view.ViewGroup import android.widget.LinearLayout +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.LAYOUTS import com.itsaky.androidide.inflater.AttributeHandlerScope import com.itsaky.androidide.inflater.INamespace +import com.itsaky.androidide.inflater.IView import com.itsaky.androidide.inflater.IViewGroup import com.itsaky.androidide.inflater.LayoutBehavior import com.itsaky.androidide.inflater.LayoutBehavior.HORIZONTAL import com.itsaky.androidide.inflater.LayoutBehavior.VERTICAL +import com.itsaky.androidide.inflater.internal.LayoutFile +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.inflater.utils.newAttribute +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [LinearLayout]. @@ -32,6 +44,7 @@ import com.itsaky.androidide.inflater.LayoutBehavior.VERTICAL * @author Akash Yadav */ @ViewAdapter(LinearLayout::class) +@IncludeInDesigner(group = LAYOUTS) open class LinearLayoutAdapter : ViewGroupAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { @@ -49,6 +62,22 @@ open class LinearLayoutAdapter : ViewGroupAdapter() { create("weightSum") { view.weightSum = parseFloat(value) } } + override fun createUiWidgets(): List { + val horizontal = + LinearLayoutWidget( + title = string.widget_linear_layout_horz, + icon = drawable.ic_widget_linear_layout_horz, + isVertical = false + ) + val vertical = + LinearLayoutWidget( + title = string.widget_linear_layout_vert, + icon = drawable.ic_widget_linear_layout_vert, + isVertical = true + ) + return listOf(horizontal, vertical) + } + override fun getLayoutBehavior(group: IViewGroup): LayoutBehavior { val orientation = group.findAttribute(INamespace.ANDROID.uri, "orientation") return if (orientation?.value == "vertical") VERTICAL else HORIZONTAL @@ -61,4 +90,24 @@ open class LinearLayoutAdapter : ViewGroupAdapter() { else -> LinearLayout.HORIZONTAL } } + + private class LinearLayoutWidget( + @StringRes title: Int, + @DrawableRes icon: Int, + private val isVertical: Boolean + ) : UiWidget(LinearLayout::class.java, title, icon) { + + companion object { + private const val HORIZONTAL = "horizontal" + private const val VERTICAL = "vertical" + } + + override fun createView(context: Context, parent: ViewGroup, layoutFile: LayoutFile): IView { + return super.createView(context, parent, layoutFile).apply { + addAttribute( + newAttribute(this, name = "orientation", value = if (isVertical) VERTICAL else HORIZONTAL) + ) + } + } + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ListViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ListViewAdapter.kt index 2baca964a..7885c5219 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ListViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ListViewAdapter.kt @@ -22,7 +22,12 @@ import android.widget.ArrayAdapter import android.widget.ListView import com.blankj.utilcode.util.SizeUtils import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [ListView]. @@ -30,6 +35,7 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(ListView::class) +@IncludeInDesigner(group = WIDGETS) open class ListViewAdapter : AbsListViewAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { @@ -45,4 +51,10 @@ open class ListViewAdapter : AbsListViewAdapter() { create("footerDividersEnabled") { view.setFooterDividersEnabled(parseBoolean(value)) } create("headerDividersEnabled") { view.setHeaderDividersEnabled(parseBoolean(value)) } } + + override fun createUiWidgets(): List { + return listOf( + UiWidget(ListView::class.java, string.widget_listview, drawable.ic_widget_list_view) + ) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ProgressBarAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ProgressBarAdapter.kt index 86253aa15..96451b766 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ProgressBarAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ProgressBarAdapter.kt @@ -19,6 +19,9 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.ProgressBar import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [ProgressBar]. @@ -74,4 +77,10 @@ open class ProgressBarAdapter : ViewAdapter() { view.secondaryProgressTintMode = parsePorterDuffMode(value) } } + + override fun createUiWidgets(): List { + return listOf( + UiWidget(ProgressBar::class.java, string.widget_progressbar, drawable.ic_widget_progress_bar) + ) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RadioButtonAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RadioButtonAdapter.kt index e60c244c8..522bf7b60 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RadioButtonAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RadioButtonAdapter.kt @@ -19,6 +19,11 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.RadioButton import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [RadioButton]. @@ -26,4 +31,12 @@ import com.itsaky.androidide.annotations.inflater.ViewAdapter * @author Akash Yadav */ @ViewAdapter(RadioButton::class) -open class RadioButtonAdapter : CompoundButtonAdapter() +@IncludeInDesigner(group = WIDGETS) +open class RadioButtonAdapter : CompoundButtonAdapter() { + + override fun createUiWidgets(): List { + return listOf( + UiWidget(RadioButton::class.java, string.widget_radio_button, drawable.ic_widget_radio_button) + ) + } +} diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RelativeLayoutAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RelativeLayoutAdapter.kt index 7c9a28dd6..09102d71c 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RelativeLayoutAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/RelativeLayoutAdapter.kt @@ -19,7 +19,12 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.RelativeLayout import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.LAYOUTS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [RelativeLayout]. @@ -27,6 +32,7 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(RelativeLayout::class) +@IncludeInDesigner(group = LAYOUTS) open class RelativeLayoutAdapter : ViewGroupAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { @@ -34,4 +40,14 @@ open class RelativeLayoutAdapter : ViewGroupAdapter() { create("gravity") { view.gravity = parseGravity(value) } create("ignoreGravity") { view.ignoreGravity = parseId(file.resName, value) } } + + override fun createUiWidgets(): List { + return listOf( + UiWidget( + RelativeLayout::class.java, + string.widget_relative_layout, + drawable.ic_widget_relative_layout + ) + ) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SeekBarAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SeekBarAdapter.kt index 04335fee9..1c867ca9a 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SeekBarAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SeekBarAdapter.kt @@ -19,7 +19,12 @@ package com.itsaky.androidide.inflater.internal.adapters import android.widget.SeekBar import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [SeekBar]. @@ -27,6 +32,7 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(SeekBar::class) +@IncludeInDesigner(group = WIDGETS) open class SeekBarAdapter : AbsSeekBarAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { @@ -34,4 +40,8 @@ open class SeekBarAdapter : AbsSeekBarAdapter() { create("thumb") { view.thumb = parseDrawable(context, value) } } + + override fun createUiWidgets(): List { + return listOf(UiWidget(SeekBar::class.java, string.widget_seekbar, drawable.ic_widget_seek_bar)) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SpinnerAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SpinnerAdapter.kt index 969dabfaf..cb7c13d37 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SpinnerAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SpinnerAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.widget.Spinner import com.itsaky.androidide.annotations.inflater.ViewAdapter import com.itsaky.androidide.inflater.AttributeHandlerScope diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SurfaceViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SurfaceViewAdapter.kt index b1274946b..f7d21b3bd 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SurfaceViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SurfaceViewAdapter.kt @@ -18,10 +18,22 @@ package com.itsaky.androidide.inflater.internal.adapters import android.view.SurfaceView +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [SurfaceView]. * @author Akash Yadav */ @com.itsaky.androidide.annotations.inflater.ViewAdapter(SurfaceView::class) -open class SurfaceViewAdapter : ViewAdapter() +@IncludeInDesigner(group = WIDGETS) +open class SurfaceViewAdapter : ViewAdapter() { + override fun createUiWidgets(): List { + return listOf( + UiWidget(SurfaceView::class.java, string.widget_surfaceview, drawable.ic_widget_surface_view) + ) + } +} diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SwitchAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SwitchAdapter.kt index 5853db029..2b3c85269 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SwitchAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/SwitchAdapter.kt @@ -20,7 +20,12 @@ package com.itsaky.androidide.inflater.internal.adapters import android.graphics.Typeface.DEFAULT import android.widget.Switch import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [Switch]. @@ -28,6 +33,7 @@ import com.itsaky.androidide.inflater.AttributeHandlerScope * @author Akash Yadav */ @ViewAdapter(Switch::class) +@IncludeInDesigner(group = WIDGETS) open class SwitchAdapter : CompoundButtonAdapter() { override fun createAttrHandlers(create: (String, AttributeHandlerScope.() -> Unit) -> Unit) { super.createAttrHandlers(create) @@ -47,4 +53,8 @@ open class SwitchAdapter : CompoundButtonAdapter() { create("trackTintMode") { view.trackTintMode = parsePorterDuffMode(value) } create("typeface") { view.setSwitchTypeface(parseTypeface(value)) } } + + override fun createUiWidgets(): List { + return listOf(UiWidget(Switch::class.java, string.widget_switch, drawable.ic_widget_switch)) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAdapter.kt index 30db68b4c..43b7eeab6 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.graphics.Typeface import android.text.TextUtils import android.text.util.Linkify diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextureViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextureViewAdapter.kt index fe44c95ae..366ea0164 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextureViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextureViewAdapter.kt @@ -18,6 +18,11 @@ package com.itsaky.androidide.inflater.internal.adapters import android.view.TextureView +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [TextureView]. @@ -25,4 +30,11 @@ import android.view.TextureView * @author Akash Yadav */ @com.itsaky.androidide.annotations.inflater.ViewAdapter(TextureView::class) -open class TextureViewAdapter : ViewAdapter() \ No newline at end of file +@IncludeInDesigner(group = WIDGETS) +open class TextureViewAdapter : ViewAdapter() { + override fun createUiWidgets(): List { + return listOf( + UiWidget(TextureView::class.java, string.widget_textureview, drawable.ic_widget_textureview) + ) + } +} diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ToggleButtonAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ToggleButtonAdapter.kt index e112ec00d..7bd60affb 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ToggleButtonAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ToggleButtonAdapter.kt @@ -21,6 +21,9 @@ import android.widget.ToggleButton import com.blankj.utilcode.util.ReflectUtils.reflect import com.itsaky.androidide.annotations.inflater.ViewAdapter import com.itsaky.androidide.inflater.AttributeHandlerScope +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [ToggleButton]. @@ -36,4 +39,14 @@ open class ToggleButtonAdapter : CompoundButtonAdapter() { create("textOff") { view.textOff = parseString(value) } create("textOn") { view.textOn = parseString(value) } } + + override fun createUiWidgets(): List { + return listOf( + UiWidget( + ToggleButton::class.java, + string.widget_togglebutton, + drawable.ic_widget_toggle_button + ) + ) + } } diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAdapter.kt index 7fdc6dcab..79c4231f2 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.content.Context import android.graphics.PorterDuff import android.os.Build diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewGroupAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewGroupAdapter.kt index 3317e5939..999bf10bc 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewGroupAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewGroupAdapter.kt @@ -17,6 +17,8 @@ package com.itsaky.androidide.inflater.internal.adapters +import com.itsaky.androidide.resources.R.string +import com.itsaky.androidide.resources.R.drawable import android.animation.LayoutTransition import android.view.ViewGroup import com.itsaky.androidide.inflater.AttributeHandlerScope diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/WebViewAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/WebViewAdapter.kt index 9fa4d326a..a3ad62a21 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/WebViewAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/WebViewAdapter.kt @@ -19,6 +19,11 @@ package com.itsaky.androidide.inflater.internal.adapters import android.webkit.WebView import com.itsaky.androidide.annotations.inflater.ViewAdapter +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner +import com.itsaky.androidide.annotations.uidesigner.IncludeInDesigner.Group.WIDGETS +import com.itsaky.androidide.inflater.models.UiWidget +import com.itsaky.androidide.resources.R.drawable +import com.itsaky.androidide.resources.R.string /** * Attribute adapter for [WebView]. @@ -26,4 +31,9 @@ import com.itsaky.androidide.annotations.inflater.ViewAdapter * @author Akash Yadav */ @ViewAdapter(WebView::class) -open class WebViewAdapter : AbsoluteLayoutAdapter() \ No newline at end of file +@IncludeInDesigner(group = WIDGETS) +open class WebViewAdapter : AbsoluteLayoutAdapter() { + override fun createUiWidgets(): List { + return listOf(UiWidget(WebView::class.java, string.widget_webview, drawable.ic_widget_webview)) + } +} diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/models/UiWidget.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/models/UiWidget.kt similarity index 78% rename from uidesigner/src/main/java/com/itsaky/androidide/uidesigner/models/UiWidget.kt rename to xml-inflater/src/main/java/com/itsaky/androidide/inflater/models/UiWidget.kt index 4dac7fd78..21b1bce18 100644 --- a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/models/UiWidget.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/models/UiWidget.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.uidesigner.models +package com.itsaky.androidide.inflater.models import android.content.Context import android.view.View @@ -25,10 +25,14 @@ import androidx.annotation.StringRes import androidx.core.view.updatePaddingRelative import com.blankj.utilcode.util.SizeUtils import com.itsaky.androidide.inflater.IView +import com.itsaky.androidide.inflater.IViewGroup import com.itsaky.androidide.inflater.internal.LayoutFile import com.itsaky.androidide.inflater.internal.ViewAdapterIndex.getAdapter +import com.itsaky.androidide.inflater.internal.ViewGroupImpl +import com.itsaky.androidide.inflater.internal.ViewImpl import com.itsaky.androidide.inflater.internal.utils.ViewFactory import com.itsaky.androidide.inflater.internal.utils.ViewFactory.generateLayoutParams +import com.itsaky.androidide.inflater.utils.lookupComponentFactory open class UiWidget(val name: String, @StringRes val label: Int, @DrawableRes val icon: Int) { @@ -46,13 +50,14 @@ open class UiWidget(val name: String, @StringRes val label: Int, @DrawableRes va */ open fun createView(context: Context, parent: ViewGroup, layoutFile: LayoutFile): IView { val v = ViewFactory.createViewInstance(name, context) - val view: IView = - if (v is ViewGroup) { - UiViewGroup(layoutFile, name, v).apply { - val dp8 = SizeUtils.dp2px(8f) - view.updatePaddingRelative(start = dp8, top = dp8, end = dp8, bottom = dp8) - } - } else UiView(layoutFile, name, v) + + val view: IView = createView(layoutFile, v) + + (view as? IViewGroup)?.apply { + val dp8 = SizeUtils.dp2px(8f) + this.view.updatePaddingRelative(start = dp8, top = dp8, end = dp8, bottom = dp8) + } + view.view.layoutParams = generateLayoutParams(parent) val adapter = getAdapter(view.name) @@ -61,6 +66,12 @@ open class UiWidget(val name: String, @StringRes val label: Int, @DrawableRes va return view } + private fun createView(layoutFile: LayoutFile, v: View) = + (lookupComponentFactory()?.createView(layoutFile, name, v) + ?: if (v is ViewGroup) { + ViewGroupImpl(layoutFile, name, v) + } else ViewImpl(layoutFile, name, v)) + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is UiWidget) return false