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) + } +}