Add Kotlin Multiplatform mobile client

A Compose Multiplatform app (mobile/) targeting Android, iOS, and desktop,
sharing one Kotlin codebase: OIDC login via PKCE against the same Zitadel
instance the web player uses (loopback redirect on desktop, a musify://
custom scheme on mobile), a token store per platform, and a first pass at
browsing/streaming tracks through the existing API.

deploy/compose.yml's zitadel-init gains a third OIDC app registration
("Musify Native", public client, no secret) alongside the existing API and
web apps, writing its client id to NATIVE_CLIENT_ID in the env file the same
way the other two already do.
This commit is contained in:
2026-09-10 10:03:11 +02:00
parent aca7238020
commit c5b2b71eea
50 changed files with 2200 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
[versions]
kotlin = "2.1.0"
agp = "8.6.1"
compose-multiplatform = "1.7.1"
compileSdk = "35"
minSdk = "26"
targetSdk = "35"
ktor = "3.0.1"
coroutines = "1.9.0"
serialization = "1.7.3"
coil = "3.0.4"
androidx-activity = "1.9.3"
androidx-browser = "1.8.0"
androidx-lifecycle = "2.8.4"
androidx-datastore = "1.1.1"
media3 = "1.4.1"
javafx = "21.0.4"
[libraries]
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-browser = { module = "androidx.browser:browser", version.ref = "androidx-browser" }
androidx-lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtime-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
androidx-media3-common = { module = "androidx.media3:media3-common", version.ref = "media3" }
[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
androidApplication = { id = "com.android.application", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists