Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:56:56 +01:00
parent 75dc487a7a
commit 39c29d175b
6317 changed files with 388324 additions and 2 deletions

3
core/ui/theme/README.md Normal file
View file

@ -0,0 +1,3 @@
## Theme
The app theme is provided by the `ThemeProvider` found in the `api` module and should be implemented in the app modules with the required app themes from the `core:compose` and `core:legacy` modules.

View file

@ -0,0 +1,7 @@
plugins {
id(ThunderbirdPlugins.Library.kmpCompose)
}
android {
namespace = "net.thunderbird.core.ui.theme.api"
}

View file

@ -0,0 +1,19 @@
package net.thunderbird.core.ui.theme.api
import androidx.compose.runtime.Composable
/**
* Provides the compose theme for a feature.
*/
interface FeatureThemeProvider {
@Composable
fun WithTheme(
content: @Composable () -> Unit,
)
@Composable
fun WithTheme(
darkTheme: Boolean,
content: @Composable () -> Unit,
)
}

View file

@ -0,0 +1,6 @@
package net.thunderbird.core.ui.theme.api
enum class Theme {
LIGHT,
DARK,
}

View file

@ -0,0 +1,25 @@
package net.thunderbird.core.ui.theme.api
import androidx.annotation.StyleRes
interface ThemeManager {
val appTheme: Theme
val messageViewTheme: Theme
val messageComposeTheme: Theme
@get:StyleRes
val appThemeResourceId: Int
@get:StyleRes
val messageViewThemeResourceId: Int
@get:StyleRes
val messageComposeThemeResourceId: Int
@get:StyleRes
val dialogThemeResourceId: Int
@get:StyleRes
val translucentDialogThemeResourceId: Int
}

View file

@ -0,0 +1,20 @@
package net.thunderbird.core.ui.theme.api
import androidx.annotation.StyleRes
interface ThemeProvider {
@get:StyleRes
val appThemeResourceId: Int
@get:StyleRes
val appLightThemeResourceId: Int
@get:StyleRes
val appDarkThemeResourceId: Int
@get:StyleRes
val dialogThemeResourceId: Int
@get:StyleRes
val translucentDialogThemeResourceId: Int
}

View file

@ -0,0 +1,15 @@
plugins {
id(ThunderbirdPlugins.Library.android)
}
android {
namespace = "net.thunderbird.core.ui.theme.manager"
}
dependencies {
api(projects.core.ui.theme.api)
implementation(projects.core.ui.legacy.designsystem)
implementation(projects.core.preference.api)
}

View file

@ -0,0 +1,125 @@
package net.thunderbird.core.ui.theme.manager
import android.content.Context
import android.content.res.Configuration
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatDelegate
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus
import net.thunderbird.core.preference.AppTheme
import net.thunderbird.core.preference.GeneralSettings
import net.thunderbird.core.preference.GeneralSettingsManager
import net.thunderbird.core.preference.SubTheme
import net.thunderbird.core.preference.update
import net.thunderbird.core.ui.theme.api.Theme
import net.thunderbird.core.ui.theme.api.ThemeManager
import net.thunderbird.core.ui.theme.api.ThemeProvider
class ThemeManager(
private val context: Context,
private val themeProvider: ThemeProvider,
private val generalSettingsManager: GeneralSettingsManager,
private val appCoroutineScope: CoroutineScope,
) : ThemeManager {
private val generalSettings: GeneralSettings
get() = generalSettingsManager.getConfig()
override val appTheme: Theme
get() = when (generalSettings.display.coreSettings.appTheme) {
AppTheme.LIGHT -> Theme.LIGHT
AppTheme.DARK -> Theme.DARK
AppTheme.FOLLOW_SYSTEM -> getSystemTheme()
}
override val messageViewTheme: Theme
get() = resolveTheme(generalSettings.display.coreSettings.messageViewTheme)
override val messageComposeTheme: Theme
get() = resolveTheme(generalSettings.display.coreSettings.messageComposeTheme)
@get:StyleRes
override val appThemeResourceId: Int = themeProvider.appThemeResourceId
@get:StyleRes
override val messageViewThemeResourceId: Int
get() = getSubThemeResourceId(generalSettings.display.coreSettings.messageViewTheme)
@get:StyleRes
override val messageComposeThemeResourceId: Int
get() = getSubThemeResourceId(generalSettings.display.coreSettings.messageComposeTheme)
@get:StyleRes
override val dialogThemeResourceId: Int = themeProvider.dialogThemeResourceId
@get:StyleRes
override val translucentDialogThemeResourceId: Int = themeProvider.translucentDialogThemeResourceId
fun init() {
generalSettingsManager.getSettingsFlow()
.map { it.display.coreSettings.appTheme }
.distinctUntilChanged()
.onEach {
updateAppTheme(it)
}
.launchIn(appCoroutineScope + Dispatchers.Main.immediate)
}
private fun updateAppTheme(appTheme: AppTheme) {
val defaultNightMode = when (appTheme) {
AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES
AppTheme.FOLLOW_SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
AppCompatDelegate.setDefaultNightMode(defaultNightMode)
}
fun toggleMessageViewTheme() {
if (messageViewTheme === Theme.DARK) {
generalSettingsManager.update { settings ->
settings.copy(
display = settings.display.copy(
coreSettings = settings.display.coreSettings.copy(
messageViewTheme = SubTheme.LIGHT,
),
),
)
}
} else {
generalSettingsManager.update { settings ->
settings.copy(
display = settings.display.copy(
coreSettings = settings.display.coreSettings.copy(
messageViewTheme = SubTheme.DARK,
),
),
)
}
}
}
private fun getSubThemeResourceId(subTheme: SubTheme): Int = when (subTheme) {
SubTheme.LIGHT -> themeProvider.appLightThemeResourceId
SubTheme.DARK -> themeProvider.appDarkThemeResourceId
SubTheme.USE_GLOBAL -> themeProvider.appThemeResourceId
}
private fun resolveTheme(theme: SubTheme): Theme = when (theme) {
SubTheme.LIGHT -> Theme.LIGHT
SubTheme.DARK -> Theme.DARK
SubTheme.USE_GLOBAL -> appTheme
}
private fun getSystemTheme(): Theme {
return when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_NO -> Theme.LIGHT
Configuration.UI_MODE_NIGHT_YES -> Theme.DARK
else -> Theme.LIGHT
}
}
}