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:
@@ -0,0 +1,156 @@
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
// Backend connection settings, read from gradle.properties so every target
|
||||
// (Android, Desktop, iOS) is generated with the same values without
|
||||
// duplicating them in three places. See mobile/README.md.
|
||||
val apiBaseUrl: String = providers.gradleProperty("musify.apiBaseUrl").get()
|
||||
val authIssuer: String = providers.gradleProperty("musify.authIssuer").get()
|
||||
val authClientId: String = providers.gradleProperty("musify.authClientId").get()
|
||||
|
||||
// JavaFX Media's os/arch classifier — the demo only needs to run on this box, but
|
||||
// this keeps `./gradlew :composeApp:run` working on any dev machine unmodified.
|
||||
val javafxClassifier: String = run {
|
||||
val os = org.gradle.internal.os.OperatingSystem.current()
|
||||
val arch = System.getProperty("os.arch")
|
||||
when {
|
||||
os.isWindows -> "win"
|
||||
os.isMacOsX -> if (arch.contains("aarch64")) "mac-aarch64" else "mac"
|
||||
else -> if (arch.contains("aarch64")) "linux-aarch64" else "linux"
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
androidTarget {
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
}
|
||||
}
|
||||
|
||||
jvm("desktop")
|
||||
|
||||
listOf(
|
||||
iosX64(),
|
||||
iosArm64(),
|
||||
iosSimulatorArm64()
|
||||
).forEach { iosTarget: KotlinNativeTarget ->
|
||||
iosTarget.binaries.framework {
|
||||
baseName = "ComposeApp"
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.ui)
|
||||
implementation(compose.components.resources)
|
||||
implementation(compose.components.uiToolingPreview)
|
||||
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.client.content.negotiation)
|
||||
implementation(libs.ktor.serialization.kotlinx.json)
|
||||
implementation(libs.ktor.client.logging)
|
||||
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.network.ktor)
|
||||
}
|
||||
}
|
||||
|
||||
val androidMain by getting {
|
||||
dependencies {
|
||||
implementation(libs.ktor.client.okhttp)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.browser)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(libs.androidx.media3.exoplayer)
|
||||
implementation(libs.androidx.media3.common)
|
||||
}
|
||||
}
|
||||
|
||||
val desktopMain by getting {
|
||||
dependencies {
|
||||
implementation(compose.desktop.currentOs)
|
||||
implementation(libs.ktor.client.cio)
|
||||
// MediaPlayer only, via Platform.startup() — no javafx-swing/AWT needed.
|
||||
// OpenJFX's sibling-module POM dependencies aren't classifier-aware in
|
||||
// Gradle, so javafx-base/-graphics must be pinned explicitly too, or
|
||||
// Gradle resolves their classifier-less (empty) stub jars instead.
|
||||
val javafxVersion = libs.versions.javafx.get()
|
||||
implementation("org.openjfx:javafx-base:$javafxVersion:$javafxClassifier")
|
||||
implementation("org.openjfx:javafx-graphics:$javafxVersion:$javafxClassifier")
|
||||
implementation("org.openjfx:javafx-media:$javafxVersion:$javafxClassifier")
|
||||
}
|
||||
}
|
||||
|
||||
val iosMain by creating {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
implementation(libs.ktor.client.darwin)
|
||||
}
|
||||
}
|
||||
getByName("iosX64Main").dependsOn(iosMain)
|
||||
getByName("iosArm64Main").dependsOn(iosMain)
|
||||
getByName("iosSimulatorArm64Main").dependsOn(iosMain)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.musify.app"
|
||||
compileSdk = libs.versions.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.musify.app"
|
||||
minSdk = libs.versions.minSdk.get().toInt()
|
||||
targetSdk = libs.versions.targetSdk.get().toInt()
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
buildConfigField("String", "API_BASE_URL", "\"$apiBaseUrl\"")
|
||||
buildConfigField("String", "AUTH_ISSUER", "\"$authIssuer\"")
|
||||
buildConfigField("String", "AUTH_CLIENT_ID", "\"$authClientId\"")
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
compose = true
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
packaging {
|
||||
resources.excludes.add("/META-INF/{AL2.0,LGPL2.1}")
|
||||
}
|
||||
}
|
||||
|
||||
compose.desktop {
|
||||
application {
|
||||
mainClass = "com.musify.app.MainKt"
|
||||
jvmArgs += listOf(
|
||||
"-Dmusify.apiBaseUrl=$apiBaseUrl",
|
||||
"-Dmusify.authIssuer=$authIssuer",
|
||||
"-Dmusify.authClientId=$authClientId"
|
||||
)
|
||||
nativeDistributions {
|
||||
targetFormats(org.jetbrains.compose.desktop.application.dsl.TargetFormat.Msi)
|
||||
packageName = "MusifyMobile"
|
||||
packageVersion = "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user