diff --git a/docs/kotlin/android-studio-new-project-new.png b/docs/kotlin/android-studio-new-project-new.png new file mode 100644 index 00000000..deb3b437 Binary files /dev/null and b/docs/kotlin/android-studio-new-project-new.png differ diff --git a/docs/kotlin/android-studio-select-empty-activity-new.png b/docs/kotlin/android-studio-select-empty-activity-new.png new file mode 100644 index 00000000..53610afc Binary files /dev/null and b/docs/kotlin/android-studio-select-empty-activity-new.png differ diff --git a/docs/kotlin/getting-started.md b/docs/kotlin/getting-started.md index efb5fd88..dc07ae32 100644 --- a/docs/kotlin/getting-started.md +++ b/docs/kotlin/getting-started.md @@ -30,8 +30,8 @@ use a physical device. Once Android Studio is set up, go through the setup wizard and select an empty Activity to start building an application: -import SelectActivityScreenshot from './android-studio-select-empty-activity.png'; -import NewProjectScreenshot from './android-studio-new-project.png'; +import SelectActivityScreenshot from './android-studio-select-empty-activity-new.png'; +import NewProjectScreenshot from './android-studio-new-project-new.png'; 1. Create a new project with Android Studio. 2. Select an empty activity for the project and click "Next".
@@ -54,7 +54,7 @@ defining a new API for talking with Eliza! First, we need to add a Protobuf file that includes our service definition. For this tutorial, we are going to construct a unary endpoint for a service that is a stripped-down implementation of ELIZA, the famous natural language processing program. ```shell-session -$ mkdir -p proto && touch proto/eliza.proto +$ mkdir -p proto/connectrpc/eliza/v1 && touch proto/connectrpc/eliza/v1/eliza.proto ``` Open up the above file and add the following service definition: @@ -176,48 +176,47 @@ models for the `SayRequest` and `SayResponse` we defined in our Protobuf file. Now, let's bootstrap the Android application. Declare the following additional dependencies in our app's `build.gradle`. -:::note there exists two `build.gradle` files in the project. -Use the `build.gradle` located in `./app/build.gradle` and **not** the one in the root directory. +:::note there exists two `build.gradle.kts` files in the project. +Use the `build.gradle.kts` located in `./app` and **not** the one in the root directory. ::: -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; +build.gradle.kts: - - - -```groovy +```kotlin dependencies { - // ... - implementation 'androidx.recyclerview:recyclerview:1.2.1' - implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1" - implementation "com.squareup.okhttp3:okhttp:4.10.0" - implementation "com.connectrpc:connect-kotlin-okhttp:0.1.11" - // Java specific dependencies. - implementation "com.connectrpc:connect-kotlin-google-java-ext:0.1.11" - implementation "com.google.protobuf:protobuf-java:3.22.0" + // Compose + implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.lifecycle.runtime.compose) + + // Connect-Kotlin (latest as of writing: 0.8.0) + implementation(libs.okhttp) + implementation(libs.connect.kotlin.okhttp) + implementation(libs.connectrpc.connect.kotlin.google.java.ext) + implementation(libs.protobuf.java) } ``` - - - -```groovy -dependencies { - // ... - implementation 'androidx.recyclerview:recyclerview:1.2.1' - implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1" - implementation "com.squareup.okhttp3:okhttp:4.10.0" - implementation "com.connectrpc:connect-kotlin-okhttp:0.1.11" - // JavaLite specific dependencies. - implementation "com.connectrpc:connect-kotlin-google-javalite-ext:0.1.11" - implementation "com.google.protobuf:protobuf-javalite:3.22.0" -} +libs.versions.toml: +```toml +[versions] +#... +connectKotlinGoogleJavaExt = "0.8.0" +connectKotlinOkhttp = "0.8.0" +okhttp = "5.3.2" +protobufJava = "4.34.1" +lifecycleViewmodelCompose = "2.10.0" +lifecycleRuntimeCompose = "2.10.0" + +[libraries] +#... +androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } +connect-kotlin-okhttp = { module = "com.connectrpc:connect-kotlin-okhttp", version.ref = "connectKotlinOkhttp" } +connectrpc-connect-kotlin-google-java-ext = { module = "com.connectrpc:connect-kotlin-google-java-ext", version.ref = "connectKotlinGoogleJavaExt" } +lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +protobuf-java = { module = "com.google.protobuf:protobuf-java", version.ref = "protobufJava" } ``` - - - Once all the dependencies are declared, make sure Gradle is synced up. :::note @@ -228,63 +227,128 @@ versions between the runtime and the Google Java plugin version. Here we are usi
Having trouble with Gradle files? Here is what one might look like: -```groovy title="app/build.gradle" +```kotlin title="app/build.gradle.kts" plugins { - id 'com.android.application' - id 'org.jetbrains.kotlin.android' + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.compose) } android { - namespace 'com.example.eliza' - compileSdk 33 - - defaultConfig { - applicationId "com.example.eliza" - minSdk 24 - targetSdk 33 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + namespace = "com.example.eliza" + compileSdk { + version = release(36) { + minorApiLevel = 1 + } } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} -dependencies { - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + defaultConfig { + applicationId = "com.example.eliza" + minSdk = 24 + targetSdk = 36 + versionCode = 1 + versionName = "1.0" - implementation "androidx.recyclerview:recyclerview:1.2.1" - implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } - implementation "com.squareup.okhttp3:okhttp:4.10.0" - implementation "com.connectrpc:connect-kotlin-okhttp:$version" + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + buildFeatures { + compose = true + } +} - implementation "com.connectrpc:connect-kotlin-google-java-ext:$version" - implementation "com.google.protobuf:protobuf-java:3.22.0" +dependencies { + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.activity.compose) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.graphics) + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + + // Compose + implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.lifecycle.runtime.compose) + + // Connect-Kotlin (latest as of writing: 0.8.0) + implementation(libs.okhttp) + implementation(libs.connect.kotlin.okhttp) + implementation(libs.connectrpc.connect.kotlin.google.java.ext) + implementation(libs.protobuf.java) + + testImplementation(libs.junit) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.compose.ui.test.junit4) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + debugImplementation(libs.androidx.compose.ui.test.manifest) + debugImplementation(libs.androidx.compose.ui.tooling) } ``` +```toml title="gradle/libs.versions.toml" +[versions] +agp = "9.2.0" +connectKotlinGoogleJavaExt = "0.8.0" +connectKotlinOkhttp = "0.8.0" +coreKtx = "1.18.0" +junit = "4.13.2" +junitVersion = "1.3.0" +espressoCore = "3.7.0" +lifecycleRuntimeCompose = "2.10.0" +lifecycleRuntimeKtx = "2.10.0" +activityCompose = "1.13.0" +kotlin = "2.3.21" +composeBom = "2026.04.01" +lifecycleViewmodelCompose = "2.10.0" +okhttp = "5.3.2" +protobufJava = "4.34.1" + + +[libraries] +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } +connect-kotlin-okhttp = { module = "com.connectrpc:connect-kotlin-okhttp", version.ref = "connectKotlinOkhttp" } +connectrpc-connect-kotlin-google-java-ext = { module = "com.connectrpc:connect-kotlin-google-java-ext", version.ref = "connectKotlinGoogleJavaExt" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } +androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } +lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +protobuf-java = { module = "com.google.protobuf:protobuf-java", version.ref = "protobufJava" } + + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +``` + :::note -The default for Android Studio isn't set up with Gradle `kts`. The examples here are using -classic Gradle with Groovy. With Gradle `kts`, the changes are pretty similar for the dependency -declarations. +The default for Android Studio is now Gradle `kts`. For classic Gradle with Groovy, the changes are similar for the dependency declarations, but there is no `libs.versions.toml` file. ::: @@ -292,252 +356,118 @@ declarations. ## Set up an Android application -### Set up resources and Android XML +### Create the View and Model -First, set up the `res` directory files by creating and copying the following files to the -project: +Now we are ready to write the Kotlin code to speak with Eliza! -Create a new file in the `layout` directory called `item.xml` for a chat view item. +Create a new Kotlin file in the `app/src/main/java/com/example/eliza` directory called `ElizaViewModel.kt`. First, we will create the model classes for the UI state and the Eliza service client. -```shell-session -$ touch app/src/main/res/layout/item.xml -``` - -The following layout XML is going to be used as the chat list item. -It's the XML representation of what a single chat entry looks like. - -```xml title="app/src/main/res/layout/item.xml" - - - - - -``` - -Next, we'll need to set up our main view's XML. This will be the view displayed on app launch. - -```xml title="app/src/main/res/layout/activity_main.xml" - - - - - -