diff --git a/build.gradle.kts b/build.gradle.kts index 2c9173f57..ca5003b2c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,8 +5,13 @@ plugins { alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false alias(libs.plugins.jetbrains.kotlin.kapt) apply false + alias(libs.plugins.jetbrains.kotlin.compose) apply false + alias(libs.plugins.jetbrains.kotlin.multiplatform) apply false + alias(libs.plugins.jetbrains.compose.multiplatform) apply false + alias(libs.plugins.jetbrains.compose.hotreload) apply false alias(libs.plugins.google.ksp) apply false alias(libs.plugins.jetbrains.kotlin.parcelize) apply false alias(libs.plugins.sonarqube) apply false diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts new file mode 100644 index 000000000..952c7063c --- /dev/null +++ b/composeApp/build.gradle.kts @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.jetbrains.kotlin.multiplatform) + alias(libs.plugins.jetbrains.kotlin.compose) + alias(libs.plugins.jetbrains.compose.multiplatform) + alias(libs.plugins.jetbrains.compose.hotreload) + alias(libs.plugins.google.ksp) + alias(libs.plugins.jetbrains.kotlin.parcelize) +} + +kotlin { + jvmToolchain(17) + + androidLibrary { + namespace = "net.newpipe.app" + compileSdk = 36 + minSdk = 21 + } + + listOf( + iosArm64(), + iosSimulatorArm64() + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = "ComposeApp" + isStatic = true + } + } + + jvm() + + sourceSets { + commonMain.dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + implementation(libs.jetbrains.lifecycle.viewmodel) + } + commonTest.dependencies { + implementation(libs.kotlin.test) + } + androidMain.dependencies { + implementation(compose.preview) + implementation(libs.androidx.activity) + } + jvmMain.dependencies { + implementation(compose.desktop.currentOs) + implementation(libs.jetbrains.kotlinx.coroutinesSwing) + } + } +} + +compose.desktop { + application { + mainClass = "net.newpipe.app.MainKt" + + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "net.newpipe.app" + packageVersion = "1.0.0" + } + } +} diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..2a78e9e3c --- /dev/null +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/composeApp/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt b/composeApp/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt new file mode 100644 index 000000000..ab716d9ed --- /dev/null +++ b/composeApp/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge + +class ComposeActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + + setContent { + App() + } + } +} diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 000000000..db4b09e00 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,8 @@ + + + + NewPipe + diff --git a/composeApp/src/commonMain/kotlin/net/newpipe/app/App.kt b/composeApp/src/commonMain/kotlin/net/newpipe/app/App.kt new file mode 100644 index 000000000..b6cfe27b1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/newpipe/app/App.kt @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +@Preview +fun App() { + MaterialTheme { + + } +} diff --git a/composeApp/src/commonTest/kotlin/net/newpipe/app/ComposeAppCommonTest.kt b/composeApp/src/commonTest/kotlin/net/newpipe/app/ComposeAppCommonTest.kt new file mode 100644 index 000000000..1dc476190 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/net/newpipe/app/ComposeAppCommonTest.kt @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app + +import kotlin.test.Test +import kotlin.test.assertEquals + +class ComposeAppCommonTest { + + @Test + fun example() { + assertEquals(3, 1 + 2) + } +} diff --git a/composeApp/src/iosMain/kotlin/net/newpipe/app/MainViewController.kt b/composeApp/src/iosMain/kotlin/net/newpipe/app/MainViewController.kt new file mode 100644 index 000000000..d368521e8 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/net/newpipe/app/MainViewController.kt @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app + +import androidx.compose.ui.window.ComposeUIViewController + +fun MainViewController() = ComposeUIViewController { App() } diff --git a/composeApp/src/jvmMain/kotlin/net/newpipe/app/main.kt b/composeApp/src/jvmMain/kotlin/net/newpipe/app/main.kt new file mode 100644 index 000000000..b5faff050 --- /dev/null +++ b/composeApp/src/jvmMain/kotlin/net/newpipe/app/main.kt @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app + +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import newpipe.composeapp.generated.resources.Res +import newpipe.composeapp.generated.resources.app_name +import org.jetbrains.compose.resources.stringResource + +fun main() = application { + Window(onCloseRequest = ::exitApplication, title = stringResource(Res.string.app_name)) { + App() + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a7a856a41..ad2591358 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ [versions] acra = "5.13.1" +activity = "1.12.2" agp = "8.13.2" appcompat = "1.7.1" assertj = "3.27.6" @@ -15,11 +16,13 @@ cardview = "1.0.0" checkstyle = "12.2.0" constraintlayout = "2.2.1" core = "1.17.0" +coroutines = "1.10.2" desugar = "2.1.5" documentfile = "1.1.0" exoplayer = "2.19.1" fragment = "1.8.9" groupie = "2.10.1" +hotreload = "1.0.0" jsoup = "1.21.2" junit = "4.13.2" junit-ext = "1.3.0" @@ -28,11 +31,13 @@ ksp = "2.3.2" ktlint = "1.8.0" leakcanary = "2.14" lifecycle = "2.9.4" # Newer versions require minSdk >= 23 +lifecycle-jetbrains = "2.9.6" localbroadcastmanager = "1.1.0" markwon = "4.6.2" material = "1.11.0" # TODO: update to newer version after bug is fixed. See https://github.com/TeamNewPipe/NewPipe/pull/13018 media = "1.7.1" mockitoCore = "5.21.0" +multiplatform = "1.9.3" okhttp = "5.3.2" phoenix = "3.0.0" #noinspection NewerVersionAvailable,GradleDependency --> 2.8 is the last version, not 2.71828! @@ -67,6 +72,7 @@ work = "2.10.5" # Newer versions require minSdk >= 23 [libraries] acra-core = { module = "ch.acra:acra-core", version.ref = "acra" } android-desugar = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "desugar" } +androidx-activity = { module = "androidx.activity:activity-compose", version.ref = "activity" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } androidx-cardview = { module = "androidx.cardview:cardview", version.ref = "cardview" } androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } @@ -107,8 +113,11 @@ google-exoplayer-smoothstreaming = { module = "com.google.android.exoplayer:exop google-exoplayer-ui = { module = "com.google.android.exoplayer:exoplayer-ui", version.ref = "exoplayer" } jakewharton-phoenix = { module = "com.jakewharton:process-phoenix", version.ref = "phoenix" } jakewharton-rxbinding = { module = "com.jakewharton.rxbinding4:rxbinding", version.ref = "rxbinding" } +jetbrains-kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" } +jetbrains-lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle-jetbrains" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } junit = { module = "junit:junit", version.ref = "junit" } +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } lisawray-groupie-core = { module = "com.github.lisawray.groupie:groupie", version.ref = "groupie" } lisawray-groupie-viewbinding = { module = "com.github.lisawray.groupie:groupie-viewbinding", version.ref = "groupie" } livefront-bridge = { module = "com.github.livefront:bridge", version.ref = "bridge" } @@ -132,8 +141,13 @@ zacsweers-autoservice-compiler = { module = "dev.zacsweers.autoservice:auto-serv [plugins] android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" } google-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +jetbrains-compose-hotreload = { id = "org.jetbrains.compose.hot-reload", version.ref = "hotreload" } +jetbrains-compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "multiplatform" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +jetbrains-kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } jetbrains-kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } # Needed for statesaver +jetbrains-kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" } diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..e69de29bb diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift new file mode 100644 index 000000000..e69de29bb diff --git a/settings.gradle.kts b/settings.gradle.kts index 60a40c985..1958e2ff8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,6 +20,7 @@ dependencyResolutionManagement { } } include (":app") +include("composeApp") // Use a local copy of NewPipe Extractor by uncommenting the lines below. // We assume, that NewPipe and NewPipe Extractor have the same parent directory.