From f6129aa3a3fc2db634e48362454af716954526bc Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Fri, 20 Mar 2026 19:18:51 +0800 Subject: [PATCH] Setup multiplatform settings with KMP and theme Signed-off-by: Aayush Gupta --- composeApp/build.gradle.kts | 6 +++++ .../newpipe/app/di/settings/SettingsModule.kt | 20 ++++++++++++++++ .../newpipe/app/di/settings/SettingsModule.kt | 19 +++++++++++++++ .../kotlin/net/newpipe/app/theme/Theme.kt | 24 +++++++++++++++---- .../newpipe/app/di/settings/SettingsModule.kt | 17 +++++++++++++ .../newpipe/app/di/settings/SettingsModule.kt | 17 +++++++++++++ gradle/libs.versions.toml | 4 +++- 7 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 composeApp/src/androidMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt create mode 100644 composeApp/src/commonMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt create mode 100644 composeApp/src/iosMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt create mode 100644 composeApp/src/jvmMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 9741b6659..33a3eb353 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -17,6 +17,9 @@ kotlin { jvmToolchain(21) compilerOptions { + freeCompilerArgs.addAll( + "-Xexpect-actual-classes" + ) optIn.addAll( "androidx.compose.material3.ExperimentalMaterial3Api", "androidx.compose.material3.ExperimentalMaterial3ExpressiveApi", @@ -65,6 +68,8 @@ kotlin { implementation(libs.koin.compose.viewmodel) implementation(libs.koin.annotations) + + implementation(libs.russhwolf.settings) } commonTest.dependencies { implementation(libs.kotlin.test) @@ -72,6 +77,7 @@ kotlin { androidMain.dependencies { implementation(libs.jetbrains.compose.preview) implementation(libs.androidx.activity) + implementation(libs.androidx.preference) } jvmMain.dependencies { implementation(compose.desktop.currentOs) diff --git a/composeApp/src/androidMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt b/composeApp/src/androidMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt new file mode 100644 index 000000000..8c2f7b4ed --- /dev/null +++ b/composeApp/src/androidMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.di.settings + +import android.content.Context +import androidx.preference.PreferenceManager +import com.russhwolf.settings.Settings +import com.russhwolf.settings.SharedPreferencesSettings +import org.koin.core.annotation.Singleton + +/** + * Settings for Android based on SharedPreferences + */ +@Singleton +fun provideSettings(context: Context): Settings = SharedPreferencesSettings( + PreferenceManager.getDefaultSharedPreferences(context) +) diff --git a/composeApp/src/commonMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt b/composeApp/src/commonMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt new file mode 100644 index 000000000..698b29f29 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.di.settings + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Configuration +import org.koin.core.annotation.Module + +/** + * Settings module to access key-value pairs across different platforms. + * See individual platform packages for the declarations included in this module. + */ +@Module +@ComponentScan +@Configuration +class SettingsModule diff --git a/composeApp/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt b/composeApp/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt index 56f9e0509..af0412cec 100644 --- a/composeApp/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt +++ b/composeApp/src/commonMain/kotlin/net/newpipe/app/theme/Theme.kt @@ -12,6 +12,9 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import com.russhwolf.settings.Settings +import org.koin.compose.koinInject private val lightScheme = lightColorScheme( primary = primaryLight, @@ -89,12 +92,25 @@ private val darkScheme = darkColorScheme( surfaceContainerHighest = surfaceContainerHighestDark, ) +private val blackScheme = darkScheme.copy(surface = Color.Black) + @Composable -fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { +fun AppTheme( + useDarkTheme: Boolean = isSystemInDarkTheme(), + settings: Settings = koinInject(), + content: @Composable () -> Unit +) { + val nightScheme = when(settings.getString("night_theme", "dark_theme")) { + "black_theme" -> blackScheme + else -> darkScheme + } + MaterialExpressiveTheme( - colorScheme = when { - !useDarkTheme -> lightScheme - else -> darkScheme + colorScheme = when(settings.getString("theme", "auto_device_theme")) { + "light_theme" -> lightScheme + "dark_theme" -> darkScheme + "black_theme" -> blackScheme + else -> if (!useDarkTheme) lightScheme else nightScheme }, content = content ) diff --git a/composeApp/src/iosMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt b/composeApp/src/iosMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt new file mode 100644 index 000000000..b5f7ef3e3 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.di.settings + +import com.russhwolf.settings.NSUserDefaultsSettings +import com.russhwolf.settings.Settings +import org.koin.core.annotation.Singleton +import platform.Foundation.NSUserDefaults + +/** + * Settings for iOS based on UserDefaultsSettings + */ +@Singleton +fun provideSettings(): Settings = NSUserDefaultsSettings(NSUserDefaults()) diff --git a/composeApp/src/jvmMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt b/composeApp/src/jvmMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt new file mode 100644 index 000000000..356407df4 --- /dev/null +++ b/composeApp/src/jvmMain/kotlin/net/newpipe/app/di/settings/SettingsModule.kt @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.di.settings + +import com.russhwolf.settings.PreferencesSettings +import com.russhwolf.settings.Settings +import org.koin.core.annotation.Singleton +import java.util.prefs.Preferences + +/** + * Settings for JVM devices based on Java Preferences + */ +@Singleton +fun provideSettings(): Settings = PreferencesSettings(Preferences.userRoot()) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d8f1d67c2..f3cae3b9c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,8 +5,8 @@ [versions] acra = "5.13.1" -agp = "9.2.0" activity = "1.13.0" +agp = "9.2.0" appcompat = "1.7.1" assertj = "3.27.7" autoservice-google = "1.1.1" @@ -53,6 +53,7 @@ runner = "1.7.0" rxandroid = "3.0.2" rxbinding = "4.0.0" rxjava = "3.1.12" +settings = "1.3.0" sonarqube = "7.2.3.7755" statesaver = "1.4.1" # TODO: Drop because it is deprecated and incompatible with KSP2 stetho = "1.6.0" @@ -150,6 +151,7 @@ pinterest-ktlint = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = " puppycrawl-checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" } reactivex-rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" } reactivex-rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" } +russhwolf-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "settings" } squareup-leakcanary-core = { module = "com.squareup.leakcanary:leakcanary-android-core", version.ref = "leakcanary" } squareup-leakcanary-plumber = { module = "com.squareup.leakcanary:plumber-android", version.ref = "leakcanary" } squareup-leakcanary-watcher = { module = "com.squareup.leakcanary:leakcanary-object-watcher-android", version.ref = "leakcanary" }