diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 697b3419f..9c372dbdd 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -790,7 +790,22 @@ style: Compose: CompositionLocalAllowlist: active: true - allowedCompositionLocals: [LocalColors, LocalElevations, LocalImages, LocalShapes, LocalSizes, LocalSpacings, LocalActivity] + allowedCompositionLocals: [ + LocalColors, + LocalElevations, + LocalImages, + LocalShapes, + LocalSizes, + LocalSpacings, + LocalActivity, + LocalThemeColorScheme, + LocalThemeElevations, + LocalThemeImages, + LocalThemeShapes, + LocalThemeSizes, + LocalThemeSpacings, + LocalThemeTypography + ] ContentEmitterReturningValues: active: true ModifierComposable: diff --git a/core/ui/compose/theme2/common/README.md b/core/ui/compose/theme2/common/README.md new file mode 100644 index 000000000..147ca1074 --- /dev/null +++ b/core/ui/compose/theme2/common/README.md @@ -0,0 +1,15 @@ +## Core - UI - Compose - Theme2 - Common + +This provides the common `MainTheme` with dark/light variation support, a wrapper for the Compose Material 3 theme. It supports [CompositionLocal](https://developer.android.com/jetpack/compose/compositionlocal) changes to colors, typography, shapes and adds additionally elevations, sizes, spacings and images. + +To change Material 3 related properties use `MainTheme` instead of `MaterialTheme`: + +- `MainTheme.colors`: Material 3 color scheme +- `MainTheme.elevations`: Elevation levels as [defined](https://m3.material.io/styles/elevation/overview) in Material3 +- `MainTheme.images`: Images used across the theme, e.g. logo +- `MainTheme.shapes`: Shapes as [defined](https://m3.material.io/styles/shape/overview) in Material 3 +- `MainTheme.sizes`: Sizes (smaller, small, medium, large, larger, huge, huger) +- `MainTheme.spacings`: Spacings (quarter, half, default, oneHalf, double, triple, quadruple) while default is 8 dp. +- `MainTheme.typography`: Material 3 typography + +To use the MainTheme, you need to provide a `ThemeConfig` with your desired colors, typography, shapes, elevations, sizes, spacings and images. The `ThemeConfig` is a data class that holds all the necessary information for the `MainTheme` to work. diff --git a/core/ui/compose/theme2/common/build.gradle.kts b/core/ui/compose/theme2/common/build.gradle.kts new file mode 100644 index 000000000..0f6735ee1 --- /dev/null +++ b/core/ui/compose/theme2/common/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id(ThunderbirdPlugins.Library.androidCompose) +} + +android { + namespace = "app.k9mail.core.ui.compose.theme2" + resourcePrefix = "core_ui_theme2" +} + +dependencies { + api(projects.core.ui.compose.common) + + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.material.icons.extended) + + implementation(libs.androidx.activity) +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/MainTheme.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/MainTheme.kt new file mode 100644 index 000000000..d66440f9d --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/MainTheme.kt @@ -0,0 +1,111 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable + +@Composable +fun MainTheme( + themeConfig: ThemeConfig, + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = true, + content: @Composable () -> Unit, +) { + val themeColorScheme = selectThemeColorScheme( + themeConfig = themeConfig, + darkTheme = darkTheme, + dynamicColor = dynamicColor, + ) + val themeImages = selectThemeImages( + themeConfig = themeConfig, + darkTheme = darkTheme, + ) + + SystemBar( + darkTheme = darkTheme, + colorScheme = themeColorScheme, + ) + + CompositionLocalProvider( + LocalThemeColorScheme provides themeColorScheme, + LocalThemeElevations provides themeConfig.elevations, + LocalThemeImages provides themeImages, + LocalThemeShapes provides themeConfig.shapes, + LocalThemeSizes provides themeConfig.sizes, + LocalThemeSpacings provides themeConfig.spacings, + LocalThemeTypography provides themeConfig.typography, + ) { + MaterialTheme( + colorScheme = themeColorScheme.toMaterial3ColorScheme(), + shapes = themeConfig.shapes.toMaterial3Shapes(), + typography = themeConfig.typography.toMaterial3Typography(), + content = content, + ) + } +} + +/** + * Contains functions to access the current theme values provided at the call site's position in + * the hierarchy. + */ +object MainTheme { + + /** + * Retrieves the current [ColorScheme] at the call site's position in the hierarchy. + */ + val colors: ThemeColorScheme + @Composable + @ReadOnlyComposable + get() = LocalThemeColorScheme.current + + /** + * Retrieves the current [ThemeElevations] at the call site's position in the hierarchy. + */ + val elevations: ThemeElevations + @Composable + @ReadOnlyComposable + get() = LocalThemeElevations.current + + /** + * Retrieves the current [ThemeImages] at the call site's position in the hierarchy. + */ + val images: ThemeImages + @Composable + @ReadOnlyComposable + get() = LocalThemeImages.current + + /** + * Retrieves the current [ThemeShapes] at the call site's position in the hierarchy. + */ + val shapes: ThemeShapes + @Composable + @ReadOnlyComposable + get() = LocalThemeShapes.current + + /** + * Retrieves the current [ThemeSizes] at the call site's position in the hierarchy. + */ + val sizes: ThemeSizes + @Composable + @ReadOnlyComposable + get() = LocalThemeSizes.current + + /** + * Retrieves the current [ThemeSpacings] at the call site's position in the hierarchy. + */ + val spacings: ThemeSpacings + @Composable + @ReadOnlyComposable + get() = LocalThemeSpacings.current + + /** + * Retrieves the current [ThemeTypography] at the call site's position in the hierarchy. + */ + val typography: ThemeTypography + @Composable + @ReadOnlyComposable + get() = LocalThemeTypography.current +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt new file mode 100644 index 000000000..f763e031f --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt @@ -0,0 +1,73 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +@Composable +internal fun selectThemeColorScheme( + themeConfig: ThemeConfig, + darkTheme: Boolean, + dynamicColor: Boolean, +): ThemeColorScheme { + return when { + dynamicColor && supportsDynamicColor() -> { + val context = LocalContext.current + val colorScheme = if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + colorScheme.toThemeColorScheme() + } + + darkTheme -> themeConfig.colors.dark + else -> themeConfig.colors.light + } +} + +// Supported from Android 12+ +private fun supportsDynamicColor(): Boolean { + return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S +} + +private fun ColorScheme.toThemeColorScheme() = ThemeColorScheme( + primary = primary, + onPrimary = onPrimary, + primaryContainer = primaryContainer, + onPrimaryContainer = onPrimaryContainer, + + secondary = secondary, + onSecondary = onSecondary, + secondaryContainer = secondaryContainer, + onSecondaryContainer = onSecondaryContainer, + + tertiary = tertiary, + onTertiary = onTertiary, + tertiaryContainer = tertiaryContainer, + onTertiaryContainer = onTertiaryContainer, + + error = error, + onError = onError, + errorContainer = errorContainer, + onErrorContainer = onErrorContainer, + + surface = surface, + onSurface = onSurface, + onSurfaceVariant = onSurfaceVariant, + surfaceContainerLowest = surfaceContainerLowest, + surfaceContainerLow = surfaceContainerLow, + surfaceContainer = surfaceContainer, + surfaceContainerHigh = surfaceContainerHigh, + surfaceContainerHighest = surfaceContainerHighest, + + inverseSurface = inverseSurface, + inverseOnSurface = inverseOnSurface, + inversePrimary = inversePrimary, + + outline = outline, + outlineVariant = outlineVariant, + + surfaceBright = surfaceBright, + surfaceDim = surfaceDim, + + scrim = scrim, +) diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeImages.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeImages.kt new file mode 100644 index 000000000..534f45b35 --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeImages.kt @@ -0,0 +1,12 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.runtime.Composable + +@Composable +internal fun selectThemeImages( + themeConfig: ThemeConfig, + darkTheme: Boolean, +) = when { + darkTheme -> themeConfig.images.dark + else -> themeConfig.images.light +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SystemBar.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SystemBar.kt new file mode 100644 index 000000000..fe317721b --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SystemBar.kt @@ -0,0 +1,23 @@ +package app.k9mail.core.ui.compose.theme2 + +import android.app.Activity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +@Composable +fun SystemBar( + darkTheme: Boolean, + colorScheme: ThemeColorScheme, +) { + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.surfaceContainer.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme + } + } +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeColorScheme.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeColorScheme.kt new file mode 100644 index 000000000..8bcc3ef09 --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeColorScheme.kt @@ -0,0 +1,121 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.material3.ColorScheme +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color + +/** + * Theme color scheme following Material 3 color roles. + * + * This supports tone-based Surfaces introduced for Material 3. + * + * @see: https://m3.material.io/styles/color/roles + * @see: https://material.io/blog/tone-based-surface-color-m3 + */ +@Immutable +data class ThemeColorScheme( + val primary: Color, + val onPrimary: Color, + val primaryContainer: Color, + val onPrimaryContainer: Color, + + val secondary: Color, + val onSecondary: Color, + val secondaryContainer: Color, + val onSecondaryContainer: Color, + + val tertiary: Color, + val onTertiary: Color, + val tertiaryContainer: Color, + val onTertiaryContainer: Color, + + val error: Color, + val onError: Color, + val errorContainer: Color, + val onErrorContainer: Color, + + val surface: Color, + val onSurface: Color, + val onSurfaceVariant: Color, + val surfaceContainerLowest: Color, + val surfaceContainerLow: Color, + val surfaceContainer: Color, + val surfaceContainerHigh: Color, + val surfaceContainerHighest: Color, + + val inverseSurface: Color, + val inverseOnSurface: Color, + val inversePrimary: Color, + + val outline: Color, + val outlineVariant: Color, + + val surfaceBright: Color, + val surfaceDim: Color, + + val scrim: Color, +) + +/** + * Convert a [ThemeColorScheme] to a Material 3 [ColorScheme]. + * + * Note: background, onBackground are deprecated and mapped to surface, onSurface. + */ +internal fun ThemeColorScheme.toMaterial3ColorScheme(): ColorScheme { + return ColorScheme( + primary = primary, + onPrimary = onPrimary, + primaryContainer = primaryContainer, + onPrimaryContainer = onPrimaryContainer, + + secondary = secondary, + onSecondary = onSecondary, + secondaryContainer = secondaryContainer, + onSecondaryContainer = onSecondaryContainer, + + tertiary = tertiary, + onTertiary = onTertiary, + tertiaryContainer = tertiaryContainer, + onTertiaryContainer = onTertiaryContainer, + + error = error, + onError = onError, + errorContainer = errorContainer, + onErrorContainer = onErrorContainer, + + surface = surface, + onSurface = onSurface, + onSurfaceVariant = onSurfaceVariant, + + surfaceContainerLowest = surfaceContainerLowest, + surfaceContainerLow = surfaceContainerLow, + surfaceContainer = surfaceContainer, + surfaceContainerHigh = surfaceContainerHigh, + surfaceContainerHighest = surfaceContainerHighest, + + inverseSurface = inverseSurface, + inverseOnSurface = inverseOnSurface, + inversePrimary = inversePrimary, + + outline = outline, + outlineVariant = outlineVariant, + + surfaceBright = surfaceBright, + surfaceDim = surfaceDim, + + scrim = scrim, + + // Remapping properties due to changes in Material 3 tone based surface colors + // https://material.io/blog/tone-based-surface-color-m3 + background = surface, + onBackground = onSurface, + surfaceVariant = surfaceContainerHighest, + + surfaceTint = surfaceContainerHighest, + ) +} + +internal val LocalThemeColorScheme = staticCompositionLocalOf { + error("No ThemeColorScheme provided") +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeConfig.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeConfig.kt new file mode 100644 index 000000000..756d70e8d --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeConfig.kt @@ -0,0 +1,26 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.runtime.Immutable + +@Immutable +data class ThemeConfig( + val colors: ThemeColorSchemeVariants, + val elevations: ThemeElevations, + val images: ThemeImageVariants, + val shapes: ThemeShapes, + val sizes: ThemeSizes, + val spacings: ThemeSpacings, + val typography: ThemeTypography, +) + +@Immutable +data class ThemeColorSchemeVariants( + val dark: ThemeColorScheme, + val light: ThemeColorScheme, +) + +@Immutable +data class ThemeImageVariants( + val dark: ThemeImages, + val light: ThemeImages, +) diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeElevations.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeElevations.kt new file mode 100644 index 000000000..2a8989cab --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeElevations.kt @@ -0,0 +1,29 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +/** + * Elevation values used in the app. + * + * Material uses six levels of elevation, each with a corresponding dp value. These values are named for their + * relative distance above the UI’s surface: 0, +1, +2, +3, +4, and +5. An element’s resting state can be on + * levels 0 to +3, while levels +4 and +5 are reserved for user-interacted states such as hover and dragged. + * + * @see: https://m3.material.io/styles/elevation/tokens + */ +@Immutable +data class ThemeElevations( + val level0: Dp = 0.dp, + val level1: Dp = 1.dp, + val level2: Dp = 3.dp, + val level3: Dp = 6.dp, + val level4: Dp = 8.dp, + val level5: Dp = 12.dp, +) + +internal val LocalThemeElevations = staticCompositionLocalOf { + error("No ThemeElevations provided") +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeImages.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeImages.kt new file mode 100644 index 000000000..2f700dfc5 --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeImages.kt @@ -0,0 +1,14 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf + +@Immutable +data class ThemeImages( + @DrawableRes val logo: Int, +) + +internal val LocalThemeImages = staticCompositionLocalOf { + error("No ThemeImages provided") +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeShapes.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeShapes.kt new file mode 100644 index 000000000..9f1402d5a --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeShapes.kt @@ -0,0 +1,51 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.foundation.shape.CornerBasedShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Shapes +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.dp + +/** + * The shapes used in the app. + * + * The shapes are defined as: + * + * - None + * - ExtraSmall + * - Small + * - Medium + * - Large + * - ExtraLarge + * - Full + * + * The default values are based on the Material Design guidelines. + * + * Shapes None and Full are omitted as None is a RectangleShape and Full is a CircleShape. + * + * @see: https://m3.material.io/styles/shape/overview + */ +@Immutable +data class ThemeShapes( + val extraSmall: CornerBasedShape = RoundedCornerShape(4.dp), + val small: CornerBasedShape = RoundedCornerShape(8.dp), + val medium: CornerBasedShape = RoundedCornerShape(12.dp), + val large: CornerBasedShape = RoundedCornerShape(16.dp), + val extraLarge: CornerBasedShape = RoundedCornerShape(28.dp), +) + +/** + * Converts the [ThemeShapes] to Material 3 [Shapes]. + */ +internal fun ThemeShapes.toMaterial3Shapes() = Shapes( + extraSmall = extraSmall, + small = small, + medium = medium, + large = large, + extraLarge = extraLarge, +) + +internal val LocalThemeShapes = staticCompositionLocalOf { + error("No ThemeShapes provided") +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSizes.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSizes.kt new file mode 100644 index 000000000..77ca911c5 --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSizes.kt @@ -0,0 +1,26 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.Dp + +@Immutable +data class ThemeSizes( + val smaller: Dp, + val small: Dp, + val medium: Dp, + val large: Dp, + val larger: Dp, + val huge: Dp, + val huger: Dp, + + val icon: Dp, + val largeIcon: Dp, + + val topBarHeight: Dp, + val bottomBarHeight: Dp, +) + +internal val LocalThemeSizes = staticCompositionLocalOf { + error("No ThemeSizes provided") +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSpacings.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSpacings.kt new file mode 100644 index 000000000..d8c831a9f --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSpacings.kt @@ -0,0 +1,21 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.Dp + +@Immutable +data class ThemeSpacings( + val zero: Dp, + val quarter: Dp, + val half: Dp, + val default: Dp, + val oneHalf: Dp, + val double: Dp, + val triple: Dp, + val quadruple: Dp, +) + +internal val LocalThemeSpacings = staticCompositionLocalOf { + error("No ThemeSpacings provided") +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeTypography.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeTypography.kt new file mode 100644 index 000000000..4387a6915 --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeTypography.kt @@ -0,0 +1,50 @@ +package app.k9mail.core.ui.compose.theme2 + +import androidx.compose.material3.Typography +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.text.TextStyle + +@Immutable +data class ThemeTypography( + val displayLarge: TextStyle, + val displayMedium: TextStyle, + val displaySmall: TextStyle, + val headlineLarge: TextStyle, + val headlineMedium: TextStyle, + val headlineSmall: TextStyle, + val titleLarge: TextStyle, + val titleMedium: TextStyle, + val titleSmall: TextStyle, + val bodyLarge: TextStyle, + val bodyMedium: TextStyle, + val bodySmall: TextStyle, + val labelLarge: TextStyle, + val labelMedium: TextStyle, + val labelSmall: TextStyle, +) + +/** + * Convert [ThemeTypography] to Material 3 [Typography] + */ +internal fun ThemeTypography.toMaterial3Typography() = Typography( + displayLarge = displayLarge, + displayMedium = displayMedium, + displaySmall = displaySmall, + headlineLarge = headlineLarge, + headlineMedium = headlineMedium, + headlineSmall = headlineSmall, + titleLarge = titleLarge, + titleMedium = titleMedium, + titleSmall = titleSmall, + bodyLarge = bodyLarge, + bodyMedium = bodyMedium, + bodySmall = bodySmall, + labelLarge = labelLarge, + labelMedium = labelMedium, + labelSmall = labelSmall, +) + +internal val LocalThemeTypography = staticCompositionLocalOf { + error("No ThemeTypography provided") +} diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeElevations.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeElevations.kt new file mode 100644 index 000000000..9bd0a3cbc --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeElevations.kt @@ -0,0 +1,16 @@ +package app.k9mail.core.ui.compose.theme2.default + +import androidx.compose.ui.unit.dp +import app.k9mail.core.ui.compose.theme2.ThemeElevations + +/** + * Default values for Material elevation taken from https://m3.material.io/styles/elevation/tokens + */ +val defaultThemeElevations = ThemeElevations( + level0 = 0.dp, + level1 = 1.dp, + level2 = 3.dp, + level3 = 6.dp, + level4 = 8.dp, + level5 = 12.dp, +) diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeShapes.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeShapes.kt new file mode 100644 index 000000000..7a1c6a5ce --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeShapes.kt @@ -0,0 +1,13 @@ +package app.k9mail.core.ui.compose.theme2.default + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.unit.dp +import app.k9mail.core.ui.compose.theme2.ThemeShapes + +val defaultThemeShapes = ThemeShapes( + extraSmall = RoundedCornerShape(4.dp), + small = RoundedCornerShape(8.dp), + medium = RoundedCornerShape(12.dp), + large = RoundedCornerShape(16.dp), + extraLarge = RoundedCornerShape(28.dp), +) diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSizes.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSizes.kt new file mode 100644 index 000000000..48cc23f79 --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSizes.kt @@ -0,0 +1,20 @@ +package app.k9mail.core.ui.compose.theme2.default + +import androidx.compose.ui.unit.dp +import app.k9mail.core.ui.compose.theme2.ThemeSizes + +val defaultThemeSizes = ThemeSizes( + smaller = 8.dp, + small = 16.dp, + medium = 32.dp, + large = 64.dp, + larger = 128.dp, + huge = 256.dp, + huger = 384.dp, + + icon = 24.dp, + largeIcon = 32.dp, + + topBarHeight = 56.dp, + bottomBarHeight = 56.dp, +) diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSpacings.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSpacings.kt new file mode 100644 index 000000000..95c6c600e --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSpacings.kt @@ -0,0 +1,15 @@ +package app.k9mail.core.ui.compose.theme2.default + +import androidx.compose.ui.unit.dp +import app.k9mail.core.ui.compose.theme2.ThemeSpacings + +val defaultThemeSpacings = ThemeSpacings( + zero = 0.dp, + quarter = 2.dp, + half = 4.dp, + default = 8.dp, + oneHalf = 12.dp, + double = 16.dp, + triple = 24.dp, + quadruple = 32.dp, +) diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeTypography.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeTypography.kt new file mode 100644 index 000000000..8dd6ed679 --- /dev/null +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeTypography.kt @@ -0,0 +1,116 @@ +package app.k9mail.core.ui.compose.theme2.default + +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import app.k9mail.core.ui.compose.theme2.ThemeTypography + +@Suppress("MagicNumber") +val defaultTypography = ThemeTypography( + displayLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 57.sp, + lineHeight = 64.sp, + letterSpacing = (-0.2).sp, + ), + displayMedium = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 45.sp, + lineHeight = 52.sp, + letterSpacing = 0.sp, + ), + displaySmall = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 36.sp, + lineHeight = 44.sp, + letterSpacing = 0.sp, + ), + headlineLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 32.sp, + lineHeight = 40.sp, + letterSpacing = 0.sp, + ), + headlineMedium = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 28.sp, + lineHeight = 36.sp, + letterSpacing = 0.sp, + ), + headlineSmall = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 24.sp, + lineHeight = 32.sp, + letterSpacing = 0.sp, + ), + titleLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp, + ), + titleMedium = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Medium, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.2.sp, + ), + titleSmall = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp, + ), + bodyLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp, + ), + bodyMedium = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.2.sp, + ), + bodySmall = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.4.sp, + ), + labelLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp, + ), + labelMedium = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp, + ), + labelSmall = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp, + ), +) diff --git a/settings.gradle.kts b/settings.gradle.kts index 3f091fb7c..0d7664363 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -76,6 +76,7 @@ include( ":core:ui:compose:common", ":core:ui:compose:designsystem", ":core:ui:compose:theme", + ":core:ui:compose:theme2:common", ":core:ui:compose:testing", )