mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
feat(templates): autofill 'packageName' when 'projectName' parameter is changed
This commit is contained in:
@@ -68,14 +68,12 @@ fun baseProject(block: ProjectTemplateBuilder.() -> Unit
|
||||
val projectName = projectNameParameter()
|
||||
val language = projectLanguageParameter()
|
||||
val minSdk = minSdkParameter()
|
||||
val packageName = packageNameParameter()
|
||||
|
||||
val packageName = packageNameParameter {
|
||||
|
||||
suggest = {
|
||||
AndroidUtils.appNameToPackageName(/* appName = */
|
||||
projectName.value, /* packageName = */
|
||||
this.value ?: "")
|
||||
}
|
||||
// When project name is changed, change the package name accordingly
|
||||
projectName.observe { name ->
|
||||
val newPackage = AndroidUtils.appNameToPackageName(name.value!!, packageName.value!!)
|
||||
packageName.setValue(newPackage)
|
||||
}
|
||||
|
||||
val saveLocation = stringParameter {
|
||||
@@ -83,12 +81,6 @@ fun baseProject(block: ProjectTemplateBuilder.() -> Unit
|
||||
default = Environment.PROJECTS_DIR.absolutePath
|
||||
endIcon = R.drawable.ic_folder
|
||||
constraints = listOf(NONEMPTY, DIRECTORY, EXISTS)
|
||||
|
||||
suggest = {
|
||||
projectName.value!!.replace(" ", "").let {
|
||||
File(Environment.PROJECTS_DIR, it).absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
widgets(TextFieldWidget(projectName), TextFieldWidget(packageName),
|
||||
@@ -151,33 +143,17 @@ fun baseAndroidModule(isLibrary: Boolean = false,
|
||||
): ModuleTemplate {
|
||||
return AndroidModuleTemplateBuilder().apply {
|
||||
|
||||
val appName = if (isLibrary) null else projectNameParameter()
|
||||
val language = projectLanguageParameter()
|
||||
val minSdk = minSdkParameter()
|
||||
val packageName = packageNameParameter()
|
||||
|
||||
val moduleName = stringParameter {
|
||||
name = R.string.wizard_module_name
|
||||
default = ":app"
|
||||
constraints = listOf(NONEMPTY, MODULE_NAME)
|
||||
}
|
||||
|
||||
val appName = if (isLibrary) null else stringParameter {
|
||||
name = R.string.project_app_name
|
||||
default = "My Application"
|
||||
startIcon = R.drawable.ic_android
|
||||
constraints = listOf(NONEMPTY)
|
||||
}
|
||||
|
||||
val packageName = stringParameter {
|
||||
name = R.string.package_name
|
||||
default = "com.example.myapplication"
|
||||
startIcon = R.drawable.ic_package
|
||||
constraints = listOf(NONEMPTY, PACKAGE)
|
||||
}
|
||||
|
||||
val minSdk = enumParameter<Sdk> {
|
||||
name = R.string.minimum_sdk
|
||||
default = Sdk.Lollipop
|
||||
startIcon = R.drawable.ic_min_sdk
|
||||
displayName = Sdk::displayName
|
||||
}
|
||||
|
||||
val type = enumParameter<ModuleType> {
|
||||
name = R.string.wizard_module_type
|
||||
default = AndroidLibrary
|
||||
@@ -185,13 +161,6 @@ fun baseAndroidModule(isLibrary: Boolean = false,
|
||||
displayName = ModuleType::typeName
|
||||
}
|
||||
|
||||
val language = enumParameter<Language> {
|
||||
name = R.string.wizard_language
|
||||
default = Java
|
||||
startIcon = R.drawable.ic_language_java
|
||||
displayName = Language::lang
|
||||
}
|
||||
|
||||
widgets(TextFieldWidget(moduleName))
|
||||
|
||||
appName?.let {
|
||||
|
||||
@@ -133,7 +133,9 @@ abstract class Parameter<T>(@StringRes val name: Int,
|
||||
private fun notifyObservers() {
|
||||
lock.withLock {
|
||||
observers.forEach {
|
||||
it.onChanged(this)
|
||||
if (it !is DefaultObserver || it.isEnabled) {
|
||||
it.onChanged(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,9 +152,27 @@ abstract class Parameter<T>(@StringRes val name: Int,
|
||||
*/
|
||||
fun onChanged(parameter: Parameter<T>)
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of [Observer] which can enabled or disabled.
|
||||
*/
|
||||
abstract class DefaultObserver<T>(var isEnabled: Boolean = true) : Observer<T> {
|
||||
|
||||
/**
|
||||
* Executes the given [action] with this observer disabled.
|
||||
*
|
||||
* @param action The action to perform.
|
||||
*/
|
||||
fun disableAndRun(action: () -> Unit) {
|
||||
val enabled = isEnabled
|
||||
isEnabled = false
|
||||
action()
|
||||
isEnabled = enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ParameterBuilder<T>() {
|
||||
abstract class ParameterBuilder<T> {
|
||||
|
||||
@StringRes
|
||||
var name: Int? = null
|
||||
@@ -221,7 +241,7 @@ class StringParameter(@StringRes name: Int, @StringRes description: Int?,
|
||||
) : TextFieldParameter<String>(name, description, default, startIcon, endIcon,
|
||||
onStartIconClick, onEndIconClick, constraints, suggest)
|
||||
|
||||
class StringParameterBuilder() : TextFieldParameterBuilder<String>() {
|
||||
class StringParameterBuilder : TextFieldParameterBuilder<String>() {
|
||||
|
||||
override fun build(): StringParameter {
|
||||
return StringParameter(name = name!!, description = description,
|
||||
@@ -279,7 +299,7 @@ fun projectNameParameter(configure: StringParameterBuilder.() -> Unit = {}) =
|
||||
configure()
|
||||
}
|
||||
|
||||
fun packageNameParameter(configure: StringParameterBuilder.() -> Unit) =
|
||||
fun packageNameParameter(configure: StringParameterBuilder.() -> Unit = {}) =
|
||||
stringParameter {
|
||||
name = R.string.package_name
|
||||
default = "com.example.myapplication"
|
||||
|
||||
+58
-12
@@ -28,6 +28,8 @@ import com.itsaky.androidide.templates.BooleanParameter
|
||||
import com.itsaky.androidide.templates.CheckBoxWidget
|
||||
import com.itsaky.androidide.templates.EnumParameter
|
||||
import com.itsaky.androidide.templates.ITemplateWidgetViewProvider
|
||||
import com.itsaky.androidide.templates.Parameter
|
||||
import com.itsaky.androidide.templates.Parameter.DefaultObserver
|
||||
import com.itsaky.androidide.templates.SpinnerWidget
|
||||
import com.itsaky.androidide.templates.StringParameter
|
||||
import com.itsaky.androidide.templates.TextFieldParameter
|
||||
@@ -79,17 +81,40 @@ class TemplateWidgetViewProviderImpl : ITemplateWidgetViewProvider {
|
||||
root.setText(param.name)
|
||||
root.isChecked = param.default
|
||||
|
||||
root.addOnCheckedStateChangedListener { checkbox, _ ->
|
||||
param.setValue(checkbox.isChecked)
|
||||
val observer = object : DefaultObserver<Boolean>() {
|
||||
override fun onChanged(parameter: Parameter<Boolean>) {
|
||||
disableAndRun {
|
||||
root.isChecked = param.value ?: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root.addOnCheckedStateChangedListener { checkbox, _ ->
|
||||
observer.disableAndRun {
|
||||
param.setValue(checkbox.isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
param.observe(observer)
|
||||
}.root
|
||||
}
|
||||
|
||||
private fun createTextField(context: Context, widget: TextFieldWidget): View {
|
||||
return LayoutTextfieldBinding.inflate(LayoutInflater.from(context)).apply {
|
||||
val param = widget.parameter as StringParameter
|
||||
val observer = object : DefaultObserver<String>() {
|
||||
override fun onChanged(parameter: Parameter<String>) {
|
||||
disableAndRun {
|
||||
input.setText(param.value ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
param.configureTextField(context, root) { value ->
|
||||
param.setValue(value)
|
||||
observer.disableAndRun {
|
||||
param.setValue(value)
|
||||
}
|
||||
|
||||
val err =
|
||||
ConstraintVerifier.verify(value, constraints = param.constraints)
|
||||
|
||||
@@ -97,8 +122,12 @@ class TemplateWidgetViewProviderImpl : ITemplateWidgetViewProvider {
|
||||
if (err != null) {
|
||||
root.error = err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
input.setText(param.default)
|
||||
param.observe(observer)
|
||||
|
||||
}.root
|
||||
}
|
||||
|
||||
@@ -106,31 +135,48 @@ class TemplateWidgetViewProviderImpl : ITemplateWidgetViewProvider {
|
||||
return LayoutSpinnerBinding.inflate(LayoutInflater.from(context)).apply {
|
||||
val param = widget.parameter as EnumParameter<Enum<*>>
|
||||
|
||||
val items = mutableMapOf<String, Enum<*>>()
|
||||
val nameToEnum = mutableMapOf<String, Enum<*>>()
|
||||
val enumToName = mutableMapOf<Enum<*>, String>()
|
||||
param.default.javaClass.enumConstants?.forEach {
|
||||
val displayName = param.displayName?.invoke(it) ?: it.name
|
||||
items[displayName] = it
|
||||
nameToEnum[displayName] = it
|
||||
enumToName[it] = displayName
|
||||
}
|
||||
|
||||
check(
|
||||
items.isNotEmpty()) { "Cannot retrive values for enum parameter $param with default value ${param.default}" }
|
||||
nameToEnum.isNotEmpty()) { "Cannot retrive values for enum parameter $param with default value ${param.default}" }
|
||||
|
||||
val array = items.keys.toTypedArray()
|
||||
val array = nameToEnum.keys.toTypedArray()
|
||||
|
||||
input.setAdapter(ArrayAdapter(context,
|
||||
androidx.appcompat.R.layout.support_simple_spinner_dropdown_item,
|
||||
array))
|
||||
|
||||
val defaultName =
|
||||
param.displayName?.invoke(param.default) ?: param.default.name
|
||||
|
||||
root.isEnabled = items.size > 1
|
||||
val defaultName =
|
||||
enumToName[param.default] ?: param.default.name
|
||||
|
||||
root.isEnabled = nameToEnum.size > 1
|
||||
input.listSelection = array.indexOf(defaultName)
|
||||
input.setText(defaultName, false)
|
||||
|
||||
param.configureTextField(context, root) {
|
||||
param.setValue(items[it] ?: param.default)
|
||||
val observer = object : DefaultObserver<Enum<*>>() {
|
||||
override fun onChanged(parameter: Parameter<Enum<*>>) {
|
||||
(parameter as EnumParameter<*>).apply {
|
||||
disableAndRun {
|
||||
input.setText(enumToName[value ?: default])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
param.configureTextField(context, root) {
|
||||
observer.disableAndRun {
|
||||
param.setValue(nameToEnum[it] ?: param.default)
|
||||
}
|
||||
}
|
||||
|
||||
param.observe(observer)
|
||||
}.root
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user