diff --git a/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt b/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt
index 8591d00a6c..28435aad93 100644
--- a/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt
+++ b/app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt
@@ -35,6 +35,7 @@ import com.itsaky.androidide.projects.IProjectManager
import com.itsaky.androidide.projects.ProjectManagerImpl
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.templates.base.models.Dependency
+import com.itsaky.androidide.utils.ClassBuilder.SourceLanguage
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ProjectWriter
@@ -174,13 +175,13 @@ class NewFileAction(val context: Context, override val order: Int) :
}
)
builder.setView(binding.root)
- builder.setTitle(R.string.new_java_class)
+ builder.setTitle(R.string.new_file)
builder.setPositiveButton(R.string.text_create) { dialogInterface, _ ->
dialogInterface.dismiss()
try {
- doCreateJavaFile(binding, file, context, node)
+ doCreateSourceFile(binding, file, context, node)
} catch (e: Exception) {
- log.error("Failed to create Java file", e)
+ log.error("Failed to create source file", e)
flashError(e.cause?.message ?: e.message)
}
}
@@ -189,7 +190,6 @@ class NewFileAction(val context: Context, override val order: Int) :
.showWithLongPressTooltip(
context = context,
tooltipTag = TooltipTag.PROJECT_FOLDER_NEWTYPE,
- // Pass the specific buttons to the helper function
binding.typeClass,
binding.typeActivity,
binding.typeInterface,
@@ -197,7 +197,7 @@ class NewFileAction(val context: Context, override val order: Int) :
)
}
- private fun doCreateJavaFile(
+ private fun doCreateSourceFile(
binding: LayoutCreateFileJavaBinding,
file: File,
context: Context,
@@ -214,78 +214,79 @@ class NewFileAction(val context: Context, override val order: Int) :
return
}
+ val isKotlin = binding.languageGroup.checkedButtonId == binding.langKotlin.id
+ val language = if (isKotlin) SourceLanguage.KOTLIN else SourceLanguage.JAVA
+ val extension = if (isKotlin) ".kt" else ".java"
+
val autoLayout =
binding.typeGroup.checkedButtonId == binding.typeActivity.id &&
binding.createLayout.isChecked
val pkgName = ProjectWriter.getPackageName(file)
val id: Int = binding.typeGroup.checkedButtonId
- val javaName = if (name.endsWith(".java")) name else "$name.java"
+ val fileName = if (name.endsWith(extension)) name else "$name$extension"
val className = if (!name.contains(".")) name else name.substring(0, name.lastIndexOf("."))
- // Package Structure Check: When the package is "com", it checks if a com subdirectory exists within the current directory.
- // If com/ exists, it means the package structure is organized correctly.
- // The Java file should be created within com/ to maintain package consistency.
- // If the com subdirectory does not exist, use the original directory (file), avoiding FileNotFoundException errors.
- val javaFileDirectory = if (pkgName == "com") {
+ val sourceFileDirectory = if (pkgName == "com") {
val subDir = File(file, "com")
if (subDir.exists() && subDir.isDirectory) subDir else file
} else {
file
}
- when (id) {
- binding.typeClass.id ->
- createFile(
- node,
- javaFileDirectory,
- javaName,
- ProjectWriter.createJavaClass(pkgName, className),
- )
+ when (id) {
+ binding.typeClass.id ->
+ createFile(
+ node,
+ sourceFileDirectory,
+ fileName,
+ ProjectWriter.createClass(pkgName, className, language),
+ )
- binding.typeInterface.id ->
- createFile(
- node,
- javaFileDirectory,
- javaName,
- ProjectWriter.createJavaInterface(pkgName, className)
- )
+ binding.typeInterface.id ->
+ createFile(
+ node,
+ sourceFileDirectory,
+ fileName,
+ ProjectWriter.createInterface(pkgName, className, language)
+ )
- binding.typeEnum.id ->
- createFile(
- node,
- javaFileDirectory,
- javaName,
- ProjectWriter.createJavaEnum(pkgName, className)
- )
+ binding.typeEnum.id ->
+ createFile(
+ node,
+ sourceFileDirectory,
+ fileName,
+ ProjectWriter.createEnum(pkgName, className, language)
+ )
- binding.typeActivity.id -> {
- val appCompat = Dependency.AndroidX.AppCompat
- val projectManager = ProjectManagerImpl.getInstance()
- val hasAppCompatDependency = projectManager.findModuleForFile(file)
- ?.hasExternalDependency(appCompat.group, appCompat.artifact)
- createFile(
- node,
- javaFileDirectory,
- javaName,
- ProjectWriter.createActivity(
- pkgName,
- className,
- hasAppCompatDependency ?: false
- )
- )
- }
-
- else -> createFile(node, javaFileDirectory, name, "")
+ binding.typeActivity.id -> {
+ val appCompat = Dependency.AndroidX.AppCompat
+ val projectManager = ProjectManagerImpl.getInstance()
+ val hasAppCompatDependency = projectManager.findModuleForFile(file)
+ ?.hasExternalDependency(appCompat.group, appCompat.artifact)
+ createFile(
+ node,
+ sourceFileDirectory,
+ fileName,
+ ProjectWriter.createActivity(
+ pkgName,
+ className,
+ hasAppCompatDependency ?: false,
+ language
+ )
+ )
}
+ else -> createFile(node, sourceFileDirectory, name, "")
+ }
+
node?.let {
requestCollapseNode(it, true)
}
if (autoLayout) {
val packagePath = pkgName.toString().replace(".", "/")
- createAutoLayout(context, javaFileDirectory, name, packagePath)
+ createAutoLayout(context, sourceFileDirectory, name, packagePath, isKotlin)
}
}
@@ -306,10 +307,12 @@ class NewFileAction(val context: Context, override val order: Int) :
context: Context,
directory: File,
fileName: String,
- packagePath: String
+ packagePath: String,
+ isKotlin: Boolean = false
) {
val dir = directory.toString().replace("java/$packagePath", "res/layout/")
- val layoutName = ProjectWriter.createLayoutName(fileName.replace(".java", ".xml"))
+ val sourceExtension = if (isKotlin) ".kt" else ".java"
+ val layoutName = ProjectWriter.createLayoutName(fileName.replace(sourceExtension, ".xml"))
val newFileLayout = File(dir, layoutName)
if (newFileLayout.exists()) {
flashError(R.string.msg_layout_file_exists)
diff --git a/app/src/main/java/com/itsaky/androidide/utils/ClassBuilder.kt b/app/src/main/java/com/itsaky/androidide/utils/ClassBuilder.kt
index 8895eda1ca..bf569dc5a5 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/ClassBuilder.kt
+++ b/app/src/main/java/com/itsaky/androidide/utils/ClassBuilder.kt
@@ -21,6 +21,8 @@ import android.app.Activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.itsaky.androidide.preferences.utils.indentationString
+import com.itsaky.androidide.utils.ClassBuilder.SourceLanguage.JAVA
+import com.itsaky.androidide.utils.ClassBuilder.SourceLanguage.KOTLIN
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
@@ -28,9 +30,15 @@ import jdkx.lang.model.element.Modifier.PROTECTED
import jdkx.lang.model.element.Modifier.PUBLIC
object ClassBuilder {
+
+ enum class SourceLanguage { JAVA, KOTLIN }
+
@JvmStatic
- fun createClass(packageName: String, className: String): String {
- return toJavaFile(packageName, newClassSpec(className)).toString()
+ fun createClass(packageName: String, className: String, language: SourceLanguage = JAVA): String {
+ return when (language) {
+ JAVA -> toJavaFile(packageName, newClassSpec(className)).toString()
+ KOTLIN -> buildKotlinFile(packageName) { appendLine("class $className {"); appendLine("}") }
+ }
}
private fun toJavaFile(
@@ -49,8 +57,11 @@ object ClassBuilder {
}
@JvmStatic
- fun createInterface(packageName: String, className: String): String {
- return toJavaFile(packageName, newInterfaceSpec(className)).toString()
+ fun createInterface(packageName: String, className: String, language: SourceLanguage = JAVA): String {
+ return when (language) {
+ JAVA -> toJavaFile(packageName, newInterfaceSpec(className)).toString()
+ KOTLIN -> buildKotlinFile(packageName) { appendLine("interface $className {"); appendLine("}") }
+ }
}
private fun newInterfaceSpec(className: String): TypeSpec {
@@ -58,8 +69,15 @@ object ClassBuilder {
}
@JvmStatic
- fun createEnum(packageName: String, className: String): String {
- return toJavaFile(packageName, newEnumSpec(className)).toString()
+ fun createEnum(packageName: String, className: String, language: SourceLanguage = JAVA): String {
+ return when (language) {
+ JAVA -> toJavaFile(packageName, newEnumSpec(className)).toString()
+ KOTLIN -> buildKotlinFile(packageName) {
+ appendLine("enum class $className {")
+ appendLine("${indentationString}ENUM_DECLARED")
+ appendLine("}")
+ }
+ }
}
private fun newEnumSpec(className: String): TypeSpec {
@@ -71,10 +89,18 @@ object ClassBuilder {
@JvmStatic
fun createActivity(
- packageName: String,
- className: String,
- appCompatActivity: Boolean = true,
+ packageName: String,
+ className: String,
+ appCompatActivity: Boolean = true,
+ language: SourceLanguage = JAVA
): String {
+ return when (language) {
+ JAVA -> createJavaActivity(packageName, className, appCompatActivity)
+ KOTLIN -> createKotlinActivity(packageName, className, appCompatActivity)
+ }
+ }
+
+ private fun createJavaActivity(packageName: String, className: String, appCompatActivity: Boolean): String {
val onCreate =
MethodSpec.methodBuilder("onCreate")
.addAnnotation(Override::class.java)
@@ -89,4 +115,30 @@ object ClassBuilder {
.addMethod(onCreate)
return toJavaFile(packageName, activity.build()) { skipJavaLangImports(true) }.toString()
}
+
+ private fun createKotlinActivity(packageName: String, className: String, appCompatActivity: Boolean): String {
+ val superClass = if (appCompatActivity) "AppCompatActivity" else "Activity"
+ val import = if (appCompatActivity) "androidx.appcompat.app.AppCompatActivity" else "android.app.Activity"
+ return buildString {
+ if (packageName.isNotEmpty()) appendLine("package $packageName")
+ appendLine()
+ appendLine("import android.os.Bundle")
+ appendLine("import $import")
+ appendLine()
+ appendLine("class $className : $superClass() {")
+ appendLine()
+ appendLine("${indentationString}override fun onCreate(savedInstanceState: Bundle?) {")
+ appendLine("${indentationString}${indentationString}super.onCreate(savedInstanceState)")
+ appendLine("$indentationString}")
+ appendLine("}")
+ }
+ }
+
+ private fun buildKotlinFile(packageName: String, block: StringBuilder.() -> Unit): String {
+ return buildString {
+ if (packageName.isNotEmpty()) appendLine("package $packageName")
+ appendLine()
+ block()
+ }
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java b/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java
index 5306848a04..df1357d2fc 100755
--- a/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java
+++ b/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java
@@ -22,6 +22,7 @@
import androidx.annotation.NonNull;
import com.blankj.utilcode.util.ResourceUtils;
+import com.itsaky.androidide.utils.ClassBuilder.SourceLanguage;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -121,19 +122,19 @@ private static boolean isValidPackageName(String name) {
return name.matches("^[a-zA-Z_][a-zA-Z0-9_]*$");
}
- public static String createJavaClass(String packageName, String className) {
- return ClassBuilder.createClass(packageName, className);
+ public static String createClass(String packageName, String className, SourceLanguage language) {
+ return ClassBuilder.createClass(packageName, className, language);
}
- public static String createJavaInterface(String packageName, String className) {
- return ClassBuilder.createInterface(packageName, className);
+ public static String createInterface(String packageName, String className, SourceLanguage language) {
+ return ClassBuilder.createInterface(packageName, className, language);
}
- public static String createJavaEnum(String packageName, String className) {
- return ClassBuilder.createEnum(packageName, className);
+ public static String createEnum(String packageName, String className, SourceLanguage language) {
+ return ClassBuilder.createEnum(packageName, className, language);
}
- public static String createActivity(String packageName, String className, boolean appCompatActivity) {
- return ClassBuilder.createActivity(packageName, className, appCompatActivity);
+ public static String createActivity(String packageName, String className, boolean appCompatActivity, SourceLanguage language) {
+ return ClassBuilder.createActivity(packageName, className, appCompatActivity, language);
}
}
diff --git a/app/src/main/res/layout/layout_create_file_java.xml b/app/src/main/res/layout/layout_create_file_java.xml
index 3ecd0c0c6e..15b4cd9c18 100755
--- a/app/src/main/res/layout/layout_create_file_java.xml
+++ b/app/src/main/res/layout/layout_create_file_java.xml
@@ -8,6 +8,35 @@
android:paddingTop="16dp"
android:paddingBottom="16dp">
+
+
+
+
+
+
+
+
Activity
Interface
Enum
+ Java
+ Kotlin
+ New Kotlin class
Failed to list project files