From f00d2ff06d411790f868d3e889f6280ac9f5e70c Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 13 Jun 2024 10:33:15 +0930 Subject: [PATCH] Organise themes --- .../org/thoughtcrime/securesms/ui/Colors.kt | 82 +++++++++ .../thoughtcrime/securesms/ui/Dimensions.kt | 21 +++ .../securesms/ui/SessionTypography.kt | 42 +++++ .../securesms/ui/{Data.kt => Strings.kt} | 0 .../org/thoughtcrime/securesms/ui/Themes.kt | 160 +----------------- .../securesms/ui/components/QrImage.kt | 11 +- 6 files changed, 154 insertions(+), 162 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt rename app/src/main/java/org/thoughtcrime/securesms/ui/{Data.kt => Strings.kt} (100%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Colors.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Colors.kt index 283739de1a..162496b7cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Colors.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Colors.kt @@ -9,15 +9,21 @@ import androidx.compose.material.Colors import androidx.compose.material.ContentAlpha import androidx.compose.material.LocalContentColor import androidx.compose.material.MaterialTheme +import androidx.compose.material.RadioButtonDefaults +import androidx.compose.material.TabRowDefaults import androidx.compose.material.Text import androidx.compose.material.TextFieldDefaults import androidx.compose.material.primarySurface import androidx.compose.runtime.Composable +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import org.session.libsession.utilities.AppTextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences +import org.thoughtcrime.securesms.util.ThemeState +import org.thoughtcrime.securesms.util.themeState const val classicDark0 = 0xff111111 const val classicDark1 = 0xff1B1B1B @@ -67,6 +73,82 @@ val classicDarkColors = classicDarks.map(::Color) val blackAlpha40 = Color.Black.copy(alpha = 0.4f) +val LocalColors = staticCompositionLocalOf { sessionColors(isLight = false, isClassic = true) } + +data class SessionColors( + val isLight: Boolean = false, + val primary: Color = Color.Unspecified, + val danger: Color = Color.Unspecified, + val disabled: Color = Color.Unspecified, + val background: Color = Color.Unspecified, + val backgroundSecondary: Color = Color.Unspecified, + val text: Color = Color.Unspecified, + val textSecondary: Color = Color.Unspecified, + val borders: Color = Color.Unspecified, + val textBubbleSent: Color = Color.Unspecified, + val backgroundBubbleReceived: Color = Color.Unspecified, + val textBubbleReceived: Color = Color.Unspecified, +) { + val backgroundLight get() = if (isLight) backgroundSecondary else Color.White + val onBackgroundLight get() = if (isLight) text else background + val button get() = if (isLight) text else primary + val divider get() = text.copy(alpha = TabRowDefaults.DividerOpacity) + val backgroundBubbleSent get() = primary + @Composable fun radioButtonColors() = RadioButtonDefaults.colors(selectedColor = primary, unselectedColor = text, disabledColor = disabled) +} + +val primaryGreen = Color(0xFF31F196) +val primaryBlue = Color(0xFF57C9FA) +val primaryPurple = Color(0xFFC993FF) +val primaryPink = Color(0xFFFF95EF) +val primaryRed = Color(0xFFFF9C8E) +val primaryOrange = Color(0xFFFCB159) +val primaryYellow = Color(0xFFFAD657) + +val dangerDark = Color(0xFFFF3A3A) +val dangerLight = Color(0xFFE12D19) +val disabledDark = Color(0xFFA1A2A1) +val disabledLioht = Color(0xFF6D6D6D) + +val primaryColors = listOf( + primaryGreen, + primaryBlue, + primaryPurple, + primaryPink, + primaryRed, + primaryOrange, + primaryYellow, +) + +private class UnresolvedColor(val function: (Boolean, Boolean) -> Color) { + operator fun invoke(isLight: Boolean, isClassic: Boolean) = function(isLight, isClassic) + + constructor(light: Color, dark: Color): this(function = { isLight, _ -> if (isLight) light else dark }) + constructor(classicDark: Color, classicLight: Color, oceanDark: Color, oceanLight: Color): this(function = { isLight, isClassic -> if (isLight) if (isClassic) classicLight else oceanLight else if (isClassic) classicDark else oceanDark }) +} + +fun sessionColors( + isLight: Boolean, + isClassic: Boolean, + primary: Color = if (isClassic) primaryGreen else primaryBlue +): SessionColors { + val index = (if (isLight) 1 else 0) + if (isClassic) 0 else 2 + return SessionColors( + isLight = isLight, + primary = primary, + danger = if (isLight) dangerLight else dangerDark, + disabled = if (isLight) disabledLioht else disabledDark, + background = listOf(Color.Black, Color.White, oceanDarkColors[2], oceanLightColors[7])[index], + backgroundSecondary = listOf(classicDarkColors[1], classicLightColors[5], oceanDarkColors[1], oceanLightColors[6])[index], + text = listOf(Color.White, Color.Black, Color.White, oceanLightColors[1])[index], + textSecondary = listOf(classicDarkColors[5], classicLightColors[1], oceanDarkColors[5], oceanLightColors[2])[index], + borders = listOf(classicDarkColors[3], classicLightColors[3], oceanDarkColors[4], oceanLightColors[3])[index], + textBubbleSent = listOf(Color.Black, Color.Black, Color.Black, oceanLightColors[1])[index], + backgroundBubbleReceived = listOf(classicDarkColors[2], classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index], + textBubbleReceived = listOf(Color.White, classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index], + ) +} + @Composable fun transparentButtonColors() = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt new file mode 100644 index 0000000000..52ee6f915c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Dimensions.kt @@ -0,0 +1,21 @@ +package org.thoughtcrime.securesms.ui + +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +val LocalDimensions = staticCompositionLocalOf { Dimensions() } + +data class Dimensions( + val itemSpacingTiny: Dp = 4.dp, + val itemSpacingExtraSmall: Dp = 8.dp, + val itemSpacingSmall: Dp = 16.dp, + val itemSpacingMedium: Dp = 24.dp, + val marginTiny: Dp = 8.dp, + val marginExtraExtraSmall: Dp = 12.dp, + val marginExtraSmall: Dp = 16.dp, + val marginSmall: Dp = 24.dp, + val marginMedium: Dp = 32.dp, + val marginLarge: Dp = 64.dp, + val dividerIndent: Dp = 80.dp, +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt new file mode 100644 index 0000000000..61305c089f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/SessionTypography.kt @@ -0,0 +1,42 @@ +package org.thoughtcrime.securesms.ui + +import androidx.compose.material.Typography +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.TextUnit +import androidx.compose.ui.unit.sp + +fun boldStyle(size: TextUnit) = TextStyle.Default.copy( + fontWeight = FontWeight.Bold, + fontSize = size +) + +fun defaultStyle(size: TextUnit) = TextStyle.Default.copy( + fontSize = size, + lineHeight = size * 1.2 +) + +val sessionTypography = Typography( + h1 = boldStyle(36.sp), + h2 = boldStyle(32.sp), + h3 = boldStyle(29.sp), + h4 = boldStyle(26.sp), + h5 = boldStyle(23.sp), + h6 = boldStyle(20.sp), +) + +val Typography.xl get() = defaultStyle(18.sp) +val Typography.large get() = defaultStyle(16.sp) +val Typography.base get() = defaultStyle(14.sp) +val Typography.baseBold get() = boldStyle(14.sp) +val Typography.baseMonospace get() = defaultStyle(14.sp).copy(fontFamily = FontFamily.Monospace) +val Typography.small get() = defaultStyle(12.sp) +val Typography.smallMonospace get() = defaultStyle(12.sp).copy(fontFamily = FontFamily.Monospace) +val Typography.extraSmall get() = defaultStyle(11.sp) +val Typography.extraSmallMonospace get() = defaultStyle(11.sp).copy(fontFamily = FontFamily.Monospace) +val Typography.fine get() = defaultStyle(9.sp) + +val Typography.h7 get() = boldStyle(18.sp) +val Typography.h8 get() = boldStyle(16.sp) +val Typography.h9 get() = boldStyle(14.sp) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Strings.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt rename to app/src/main/java/org/thoughtcrime/securesms/ui/Strings.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Themes.kt index b98cb2f3f7..a206a293bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Themes.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.ui import android.content.Context import androidx.annotation.AttrRes -import androidx.appcompat.view.ContextThemeWrapper import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.RoundedCornerShape @@ -11,132 +10,20 @@ import androidx.compose.foundation.text.selection.TextSelectionColors import androidx.compose.material.Colors import androidx.compose.material.LocalContentColor import androidx.compose.material.MaterialTheme -import androidx.compose.material.RadioButtonDefaults import androidx.compose.material.Shapes -import androidx.compose.material.TabRowDefaults -import androidx.compose.material.Typography import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.remember -import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.google.accompanist.themeadapter.appcompat.createAppCompatTheme import com.google.android.material.color.MaterialColors -import network.loki.messenger.R import org.session.libsession.utilities.AppTextSecurePreferences import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.themeState -val LocalOnLightCell = staticCompositionLocalOf { Color.Black } - -val LocalDimensions = staticCompositionLocalOf { Dimensions() } - -data class Dimensions( - val itemSpacingTiny: Dp = 4.dp, - val itemSpacingExtraSmall: Dp = 8.dp, - val itemSpacingSmall: Dp = 16.dp, - val itemSpacingMedium: Dp = 24.dp, - val marginTiny: Dp = 8.dp, - val marginExtraExtraSmall: Dp = 12.dp, - val marginExtraSmall: Dp = 16.dp, - val marginSmall: Dp = 24.dp, - val marginMedium: Dp = 32.dp, - val marginLarge: Dp = 64.dp, - val dividerIndent: Dp = 80.dp, -) - -val LocalColors = staticCompositionLocalOf { sessionColors(isLight = false, isClassic = true) } - -data class SessionColors( - val isLight: Boolean = false, - val primary: Color = Color.Unspecified, - val danger: Color = Color.Unspecified, - val disabled: Color = Color.Unspecified, - val background: Color = Color.Unspecified, - val backgroundSecondary: Color = Color.Unspecified, - val text: Color = Color.Unspecified, - val textSecondary: Color = Color.Unspecified, - val borders: Color = Color.Unspecified, - val textBubbleSent: Color = Color.Unspecified, - val backgroundBubbleReceived: Color = Color.Unspecified, - val textBubbleReceived: Color = Color.Unspecified, -) { - val backgroundLight get() = if (isLight) backgroundSecondary else Color.White - val onBackgroundLight get() = if (isLight) text else background - val button get() = if (isLight) text else primary - val divider get() = text.copy(alpha = TabRowDefaults.DividerOpacity) - val backgroundBubbleSent get() = primary - @Composable fun radioButtonColors() = RadioButtonDefaults.colors(selectedColor = primary, unselectedColor = text, disabledColor = disabled) -} - -val primaryGreen = Color(0xFF31F196) -val primaryBlue = Color(0xFF57C9FA) -val primaryPurple = Color(0xFFC993FF) -val primaryPink = Color(0xFFFF95EF) -val primaryRed = Color(0xFFFF9C8E) -val primaryOrange = Color(0xFFFCB159) -val primaryYellow = Color(0xFFFAD657) - -val dangerDark = Color(0xFFFF3A3A) -val dangerLight = Color(0xFFE12D19) -val disabledDark = Color(0xFFA1A2A1) -val disabledLioht = Color(0xFF6D6D6D) - -val primaryColors = listOf( - primaryGreen, - primaryBlue, - primaryPurple, - primaryPink, - primaryRed, - primaryOrange, - primaryYellow, -) - -private class UnresolvedColor(val function: (Boolean, Boolean) -> Color) { - operator fun invoke(isLight: Boolean, isClassic: Boolean) = function(isLight, isClassic) - - constructor(light: Color, dark: Color): this(function = { isLight, _ -> if (isLight) light else dark }) - constructor(classicDark: Color, classicLight: Color, oceanDark: Color, oceanLight: Color): this(function = { isLight, isClassic -> if (isLight) if (isClassic) classicLight else oceanLight else if (isClassic) classicDark else oceanDark }) -} - -private fun sessionColors( - isLight: Boolean, - isClassic: Boolean, - primary: Color = if (isClassic) primaryGreen else primaryBlue -): SessionColors { - val index = (if (isLight) 1 else 0) + if (isClassic) 0 else 2 - return SessionColors( - isLight = isLight, - primary = primary, - danger = if (isLight) dangerLight else dangerDark, - disabled = if (isLight) disabledLioht else disabledDark, - background = listOf(Color.Black, Color.White, oceanDarkColors[2], oceanLightColors[7])[index], - backgroundSecondary = listOf(classicDarkColors[1], classicLightColors[5], oceanDarkColors[1], oceanLightColors[6])[index], - text = listOf(Color.White, Color.Black, Color.White, oceanLightColors[1])[index], - textSecondary = listOf(classicDarkColors[5], classicLightColors[1], oceanDarkColors[5], oceanLightColors[2])[index], - borders = listOf(classicDarkColors[3], classicLightColors[3], oceanDarkColors[4], oceanLightColors[3])[index], - textBubbleSent = listOf(Color.Black, Color.Black, Color.Black, oceanLightColors[1])[index], - backgroundBubbleReceived = listOf(classicDarkColors[2], classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index], - textBubbleReceived = listOf(Color.White, classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index], - ) -} - - -private fun Context.sessionColors() = AppTextSecurePreferences(this).themeState().sessionColors() -private fun ThemeState.sessionColors() = sessionColors(isLight, isClassic, accent) - /** - * Sets a Material2 compose theme based on your selections in SharedPreferences. + * Apply a Material2 compose theme based on user selections in SharedPreferences. */ @Composable fun SessionMaterialTheme( @@ -145,9 +32,8 @@ fun SessionMaterialTheme( SessionMaterialTheme(LocalContext.current.sessionColors()) { content() } } - /** - * + * Apply a given [SessionColors], and our typography and shapes as a Material 2 Compose Theme. **/ @Composable fun SessionMaterialTheme( @@ -190,49 +76,15 @@ private fun SessionColors.toMaterialColors() = Colors( isLight = isLight ) +private fun Context.sessionColors() = AppTextSecurePreferences(this).themeState().sessionColors() +private fun ThemeState.sessionColors() = sessionColors(isLight, isClassic, accent) + val sessionShapes = Shapes( small = RoundedCornerShape(50) ) -fun boldStyle(size: TextUnit) = TextStyle.Default.copy( - fontWeight = FontWeight.Bold, - fontSize = size -) - -fun defaultStyle(size: TextUnit) = TextStyle.Default.copy( - fontSize = size, - lineHeight = size * 1.2 -) - -val sessionTypography = Typography( - h1 = boldStyle(36.sp), - h2 = boldStyle(32.sp), - h3 = boldStyle(29.sp), - h4 = boldStyle(26.sp), - h5 = boldStyle(23.sp), - h6 = boldStyle(20.sp), -) - -val Typography.xl get() = defaultStyle(18.sp) -val Typography.large get() = defaultStyle(16.sp) -val Typography.base get() = defaultStyle(14.sp) -val Typography.baseBold get() = boldStyle(14.sp) -val Typography.baseMonospace get() = defaultStyle(14.sp).copy(fontFamily = FontFamily.Monospace) -val Typography.small get() = defaultStyle(12.sp) -val Typography.smallMonospace get() = defaultStyle(12.sp).copy(fontFamily = FontFamily.Monospace) -val Typography.extraSmall get() = defaultStyle(11.sp) -val Typography.extraSmallMonospace get() = defaultStyle(11.sp).copy(fontFamily = FontFamily.Monospace) -val Typography.fine get() = defaultStyle(9.sp) - -val Typography.h7 get() = boldStyle(18.sp) -val Typography.h8 get() = boldStyle(16.sp) -val Typography.h9 get() = boldStyle(14.sp) - -fun Context.getColorFromTheme(@AttrRes attr: Int, defaultValue: Int = 0x0): Color = - MaterialColors.getColor(this, attr, defaultValue).let(::Color) - /** - * Set the theme and a background for Compose Previews. + * Set the Material 2 theme and a background for Compose Previews. */ @Composable fun PreviewTheme( diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt index de4c31cd3b..037f966b65 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QrImage.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.Card import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -26,7 +25,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp @@ -34,7 +32,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import network.loki.messenger.R import org.thoughtcrime.securesms.ui.LocalColors -import org.thoughtcrime.securesms.ui.LocalOnLightCell import org.thoughtcrime.securesms.util.QRCodeUtilities @Composable @@ -47,13 +44,11 @@ fun QrImage( mutableStateOf(null) } - val dark = LocalColors.current.onBackgroundLight.toArgb() - val scope = rememberCoroutineScope() LaunchedEffect(string) { scope.launch(Dispatchers.IO) { bitmap = (300..500 step 100).firstNotNullOf { - runCatching { QRCodeUtilities.encode(string, it, dark = dark) }.getOrNull() + runCatching { QRCodeUtilities.encode(string, it) }.getOrNull() } } } @@ -74,7 +69,7 @@ private fun Content( bitmap: Bitmap?, icon: Int, modifier: Modifier = Modifier, - qrColor: Color = LocalOnLightCell.current, + qrColor: Color = LocalColors.current.onBackgroundLight, backgroundColor: Color, ) { Box( @@ -104,7 +99,7 @@ private fun Content( Icon( painter = painterResource(id = icon), contentDescription = "", - tint = LocalOnLightCell.current, + tint = qrColor, modifier = Modifier .size(62.dp) .align(Alignment.Center)