From cc74ac8ce85d46fae4518d9e15ae0b9484145da0 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 7 Jan 2026 12:45:46 +0800 Subject: [PATCH] Initial support for compose multiplatform Signed-off-by: Aayush Gupta --- .gitignore | 15 +- build.gradle.kts | 6 + desktopApp/build.gradle.kts | 32 ++ .../src/main/kotlin/net/newpipe/app/Main.kt | 18 + gradle/libs.versions.toml | 34 ++ iosApp/Configuration/Config.xcconfig | 7 + iosApp/iosApp.xcodeproj/project.pbxproj | 373 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 36 ++ .../AppIcon.appiconset/app-icon-1024.png | Bin 0 -> 13511 bytes iosApp/iosApp/Assets.xcassets/Contents.json | 6 + iosApp/iosApp/ContentView.swift | 21 + iosApp/iosApp/Info.plist | 8 + .../Preview Assets.xcassets/Contents.json | 6 + iosApp/iosApp/iOSApp.swift | 10 + settings.gradle.kts | 5 +- shared/build.gradle.kts | 117 ++++++ shared/consumer-proguard-rules.pro | 6 + shared/src/androidMain/AndroidManifest.xml | 14 + .../kotlin/net/newpipe/app/ComposeActivity.kt | 25 ++ .../composeResources/values/strings.xml | 8 + .../commonMain/kotlin/net/newpipe/app/App.kt | 22 ++ .../kotlin/net/newpipe/app/di/KoinApp.kt | 14 + .../kotlin/net/newpipe/app/theme/Color.kt | 81 ++++ .../kotlin/net/newpipe/app/theme/Theme.kt | 101 +++++ .../net/newpipe/app/MainViewController.kt | 10 + 27 files changed, 991 insertions(+), 2 deletions(-) create mode 100644 desktopApp/build.gradle.kts create mode 100644 desktopApp/src/main/kotlin/net/newpipe/app/Main.kt create mode 100644 iosApp/Configuration/Config.xcconfig create mode 100644 iosApp/iosApp.xcodeproj/project.pbxproj create mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png create mode 100644 iosApp/iosApp/Assets.xcassets/Contents.json create mode 100644 iosApp/iosApp/ContentView.swift create mode 100644 iosApp/iosApp/Info.plist create mode 100644 iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 iosApp/iosApp/iOSApp.swift create mode 100644 shared/build.gradle.kts create mode 100644 shared/consumer-proguard-rules.pro create mode 100644 shared/src/androidMain/AndroidManifest.xml create mode 100644 shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt create mode 100644 shared/src/commonMain/composeResources/values/strings.xml create mode 100644 shared/src/commonMain/kotlin/net/newpipe/app/App.kt create mode 100644 shared/src/commonMain/kotlin/net/newpipe/app/di/KoinApp.kt create mode 100644 shared/src/commonMain/kotlin/net/newpipe/app/theme/Color.kt create mode 100644 shared/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt create mode 100644 shared/src/iosMain/kotlin/net/newpipe/app/MainViewController.kt diff --git a/.gitignore b/.gitignore index 49267a9f0..3d5b6ea66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ .gradle/ local.properties .DS_Store -build/ +**/build/ +!src/**/build/ captures/ .idea/ *.iml @@ -19,3 +20,15 @@ app/release/ bin/ .vscode/ *.code-workspace + +# xcode files +xcuserdata +.externalNativeBuild +.cxx +node_modules/ +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcodeproj/project.xcworkspace/ +!*.xcworkspace/contents.xcworkspacedata +**/xcshareddata/WorkspaceSettings.xcsettings diff --git a/build.gradle.kts b/build.gradle.kts index 53c8a4c42..e3e25c4fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,9 +12,15 @@ buildscript { plugins { alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false alias(libs.plugins.android.legacy.kapt) apply false alias(libs.plugins.google.ksp) apply false + alias(libs.plugins.jetbrains.kotlin.compose) apply false + alias(libs.plugins.jetbrains.kotlin.jvm) apply false + alias(libs.plugins.jetbrains.kotlin.multiplatform) apply false + alias(libs.plugins.jetbrains.compose.multiplatform) apply false alias(libs.plugins.jetbrains.kotlin.parcelize) apply false alias(libs.plugins.jetbrains.kotlinx.serialization) apply false alias(libs.plugins.sonarqube) apply false + alias(libs.plugins.koin) apply false } diff --git a/desktopApp/build.gradle.kts b/desktopApp/build.gradle.kts new file mode 100644 index 000000000..910d73647 --- /dev/null +++ b/desktopApp/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * 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.jetbrains.kotlin.jvm) + alias(libs.plugins.jetbrains.kotlin.compose) + alias(libs.plugins.jetbrains.compose.multiplatform) +} + +dependencies { + implementation(projects.shared) + + implementation(compose.desktop.currentOs) + implementation(libs.jetbrains.coroutines.swing) + implementation(libs.jetbrains.compose.preview) +} + +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/desktopApp/src/main/kotlin/net/newpipe/app/Main.kt b/desktopApp/src/main/kotlin/net/newpipe/app/Main.kt new file mode 100644 index 000000000..609ce7289 --- /dev/null +++ b/desktopApp/src/main/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 + +/** + * Entry point for compose-related UI components on Desktop + */ +fun main() = application { + Window(onCloseRequest = ::exitApplication, title = "NewPipe") { + App() + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 365d439a5..67eba5cbf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ [versions] acra = "5.13.1" +activity = "1.13.0" agp = "9.2.1" appcompat = "1.7.1" assertj = "3.27.7" @@ -14,16 +15,21 @@ bridge = "v2.0.2" cardview = "1.0.0" checkstyle = "13.4.2" coil = "3.4.0" +compose = "1.11.1" constraintlayout = "2.2.1" core = "1.18.0" +coroutines = "1.11.0" desugar = "2.1.5" documentfile = "1.1.0" +espresso = "3.7.0" exoplayer = "2.19.1" fragment = "1.8.9" groupie = "2.10.1" jsoup = "1.22.2" junit = "4.13.2" junit-ext = "1.3.0" +koin = "4.2.1" +koin-plugin = "1.0.0-RC2" kotlin = "2.3.21" kotlinx-coroutines-rx3 = "1.11.0" kotlinx-serialization-json = "1.11.0" @@ -34,8 +40,11 @@ lifecycle = "2.10.0" 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 +material3 = "1.11.0-alpha07" media = "1.7.1" mockitoCore = "5.23.0" +multiplatform = "1.11.0" +navigation3 = "1.1.1" okhttp = "5.3.2" phoenix = "3.0.0" preference = "1.2.1" @@ -68,8 +77,11 @@ work = "2.11.2" [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-compose-test-ui-junit = { module = "androidx.compose.ui:ui-test-junit4-android", version.ref = "compose" } +androidx-compose-test-ui-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose" } androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx-core = { module = "androidx.core:core-ktx", version.ref = "core" } androidx-documentfile = { module = "androidx.documentfile:documentfile", version.ref = "documentfile" } @@ -87,6 +99,7 @@ androidx-room-rxjava3 = { module = "androidx.room:room-rxjava3", version.ref = " androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "room" } androidx-runner = { module = "androidx.test:runner", version.ref = "runner" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" } +androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" } androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "work" } @@ -110,8 +123,23 @@ 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-compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "multiplatform" } +jetbrains-compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" } +jetbrains-compose-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "multiplatform" } +jetbrains-compose-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "multiplatform" } +jetbrains-compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "multiplatform" } +jetbrains-compose-test-ui = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "multiplatform" } +jetbrains-compose-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "multiplatform" } +jetbrains-compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "multiplatform" } +jetbrains-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" } +jetbrains-lifecycle-navigation3 = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycle" } +jetbrains-lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" } +jetbrains-navigation3-ui = { module = "org.jetbrains.androidx.navigation3:navigation3-ui", version.ref = "navigation3" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } junit = { module = "junit:junit", version.ref = "junit" } +koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin" } +koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" } +kotlin-test-core = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlinx-coroutines-rx3 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-rx3", version.ref = "kotlinx-coroutines-rx3" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } lisawray-groupie-core = { module = "com.github.lisawray.groupie:groupie", version.ref = "groupie" } @@ -137,7 +165,13 @@ zacsweers-autoservice-compiler = { module = "dev.zacsweers.autoservice:auto-serv [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-legacy-kapt = { id = "com.android.legacy-kapt", version.ref = "agp" } # Needed for statesaver +android-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" } google-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +jetbrains-compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "multiplatform" } +jetbrains-kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +jetbrains-kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } jetbrains-kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +koin = { id = "io.insert-koin.compiler.plugin", version.ref = "koin-plugin" } 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..46d815851 --- /dev/null +++ b/iosApp/Configuration/Config.xcconfig @@ -0,0 +1,7 @@ +TEAM_ID= + +PRODUCT_NAME=NewPipe +PRODUCT_BUNDLE_IDENTIFIER=net.newpipe.app.NewPipe$(TEAM_ID) + +CURRENT_PROJECT_VERSION=1 +MARKETING_VERSION=1.0 \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 000000000..59ffd37b9 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,373 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + E903CDBEAD6067C2E88D0E13 /* NewPipe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NewPipe.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 2D8686880DF217DCF0163629 /* Exceptions for "iosApp" folder in "iosApp" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = CB7D703B7BE102A8C9442815 /* iosApp */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + ABCCA30A0B282AA0C430F5BD /* iosApp */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 2D8686880DF217DCF0163629 /* Exceptions for "iosApp" folder in "iosApp" target */, + ); + path = iosApp; + sourceTree = ""; + }; + F0463985A41AA4941DD32D9F /* Configuration */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Configuration; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + FDFBDD5BD804F728F46628B8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6ED4AC48A20C6EE0784AF47A = { + isa = PBXGroup; + children = ( + F0463985A41AA4941DD32D9F /* Configuration */, + ABCCA30A0B282AA0C430F5BD /* iosApp */, + 80BC5A75E8EEBCC3CC64764B /* Products */, + ); + sourceTree = ""; + }; + 80BC5A75E8EEBCC3CC64764B /* Products */ = { + isa = PBXGroup; + children = ( + E903CDBEAD6067C2E88D0E13 /* NewPipe.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CB7D703B7BE102A8C9442815 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2E1AAB880620FB3FB6B50768 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + EEF9A261A7B4C71C731372F8 /* Compile Kotlin Framework */, + 388E86B7C80E4C58A395F32A /* Sources */, + FDFBDD5BD804F728F46628B8 /* Frameworks */, + 7B2130143F9C3DD4CB6AF311 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + ABCCA30A0B282AA0C430F5BD /* iosApp */, + ); + name = iosApp; + packageProductDependencies = ( + ); + productName = iosApp; + productReference = E903CDBEAD6067C2E88D0E13 /* NewPipe.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE9DC708D8CDDFF57F3EFEE0 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1620; + LastUpgradeCheck = 1620; + TargetAttributes = { + CB7D703B7BE102A8C9442815 = { + CreatedOnToolsVersion = 16.2; + }; + }; + }; + buildConfigurationList = 116B11CF62B79F066B5E8101 /* Build configuration list for PBXProject "iosApp" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6ED4AC48A20C6EE0784AF47A; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 80BC5A75E8EEBCC3CC64764B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CB7D703B7BE102A8C9442815 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7B2130143F9C3DD4CB6AF311 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + EEF9A261A7B4C71C731372F8 /* Compile Kotlin Framework */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 388E86B7C80E4C58A395F32A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4CF1E65647AFD6A50CE8B9F1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = F0463985A41AA4941DD32D9F /* Configuration */; + baseConfigurationReferenceRelativePath = Config.xcconfig; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 0665CA5FEC66700A6C35B2D1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = F0463985A41AA4941DD32D9F /* Configuration */; + baseConfigurationReferenceRelativePath = Config.xcconfig; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F4EC259464D79C8283923A63 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = iosApp/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 263152656D22EC09900DE6E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = iosApp/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 116B11CF62B79F066B5E8101 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CF1E65647AFD6A50CE8B9F1 /* Debug */, + 0665CA5FEC66700A6C35B2D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2E1AAB880620FB3FB6B50768 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F4EC259464D79C8283923A63 /* Debug */, + 263152656D22EC09900DE6E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE9DC708D8CDDFF57F3EFEE0 /* Project object */; +} \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + 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..eb8789700 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} 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..4e8d485bf --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,36 @@ +{ + "images" : [ + { + "filename" : "app-icon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} 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 0000000000000000000000000000000000000000..e143fc22303d3dbe0f202fa848cffa0e9f9ef6f7 GIT binary patch literal 13511 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 literal 0 HcmV?d00001 diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift new file mode 100644 index 000000000..5ef00da8d --- /dev/null +++ b/iosApp/iosApp/ContentView.swift @@ -0,0 +1,21 @@ +import UIKit +import SwiftUI +import ComposeApp + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + MainViewControllerKt.mainViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView() + .ignoresSafeArea() + } +} + + + diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 000000000..11845e1da --- /dev/null +++ b/iosApp/iosApp/Info.plist @@ -0,0 +1,8 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + + 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..73c00596a --- /dev/null +++ b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift new file mode 100644 index 000000000..d83dca611 --- /dev/null +++ b/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 60a40c985..1b616793f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ * SPDX-FileCopyrightText: 2025 NewPipe e.V. * SPDX-License-Identifier: GPL-3.0-or-later */ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") pluginManagement { repositories { @@ -19,7 +20,9 @@ dependencyResolutionManagement { maven(url = "https://repo.clojars.org") } } -include (":app") +include (":app") // androidApp +include(":desktopApp") +include("shared") // Use a local copy of NewPipe Extractor by uncommenting the lines below. // We assume, that NewPipe and NewPipe Extractor have the same parent directory. diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts new file mode 100644 index 000000000..8848e9ac5 --- /dev/null +++ b/shared/build.gradle.kts @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +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.koin) +} + +kotlin { + jvmToolchain(21) + + compilerOptions { + optIn.addAll( + "androidx.compose.material3.ExperimentalMaterial3Api", + "androidx.compose.material3.ExperimentalMaterial3ExpressiveApi", + "androidx.compose.foundation.layout.ExperimentalLayoutApi" + ) + } + + android { + namespace = "net.newpipe.app" + compileSdk { + version = release(36) { + minorApiLevel = 1 + } + } + minSdk = 23 + androidResources { + enable = true + } + + optimization { + consumerKeepRules.apply { + publish = true + file("consumer-proguard-rules.pro") + } + } + + withHostTest { + isIncludeAndroidResources = true + } + withDeviceTestBuilder { + sourceSetTreeName = "test" + }.configure { + instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + } + + listOf( + iosArm64(), + iosSimulatorArm64() + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = "ComposeApp" + isStatic = true + } + } + + jvm() + + sourceSets { + commonMain { + dependencies { + implementation(libs.jetbrains.compose.runtime) + implementation(libs.jetbrains.compose.foundation) + implementation(libs.jetbrains.compose.material3) + implementation(libs.jetbrains.compose.ui) + implementation(libs.jetbrains.compose.resources) + implementation(libs.jetbrains.compose.preview) + + implementation(libs.jetbrains.lifecycle.viewmodel) + + implementation(libs.jetbrains.navigation3.ui) + implementation(libs.jetbrains.lifecycle.navigation3) + + implementation(libs.koin.compose.viewmodel) + implementation(libs.koin.annotations) + } + } + commonTest.dependencies { + implementation(libs.kotlin.test.core) + implementation(libs.jetbrains.compose.test.ui) + } + androidMain.dependencies { + implementation(libs.jetbrains.compose.preview) + implementation(libs.androidx.activity) + } + val androidDeviceTest by getting { + dependencies { + implementation(libs.androidx.compose.test.ui.manifest) + implementation(libs.androidx.compose.test.ui.junit) + + // Needed because androidx.compose.test.ui.junit pulls an older dependency + // which crashes on new Android versions + implementation(libs.androidx.test.espresso.core) + } + } + val jvmTest by getting { + dependencies { + implementation(compose.desktop.currentOs) + } + } + } +} + +dependencies { + androidRuntimeClasspath(libs.jetbrains.compose.tooling) +} + +koinCompiler { + userLogs = true // See what the compiler plugin detects +} diff --git a/shared/consumer-proguard-rules.pro b/shared/consumer-proguard-rules.pro new file mode 100644 index 000000000..e0c18985c --- /dev/null +++ b/shared/consumer-proguard-rules.pro @@ -0,0 +1,6 @@ +# +# SPDX-FileCopyrightText: 2026 NewPipe e.V. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +# Proguard rules for Android platform: https://developer.android.com/build/shrink-code diff --git a/shared/src/androidMain/AndroidManifest.xml b/shared/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..ce4962141 --- /dev/null +++ b/shared/src/androidMain/AndroidManifest.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt b/shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt new file mode 100644 index 000000000..dc1184db1 --- /dev/null +++ b/shared/src/androidMain/kotlin/net/newpipe/app/ComposeActivity.kt @@ -0,0 +1,25 @@ +/* + * 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 + +/** + * Entry point for compose-related UI components on Android + */ +class ComposeActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + + setContent { + App() + } + } +} diff --git a/shared/src/commonMain/composeResources/values/strings.xml b/shared/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 000000000..db4b09e00 --- /dev/null +++ b/shared/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,8 @@ + + + + NewPipe + diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/App.kt b/shared/src/commonMain/kotlin/net/newpipe/app/App.kt new file mode 100644 index 000000000..4cc36fc34 --- /dev/null +++ b/shared/src/commonMain/kotlin/net/newpipe/app/App.kt @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +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.theme.AppTheme +import org.koin.compose.KoinApplication +import org.koin.plugin.module.dsl.koinConfiguration + +@Composable +@Preview +fun App() { + KoinApplication(configuration = koinConfiguration()) { + AppTheme { + } + } +} diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/di/KoinApp.kt b/shared/src/commonMain/kotlin/net/newpipe/app/di/KoinApp.kt new file mode 100644 index 000000000..15f874cdb --- /dev/null +++ b/shared/src/commonMain/kotlin/net/newpipe/app/di/KoinApp.kt @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.di + +import org.koin.core.annotation.KoinApplication + +/** + * Entry point for Koin-related configuration + */ +@KoinApplication +object KoinApp diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/theme/Color.kt b/shared/src/commonMain/kotlin/net/newpipe/app/theme/Color.kt new file mode 100644 index 000000000..5bb59ee2e --- /dev/null +++ b/shared/src/commonMain/kotlin/net/newpipe/app/theme/Color.kt @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2024 NewPipe contributors + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.theme + +import androidx.compose.ui.graphics.Color + +val primaryLight = Color(0xFF904A45) +val onPrimaryLight = Color(0xFFFFFFFF) +val primaryContainerLight = Color(0xFFFFDAD6) +val onPrimaryContainerLight = Color(0xFF3B0908) +val secondaryLight = Color(0xFF775653) +val onSecondaryLight = Color(0xFFFFFFFF) +val secondaryContainerLight = Color(0xFFFFDAD6) +val onSecondaryContainerLight = Color(0xFF2C1513) +val tertiaryLight = Color(0xFF725B2E) +val onTertiaryLight = Color(0xFFFFFFFF) +val tertiaryContainerLight = Color(0xFFFEDEA6) +val onTertiaryContainerLight = Color(0xFF261900) +val errorLight = Color(0xFFBA1A1A) +val onErrorLight = Color(0xFFFFFFFF) +val errorContainerLight = Color(0xFFFFDAD6) +val onErrorContainerLight = Color(0xFF410002) +val backgroundLight = Color(0xFFFFF8F7) +val onBackgroundLight = Color(0xFF231918) +val surfaceLight = Color(0xFFFFF8F7) +val onSurfaceLight = Color(0xFF231918) +val surfaceVariantLight = Color(0xFFF5DDDB) +val onSurfaceVariantLight = Color(0xFF534342) +val outlineLight = Color(0xFF857371) +val outlineVariantLight = Color(0xFFD8C2BF) +val scrimLight = Color(0xFF000000) +val inverseSurfaceLight = Color(0xFF392E2D) +val inverseOnSurfaceLight = Color(0xFFFFEDEB) +val inversePrimaryLight = Color(0xFFFFB3AC) +val surfaceDimLight = Color(0xFFE8D6D4) +val surfaceBrightLight = Color(0xFFFFF8F7) +val surfaceContainerLowestLight = Color(0xFFFFFFFF) +val surfaceContainerLowLight = Color(0xFFFFF0EF) +val surfaceContainerLight = Color(0xFFFCEAE8) +val surfaceContainerHighLight = Color(0xFFF6E4E2) +val surfaceContainerHighestLight = Color(0xFFF1DEDC) + +val primaryDark = Color(0xFFFFB3AC) +val onPrimaryDark = Color(0xFF571E1B) +val primaryContainerDark = Color(0xFF73332F) +val onPrimaryContainerDark = Color(0xFFFFDAD6) +val secondaryDark = Color(0xFFE7BDB8) +val onSecondaryDark = Color(0xFF442927) +val secondaryContainerDark = Color(0xFF5D3F3C) +val onSecondaryContainerDark = Color(0xFFFFDAD6) +val tertiaryDark = Color(0xFFE1C38C) +val onTertiaryDark = Color(0xFF402D04) +val tertiaryContainerDark = Color(0xFF584419) +val onTertiaryContainerDark = Color(0xFFFEDEA6) +val errorDark = Color(0xFFFFB4AB) +val onErrorDark = Color(0xFF690005) +val errorContainerDark = Color(0xFF93000A) +val onErrorContainerDark = Color(0xFFFFDAD6) +val backgroundDark = Color(0xFF1A1110) +val onBackgroundDark = Color(0xFFF1DEDC) +val surfaceDark = Color(0xFF1A1110) +val onSurfaceDark = Color(0xFFF1DEDC) +val surfaceVariantDark = Color(0xFF534342) +val onSurfaceVariantDark = Color(0xFFD8C2BF) +val outlineDark = Color(0xFFA08C8A) +val outlineVariantDark = Color(0xFF534342) +val scrimDark = Color(0xFF000000) +val inverseSurfaceDark = Color(0xFFF1DEDC) +val inverseOnSurfaceDark = Color(0xFF392E2D) +val inversePrimaryDark = Color(0xFF904A45) +val surfaceDimDark = Color(0xFF1A1110) +val surfaceBrightDark = Color(0xFF423735) +val surfaceContainerLowestDark = Color(0xFF140C0B) +val surfaceContainerLowDark = Color(0xFF231918) +val surfaceContainerDark = Color(0xFF271D1C) +val surfaceContainerHighDark = Color(0xFF322827) +val surfaceContainerHighestDark = Color(0xFF3D3231) diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt b/shared/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt new file mode 100644 index 000000000..28088a6d0 --- /dev/null +++ b/shared/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2024 NewPipe contributors + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialExpressiveTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable + +private val lightScheme = lightColorScheme( + primary = primaryLight, + onPrimary = onPrimaryLight, + primaryContainer = primaryContainerLight, + onPrimaryContainer = onPrimaryContainerLight, + secondary = secondaryLight, + onSecondary = onSecondaryLight, + secondaryContainer = secondaryContainerLight, + onSecondaryContainer = onSecondaryContainerLight, + tertiary = tertiaryLight, + onTertiary = onTertiaryLight, + tertiaryContainer = tertiaryContainerLight, + onTertiaryContainer = onTertiaryContainerLight, + error = errorLight, + onError = onErrorLight, + errorContainer = errorContainerLight, + onErrorContainer = onErrorContainerLight, + background = backgroundLight, + onBackground = onBackgroundLight, + surface = surfaceLight, + onSurface = onSurfaceLight, + surfaceVariant = surfaceVariantLight, + onSurfaceVariant = onSurfaceVariantLight, + outline = outlineLight, + outlineVariant = outlineVariantLight, + scrim = scrimLight, + inverseSurface = inverseSurfaceLight, + inverseOnSurface = inverseOnSurfaceLight, + inversePrimary = inversePrimaryLight, + surfaceDim = surfaceDimLight, + surfaceBright = surfaceBrightLight, + surfaceContainerLowest = surfaceContainerLowestLight, + surfaceContainerLow = surfaceContainerLowLight, + surfaceContainer = surfaceContainerLight, + surfaceContainerHigh = surfaceContainerHighLight, + surfaceContainerHighest = surfaceContainerHighestLight +) + +private val darkScheme = darkColorScheme( + primary = primaryDark, + onPrimary = onPrimaryDark, + primaryContainer = primaryContainerDark, + onPrimaryContainer = onPrimaryContainerDark, + secondary = secondaryDark, + onSecondary = onSecondaryDark, + secondaryContainer = secondaryContainerDark, + onSecondaryContainer = onSecondaryContainerDark, + tertiary = tertiaryDark, + onTertiary = onTertiaryDark, + tertiaryContainer = tertiaryContainerDark, + onTertiaryContainer = onTertiaryContainerDark, + error = errorDark, + onError = onErrorDark, + errorContainer = errorContainerDark, + onErrorContainer = onErrorContainerDark, + background = backgroundDark, + onBackground = onBackgroundDark, + surface = surfaceDark, + onSurface = onSurfaceDark, + surfaceVariant = surfaceVariantDark, + onSurfaceVariant = onSurfaceVariantDark, + outline = outlineDark, + outlineVariant = outlineVariantDark, + scrim = scrimDark, + inverseSurface = inverseSurfaceDark, + inverseOnSurface = inverseOnSurfaceDark, + inversePrimary = inversePrimaryDark, + surfaceDim = surfaceDimDark, + surfaceBright = surfaceBrightDark, + surfaceContainerLowest = surfaceContainerLowestDark, + surfaceContainerLow = surfaceContainerLowDark, + surfaceContainer = surfaceContainerDark, + surfaceContainerHigh = surfaceContainerHighDark, + surfaceContainerHighest = surfaceContainerHighestDark +) + +@Composable +fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { + MaterialExpressiveTheme( + colorScheme = when { + !useDarkTheme -> lightScheme + else -> darkScheme + }, + content = content + ) +} diff --git a/shared/src/iosMain/kotlin/net/newpipe/app/MainViewController.kt b/shared/src/iosMain/kotlin/net/newpipe/app/MainViewController.kt new file mode 100644 index 000000000..9a1701c0f --- /dev/null +++ b/shared/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() }