Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ fun createAssetsZip(arch: String) {
"documentation.db",
bootstrapName,
"plugin-artifacts.zip",
"core.cgt"
).forEach { fileName ->
val filePath = sourceDir.resolve(fileName)
if (!filePath.exists()) {
Expand Down Expand Up @@ -1010,6 +1011,12 @@ val debugAssets =
"localMvnRepository.zip",
"debug",
),
Asset(
"assets/core.cgt",
"https://appdevforall.org/dev-assets/debug/core.cgt",
"core.cgt",
"debug",
),
)

val releaseAssets =
Expand Down Expand Up @@ -1062,6 +1069,12 @@ val releaseAssets =
"v8/bootstrap.zip.br",
"release",
),
Asset(
"assets/release/common/data/common/core.cgt.br",
"https://appdevforall.org/dev-assets/release/core.cgt.br",
"core.cgt.br",
"release",
),
)

fun assetsBatch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.ConvertUtils
import com.bumptech.glide.Glide
import com.google.android.material.shape.CornerFamily
import com.itsaky.androidide.adapters.TemplateListAdapter.ViewHolder
import com.itsaky.androidide.databinding.LayoutTemplateListItemBinding
import com.itsaky.androidide.templates.Template
import org.slf4j.LoggerFactory

/**
* [RecyclerView.Adapter] for showing templates in a [RecyclerView].
Expand All @@ -38,6 +40,10 @@ class TemplateListAdapter(
private val onClick: ((Template<*>, ViewHolder) -> Unit)? = null,
private val onLongClick: ((Template<*>, View) -> Unit)? = null,
) : RecyclerView.Adapter<ViewHolder>() {
companion object {
private val log = LoggerFactory.getLogger(TemplateListAdapter::class.java)
}

private val templates = templates.toMutableList()

class ViewHolder(
Expand All @@ -63,13 +69,25 @@ class TemplateListAdapter(
position: Int,
) {
holder.binding.apply {
val template = templates[position]
val template = templates[position]
if (template == Template.EMPTY) {
root.visibility = View.INVISIBLE
return@apply
}
templateName.text = templateName.context.getString(template.templateName)
templateIcon.setImageResource(template.thumb)

log.debug("template: $template")
templateName.text = template.templateNameStr
log.debug("text: ${template.templateNameStr} templateName.text: ${templateName.text}")
if (template.thumbData != null) {
log.debug("thumbData is not null")
Glide.with(templateIcon.context)
.asBitmap()
.load(template.thumbData)
.into(templateIcon)
} else {
templateIcon.setImageResource(template.thumb)
}

templateIcon.shapeAppearanceModel =
templateIcon.shapeAppearanceModel
.toBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.adfa.constants.DOCUMENTATION_DB
import org.adfa.constants.GRADLE_API_NAME_JAR_ZIP
import org.adfa.constants.GRADLE_DISTRIBUTION_ARCHIVE_NAME
import org.adfa.constants.LOCAL_MAVEN_REPO_ARCHIVE_ZIP_NAME
import org.adfa.constants.TEMPLATE_CORE_ARCHIVE
import org.slf4j.LoggerFactory
import com.itsaky.androidide.resources.R
import java.io.File
Expand Down Expand Up @@ -108,7 +109,8 @@ object AssetsInstallationHelper {
BOOTSTRAP_ENTRY_NAME,
GRADLE_API_NAME_JAR_ZIP,
LLAMA_AAR,
PLUGIN_ARTIFACTS_ZIP
PLUGIN_ARTIFACTS_ZIP,
TEMPLATE_CORE_ARCHIVE,
)

val stagingDir = Files.createTempDirectory(UUID.randomUUID().toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import org.adfa.constants.GRADLE_API_NAME_JAR_BR
import org.adfa.constants.GRADLE_API_NAME_JAR_ZIP
import org.adfa.constants.GRADLE_DISTRIBUTION_ARCHIVE_NAME
import org.adfa.constants.LOCAL_MAVEN_REPO_ARCHIVE_ZIP_NAME
import org.adfa.constants.TEMPLATE_CORE_ARCHIVE
import org.adfa.constants.TEMPLATE_CORE_ARCHIVE_BR
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileNotFoundException
Expand Down Expand Up @@ -78,7 +80,17 @@ data object BundledAssetsInstaller : BaseAssetsInstaller() {
}
}

AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME -> {
TEMPLATE_CORE_ARCHIVE -> {
val assetPath = ToolsManager.getCommonAsset(TEMPLATE_CORE_ARCHIVE_BR)
BrotliInputStream(assets.open(assetPath)).use { input ->
val destFile = Environment.TEMPLATES_DIR.resolve(TEMPLATE_CORE_ARCHIVE)
destFile.outputStream().use { output ->
input.copyTo(output)
}
}
}

AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME -> {
val assetPath =
ToolsManager.getCommonAsset("${AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME}.br")

Expand Down Expand Up @@ -217,6 +229,7 @@ data object BundledAssetsInstaller : BaseAssetsInstaller() {
AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME -> 124120151L
GRADLE_API_NAME_JAR_ZIP -> 29447748L
AssetsInstallationHelper.PLUGIN_ARTIFACTS_ZIP -> 86442L
TEMPLATE_CORE_ARCHIVE -> 133120L
else -> 0L
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.adfa.constants.DOCUMENTATION_DB
import org.adfa.constants.GRADLE_API_NAME_JAR_ZIP
import org.adfa.constants.GRADLE_DISTRIBUTION_ARCHIVE_NAME
import org.adfa.constants.LOCAL_MAVEN_REPO_ARCHIVE_ZIP_NAME
import org.adfa.constants.TEMPLATE_CORE_ARCHIVE
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileNotFoundException
Expand Down Expand Up @@ -72,7 +73,14 @@ data object SplitAssetsInstaller : BaseAssetsInstaller() {
logger.debug("Completed extracting '{}' to dir: {}", entry.name, destDir)
}

AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME -> {
TEMPLATE_CORE_ARCHIVE -> {
val coreCgt = Environment.TEMPLATES_DIR.resolve(TEMPLATE_CORE_ARCHIVE)
coreCgt.outputStream().use { output ->
zipInput.copyTo(output)
}
}

AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME -> {
logger.debug("Extracting 'bootstrap.zip' to dir: {}", stagingDir)

val result = retryOnceOnNoSuchFile(
Expand Down Expand Up @@ -188,6 +196,7 @@ data object SplitAssetsInstaller : BaseAssetsInstaller() {
LOCAL_MAVEN_REPO_ARCHIVE_ZIP_NAME -> 215389106L
AssetsInstallationHelper.BOOTSTRAP_ENTRY_NAME -> 456462823L
GRADLE_API_NAME_JAR_ZIP -> 46758608L
TEMPLATE_CORE_ARCHIVE -> 702001L
else -> 0L
}

Expand All @@ -197,6 +206,7 @@ data object SplitAssetsInstaller : BaseAssetsInstaller() {
ANDROID_SDK_ZIP -> Environment.ANDROID_HOME
LOCAL_MAVEN_REPO_ARCHIVE_ZIP_NAME -> Environment.LOCAL_MAVEN_DIR
GRADLE_API_NAME_JAR_ZIP -> Environment.GRADLE_GEN_JARS
TEMPLATE_CORE_ARCHIVE -> Environment.TEMPLATES_DIR
else -> throw IllegalStateException("Entry '$entryName' is not expected to be an archive")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,10 @@ class TemplateDetailsFragment :
name = result.data.name,
createdAt = now,
lastModified = now,
templateName = getString(template.templateName),
language = result.data.language.name
// templateName = getString(template.templateName),
templateName = template.templateNameStr,
// language = result.data.language.name
language = result.data.language?.name ?: "unknown"
Comment on lines +139 to +142
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider using a string resource for the "unknown" fallback.

The changes correctly adapt to the new nullable language property and templateNameStr field. However, the hardcoded "unknown" string bypasses internationalization.

💡 Suggested improvement
-                        language = result.data.language?.name ?: "unknown"
+                        language = result.data.language?.name ?: getString(R.string.language_unknown)

Add the string resource in strings.xml:

<string name="language_unknown">Unknown</string>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// templateName = getString(template.templateName),
templateName = template.templateNameStr,
// language = result.data.language.name
language = result.data.language?.name ?: "unknown"
// templateName = getString(template.templateName),
templateName = template.templateNameStr,
// language = result.data.language.name
language = result.data.language?.name ?: getString(R.string.language_unknown)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt`
around lines 139 - 142, Replace the hardcoded "unknown" fallback with a string
resource: add <string name="language_unknown">Unknown</string> to strings.xml
and update the assignment in TemplateDetailsFragment (where language is set from
result.data.language?.name) to use getString(R.string.language_unknown) (or
requireContext().getString(...)) as the default; this preserves i18n and keeps
the nullable-safe fallback intact.

)
)

Expand All @@ -163,6 +165,7 @@ class TemplateDetailsFragment :
template ?: return

binding.widgets.adapter = TemplateWidgetsListAdapter(template.widgets)
binding.title.setText(template.templateName)
//binding.title.setText(template.templateName)
binding.title.text = template.templateNameStr
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ public final class Environment {
public static final String NDK_TAR_XZ = "ndk-cmake.tar.xz";
public static File NDK_DIR;

public static File TEMPLATES_DIR;

public static String getArchitecture() {
return IDEBuildConfigProvider.getInstance().getCpuAbiName();
}
Expand Down Expand Up @@ -187,6 +189,8 @@ public static void init(Context context) {

NDK_DIR = new File(ANDROID_HOME, "ndk");

TEMPLATES_DIR = mkdirIfNotExists(new File(ANDROIDIDE_HOME, "templates"));

isInitialized.set(true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,8 @@ const val LOGSENDER_AAR_NAME = "logsender.aar"
const val GRADLE_API_NAME_JAR = "gradle-api-$GRADLE_DISTRIBUTION_VERSION.jar"
const val GRADLE_API_NAME_JAR_ZIP = "${GRADLE_API_NAME_JAR}.zip"
const val GRADLE_API_NAME_JAR_BR = "${GRADLE_API_NAME_JAR}.br"

// Templates archive
const val TEMPLATE_ARCHIVE_EXTENSION = "cgt"
const val TEMPLATE_CORE_ARCHIVE = "core.$TEMPLATE_ARCHIVE_EXTENSION"
const val TEMPLATE_CORE_ARCHIVE_BR = "${TEMPLATE_CORE_ARCHIVE}.br"
7 changes: 6 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ compose-compiler = "2.1.21"

leakcanary = "2.14"

pebble = "4.1.1"


[libraries]

# Dependencies in composite build
Expand Down Expand Up @@ -220,7 +223,7 @@ androidx-vectors = { module = "androidx.vectordrawable:vectordrawable", version.
androidx-animated_vectors = { module = "androidx.vectordrawable:vectordrawable-animated", version.ref = "androidx-vectordrawable" }
androidx-core = { module = "androidx.core:core", version = "1.13.1" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version = "1.13.1" }
androidx-fragment_ktx = { module = "androidx.fragment:fragment-ktx", version = "1.6.2" }
#androidx-fragment_ktx = { module = "androidx.fragment:fragment-ktx", version = "1.6.2" }
androidx-libDesugaring = { module = "com.android.tools:desugar_jdk_libs", version = "2.0.4" }
androidx-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
androidx-transition = { module = "androidx.transition:transition-ktx", version = "1.5.1" }
Expand Down Expand Up @@ -307,6 +310,8 @@ monitor = { group = "androidx.test", name = "monitor", version.ref = "monitorVer
org-json = { module = "org.json:json", version = "20210307"}
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }

pebble = { module = "io.pebbletemplates:pebble", version.ref = "pebble" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
Expand Down
2 changes: 2 additions & 0 deletions templates-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ dependencies {
api(libs.androidx.annotation)
api(libs.androidx.appcompat)
api(libs.google.material)

implementation(libs.google.gson)
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,7 @@ class ProjectTemplateBuilder : ExecutorDataTemplateBuilder<ProjectTemplateRecipe
tooltipTag,
widgets!!,
recipe!!,
templateNameStr!!,
thumbData!!
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,104 @@ inline fun <R : FileTemplateRecipeResult> baseFile(
return FileTemplateBuilder<R>(dir).apply(configurator)
.build() as FileTemplate<R>
}

/**
* Setup base files for zip project templates.
*
* @param block Function to configure the template.
*/
inline fun baseZipProject(
projectName: StringParameter = projectNameParameter(),
packageName: StringParameter = packageNameParameter(),
useKts: BooleanParameter = useKtsParameter(),
minSdk: EnumParameter<Sdk> = minSdkParameter(),
language: EnumParameter<Language> = projectLanguageParameter(),
projectVersionData: ProjectVersionData = ProjectVersionData(),
isToml: Boolean = false,
showUseKts: Boolean = false,
showMinSdk: Boolean = true,
showLanguage: Boolean = true,
crossinline block: ProjectTemplateBuilder.() -> Unit
): ProjectTemplate {
return ProjectTemplateBuilder().apply {

// When project name is changed, change the package name accordingly
projectName.observe { name ->
val newPackage =
AndroidUtils.appNameToPackageName(name.value, packageName.value)
packageName.setValue(newPackage)
}

Environment.mkdirIfNotExists(Environment.PROJECTS_DIR)

val saveLocation = stringParameter {
name = R.string.wizard_save_location
default = Environment.PROJECTS_DIR.absolutePath
endIcon = { R.drawable.ic_folder }
constraints = listOf(NONEMPTY, DIRECTORY, EXISTS)
inputType =
android.text.InputType.TYPE_CLASS_TEXT or android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE
maxLines = 1
tooltipTag = "setup.save.location"
}

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

widgets(
TextFieldWidget(projectName), TextFieldWidget(packageName),
TextFieldWidget(saveLocation)
)

if (showLanguage) {
widgets(SpinnerWidget(language))
}

if (showMinSdk) {
widgets(SpinnerWidget(minSdk))
}

if (showUseKts) {
widgets(CheckBoxWidget(useKts))
}

// Setup the required properties before executing the recipe
preRecipe = {
this@apply._executor = this

if (!showUseKts) {
useKts.setValue(true, notify = false)
}

this@apply._data = ProjectTemplateData(
projectName.value,
File(saveLocation.value, projectName.value),
projectVersionData,
language = if (showLanguage) language.value else null,
useKts = useKts.value,
useToml = isToml
)

if (data.projectDir.exists() && data.projectDir.listFiles()
?.isNotEmpty() == true
) {
throw IllegalArgumentException("Project directory already exists")
}

setDefaultModuleData(
ModuleTemplateData(
":app", appName = data.name, packageName.value,
data.moduleNameToDir(":app"), type = AndroidApp,
language = if (showLanguage) language.value else null,
minSdk = if (showMinSdk) minSdk.value else null,
useKts = data.useKts, useToml = isToml
)
)
}

block()

}.build() as ProjectTemplate
}
Loading
Loading