diff --git a/.github/workflows/android-kotlin.yml b/.github/workflows/android-kotlin.yml
new file mode 100644
index 0000000..3d441b6
--- /dev/null
+++ b/.github/workflows/android-kotlin.yml
@@ -0,0 +1,45 @@
+name: Android Kotlin
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 8 * * 1"
+
+permissions:
+ contents: read
+
+jobs:
+ build-android-kotlin-app:
+ name: Build Android Kotlin app
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: "21"
+
+ - uses: android-actions/setup-android@v3
+
+ - name: Install Android SDK packages
+ run: sdkmanager "platforms;android-36" "build-tools;36.0.0"
+
+ - name: Build Ladybug Java jar
+ env:
+ SKIP_CMAKE_BUILD: "true"
+ run: |
+ ./gradlew jar
+ echo "LBUG_JAR=$(ls "$GITHUB_WORKSPACE"/build/libs/lbug-*.jar | head -n 1)" >> "$GITHUB_ENV"
+
+ - name: Build Android Kotlin example
+ run: ./gradlew -p examples/android-kotlin :app:assembleDebug -PlbugJar="$LBUG_JAR"
+
+ - name: Test Android Kotlin example on emulator
+ uses: ReactiveCircus/android-emulator-runner@v2
+ with:
+ api-level: 35
+ target: google_apis
+ arch: x86_64
+ profile: pixel_6
+ script: ./gradlew -p examples/android-kotlin :app:connectedDebugAndroidTest -PlbugJar="$LBUG_JAR"
diff --git a/examples/android-kotlin/app/build.gradle b/examples/android-kotlin/app/build.gradle
new file mode 100644
index 0000000..6083a27
--- /dev/null
+++ b/examples/android-kotlin/app/build.gradle
@@ -0,0 +1,27 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ namespace 'com.ladybugdb.example'
+ compileSdk 36
+
+ defaultConfig {
+ applicationId 'com.ladybugdb.example'
+ minSdk 26
+ targetSdk 36
+ versionCode 1
+ versionName '1.0'
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
+ }
+}
+
+dependencies {
+ def lbugJar = providers.gradleProperty('lbugJar')
+ if (!lbugJar.isPresent()) {
+ throw new GradleException('Pass -PlbugJar=/path/to/lbug.jar')
+ }
+ implementation files(lbugJar.get())
+ androidTestImplementation 'androidx.test:core-ktx:1.6.1'
+ androidTestImplementation 'androidx.test.ext:junit-ktx:1.2.1'
+}
diff --git a/examples/android-kotlin/app/src/androidTest/kotlin/com/ladybugdb/example/MainActivityInstrumentedTest.kt b/examples/android-kotlin/app/src/androidTest/kotlin/com/ladybugdb/example/MainActivityInstrumentedTest.kt
new file mode 100644
index 0000000..99762a3
--- /dev/null
+++ b/examples/android-kotlin/app/src/androidTest/kotlin/com/ladybugdb/example/MainActivityInstrumentedTest.kt
@@ -0,0 +1,28 @@
+package com.ladybugdb.example
+
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MainActivityInstrumentedTest {
+ @Test
+ fun kotlinApiCanCreateConfigOnAndroid() {
+ val config = exampleConfig()
+
+ assertEquals(2, config.maxNumThreads)
+ assertEquals(true, config.enableDefaultHashIndex)
+ }
+
+ @Test
+ fun activityLaunchesOnAndroid() {
+ ActivityScenario.launch(MainActivity::class.java).use { scenario ->
+ scenario.onActivity { activity ->
+ assertNotNull(activity.window.decorView)
+ }
+ }
+ }
+}
diff --git a/examples/android-kotlin/app/src/main/AndroidManifest.xml b/examples/android-kotlin/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b469852
--- /dev/null
+++ b/examples/android-kotlin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/android-kotlin/app/src/main/kotlin/com/ladybugdb/example/MainActivity.kt b/examples/android-kotlin/app/src/main/kotlin/com/ladybugdb/example/MainActivity.kt
new file mode 100644
index 0000000..fcbecf7
--- /dev/null
+++ b/examples/android-kotlin/app/src/main/kotlin/com/ladybugdb/example/MainActivity.kt
@@ -0,0 +1,24 @@
+package com.ladybugdb.example
+
+import android.app.Activity
+import android.os.Bundle
+import android.widget.TextView
+import com.ladybugdb.Database
+import com.ladybugdb.SystemConfig
+
+class MainActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val config = exampleConfig()
+ setContentView(TextView(this).apply {
+ text = "LadybugDB Kotlin example: ${config.maxNumThreads} threads"
+ })
+ }
+}
+
+fun exampleConfig(): SystemConfig = SystemConfig().apply {
+ maxNumThreads = 2
+ enableDefaultHashIndex = true
+}
+
+fun openDatabase(path: String, config: SystemConfig): Database = Database(path, config)
diff --git a/examples/android-kotlin/app/src/main/res/values/strings.xml b/examples/android-kotlin/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..5b1a276
--- /dev/null
+++ b/examples/android-kotlin/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ LadybugDB Kotlin
+
diff --git a/examples/android-kotlin/app/src/main/res/values/styles.xml b/examples/android-kotlin/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..27adb98
--- /dev/null
+++ b/examples/android-kotlin/app/src/main/res/values/styles.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/examples/android-kotlin/build.gradle b/examples/android-kotlin/build.gradle
new file mode 100644
index 0000000..2241417
--- /dev/null
+++ b/examples/android-kotlin/build.gradle
@@ -0,0 +1,3 @@
+plugins {
+ id 'com.android.application' version '9.0.0' apply false
+}
diff --git a/examples/android-kotlin/settings.gradle b/examples/android-kotlin/settings.gradle
new file mode 100644
index 0000000..1a8f12b
--- /dev/null
+++ b/examples/android-kotlin/settings.gradle
@@ -0,0 +1,18 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = 'ladybug-android-kotlin-example'
+include ':app'