Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

Commit

Permalink
fix(templates): project name is not properly updated if project with …
Browse files Browse the repository at this point in the history
…same name already exists
  • Loading branch information
itsaky committed Aug 27, 2023
1 parent c513f44 commit 90812ed
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import com.itsaky.androidide.preferences.internal.statConsentDialogShown
import com.itsaky.androidide.preferences.internal.statOptIn
import com.itsaky.androidide.projects.ProjectManager.projectPath
import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.templates.ITemplateProvider
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.flashError
Expand Down Expand Up @@ -292,6 +293,7 @@ class MainActivity : LimitlessIDEActivity() {
}

override fun onDestroy() {
ITemplateProvider.getInstance().release()
super.onDestroy()
_binding = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ class TemplateDetailsFragment :

binding.finish.setOnClickListener {
viewModel.creatingProject.value = true
val template = checkNotNull(
viewModel.template.value) { "Cannot create project. Template not found." }
val template = viewModel.template.value ?: run {
viewModel.setScreen(MainViewModel.SCREEN_MAIN)
return@setOnClickListener
}

val isValid = template.parameters.fold(true) { isValid, param ->
if (param is StringParameter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.itsaky.androidide.adapters.TemplateListAdapter
import com.itsaky.androidide.databinding.FragmentTemplateListBinding
import com.itsaky.androidide.templates.ITemplateProvider
import com.itsaky.androidide.templates.ProjectTemplate
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.viewmodel.MainViewModel
import kotlin.math.ceil

Expand All @@ -51,21 +52,10 @@ class TemplateListFragment : FragmentWithBinding<FragmentTemplateListBinding>(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Show only project templates
val templates = ITemplateProvider.getInstance(true)
.getTemplates()
.filterIsInstance<ProjectTemplate>()

layoutManager = FlexboxLayoutManager(requireContext(), FlexDirection.ROW)
layoutManager!!.justifyContent = JustifyContent.SPACE_EVENLY

adapter = TemplateListAdapter(templates) { template, _ ->
viewModel.template.value = template
viewModel.setScreen(MainViewModel.SCREEN_TEMPLATE_DETAILS)
}

binding.list.layoutManager = layoutManager
binding.list.adapter = adapter

// This makes sure that the items are evenly distributed in the list
// and the last row is always aligned to the start
Expand Down Expand Up @@ -98,12 +88,40 @@ class TemplateListFragment : FragmentWithBinding<FragmentTemplateListBinding>(
binding.exitButton.setOnClickListener {
viewModel.setScreen(MainViewModel.SCREEN_MAIN)
}

viewModel.currentScreen.observe(viewLifecycleOwner) { current ->
if (current == MainViewModel.SCREEN_TEMPLATE_DETAILS) {
return@observe
}

reloadTemplates()
}
}

override fun onDestroyView() {
binding.list.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
ITemplateProvider.getInstance().clear()
super.onDestroyView()
}

private val log = ILogger.newInstance("TemplateListFragment")

private fun reloadTemplates() {
_binding ?: return

log.debug("Reloading templates...")

// Show only project templates
// reloading the tempaltes also makes sure that the resources are
// released from template parameter widgets
val templates = ITemplateProvider.getInstance(reload = true)
.getTemplates()
.filterIsInstance<ProjectTemplate>()

adapter = TemplateListAdapter(templates) { template, _ ->
viewModel.template.value = template
viewModel.setScreen(MainViewModel.SCREEN_TEMPLATE_DETAILS)
}

binding.list.adapter = adapter
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,9 @@ interface ITemplateProvider {
@JvmStatic
@JvmOverloads
fun getInstance(reload: Boolean = false): ITemplateProvider {
if (reload) {
provider?.clear()
provider = null
}

return provider ?: ServiceLoader.load(ITemplateProvider::class.java)
return provider?.also { if (reload) it.reload() } ?: ServiceLoader.load(
ITemplateProvider::class.java)
.findFirstOrThrow()
.also { provider = it }
}
Expand All @@ -71,8 +68,13 @@ interface ITemplateProvider {
*/
fun getTemplate(templateId: String): Template<*>?

/**
* Reloads the templates.
*/
fun reload()

/**
* Clear all the templates stored in the provider.
*/
fun clear()
fun release()
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ fun baseProject(projectName: StringParameter = projectNameParameter(),
constraints = listOf(NONEMPTY, DIRECTORY, EXISTS)
}

projectName.setValue(getNewProjectName(saveLocation.value, projectName.value))
projectName.doBeforeCreateView {
it.setValue(getNewProjectName(saveLocation.value, projectName.value))
}

widgets(TextFieldWidget(projectName), TextFieldWidget(packageName),
TextFieldWidget(saveLocation), SpinnerWidget(language),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,17 @@ enum class ParameterConstraint {
}

abstract class Parameter<T>(@StringRes val name: Int,
@StringRes val description: Int?, val default: T,
var constraints: List<ParameterConstraint>
@StringRes val description: Int?, val default: T,
var constraints: List<ParameterConstraint>
) {

private val observers = hashSetOf<Observer<T>>()
private val lock = ReentrantLock()
private var _value: T? = null

private var actionBeforeCreateView: ((Parameter<T>) -> Unit)? = null
private var actionAfterCreateView: ((Parameter<T>) -> Unit)? = null

/**
* The value of this parameter.
*/
Expand All @@ -109,6 +112,16 @@ abstract class Parameter<T>(@StringRes val name: Int,
}
}

/**
* Resets the parameter value to the default value and removes any external value observers.
*
* @param notify Whether the observers should be notified about this change or not.
*/
fun reset(notify: Boolean = true) {
setValue(default, notify)
clearObservers()
}

/**
* Adds the [Observer] instance to the list of observers.
*
Expand All @@ -134,11 +147,52 @@ abstract class Parameter<T>(@StringRes val name: Int,
}

fun release() {
clearObservers()

this.actionBeforeCreateView = null
this.actionAfterCreateView = null
}

private fun clearObservers() {
lock.withLock {
observers.clear()
}
}

/**
* Perform the given action before the view is created.
*
* @param action The action to execute.
* @see beforeCreateView
*/
fun doBeforeCreateView(action: (Parameter<T>) -> Unit) {
this.actionBeforeCreateView = action
}

/**
* Perform the given action after the view is created.
*
* @param action The action to execute.
* @see afterCreateView
*/
fun doAfterCreateView(action: (Parameter<T>) -> Unit) {
this.actionBeforeCreateView = action
}

/**
* Called before the layout for this widget is created.
*/
open fun beforeCreateView() {
this.actionBeforeCreateView?.invoke(this)
}

/**
* Called after the layout for this widget is created.
*/
open fun afterCreateView() {
this.actionAfterCreateView?.invoke(this)
}

private fun notifyObservers() {
lock.withLock {
observers.forEach {
Expand Down Expand Up @@ -202,7 +256,7 @@ abstract class ParameterBuilder<T> {
}

class BooleanParameter(@StringRes name: Int, @StringRes description: Int?,
default: Boolean, constraints: List<ParameterConstraint>
default: Boolean, constraints: List<ParameterConstraint>
) : Parameter<Boolean>(name, description, default, constraints)

class BooleanParameterBuilder : ParameterBuilder<Boolean>() {
Expand All @@ -224,12 +278,12 @@ class BooleanParameterBuilder : ParameterBuilder<Boolean>() {
* clicked. Click listener to the icon will be set on if this is non-null.
*/
abstract class TextFieldParameter<T>(@StringRes name: Int,
@StringRes description: Int?, default: T,
val startIcon: ((TextFieldParameter<T>) -> Int)?,
val endIcon: ((TextFieldParameter<T>) -> Int)?,
val onStartIconClick: View.OnClickListener?,
val onEndIconClick: View.OnClickListener?,
constraints: List<ParameterConstraint>
@StringRes description: Int?, default: T,
val startIcon: ((TextFieldParameter<T>) -> Int)?,
val endIcon: ((TextFieldParameter<T>) -> Int)?,
val onStartIconClick: View.OnClickListener?,
val onEndIconClick: View.OnClickListener?,
constraints: List<ParameterConstraint>
) : Parameter<T>(name, description, default, constraints)

abstract class TextFieldParameterBuilder<T>(
Expand All @@ -240,12 +294,12 @@ abstract class TextFieldParameterBuilder<T>(
) : ParameterBuilder<T>()

class StringParameter(@StringRes name: Int, @StringRes description: Int?,
default: String,
startIcon: ((TextFieldParameter<String>) -> Int)?,
endIcon: ((TextFieldParameter<String>) -> Int)?,
onStartIconClick: View.OnClickListener?,
onEndIconClick: View.OnClickListener?,
constraints: List<ParameterConstraint>
default: String,
startIcon: ((TextFieldParameter<String>) -> Int)?,
endIcon: ((TextFieldParameter<String>) -> Int)?,
onStartIconClick: View.OnClickListener?,
onEndIconClick: View.OnClickListener?,
constraints: List<ParameterConstraint>
) : TextFieldParameter<String>(name, description, default, startIcon, endIcon,
onStartIconClick, onEndIconClick, constraints)

Expand All @@ -260,14 +314,14 @@ class StringParameterBuilder : TextFieldParameterBuilder<String>() {
}

class EnumParameter<T : Enum<*>>(@StringRes name: Int,
@StringRes description: Int?, default: T,
startIcon: ((TextFieldParameter<T>) -> Int)?,
endIcon: ((TextFieldParameter<T>) -> Int)?,
onStartIconClick: View.OnClickListener?,
onEndIconClick: View.OnClickListener?,
constraints: List<ParameterConstraint>,
val displayName: ((T) -> String)? = null,
val filter: ((T) -> Boolean)? = null
@StringRes description: Int?, default: T,
startIcon: ((TextFieldParameter<T>) -> Int)?,
endIcon: ((TextFieldParameter<T>) -> Int)?,
onStartIconClick: View.OnClickListener?,
onEndIconClick: View.OnClickListener?,
constraints: List<ParameterConstraint>,
val displayName: ((T) -> String)? = null,
val filter: ((T) -> Boolean)? = null
) : TextFieldParameter<T>(name, description, default, startIcon, endIcon,
onStartIconClick, onEndIconClick, constraints)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ class TemplateProviderImpl : ITemplateProvider {
private val templates = mutableMapOf<String, Template<*>>()

init {
templates().forEach { template ->
templates[template.templateId] = template
}
initializeTemplates()
}

private fun templates() =
Expand All @@ -59,6 +57,12 @@ class TemplateProviderImpl : ITemplateProvider {
noAndroidXActivityProject(),
composeActivityProject()
)

private fun initializeTemplates() {
templates().forEach { template ->
templates[template.templateId] = template
}
}
//@formatter:on

override fun getTemplates(): List<Template<*>> {
Expand All @@ -69,7 +73,12 @@ class TemplateProviderImpl : ITemplateProvider {
return templates[templateId]
}

override fun clear() {
override fun reload() {
release()
initializeTemplates()
}

override fun release() {
templates.forEach { it.value.release() }
templates.clear()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,21 @@ class TemplateWidgetViewProviderImpl : ITemplateWidgetViewProvider {

override fun <T> createView(context: Context, widget: Widget<T>): View {
if (widget is ParameterWidget<T>) {
widget.parameter.setValue(widget.parameter.value, false)
widget.parameter.apply {
beforeCreateView()
setValue(value, false)
}
}

return when (widget) {
is TextFieldWidget -> createTextField(context, widget)
is CheckBoxWidget -> createCheckBox(context, widget)
is SpinnerWidget -> createSpinner(context, widget)
else -> throw IllegalArgumentException("Unknown widget type : $widget")
}.also {
if (widget is ParameterWidget<T>) {
widget.parameter.afterCreateView()
}
}
}

Expand Down

0 comments on commit 90812ed

Please sign in to comment.