Skip to content

Commit 3bae71b

Browse files
authored
Merge pull request #200 from kdroidFilter/feat/nucleus-graalvm-setup
Migrate sample to Nucleus plugin with GraalVM support
2 parents a76918c + df581e3 commit 3bae71b

8 files changed

Lines changed: 96 additions & 79 deletions

File tree

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.r
4444
compose-ui-tooling-preview = { module = "org.jetbrains.compose.components:components-ui-tooling-preview", version.ref = "compose" }
4545
compose-material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version = "1.7.3" }
4646
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidx-lifecycle-runtime-ktx" }
47+
nucleus-graalvm-runtime = { module = "io.github.kdroidfilter:nucleus.graalvm-runtime", version = "1.9.1" }
4748

4849
[plugins]
4950

@@ -57,3 +58,4 @@ vannitktech-maven-publish = {id = "com.vanniktech.maven.publish", version = "0.3
5758
dokka = { id = "org.jetbrains.dokka" , version = "2.2.0"}
5859
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
5960
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
61+
nucleus = { id = "io.github.kdroidfilter.nucleus", version = "1.9.1" }

mediaplayer/src/jvmMain/resources/META-INF/native-image/native-image.properties

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,39 @@
1-
[
2-
{
3-
"type": "io.github.kdroidfilter.composemediaplayer.linux.LinuxNativeBridge",
4-
"allDeclaredFields": true,
5-
"allDeclaredMethods": true,
6-
"allDeclaredConstructors": true
7-
},
8-
{
9-
"type": "io.github.kdroidfilter.composemediaplayer.mac.MacNativeBridge",
10-
"allDeclaredFields": true,
11-
"allDeclaredMethods": true,
12-
"allDeclaredConstructors": true
13-
},
14-
{
15-
"type": "io.github.kdroidfilter.composemediaplayer.windows.WindowsNativeBridge",
16-
"allDeclaredFields": true,
17-
"allDeclaredMethods": true,
18-
"allDeclaredConstructors": true
19-
},
20-
{
21-
"type": "java.lang.Runnable",
22-
"methods": [
23-
{ "name": "run", "parameterTypes": [] }
1+
{
2+
"reflection": [
3+
{
4+
"type": "io.github.kdroidfilter.composemediaplayer.linux.LinuxNativeBridge",
5+
"allDeclaredFields": true,
6+
"allDeclaredMethods": true,
7+
"allDeclaredConstructors": true,
8+
"jniAccessible": true
9+
},
10+
{
11+
"type": "io.github.kdroidfilter.composemediaplayer.mac.MacNativeBridge",
12+
"allDeclaredFields": true,
13+
"allDeclaredMethods": true,
14+
"allDeclaredConstructors": true,
15+
"jniAccessible": true
16+
},
17+
{
18+
"type": "io.github.kdroidfilter.composemediaplayer.windows.WindowsNativeBridge",
19+
"allDeclaredFields": true,
20+
"allDeclaredMethods": true,
21+
"allDeclaredConstructors": true,
22+
"jniAccessible": true
23+
},
24+
{
25+
"type": "java.lang.Runnable",
26+
"methods": [
27+
{
28+
"name": "run",
29+
"parameterTypes": []
30+
}
31+
]
32+
}
33+
],
34+
"resources": [
35+
{
36+
"glob": "composemediaplayer/native/**"
37+
}
2438
]
25-
}
26-
]
39+
}

sample/composeApp/build.gradle.kts

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
@file:OptIn(ExperimentalWasmDsl::class)
22

3+
import io.github.kdroidfilter.nucleus.desktop.application.dsl.CompressionLevel
34
import org.apache.tools.ant.taskdefs.condition.Os
4-
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
5+
import io.github.kdroidfilter.nucleus.desktop.application.dsl.TargetFormat
56
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
67
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
78

@@ -10,6 +11,7 @@ plugins {
1011
alias(libs.plugins.compose.compiler)
1112
alias(libs.plugins.compose)
1213
alias(libs.plugins.android.application)
14+
alias(libs.plugins.nucleus)
1315
}
1416

1517

@@ -81,6 +83,7 @@ kotlin {
8183

8284
jvmMain.dependencies {
8385
implementation(compose.desktop.currentOs)
86+
implementation(libs.nucleus.graalvm.runtime)
8487
}
8588
webMain.dependencies {
8689
implementation(libs.kotlinx.browser)
@@ -106,24 +109,32 @@ dependencies {
106109
debugImplementation(libs.compose.ui.tooling)
107110
}
108111

109-
compose.desktop {
110-
application {
111-
mainClass = "sample.app.MainKt"
112-
113-
nativeDistributions {
114-
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
115-
packageName = "sample"
116-
packageVersion = "1.0.0"
117-
linux {
118-
modules("jdk.security.auth", "jdk.accessibility")
119-
}
120-
macOS {
121-
jvmArgs(
122-
"-Dapple.awt.application.appearance=system"
123-
)
124-
}
112+
nucleus.application {
113+
mainClass = "sample.app.MainKt"
114+
115+
nativeDistributions {
116+
targetFormats(TargetFormat.Dmg, TargetFormat.Nsis, TargetFormat.Deb)
117+
packageName = "Compose Media Player"
118+
description = "A Kotlin Multiplatform media player built with Compose"
119+
vendor = "KDroidFilter"
120+
cleanupNativeLibs = true
121+
packageVersion = "1.0.0"
122+
compressionLevel = CompressionLevel.Maximum
123+
windows {
124+
shortcut = true
125125
}
126126
}
127+
128+
graalvm {
129+
isEnabled = true
130+
imageName = "compose-media-player"
131+
javaLanguageVersion = 25
132+
jvmVendor = JvmVendorSpec.BELLSOFT
133+
buildArgs.addAll(
134+
"-H:+AddAllCharsets",
135+
"-Djava.awt.headless=false"
136+
)
137+
}
127138
}
128139

129140

sample/composeApp/src/commonMain/kotlin/sample/app/App.kt

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import androidx.compose.runtime.setValue
2525
import androidx.compose.ui.Modifier
2626
import androidx.compose.ui.graphics.vector.ImageVector
2727
import androidx.compose.ui.unit.dp
28+
import io.github.kdroidfilter.composemediaplayer.VideoPlayerState
29+
import io.github.kdroidfilter.composemediaplayer.rememberVideoPlayerState
2830
import sample.app.feed.FeedScreen
2931
import sample.app.gallery.GalleryScreen
3032
import sample.app.player.PlayerScreen
@@ -40,22 +42,23 @@ private enum class Screen(val label: String, val icon: ImageVector) {
4042
fun App() {
4143
AppTheme {
4244
var currentScreen by remember { mutableStateOf(Screen.Player) }
45+
val playerState = rememberVideoPlayerState()
4346

4447
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
4548
val useRail = maxWidth >= 600.dp
4649

4750
if (useRail) {
48-
RailLayout(currentScreen, onScreenChange = { currentScreen = it })
51+
RailLayout(currentScreen, onScreenChange = { currentScreen = it }, playerState = playerState)
4952
} else {
50-
BarLayout(currentScreen, onScreenChange = { currentScreen = it })
53+
BarLayout(currentScreen, onScreenChange = { currentScreen = it }, playerState = playerState)
5154
}
5255
}
5356
}
5457
}
5558

5659
// Compact: bottom NavigationBar
5760
@Composable
58-
private fun BarLayout(current: Screen, onScreenChange: (Screen) -> Unit) {
61+
private fun BarLayout(current: Screen, onScreenChange: (Screen) -> Unit, playerState: VideoPlayerState) {
5962
Scaffold(
6063
bottomBar = {
6164
NavigationBar {
@@ -70,13 +73,13 @@ private fun BarLayout(current: Screen, onScreenChange: (Screen) -> Unit) {
7073
}
7174
},
7275
) { padding ->
73-
ScreenContent(current, Modifier.fillMaxSize().padding(padding))
76+
ScreenContent(current, Modifier.fillMaxSize().padding(padding), playerState)
7477
}
7578
}
7679

7780
// Medium+: side NavigationRail
7881
@Composable
79-
private fun RailLayout(current: Screen, onScreenChange: (Screen) -> Unit) {
82+
private fun RailLayout(current: Screen, onScreenChange: (Screen) -> Unit, playerState: VideoPlayerState) {
8083
Row(modifier = Modifier.fillMaxSize()) {
8184
NavigationRail {
8285
Spacer(Modifier.weight(1f))
@@ -90,14 +93,14 @@ private fun RailLayout(current: Screen, onScreenChange: (Screen) -> Unit) {
9093
}
9194
Spacer(Modifier.weight(1f))
9295
}
93-
ScreenContent(current, Modifier.weight(1f).fillMaxHeight())
96+
ScreenContent(current, Modifier.weight(1f).fillMaxHeight(), playerState)
9497
}
9598
}
9699

97100
@Composable
98-
private fun ScreenContent(screen: Screen, modifier: Modifier) {
101+
private fun ScreenContent(screen: Screen, modifier: Modifier, playerState: VideoPlayerState) {
99102
when (screen) {
100-
Screen.Player -> PlayerScreen(modifier)
103+
Screen.Player -> PlayerScreen(modifier, playerState)
101104
Screen.Gallery -> GalleryScreen(modifier)
102105
Screen.Feed -> FeedScreen(modifier)
103106
}

sample/composeApp/src/commonMain/kotlin/sample/app/player/PlayerScreen.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import androidx.compose.material3.Snackbar
4141
import androidx.compose.material3.Text
4242
import androidx.compose.material3.TextButton
4343
import androidx.compose.runtime.Composable
44+
import androidx.compose.runtime.DisposableEffect
4445
import androidx.compose.runtime.LaunchedEffect
4546
import androidx.compose.runtime.getValue
4647
import androidx.compose.runtime.mutableStateListOf
@@ -73,8 +74,17 @@ import kotlinx.coroutines.launch
7374

7475
@OptIn(ExperimentalMaterial3Api::class)
7576
@Composable
76-
fun PlayerScreen(modifier: Modifier = Modifier) {
77-
val playerState = rememberVideoPlayerState()
77+
fun PlayerScreen(modifier: Modifier = Modifier, playerState: VideoPlayerState = rememberVideoPlayerState()) {
78+
// Pause when leaving the screen, resume when coming back
79+
DisposableEffect(playerState) {
80+
val wasPlaying = playerState.isPlaying
81+
onDispose {
82+
if (playerState.isPlaying) {
83+
playerState.pause()
84+
}
85+
}
86+
}
87+
7888
val scope = rememberCoroutineScope()
7989

8090
var videoUrl by remember { mutableStateOf(SAMPLE_VIDEOS.first().second) }
Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package sample.app.theme
22

3-
import androidx.compose.foundation.isSystemInDarkTheme
43
import androidx.compose.material3.MaterialTheme
54
import androidx.compose.material3.darkColorScheme
6-
import androidx.compose.material3.lightColorScheme
75
import androidx.compose.runtime.Composable
86
import androidx.compose.ui.graphics.Color
97

@@ -33,34 +31,12 @@ private val DarkScheme = darkColorScheme(
3331
outlineVariant = Color(0xFF49454F),
3432
)
3533

36-
private val LightScheme = lightColorScheme(
37-
primary = Color(0xFF5B4FC4),
38-
onPrimary = Color.White,
39-
primaryContainer = Color(0xFFE8DEFF),
40-
onPrimaryContainer = Color(0xFF1A0063),
41-
secondary = Color(0xFF006A6A),
42-
onSecondary = Color.White,
43-
secondaryContainer = Color(0xFF9CF1F0),
44-
onSecondaryContainer = Color(0xFF002020),
45-
tertiary = Color(0xFF8C4A3B),
46-
onTertiary = Color.White,
47-
tertiaryContainer = Color(0xFFFFDAD4),
48-
onTertiaryContainer = Color(0xFF3A0905),
49-
background = Color(0xFFFCF8FF),
50-
onBackground = Color(0xFF1C1B1F),
51-
surface = Color(0xFFFCF8FF),
52-
onSurface = Color(0xFF1C1B1F),
53-
surfaceVariant = Color(0xFFE8E0F0),
54-
onSurfaceVariant = Color(0xFF49454F),
55-
)
56-
5734
@Composable
5835
fun AppTheme(
59-
darkTheme: Boolean = isSystemInDarkTheme(),
6036
content: @Composable () -> Unit,
6137
) {
6238
MaterialTheme(
63-
colorScheme = if (darkTheme) DarkScheme else LightScheme,
39+
colorScheme = DarkScheme,
6440
content = content,
6541
)
6642
}

sample/composeApp/src/jvmMain/kotlin/sample/app/main.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import androidx.compose.ui.unit.dp
44
import androidx.compose.ui.window.Window
55
import androidx.compose.ui.window.application
66
import androidx.compose.ui.window.rememberWindowState
7-
fun main() {
7+
import io.github.kdroidfilter.nucleus.graalvm.GraalVmInitializer
8+
9+
fun main() {
10+
GraalVmInitializer.initialize()
811
application {
912
val windowState = rememberWindowState(width = 720.dp, height = 1000.dp)
1013
Window(

0 commit comments

Comments
 (0)