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() }