mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
feat(uidesigner,xml-inflater): automate process of adding widgets in UI Designer with annotations
The IViewAdapter implementations can use @IncludeInDesigner(group) annotation to indicate that the adapter supports views that can be added in the UI Designer. The adapters which are annotated with @IncludeInDesigner must override the 'List<UiWidget> createWidgets()' method and provide a list of UiWidgets that will be displayed in the UI Designer.
This commit is contained in:
-287
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String> {
|
||||
return mutableSetOf(ViewAdapter::class.java.name)
|
||||
}
|
||||
|
||||
override fun getSupportedSourceVersion(): SourceVersion {
|
||||
return SourceVersion.latestSupported()
|
||||
}
|
||||
|
||||
override fun process(
|
||||
annotations: MutableSet<out TypeElement>?,
|
||||
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 {
|
||||
"<init>".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<String> {
|
||||
val result = mutableListOf<String>()
|
||||
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
|
||||
}
|
||||
}
|
||||
+139
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String> {
|
||||
return mutableSetOf(ViewAdapter::class.java.name, IncludeInDesigner::class.java.name)
|
||||
}
|
||||
|
||||
override fun getSupportedSourceVersion(): SourceVersion {
|
||||
return SourceVersion.latestSupported()
|
||||
}
|
||||
|
||||
override fun process(
|
||||
annotations: MutableSet<out TypeElement>?,
|
||||
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 {
|
||||
"<init>".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)
|
||||
}
|
||||
}
|
||||
+274
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ExecutableElement>().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<String> {
|
||||
val result = mutableListOf<String>()
|
||||
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
|
||||
}
|
||||
}
|
||||
+55
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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())
|
||||
}
|
||||
+15
-2
@@ -15,10 +15,23 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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"
|
||||
/** 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
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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 <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
||||
Binary file not shown.
@@ -52,6 +52,7 @@ dependencies {
|
||||
implementation google.material
|
||||
|
||||
implementation project(path: ':actions')
|
||||
implementation project(path: ':annotations')
|
||||
implementation project(path: ':common')
|
||||
implementation project(path: ':editor')
|
||||
implementation project(path: ':logger')
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.itsaky.androidide.uidesigner.adapters.WidgetsItemAdapter.VH
|
||||
import com.itsaky.androidide.uidesigner.databinding.LayoutUiWidgetsItemBinding
|
||||
import com.itsaky.androidide.uidesigner.fragments.DesignerWorkspaceFragment
|
||||
import com.itsaky.androidide.uidesigner.models.UiWidget
|
||||
import com.itsaky.androidide.inflater.models.UiWidget
|
||||
import com.itsaky.androidide.uidesigner.drag.WidgetDragShadowBuilder
|
||||
import com.itsaky.androidide.uidesigner.viewmodel.WorkspaceViewModel
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import com.itsaky.androidide.inflater.viewGroup
|
||||
import com.itsaky.androidide.uidesigner.fragments.DesignerWorkspaceFragment.Companion.DRAGGING_WIDGET_MIME
|
||||
import com.itsaky.androidide.uidesigner.models.UiView
|
||||
import com.itsaky.androidide.uidesigner.models.UiViewGroup
|
||||
import com.itsaky.androidide.uidesigner.models.UiWidget
|
||||
import com.itsaky.androidide.inflater.models.UiWidget
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
+2
-1
@@ -18,5 +18,6 @@
|
||||
package com.itsaky.androidide.uidesigner.models
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.itsaky.androidide.inflater.models.UiWidget
|
||||
|
||||
data class UiWidgetCategory(@StringRes val label: Int, val widgets: MutableList<UiWidget> = mutableListOf(), var isExpanded: Boolean = false)
|
||||
data class UiWidgetCategory(@StringRes val label: Int, var widgets: List<UiWidget> = emptyList(), var isExpanded: Boolean = false)
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T : View> : 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<UiWidget> {
|
||||
return widget
|
||||
}
|
||||
|
||||
protected open fun createUiWidgets(): List<UiWidget> {
|
||||
throw UnsupportedOperationException(
|
||||
"${javaClass.simpleName} is not a UI Designer widget adapter"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given attribute to the given view.
|
||||
*
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+14
@@ -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<T : AutoCompleteTextView> : EditTextAdapter<T>() {
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
@@ -37,4 +43,12 @@ open class AutoCompleteTextViewAdapter<T : AutoCompleteTextView> : EditTextAdapt
|
||||
create("dropDownWidth") { view.dropDownWidth = parseDimension(context, value) }
|
||||
create("dropDownHeight") { view.dropDownHeight = parseDimension(context, value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(UiWidget(
|
||||
AutoCompleteTextView::class.java,
|
||||
string.widget_auto_complete_textview,
|
||||
drawable.ic_widget_auto_complete_textview
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -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<T : Button> : TextViewAdapter<T>()
|
||||
@ViewAdapter(Button::class)
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class ButtonAdapter<T : Button> : TextViewAdapter<T>() {
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(UiWidget(Button::class.java, string.widget_button, drawable.ic_widget_button))
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -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<T : CheckBox> : CompoundButtonAdapter<T>()
|
||||
@ViewAdapter(CheckBox::class)
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class CheckBoxAdapter<T : CheckBox> : CompoundButtonAdapter<T>() {
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(CheckBox::class.java, string.widget_checkbox, drawable.ic_widget_checkbox)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -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<T : CheckedTextView> : TextViewAdapter<T>() {
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
@@ -34,4 +40,14 @@ open class CheckedTextViewAdapter<T : CheckedTextView> : TextViewAdapter<T>() {
|
||||
create("checkMarkTintMode") { view.checkMarkTintMode = parsePorterDuffMode(value) }
|
||||
create("checkMarkTint") { view.checkMarkTintList = parseColorStateList(context, value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(
|
||||
CheckedTextView::class.java,
|
||||
string.widget_checked_textview,
|
||||
drawable.ic_widget_checked_textview
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+13
-1
@@ -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<T : EditText> : TextViewAdapter<T>()
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class EditTextAdapter<T : EditText> : TextViewAdapter<T>() {
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(EditText::class.java, string.widget_edittext, drawable.ic_widget_edittext)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -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<T : FrameLayout> : ViewGroupAdapter<T>() {
|
||||
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
super.createAttrHandlers(create)
|
||||
create("foregroundGravity") { view.foregroundGravity = parseGravity(value) }
|
||||
create("measureAllChildren") { view.measureAllChildren = parseBoolean(value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(FrameLayout::class.java, string.widget_frame_layout, drawable.ic_widget_frame_layout)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+12
@@ -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<T : GridLayout> : ViewGroupAdapter<T>() {
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
super.createAttrHandlers(create)
|
||||
@@ -39,6 +45,12 @@ open class GridLayoutAdapter<T : GridLayout> : ViewGroupAdapter<T>() {
|
||||
create("useDefaultMargins") { view.useDefaultMargins = parseBoolean(value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
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
|
||||
|
||||
+13
-1
@@ -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<T : ImageButton> : ImageViewAdapter<T>()
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class ImageButtonAdapter<T : ImageButton> : ImageViewAdapter<T>() {
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(ImageButton::class.java, string.widget_image_button, drawable.ic_widget_imagebutton)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -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<T : ImageView> : ViewAdapter<T>() {
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
@@ -53,6 +59,12 @@ open class ImageViewAdapter<T : ImageView> : ViewAdapter<T>() {
|
||||
create("tintMode") { view.imageTintMode = parsePorterDuffMode(value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
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)
|
||||
|
||||
+49
@@ -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<T : LinearLayout> : ViewGroupAdapter<T>() {
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
@@ -49,6 +62,22 @@ open class LinearLayoutAdapter<T : LinearLayout> : ViewGroupAdapter<T>() {
|
||||
create("weightSum") { view.weightSum = parseFloat(value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
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<T : LinearLayout> : ViewGroupAdapter<T>() {
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -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<T : ListView> : AbsListViewAdapter<T>() {
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
@@ -45,4 +51,10 @@ open class ListViewAdapter<T : ListView> : AbsListViewAdapter<T>() {
|
||||
create("footerDividersEnabled") { view.setFooterDividersEnabled(parseBoolean(value)) }
|
||||
create("headerDividersEnabled") { view.setHeaderDividersEnabled(parseBoolean(value)) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(ListView::class.java, string.widget_listview, drawable.ic_widget_list_view)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -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<T : ProgressBar> : ViewAdapter<T>() {
|
||||
view.secondaryProgressTintMode = parsePorterDuffMode(value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(ProgressBar::class.java, string.widget_progressbar, drawable.ic_widget_progress_bar)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -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<T : RadioButton> : CompoundButtonAdapter<T>()
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class RadioButtonAdapter<T : RadioButton> : CompoundButtonAdapter<T>() {
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(RadioButton::class.java, string.widget_radio_button, drawable.ic_widget_radio_button)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -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<T : RelativeLayout> : ViewGroupAdapter<T>() {
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
@@ -34,4 +40,14 @@ open class RelativeLayoutAdapter<T : RelativeLayout> : ViewGroupAdapter<T>() {
|
||||
create("gravity") { view.gravity = parseGravity(value) }
|
||||
create("ignoreGravity") { view.ignoreGravity = parseId(file.resName, value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(
|
||||
RelativeLayout::class.java,
|
||||
string.widget_relative_layout,
|
||||
drawable.ic_widget_relative_layout
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -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<T : SeekBar> : AbsSeekBarAdapter<T>() {
|
||||
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
@@ -34,4 +40,8 @@ open class SeekBarAdapter<T : SeekBar> : AbsSeekBarAdapter<T>() {
|
||||
|
||||
create("thumb") { view.thumb = parseDrawable(context, value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(UiWidget(SeekBar::class.java, string.widget_seekbar, drawable.ic_widget_seek_bar))
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+13
-1
@@ -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<T : SurfaceView> : ViewAdapter<T>()
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class SurfaceViewAdapter<T : SurfaceView> : ViewAdapter<T>() {
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(SurfaceView::class.java, string.widget_surfaceview, drawable.ic_widget_surface_view)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -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<T : Switch> : CompoundButtonAdapter<T>() {
|
||||
override fun createAttrHandlers(create: (String, AttributeHandlerScope<T>.() -> Unit) -> Unit) {
|
||||
super.createAttrHandlers(create)
|
||||
@@ -47,4 +53,8 @@ open class SwitchAdapter<T : Switch> : CompoundButtonAdapter<T>() {
|
||||
create("trackTintMode") { view.trackTintMode = parsePorterDuffMode(value) }
|
||||
create("typeface") { view.setSwitchTypeface(parseTypeface(value)) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(UiWidget(Switch::class.java, string.widget_switch, drawable.ic_widget_switch))
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+13
-1
@@ -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<T : TextureView> : ViewAdapter<T>()
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class TextureViewAdapter<T : TextureView> : ViewAdapter<T>() {
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(TextureView::class.java, string.widget_textureview, drawable.ic_widget_textureview)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -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<T : ToggleButton> : CompoundButtonAdapter<T>() {
|
||||
create("textOff") { view.textOff = parseString(value) }
|
||||
create("textOn") { view.textOn = parseString(value) }
|
||||
}
|
||||
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(
|
||||
UiWidget(
|
||||
ToggleButton::class.java,
|
||||
string.widget_togglebutton,
|
||||
drawable.ic_widget_toggle_button
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+11
-1
@@ -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<T : WebView> : AbsoluteLayoutAdapter<T>()
|
||||
@IncludeInDesigner(group = WIDGETS)
|
||||
open class WebViewAdapter<T : WebView> : AbsoluteLayoutAdapter<T>() {
|
||||
override fun createUiWidgets(): List<UiWidget> {
|
||||
return listOf(UiWidget(WebView::class.java, string.widget_webview, drawable.ic_widget_webview))
|
||||
}
|
||||
}
|
||||
|
||||
+19
-8
@@ -15,7 +15,7 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user