diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/integTest/groovy/org/gradle/api/experimental/android/AndroidApplicationAgpPreview.groovy b/unified-prototype/unified-plugin/plugin-android-init/src/integTest/groovy/org/gradle/api/experimental/android/AndroidApplicationAgpPreview.groovy
new file mode 100644
index 00000000..22010255
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/integTest/groovy/org/gradle/api/experimental/android/AndroidApplicationAgpPreview.groovy
@@ -0,0 +1,8 @@
+package org.gradle.api.experimental.android
+
+class AndroidApplicationAgpPreview extends AbstractAndroidBuildInitSpec {
+ @Override
+ protected String getProjectSpecType() {
+ return "android-application-agp-preview"
+ }
+}
\ No newline at end of file
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/java/org/gradle/api/experimental/android/AndroidEcosystemInitPlugin.java b/unified-prototype/unified-plugin/plugin-android-init/src/main/java/org/gradle/api/experimental/android/AndroidEcosystemInitPlugin.java
index 7c16fce2..23a3784a 100644
--- a/unified-prototype/unified-plugin/plugin-android-init/src/main/java/org/gradle/api/experimental/android/AndroidEcosystemInitPlugin.java
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/java/org/gradle/api/experimental/android/AndroidEcosystemInitPlugin.java
@@ -17,7 +17,8 @@ public void apply(Settings settings) {
getBuildInitSpecRegistry().register(StaticBuildGenerator.class, List.of(
new StaticBuildSpec("android-application", "Android application with Android libraries"),
new StaticBuildSpec("android-application-basic-activity", "Android Application with a basic Activity"),
- new StaticBuildSpec("android-application-empty-activity", "Android Application with an empty Activity")
+ new StaticBuildSpec("android-application-empty-activity", "Android Application with an empty Activity"),
+ new StaticBuildSpec("android-application-agp-preview", "Android Application using Official AGP Software Types Preview")
));
}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/README.md b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/README.md
new file mode 100644
index 00000000..75101c90
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/README.md
@@ -0,0 +1,20 @@
+# declarative-samples-android-app
+A sample Android application written in the Declarative Gradle DSL, using the official Android Software Types Preview `androidApplication` and `androidLibrary` defined in the `com.android.ecosystem` ecosystem plugin.
+
+## Building and Running
+
+This sample shows the definition of a multiproject Android application implemented using Kotlin source code.
+
+To build the project without running, use:
+
+```shell
+./gradlew build
+```
+
+To run the application, first install it on a connected Android device using:
+
+```shell
+./gradlew :app:installDebug
+```
+
+In IntelliJ IDEA or Android Studio you can use the `app` run configuration to launch the app in an emulator to see a hello world message.
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/build.gradle.dcl b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/build.gradle.dcl
new file mode 100644
index 00000000..52f8a546
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/build.gradle.dcl
@@ -0,0 +1,7 @@
+androidApp {
+ namespace = "org.example.app"
+ dependenciesDcl {
+ implementation("org.apache.commons:commons-text:1.11.0")
+ implementation(project(":utilities"))
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/AndroidManifest.xml b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..68131cf8
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/kotlin/org/example/app/MainActivity.kt b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/kotlin/org/example/app/MainActivity.kt
new file mode 100644
index 00000000..1d8d7548
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/kotlin/org/example/app/MainActivity.kt
@@ -0,0 +1,28 @@
+package org.example.app
+
+import org.apache.commons.text.WordUtils
+
+import org.example.list.LinkedList
+import org.example.utilities.SplitUtils
+import org.example.utilities.StringUtils
+
+import android.widget.TextView
+import android.os.Bundle
+import android.app.Activity
+
+class MainActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ val textView = findViewById(R.id.textView) as TextView
+ textView.text = buildMessage()
+ }
+
+ private fun buildMessage(): String {
+ val tokens: LinkedList
+ tokens = SplitUtils.split(MessageUtils.message())
+ val result: String = StringUtils.join(tokens)
+ return WordUtils.capitalize(result)
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/kotlin/org/example/app/MessageUtils.kt b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/kotlin/org/example/app/MessageUtils.kt
new file mode 100644
index 00000000..4d1fc1a9
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/kotlin/org/example/app/MessageUtils.kt
@@ -0,0 +1,5 @@
+package org.example.app
+
+internal object MessageUtils {
+ fun message() = "Hello World!"
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/res/layout/activity_main.xml b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..60ae7dc8
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/res/values/strings.xml b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..a4e5ed21
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Sample Declarative Gradle Android App
+
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/test/kotlin/org/example/app/MessageUtilsTest.kt b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/test/kotlin/org/example/app/MessageUtilsTest.kt
new file mode 100644
index 00000000..9d5ff979
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/app/src/test/kotlin/org/example/app/MessageUtilsTest.kt
@@ -0,0 +1,12 @@
+package org.example.app
+
+import org.junit.jupiter.api.Test
+
+import org.junit.jupiter.api.Assertions.assertEquals
+
+class MessageUtilsTest {
+ @Test
+ fun testGetMessage() {
+ assertEquals("Hello World!", MessageUtils.message())
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/gradle.properties b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/gradle.properties
new file mode 100644
index 00000000..9e4aba56
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/gradle.properties
@@ -0,0 +1,6 @@
+org.gradle.caching=true
+org.gradle.parallel=true
+org.gradle.configuration-cache=true
+kotlin.code.style=official
+android.experimental.declarative=true
+android.useAndroidX=true
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/list/build.gradle.dcl b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/list/build.gradle.dcl
new file mode 100644
index 00000000..168d0a1e
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/list/build.gradle.dcl
@@ -0,0 +1,3 @@
+androidLibrary {
+ namespace = "org.gradle.experimental.android.list"
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/list/src/main/kotlin/org/example/list/LinkedList.kt b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/list/src/main/kotlin/org/example/list/LinkedList.kt
new file mode 100644
index 00000000..388c15e6
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/list/src/main/kotlin/org/example/list/LinkedList.kt
@@ -0,0 +1,86 @@
+package org.example.list
+
+class LinkedList {
+ private var head: Node? = null
+
+ fun add(element: String?) {
+ val newNode = Node(element)
+
+ val it = tail(head)
+ if (it == null) {
+ head = newNode
+ } else {
+ it.next = newNode
+ }
+ }
+
+ fun remove(element: String): Boolean {
+ var result = false
+ var previousIt: Node? = null
+ var it: Node?
+ it = head
+ while (!result && it != null) {
+ if (0 == element.compareTo(it.data!!)) {
+ result = true
+ unlink(previousIt, it)
+ break
+ }
+ previousIt = it
+ it = it.next
+ }
+
+ return result
+ }
+
+ private fun unlink(previousIt: Node?, currentIt: Node) {
+ if (currentIt === head) {
+ head = currentIt.next
+ } else {
+ previousIt!!.next = currentIt.next
+ }
+ }
+
+ fun size(): Int {
+ var size = 0
+
+ var it = head
+ while (it != null) {
+ ++size
+ it = it.next
+ }
+
+ return size
+ }
+
+ fun get(index: Int): String? {
+ var currIdx = index
+ var it = head
+ while (currIdx > 0 && it != null) {
+ it = it.next
+ currIdx--
+ }
+
+ if (it == null) {
+ throw java.lang.IndexOutOfBoundsException("Index is out of range")
+ }
+
+ return it.data
+ }
+
+ private class Node(val data: String?) {
+ var next: Node? = null
+ }
+
+ companion object {
+ private fun tail(head: Node?): Node? {
+ var it: Node?
+
+ it = head
+ while (it?.next != null) {
+ it = it.next
+ }
+
+ return it
+ }
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/settings.gradle.dcl b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/settings.gradle.dcl
new file mode 100644
index 00000000..74badb0c
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/settings.gradle.dcl
@@ -0,0 +1,68 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ maven {
+ url = uri("https://androidx.dev/studio/builds/12648882/artifacts/artifacts/repository")
+ }
+ }
+}
+
+plugins {
+ id("com.android.ecosystem").version("8.9.0-dev")
+}
+
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ maven {
+ url = uri("https://androidx.dev/studio/builds/12648882/artifacts/artifacts/repository")
+ }
+ }
+}
+
+rootProject.name = "example-android-app"
+
+include("app")
+include("list")
+include("utilities")
+
+defaults {
+ androidApp {
+ compileSdk = 34
+ compileOptions {
+ sourceCompatibility = VERSION_17
+ targetCompatibility = VERSION_17
+ }
+ defaultConfig {
+ minSdk = 30
+ versionCode = 1
+ versionName = "0.1"
+ applicationId = "org.gradle.experimental.android.app"
+ }
+ dependenciesDcl {
+ implementation("org.jetbrains.kotlin:kotlin-stdlib:2.0.21")
+ testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
+ testImplementation("org.junit.platform:junit-platform-launcher")
+ androidTestImplementation("org.jetbrains.kotlin:kotlin-stdlib:2.0.21")
+ }
+ }
+
+ androidLibrary {
+ compileSdk = 34
+ compileOptions {
+ sourceCompatibility = VERSION_17
+ targetCompatibility = VERSION_17
+ }
+ defaultConfig {
+ minSdk = 30
+ }
+ dependenciesDcl {
+ implementation("org.jetbrains.kotlin:kotlin-stdlib:2.0.21")
+ testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
+ testImplementation("org.junit.platform:junit-platform-launcher")
+ androidTestImplementation("org.jetbrains.kotlin:kotlin-stdlib:2.0.21")
+ }
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/build.gradle.dcl b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/build.gradle.dcl
new file mode 100644
index 00000000..1cc11bac
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/build.gradle.dcl
@@ -0,0 +1,6 @@
+androidLibrary {
+ namespace = "org.gradle.experimental.android.utilities"
+ dependenciesDcl {
+ api(project(":list"))
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/JoinUtils.kt b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/JoinUtils.kt
new file mode 100644
index 00000000..f65b98e7
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/JoinUtils.kt
@@ -0,0 +1,17 @@
+package org.example.utilities
+
+import org.example.list.LinkedList
+
+internal object JoinUtils {
+ fun join(source: LinkedList): String {
+ val result: java.lang.StringBuilder = java.lang.StringBuilder()
+ for (i in 0 until source.size()) {
+ if (result.length > 0) {
+ result.append(" ")
+ }
+ result.append(source.get(i))
+ }
+
+ return result.toString()
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/SplitUtils.kt b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/SplitUtils.kt
new file mode 100644
index 00000000..0b17d67b
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/SplitUtils.kt
@@ -0,0 +1,36 @@
+package org.example.utilities
+
+import org.example.list.LinkedList
+
+object SplitUtils {
+ fun split(source: String): LinkedList {
+ var lastFind = 0
+ var currentFind: Int
+ val result: LinkedList = LinkedList()
+
+ while ((source.indexOf(" ", lastFind).also { currentFind = it }) != -1) {
+ var token: String = source.substring(lastFind)
+ if (currentFind != -1) {
+ token = token.substring(0, currentFind - lastFind)
+ }
+
+ addIfValid(token, result)
+ lastFind = currentFind + 1
+ }
+
+ val token: String = source.substring(lastFind)
+ addIfValid(token, result)
+
+ return result
+ }
+
+ private fun addIfValid(token: String, list: LinkedList) {
+ if (isTokenValid(token)) {
+ list.add(token)
+ }
+ }
+
+ private fun isTokenValid(token: String): Boolean {
+ return !token.isEmpty()
+ }
+}
diff --git a/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/StringUtils.kt b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/StringUtils.kt
new file mode 100644
index 00000000..23dc2075
--- /dev/null
+++ b/unified-prototype/unified-plugin/plugin-android-init/src/main/resources/templates/android-application-agp-preview/utilities/src/main/kotlin/org/example/utilities/StringUtils.kt
@@ -0,0 +1,13 @@
+package org.example.utilities
+
+import org.example.list.LinkedList
+
+object StringUtils {
+ fun join(source: LinkedList): String {
+ return JoinUtils.join(source)
+ }
+
+ fun split(source: String): LinkedList {
+ return SplitUtils.split(source)
+ }
+}