diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 2cd31027e..02926cfab 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -9,6 +9,7 @@ plugins { alias(libs.plugins.jetbrains.kotlin.compose) alias(libs.plugins.jetbrains.compose.multiplatform) alias(libs.plugins.koin) + alias(libs.plugins.jetbrains.kotlinx.serialization) } kotlin { @@ -80,6 +81,7 @@ kotlin { implementation(libs.jetbrains.navigation3.ui) implementation(libs.jetbrains.lifecycle.navigation3) + implementation(libs.kotlinx.serialization.json) implementation(libs.koin.compose.viewmodel) implementation(libs.koin.annotations) diff --git a/shared/src/androidMain/kotlin/net/newpipe/Constants.kt b/shared/src/androidMain/kotlin/net/newpipe/Constants.kt new file mode 100644 index 000000000..164256943 --- /dev/null +++ b/shared/src/androidMain/kotlin/net/newpipe/Constants.kt @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe + +object Constants { + + const val INTENT_SCREEN_KEY = "SCREEN" +} diff --git a/shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt b/shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt index dc1184db1..ccd7893a0 100644 --- a/shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt +++ b/shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt @@ -9,6 +9,9 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import kotlinx.serialization.json.Json +import net.newpipe.Constants +import net.newpipe.app.navigation.Screen /** * Entry point for compose-related UI components on Android @@ -19,7 +22,12 @@ class ComposeActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { - App() + App( + // TODO: Change when everything is in compose and this is the primary activity + startDestination = Json.decodeFromString( + intent.getStringExtra(Constants.INTENT_SCREEN_KEY)!! + ) + ) } } } diff --git a/shared/src/androidMain/kotlin/net/newpipe/app/extensions/Context.kt b/shared/src/androidMain/kotlin/net/newpipe/app/extensions/Context.kt new file mode 100644 index 000000000..638d10dcb --- /dev/null +++ b/shared/src/androidMain/kotlin/net/newpipe/app/extensions/Context.kt @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.extensions + +import android.content.Context +import android.content.Intent +import kotlin.jvm.java +import kotlinx.serialization.json.Json +import net.newpipe.Constants +import net.newpipe.app.ComposeActivity +import net.newpipe.app.navigation.Screen + +/** + * Navigates to a given compose destination + */ +fun Context.navigateTo(screen: Screen) = Intent(this, ComposeActivity::class.java).also { intent -> + intent.putExtra(Constants.INTENT_SCREEN_KEY, Json.encodeToString(screen)) + startActivity(intent) +} diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/App.kt b/shared/src/commonMain/kotlin/net/newpipe/app/App.kt index 4cc36fc34..81cdbce63 100644 --- a/shared/src/commonMain/kotlin/net/newpipe/app/App.kt +++ b/shared/src/commonMain/kotlin/net/newpipe/app/App.kt @@ -6,15 +6,18 @@ package net.newpipe.app import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview import net.newpipe.app.di.KoinApp +import net.newpipe.app.navigation.Screen import net.newpipe.app.theme.AppTheme import org.koin.compose.KoinApplication import org.koin.plugin.module.dsl.koinConfiguration +/** + * Entry point for the multiplatform compose application + * @param startDestination Starting destination for the app + */ @Composable -@Preview -fun App() { +fun App(startDestination: Screen? = null) { KoinApplication(configuration = koinConfiguration()) { AppTheme { } diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/navigation/NavDisplay.kt b/shared/src/commonMain/kotlin/net/newpipe/app/navigation/NavDisplay.kt new file mode 100644 index 000000000..8592b20b9 --- /dev/null +++ b/shared/src/commonMain/kotlin/net/newpipe/app/navigation/NavDisplay.kt @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.navigation + +import androidx.compose.runtime.Composable +import androidx.navigation3.runtime.entryProvider +import androidx.navigation3.runtime.rememberNavBackStack +import androidx.navigation3.ui.NavDisplay + +/** + * Navigation display for compose screens + * @param startDestination Starting destination for the app + */ +@Composable +fun NavDisplay(startDestination: Screen) { + val backstack = rememberNavBackStack(screenConfig, startDestination) + + NavDisplay( + backStack = backstack, + entryProvider = entryProvider { + } + ) +} diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/navigation/Screen.kt b/shared/src/commonMain/kotlin/net/newpipe/app/navigation/Screen.kt new file mode 100644 index 000000000..6f5822a50 --- /dev/null +++ b/shared/src/commonMain/kotlin/net/newpipe/app/navigation/Screen.kt @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.navigation + +import androidx.navigation3.runtime.NavKey +import androidx.savedstate.serialization.SavedStateConfiguration +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic + +/** + * Destinations for navigation in compose + */ +@Serializable +sealed interface Screen : NavKey + +/** + * Saved state configuration for screens + */ +@OptIn(ExperimentalSerializationApi::class) +internal val screenConfig = SavedStateConfiguration { + serializersModule = SerializersModule { + polymorphic(NavKey::class) { + subclassesOfSealed() + } + } +}