From 8c2b6ec446e15b9502acdff1ad3919e41fe7f85e Mon Sep 17 00:00:00 2001 From: Andrea Gottardo Date: Thu, 11 Apr 2024 12:54:30 -0700 Subject: [PATCH] ui: reintroduce dark mode theme Fixes tailscale/corp#19137 Reintroduces the dark mode theme for the app, using mockups on Figma. Signed-off-by: Andrea Gottardo --- .../java/com/tailscale/ipn/ui/theme/Theme.kt | 105 +++++++++++++++++- .../tailscale/ipn/ui/util/LoadingIndicator.kt | 3 +- .../com/tailscale/ipn/ui/view/AboutView.kt | 18 +-- .../tailscale/ipn/ui/view/BugReportView.kt | 9 +- .../com/tailscale/ipn/ui/view/IntroView.kt | 7 +- .../com/tailscale/ipn/ui/view/MainView.kt | 6 +- .../ipn/ui/view/TailnetLockSetupView.kt | 5 +- .../ipn/ui/view/TailscaleLogoView.kt | 24 +++- 8 files changed, 149 insertions(+), 28 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt b/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt index c045fc5..8b0ff00 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt @@ -16,6 +16,7 @@ import androidx.compose.material3.TextFieldColors import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.Typography +import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -27,7 +28,12 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController @Composable fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) { - val colors = LightColors + val colors = + if (useDarkTheme) { + DarkColors + } else { + LightColors + } val typography = Typography( @@ -78,6 +84,35 @@ private val LightColors = scrim = Color(0xAA000000), // black ) +private val DarkColors = + darkColorScheme( + primary = Color(0xFF3f5eb3), // blue-600 + onPrimary = Color(0xFFFFFFFF), // white + primaryContainer = Color(0xFFf0f5ff), // blue-0 + onPrimaryContainer = Color(0xFF3f5eb3), // blue-600 + error = Color(0xFF940822), // red-600 + onError = Color(0xFFFFFFFF), // white + errorContainer = Color(0xFFfff6f4), // red-0 + onErrorContainer = Color(0xFF940822), // red-600 + surfaceDim = Color(0xFF1f1e1e), // gray-900 + surface = Color(0xFF232222), // gray-800 + background = Color(0xFF1f1e1e), // gray-900 + surfaceBright = Color(0xFF444342), // gray-600 + surfaceContainerLowest = Color(0xFF232222), // gray-800 + surfaceContainerLow = Color(0xFF2e2d2d), // gray-700 + surfaceContainer = Color(0xFF2e2d2d), // gray-700 + surfaceContainerHigh = Color(0xFF2e2d2d), // gray-700 + surfaceContainerHighest = Color(0xFF444342), // gray-600 + surfaceVariant = Color(0xFF1f1e1e), // gray-900 + onSurface = Color(0xFFfaf9f8), // gray-0 + onSurfaceVariant = Color(0xFFafacab), // gray-400 + outline = Color(0xFF706E6D), // gray-500 + outlineVariant = Color(0xFF444342), // gray-600 + inverseSurface = Color(0xFFEDEBEA), // gray-200 + inverseOnSurface = Color(0xFF000000), // black + scrim = Color(0xAA000000), // black + ) + val ColorScheme.warning: Color get() = Color(0xFFD97916) // yellow-300 @@ -106,7 +141,13 @@ val ColorScheme.on: Color get() = Color(0xFF1CA672) // green-300 val ColorScheme.off: Color - get() = Color(0xFFD9D6D5) // gray-300 + @Composable + get() = + if (isSystemInDarkTheme()) { + Color(0xFFAFACAB) // gray-400 + } else { + Color(0xFFD9D6D5) // gray-300 + } val ColorScheme.link: Color get() = onPrimaryContainer @@ -238,6 +279,60 @@ val ColorScheme.secondaryButton: ButtonColors disabledContentColor = defaults.disabledContentColor) } +val ColorScheme.defaultTextColor: Color + @Composable + get() = + if (isSystemInDarkTheme()) { + Color.White + } else { + Color.Black + } + +val ColorScheme.logoBackground: Color + @Composable + get() = + if (isSystemInDarkTheme()) { + Color(0xFFFFFFFF) // white + } else { + Color(0xFF1F1E1E) + } + +val ColorScheme.standaloneLogoDotEnabled: Color + @Composable + get() = + if (isSystemInDarkTheme()) { + Color(0xFFFFFFFF) + } else { + Color(0xFF000000) + } + +val ColorScheme.standaloneLogoDotDisabled: Color + @Composable + get() = + if (isSystemInDarkTheme()) { + Color(0x66FFFFFF) + } else { + Color(0x661F1E1E) + } + +val ColorScheme.onBackgroundLogoDotEnabled: Color + @Composable + get() = + if (isSystemInDarkTheme()) { + Color(0xFF141414) + } else { + Color(0xFFFFFFFF) + } + +val ColorScheme.onBackgroundLogoDotDisabled: Color + @Composable + get() = + if (isSystemInDarkTheme()) { + Color(0x66141414) + } else { + Color(0x66FFFFFF) + } + val ColorScheme.disabled: Color get() = Color(0xFFAFACAB) // gray-400 @@ -252,9 +347,9 @@ val ColorScheme.searchBarColors: TextFieldColors focusedTextColor = MaterialTheme.colorScheme.onSurface, unfocusedTextColor = MaterialTheme.colorScheme.onSurfaceVariant, disabledTextColor = MaterialTheme.colorScheme.onSurfaceVariant, - focusedContainerColor = MaterialTheme.colorScheme.background, - unfocusedContainerColor = MaterialTheme.colorScheme.background, - disabledContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.surfaceContainer, + unfocusedContainerColor = MaterialTheme.colorScheme.surfaceContainer, + disabledContainerColor = MaterialTheme.colorScheme.surfaceContainer, focusedBorderColor = Color.Transparent, unfocusedBorderColor = Color.Transparent) } diff --git a/android/src/main/java/com/tailscale/ipn/ui/util/LoadingIndicator.kt b/android/src/main/java/com/tailscale/ipn/ui/util/LoadingIndicator.kt index a90a6ae..3578a50 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/util/LoadingIndicator.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/util/LoadingIndicator.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.produceState 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.unit.dp import com.tailscale.ipn.ui.view.TailscaleLogoView @@ -53,7 +54,7 @@ object LoadingIndicator { Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - TailscaleLogoView(true, Modifier.size(72.dp)) + TailscaleLogoView(true, usesOnBackgroundColors = false, Modifier.size(72.dp).alpha(0.4f)) } } } diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt index b6dfe82..f0247b4 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/AboutView.kt @@ -3,7 +3,6 @@ package com.tailscale.ipn.ui.view -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -22,7 +21,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -30,6 +28,7 @@ import androidx.compose.ui.unit.dp import com.tailscale.ipn.BuildConfig import com.tailscale.ipn.R import com.tailscale.ipn.ui.Links +import com.tailscale.ipn.ui.theme.logoBackground @Composable fun AboutView(backToSettings: BackNavigation) { @@ -39,16 +38,19 @@ fun AboutView(backToSettings: BackNavigation) { verticalArrangement = Arrangement.spacedBy(space = 20.dp, alignment = Alignment.CenterVertically), horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth().fillMaxHeight().padding(innerPadding).verticalScroll(rememberScrollState())) { - Image( + modifier = + Modifier.fillMaxWidth() + .fillMaxHeight() + .padding(innerPadding) + .verticalScroll(rememberScrollState())) { + TailscaleLogoView( + usesOnBackgroundColors = true, modifier = Modifier.width(100.dp) .height(100.dp) .clip(RoundedCornerShape(50)) - .background(MaterialTheme.colorScheme.onSurface) - .padding(15.dp), - painter = painterResource(id = R.drawable.androidicon), - contentDescription = stringResource(R.string.app_icon_content_description)) + .background(MaterialTheme.colorScheme.logoBackground) + .padding(25.dp)) Column( verticalArrangement = diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt index bcfcb1c..55dab43 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/BugReportView.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.text.withStyle import androidx.lifecycle.viewmodel.compose.viewModel import com.tailscale.ipn.R import com.tailscale.ipn.ui.Links +import com.tailscale.ipn.ui.theme.defaultTextColor import com.tailscale.ipn.ui.theme.link import com.tailscale.ipn.ui.util.ClipboardValueView import com.tailscale.ipn.ui.util.Lists @@ -60,7 +61,9 @@ fun BugReportView(backToSettings: BackNavigation, model: BugReportViewModel = vi @Composable fun contactText(): AnnotatedString { val annotatedString = buildAnnotatedString { - append(stringResource(id = R.string.bug_report_instructions_prefix)) + withStyle(SpanStyle(color = MaterialTheme.colorScheme.defaultTextColor)) { + append(stringResource(id = R.string.bug_report_instructions_prefix)) + } pushStringAnnotation(tag = "reportLink", annotation = Links.SUPPORT_URL) withStyle( @@ -72,7 +75,9 @@ fun contactText(): AnnotatedString { } pop() - append(stringResource(id = R.string.bug_report_instructions_suffix)) + withStyle(SpanStyle(color = MaterialTheme.colorScheme.defaultTextColor)) { + append(stringResource(id = R.string.bug_report_instructions_suffix)) + } } return annotatedString } diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt index 41f5a93..4b6cdde 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt @@ -3,7 +3,6 @@ package com.tailscale.ipn.ui.view -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -21,7 +20,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -33,10 +31,7 @@ fun IntroView(onContinue: () -> Unit) { modifier = Modifier.fillMaxHeight().fillMaxWidth().verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { - Image( - modifier = Modifier.width(80.dp).height(80.dp), - painter = painterResource(id = R.drawable.androidicon_light), - contentDescription = stringResource(R.string.app_icon_content_description)) + TailscaleLogoView(modifier = Modifier.width(60.dp).height(60.dp)) Spacer(modifier = Modifier.height(40.dp)) Text( modifier = Modifier.padding(start = 40.dp, end = 40.dp, bottom = 40.dp), diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt index 0ad1f43..e54b531 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt @@ -186,7 +186,9 @@ fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) { modifier = Modifier.clickable { navAction() }, colors = if (active) MaterialTheme.colorScheme.primaryListItem - else ListItemDefaults.colors(), + else ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.surfaceContainerLowest + ), overlineContent = { Text( stringResource(R.string.exit_node), @@ -241,7 +243,7 @@ fun StartingView() { modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) { - TailscaleLogoView(animated = true, Modifier.size(40.dp)) + TailscaleLogoView(animated = true, usesOnBackgroundColors = false, Modifier.size(40.dp)) } } diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt index 1d2235e..6dd347b 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/TailnetLockSetupView.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.text.withStyle import androidx.lifecycle.viewmodel.compose.viewModel import com.tailscale.ipn.R import com.tailscale.ipn.ui.Links +import com.tailscale.ipn.ui.theme.defaultTextColor import com.tailscale.ipn.ui.theme.link import com.tailscale.ipn.ui.util.ClipboardValueView import com.tailscale.ipn.ui.util.Lists @@ -97,7 +98,9 @@ private fun ExplainerView() { @Composable fun explainerText(): AnnotatedString { val annotatedString = buildAnnotatedString { - append(stringResource(id = R.string.tailnet_lock_explainer)) + withStyle(SpanStyle(color = MaterialTheme.colorScheme.defaultTextColor)) { + append(stringResource(id = R.string.tailnet_lock_explainer)) + } pushStringAnnotation(tag = "tailnetLockSupportURL", annotation = Links.TAILNET_LOCK_KB_URL) diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/TailscaleLogoView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/TailscaleLogoView.kt index 967b8f3..bffc602 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/TailscaleLogoView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/TailscaleLogoView.kt @@ -14,6 +14,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import com.tailscale.ipn.ui.theme.onBackgroundLogoDotDisabled +import com.tailscale.ipn.ui.theme.onBackgroundLogoDotEnabled +import com.tailscale.ipn.ui.theme.standaloneLogoDotDisabled +import com.tailscale.ipn.ui.theme.standaloneLogoDotEnabled import com.tailscale.ipn.ui.util.set import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -31,10 +35,24 @@ val logoDotsMatrix: DotsMatrix = ) @Composable -fun TailscaleLogoView(animated: Boolean = false, modifier: Modifier) { +fun TailscaleLogoView( + animated: Boolean = false, + usesOnBackgroundColors: Boolean = false, + modifier: Modifier +) { - val primaryColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f) - val secondaryColor: Color = primaryColor.copy(alpha = 0.1f) + val primaryColor: Color = + if (usesOnBackgroundColors) { + MaterialTheme.colorScheme.onBackgroundLogoDotEnabled + } else { + MaterialTheme.colorScheme.standaloneLogoDotEnabled + } + val secondaryColor: Color = + if (usesOnBackgroundColors) { + MaterialTheme.colorScheme.onBackgroundLogoDotDisabled + } else { + MaterialTheme.colorScheme.standaloneLogoDotDisabled + } val currentDotsMatrix: StateFlow = MutableStateFlow(logoDotsMatrix) var currentDotsMatrixIndex = 0