From 3b20b238f886a2a505ee713d550bc117b5b28494 Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Fri, 29 Sep 2023 13:57:47 +0200 Subject: [PATCH] Improve theme colors in the app - Set default theme as dark instead of light - Move to a color token system instead of colors in the theme - Replace some theme colors with alpha with normal colors - Remove some usage of color resource --- .../compose/cell/RelayLocationCell.kt | 7 ++- .../compose/cell/SplitTunnelingCell.kt | 10 +-- .../compose/component/InformationView.kt | 4 ++ .../mullvadvpn/compose/component/Text.kt | 2 + .../compose/dialog/DeviceRemovalDialog.kt | 32 ++++++---- .../mullvadvpn/compose/screen/LoginScreen.kt | 2 + .../net/mullvad/mullvadvpn/lib/theme/Alpha.kt | 11 ++++ .../net/mullvad/mullvadvpn/lib/theme/Color.kt | 14 ----- .../mullvadvpn/lib/theme/ColorTokens.kt | 63 +++++++++++++++++++ .../net/mullvad/mullvadvpn/lib/theme/Theme.kt | 43 ++++++++----- 10 files changed, 142 insertions(+), 46 deletions(-) create mode 100644 android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Alpha.kt create mode 100644 android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/ColorTokens.kt diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt index 5836a5aca18a..855bc8298222 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt @@ -24,10 +24,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.component.ChevronView +import net.mullvad.mullvadvpn.lib.theme.Alpha40 import net.mullvad.mullvadvpn.lib.theme.AlphaInactive import net.mullvad.mullvadvpn.lib.theme.AlphaInvisible import net.mullvad.mullvadvpn.lib.theme.AlphaVisible @@ -165,7 +167,10 @@ fun RelayLocationCell( when { selected -> MaterialTheme.colorScheme.inversePrimary relay.type == RelayItemType.Country -> MaterialTheme.colorScheme.primary - relay.type == RelayItemType.City -> MaterialTheme.colorScheme.primaryContainer + relay.type == RelayItemType.City -> + MaterialTheme.colorScheme.primary + .copy(alpha = Alpha40) + .compositeOver(MaterialTheme.colorScheme.background) relay.type == RelayItemType.Relay -> MaterialTheme.colorScheme.secondaryContainer else -> MaterialTheme.colorScheme.primary } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt index 72cce59124c5..e80b60d7158a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.lib.theme.Alpha40 import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.Dimens import net.mullvad.mullvadvpn.lib.theme.typeface.listItemText @@ -68,14 +69,15 @@ fun SplitTunnelingCell( .fillMaxWidth() .padding(vertical = Dimens.listItemDivider) .background( - MaterialTheme.colorScheme.primaryContainer.compositeOver( - MaterialTheme.colorScheme.background - ) + MaterialTheme.colorScheme.primary + .copy(alpha = Alpha40) + .compositeOver(MaterialTheme.colorScheme.background) ) .clickable(onClick = onCellClicked) ) { Image( - painter = icon?.let { iconImage -> BitmapPainter(iconImage) } + painter = + icon?.let { iconImage -> BitmapPainter(iconImage) } ?: painterResource(id = R.drawable.ic_icons_missing), contentDescription = null, modifier = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt index bfaaf2bacd00..184c90c0233a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/InformationView.kt @@ -10,6 +10,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import net.mullvad.mullvadvpn.lib.theme.Dimens @@ -30,12 +31,14 @@ fun InformationView( content: String, modifier: Modifier = Modifier, whenMissing: MissingPolicy = MissingPolicy.SHOW_VIEW, + color: Color = MaterialTheme.colorScheme.onBackground, maxLines: Int = 1 ) { return if (content.isNotEmpty()) { AutoResizeText( style = MaterialTheme.typography.titleSmall, text = content, + color = color, minTextSize = MaterialTheme.typography.labelMedium.fontSize, maxTextSize = MaterialTheme.typography.titleSmall.fontSize, maxLines = maxLines, @@ -52,6 +55,7 @@ fun InformationView( AutoResizeText( style = MaterialTheme.typography.titleMedium, text = content, + color = color, minTextSize = MaterialTheme.typography.labelMedium.fontSize, maxTextSize = MaterialTheme.typography.titleSmall.fontSize, maxLines = maxLines, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt index 39e3382a0967..4ba25f2c9732 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Text.kt @@ -67,6 +67,7 @@ fun AutoResizeText( text: String, minTextSize: TextUnit, maxTextSize: TextUnit, + color: Color, modifier: Modifier = Modifier, textSizeStep: TextUnit = DEFAULT_TEXT_STEP, style: TextStyle = LocalTextStyle.current, @@ -77,6 +78,7 @@ fun AutoResizeText( Text( text = text, + color = color, maxLines = maxLines, style = style, fontSize = adjustedFontSize.sp, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt index e27af82fbda4..0a8fc7394c71 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt @@ -18,17 +18,17 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.colorResource +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.text.HtmlCompat import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.component.HtmlText import net.mullvad.mullvadvpn.compose.component.textResource +import net.mullvad.mullvadvpn.compose.extensions.toAnnotatedString import net.mullvad.mullvadvpn.lib.common.util.capitalizeFirstCharOfEachWord import net.mullvad.mullvadvpn.model.Device @@ -54,18 +54,26 @@ fun ShowDeviceRemovalDialog(onDismiss: () -> Unit, onConfirm: () -> Unit, device Image( painter = painterResource(id = R.drawable.icon_alert), contentDescription = "Remove", + colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.error), modifier = Modifier.width(50.dp).height(50.dp) ) } }, text = { val htmlFormattedDialogText = - textResource( - id = R.string.max_devices_confirm_removal_description, - device.name.capitalizeFirstCharOfEachWord() + HtmlCompat.fromHtml( + textResource( + id = R.string.max_devices_confirm_removal_description, + device.name.capitalizeFirstCharOfEachWord() + ), + HtmlCompat.FROM_HTML_MODE_COMPACT ) - HtmlText(htmlFormattedString = htmlFormattedDialogText, textSize = 16.sp.value) + Text( + text = htmlFormattedDialogText.toAnnotatedString(), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onBackground + ) }, dismissButton = { Button( @@ -78,8 +86,8 @@ fun ShowDeviceRemovalDialog(onDismiss: () -> Unit, onConfirm: () -> Unit, device .fillMaxWidth(), colors = ButtonDefaults.buttonColors( - containerColor = colorResource(id = R.color.red), - contentColor = Color.White + containerColor = MaterialTheme.colorScheme.error, + contentColor = MaterialTheme.colorScheme.onError ), onClick = onConfirm, shape = MaterialTheme.shapes.small @@ -100,8 +108,8 @@ fun ShowDeviceRemovalDialog(onDismiss: () -> Unit, onConfirm: () -> Unit, device .fillMaxWidth(), colors = ButtonDefaults.buttonColors( - containerColor = colorResource(id = R.color.blue), - contentColor = Color.White + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary ), onClick = { onDismiss() }, shape = MaterialTheme.shapes.small @@ -109,6 +117,6 @@ fun ShowDeviceRemovalDialog(onDismiss: () -> Unit, onConfirm: () -> Unit, device Text(text = stringResource(id = R.string.back), fontSize = 18.sp) } }, - containerColor = colorResource(id = R.color.darkBlue) + containerColor = MaterialTheme.colorScheme.background ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt index 3c4d9e12023e..88e51f40baac 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt @@ -209,6 +209,8 @@ private fun LoginContent( unfocusedLabelColor = MaterialTheme.colorScheme.background, focusedLeadingIconColor = Color.Black, unfocusedSupportingTextColor = Color.Black, + focusedContainerColor = MaterialTheme.colorScheme.onBackground, + unfocusedContainerColor = MaterialTheme.colorScheme.onBackground, ), isError = uiState.loginState.isError(), ) diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Alpha.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Alpha.kt new file mode 100644 index 000000000000..c8d9a979ecf9 --- /dev/null +++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Alpha.kt @@ -0,0 +1,11 @@ +package net.mullvad.mullvadvpn.lib.theme + +const val AlphaVisible = 1f +const val AlphaDisabled = 0.2f +const val Alpha20 = 0.2f +const val Alpha40 = 0.4f +const val AlphaInactive = 0.4f +const val AlphaDescription = 0.6f +const val AlphaDisconnectButton = 0.6f +const val AlphaTopBar = 0.8f +const val AlphaInvisible = 0f diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Color.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Color.kt index e55bdc605a57..76568ce0a46a 100644 --- a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Color.kt +++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Color.kt @@ -2,13 +2,8 @@ package net.mullvad.mullvadvpn.lib.theme import androidx.compose.ui.graphics.Color -internal val MullvadBeige = Color(0xFFFFCD86) -internal val MullvadBlue60 = Color(0x99294D73) -internal val MullvadBlue40 = Color(0x66294D73) internal val MullvadBlue20 = Color(0x33294D73) -internal val MullvadBrown = Color(0xFFD2943B) internal val MullvadYellow = Color(0xFFFFD524) -internal val MullvadWhite40 = Color(0x66FFFFFF) @Deprecated( "Deprecated for external usage and will be marked as internal in the future. Use material colors instead." @@ -50,12 +45,3 @@ val MullvadWhite60 = Color(0x99FFFFFF) "Deprecated for external usage and will be marked as internal in the future. Use material colors instead." ) val MullvadWhite80 = Color(0xCCFFFFFF) - -const val AlphaVisible = 1f -const val AlphaDisabled = 0.2f -const val Alpha20 = 0.2f -const val AlphaInactive = 0.4f -const val AlphaDescription = 0.6f -const val AlphaDisconnectButton = 0.6f -const val AlphaTopBar = 0.8f -const val AlphaInvisible = 0f diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/ColorTokens.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/ColorTokens.kt new file mode 100644 index 000000000000..d0846ffa36bb --- /dev/null +++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/ColorTokens.kt @@ -0,0 +1,63 @@ +package net.mullvad.mullvadvpn.lib.theme + +import androidx.compose.ui.graphics.Color + +internal val md_theme_light_primary = Color(0xFF0561A3) +internal val md_theme_light_onPrimary = Color(0xFFFFFFFF) +internal val md_theme_light_primaryContainer = Color(0xFFD1E4FF) +internal val md_theme_light_onPrimaryContainer = Color(0xFF001D36) +internal val md_theme_light_secondary = Color(0xFF006E1F) +internal val md_theme_light_onSecondary = Color(0xFFFFFFFF) +internal val md_theme_light_secondaryContainer = Color(0xFF8FFA8F) +internal val md_theme_light_onSecondaryContainer = Color(0xFF002204) +internal val md_theme_light_tertiary = Color(0xFF845400) +internal val md_theme_light_onTertiary = Color(0xFFFFFFFF) +internal val md_theme_light_tertiaryContainer = Color(0xFFFFDDB5) +internal val md_theme_light_onTertiaryContainer = Color(0xFF2A1800) +internal val md_theme_light_error = Color(0xFFBA1A1A) +internal val md_theme_light_errorContainer = Color(0xFFFFDAD6) +internal val md_theme_light_onError = Color(0xFFFFFFFF) +internal val md_theme_light_onErrorContainer = Color(0xFF410002) +internal val md_theme_light_background = Color(0xFFFDFBFF) +internal val md_theme_light_onBackground = Color(0xFF001B3D) +internal val md_theme_light_surface = Color(0xFFFDFBFF) +internal val md_theme_light_onSurface = Color(0xFF001B3D) +internal val md_theme_light_surfaceVariant = Color(0xFFDFE2EB) +internal val md_theme_light_onSurfaceVariant = Color(0xFF43474E) +internal val md_theme_light_outline = Color(0xFF73777F) +internal val md_theme_light_inverseOnSurface = Color(0xFFECF0FF) +internal val md_theme_light_inverseSurface = Color(0xFF003062) +internal val md_theme_light_inversePrimary = Color(0xFF9FCAFF) +internal val md_theme_light_surfaceTint = Color(0xFF0561A3) +internal val md_theme_light_outlineVariant = Color(0xFFC3C6CF) +internal val md_theme_light_scrim = Color(0xFFFFD524) //Helmet Yellow + +internal val md_theme_dark_primary = Color(0xFF294D73) //MullvadBlue +internal val md_theme_dark_onPrimary = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_primaryContainer = Color(0xFF1C344E) //Sub-container +internal val md_theme_dark_onPrimaryContainer = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_secondary = Color(0xFF44AD4D) //MullvadGreen +internal val md_theme_dark_onSecondary = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_secondaryContainer = Color(0xFF47AC4C) //Text in connection fragment +internal val md_theme_dark_onSecondaryContainer = Color(0xFFD9EEDB) //Text in titlebar +internal val md_theme_dark_tertiary = Color(0xFF99454F) //Disconnect button +internal val md_theme_dark_onTertiary = Color(0xFFFFFFFF) //MullvadWhite/Text on disconnect button +internal val md_theme_dark_tertiaryContainer = Color(0xFF643F00) //Generated +internal val md_theme_dark_onTertiaryContainer = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_error = Color(0xFFE34039) //MullvadRed +internal val md_theme_dark_errorContainer = Color(0xFFE34039) //MullvadRed //Duplicate +internal val md_theme_dark_onError = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) //Generated +internal val md_theme_dark_background = Color(0xFF192E45) //MullvadDarkBlue //Duplicate +internal val md_theme_dark_onBackground = Color(0xFFFFFFFF) //MullvadWhite //Duplicate +internal val md_theme_dark_surface = Color(0xFF192E45) //MullvadDarkBlue +internal val md_theme_dark_onSurface = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_surfaceVariant = Color(0xFF43474E) //Generated +internal val md_theme_dark_onSurfaceVariant = Color(0xFFA3ACB5) //Subtext +internal val md_theme_dark_outline = Color(0xFF8D9199) //Generated +internal val md_theme_dark_inverseOnSurface = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_inverseSurface = Color(0xFFFFFFFF) //MullvadWhite +internal val md_theme_dark_inversePrimary = Color(0xFF0561A3) //Generated +internal val md_theme_dark_surfaceTint = Color(0xFF9FCAFF) //Generated +internal val md_theme_dark_outlineVariant = Color(0xFF43474E) //Generated +internal val md_theme_dark_scrim = Color(0xFF000000) //Generated diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt index e9490d31d4d6..d9b28069e008 100644 --- a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt +++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.TextUnit +import androidx.compose.material3.darkColorScheme import androidx.compose.ui.unit.dp import net.mullvad.mullvadvpn.lib.theme.dimensions.Dimensions import net.mullvad.mullvadvpn.lib.theme.dimensions.defaultDimensions @@ -37,28 +38,40 @@ private val MullvadTypography = ) ) -private val MullvadColorPalette = - lightColorScheme( - primary = MullvadBlue, +private val darkColorScheme = + darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + //primaryContainer = md_theme_dark_primaryContainer, + //onPrimaryContainer = md_theme_dark_onPrimaryContainer, secondary = MullvadDarkBlue, + onSecondary = MullvadWhite60, + secondaryContainer = MullvadBlue20, + //onSecondaryContainer = md_theme_dark_onSecondaryContainer, tertiary = MullvadRed, + //onTertiary = md_theme_dark_onTertiary, + //tertiaryContainer = md_theme_dark_tertiaryContainer, + //onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = MullvadYellow, + onError = md_theme_dark_onError, + //onErrorContainer = md_theme_dark_onErrorContainer, background = MullvadDarkBlue, - surface = MullvadGreen, - primaryContainer = MullvadBlue40, - secondaryContainer = MullvadBlue20, onBackground = MullvadWhite, - onSurfaceVariant = MullvadWhite, - onPrimary = MullvadWhite, - onSecondary = MullvadWhite60, - onError = MullvadWhite, + surface = MullvadGreen, onSurface = MullvadWhite, + //surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = MullvadWhite, + //outline = md_theme_dark_outline, + //inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = MullvadWhite, inversePrimary = MullvadGreen, - error = MullvadRed, - errorContainer = MullvadYellow, - outlineVariant = Color.Transparent, // Used by divider - inverseSurface = MullvadWhite + //surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = Color.Transparent, // Used by divider, + //scrim = md_theme_dark_scrim, ) + val Shapes = Shapes( small = RoundedCornerShape(4.dp), @@ -80,7 +93,7 @@ private val LocalAppDimens = staticCompositionLocalOf { defaultDimensions } @Composable fun AppTheme(content: @Composable () -> Unit) { - val colors = MullvadColorPalette + val colors = darkColorScheme val typography = MullvadTypography // Set dimensions and type scale based on configurations here val dimensions = defaultDimensions