diff --git a/app/src/test/java/org/justinvisser/wolfskin/tv/UpdatesCaptureTest.kt b/app/src/test/java/org/justinvisser/wolfskin/tv/UpdatesCaptureTest.kt index e6643fd7..347c3855 100644 --- a/app/src/test/java/org/justinvisser/wolfskin/tv/UpdatesCaptureTest.kt +++ b/app/src/test/java/org/justinvisser/wolfskin/tv/UpdatesCaptureTest.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performKeyInput import androidx.compose.ui.test.pressKey import androidx.compose.ui.input.key.Key +import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.semantics.SemanticsProperties import com.github.takahirom.roborazzi.captureRoboImage import java.io.File @@ -141,6 +142,15 @@ class UpdatesCaptureTest { assertEquals(1, (shell.contentFocus as UpdateFocus).actionIndex) } + @Test fun refreshingInstalledIdentityCannotMaskAnOngoingCheck() { + launch(UpdateUiState.UpToDate) + rule.runOnIdle { controller.showInstalled(InstalledVersion("1.1.1", 100001)) } + rule.mainClock.advanceTimeBy(320) + rule.runOnIdle { controller.show(UpdateUiState.Checking) } + rule.mainClock.advanceTimeBy(320) + rule.onNodeWithTag("update-progress").assertIsDisplayed() + } + @Test fun nativeProgressAndStateTransitionAtSixteenMillisecondFrames() { launch(UpdateUiState.Checking) val target = directory diff --git a/app/src/wolfskin/java/org/justinvisser/wolfskin/tv/update/UpdatesScreen.kt b/app/src/wolfskin/java/org/justinvisser/wolfskin/tv/update/UpdatesScreen.kt index ea69c6f7..a33341aa 100644 --- a/app/src/wolfskin/java/org/justinvisser/wolfskin/tv/update/UpdatesScreen.kt +++ b/app/src/wolfskin/java/org/justinvisser/wolfskin/tv/update/UpdatesScreen.kt @@ -51,6 +51,7 @@ import org.justinvisser.wolfskin.design.focusRing import org.justinvisser.wolfskin.design.focusScale import org.justinvisser.wolfskin.design.motionDuration import org.justinvisser.wolfskin.design.reducedMotion +import org.justinvisser.wolfskin.design.resolveUpdateAvatarColor import org.justinvisser.wolfskin.design.rowReveal import org.justinvisser.wolfskin.design.WolfskinTokens.Color as C import org.justinvisser.wolfskin.design.WolfskinTokens.Layout as L @@ -68,7 +69,9 @@ fun UpdatesScreen(shell: ShellState, controller: UpdateController, onBack: () -> val autoCheck by controller.autoCheck.collectAsStateWithLifecycle() val initialInstalled = remember { installed.versionName } val updatedVersion = installed.versionName.takeIf { it != initialInstalled } - val presentation = presentUpdate(if (updatedVersion != null) UpdateUiState.UpToDate else state, release, updatedVersion) + val presentation = remember(state, release, updatedVersion) { + presentUpdate(state, release, updatedVersion.takeIf { state == UpdateUiState.UpToDate }) + } val scroll = rememberScrollState() val scope = rememberCoroutineScope() val density = LocalDensity.current @@ -90,7 +93,7 @@ fun UpdatesScreen(shell: ShellState, controller: UpdateController, onBack: () -> Box(modifier.fillMaxSize().testTag("updates-screen")) { Row(Modifier.offset(L.safeSide, U.headerTop).height(U.headerHeight), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(S.settingsHeaderGap)) { - Avatar(shell.person, S.settingsHeaderAvatar, T.avatarLetterLibHeader, letterColor = C.muted) + Avatar(shell.person, S.settingsHeaderAvatar, T.avatarLetterLibHeader, letterColor = C.muted, backgroundColor = resolveUpdateAvatarColor(shell.person)) Text("Updates", Modifier.alignByBaseline().exactLineBox(T.screenTitle), style = T.screenTitle, color = C.fg) Text("Installed ${installed.versionName}", Modifier.alignByBaseline(), style = T.nav.copy(fontWeight = androidx.compose.ui.text.font.FontWeight.Normal), color = C.muted) } diff --git a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/Identity.kt b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/Identity.kt index d7c5570f..6aec4bf2 100644 --- a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/Identity.kt +++ b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/Identity.kt @@ -21,6 +21,10 @@ fun personColor(key: String): Color = when (key.lowercase()) { else -> WolfskinTokens.Color.accent } +/** Updates has its own locked avatar swatch; other destinations retain household colours. */ +fun resolveUpdateAvatarColor(person: ShellPerson): Color = + if (person.key.equals("justin", ignoreCase = true)) WolfskinTokens.Update.avatarJustin else personColor(person.key) + /** The same mark anchors the picker and the persistent navigation. */ @Composable fun Wordmark(modifier: Modifier = Modifier, prefix: String = "Wolf", textStyle: TextStyle = WolfskinTokens.Type.wordmark) { @@ -32,8 +36,9 @@ fun Wordmark(modifier: Modifier = Modifier, prefix: String = "Wolf", textStyle: /** Initials are drawn over opaque person colour at the exact slot size. */ @Composable -fun Avatar(person: ShellPerson, diameter: Dp, textStyle: TextStyle, modifier: Modifier = Modifier, letterColor: Color = WolfskinTokens.Color.onFocus) { - Box(modifier.size(diameter).background(personColor(person.key), CircleShape), contentAlignment = Alignment.Center) { +fun Avatar(person: ShellPerson, diameter: Dp, textStyle: TextStyle, modifier: Modifier = Modifier, letterColor: Color = WolfskinTokens.Color.onFocus, + backgroundColor: Color = personColor(person.key)) { + Box(modifier.size(diameter).background(backgroundColor, CircleShape), contentAlignment = Alignment.Center) { Text(person.initial, color = letterColor, style = textStyle) } } diff --git a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/NavBar.kt b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/NavBar.kt index 929fce42..e30392ae 100644 --- a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/NavBar.kt +++ b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/NavBar.kt @@ -117,6 +117,7 @@ fun NavBar(state: ShellState, scrolled: Boolean, modifier: Modifier = Modifier) Modifier.offset(avatar.left, avatar.top) .focusRing(avatarFocused, L.navAvatar / 2, WolfskinTokens.Focus.ringAvatarRing) .focusProperties { canFocus = false } - .clickable { state.focusNav(state.tabs.size); state.key(ShellKey.Ok) }) + .clickable { state.focusNav(state.tabs.size); state.key(ShellKey.Ok) }, + backgroundColor = if (hasSelectedTab) personColor(state.person.key) else resolveUpdateAvatarColor(state.person)) } } diff --git a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/SettingsList.kt b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/SettingsList.kt index 15c47744..e04c344f 100644 --- a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/SettingsList.kt +++ b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/SettingsList.kt @@ -70,7 +70,7 @@ fun SettingsList(state: ShellState, modifier: Modifier = Modifier, requestCount: Box(modifier.fillMaxSize()) { Row(Modifier.offset(L.safeSide, WolfskinTokens.Update.headerTop).height(WolfskinTokens.Update.headerHeight), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(S.settingsHeaderGap)) { - Avatar(state.person, S.settingsHeaderAvatar, T.avatarLetterLibHeader, letterColor = C.muted) + Avatar(state.person, S.settingsHeaderAvatar, T.avatarLetterLibHeader, letterColor = C.muted, backgroundColor = resolveUpdateAvatarColor(state.person)) Text("Settings", Modifier.alignByBaseline().exactLineBox(T.screenTitle), style = T.screenTitle, color = C.fg) Text("This TV ยท ${state.person.name}", Modifier.alignByBaseline(), style = T.nav.copy(fontWeight = FontWeight.Normal), color = C.muted) } diff --git a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt index 0cfda025..bb8cfdc6 100644 --- a/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt +++ b/wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt @@ -702,6 +702,7 @@ object WolfskinTokens { const val ambientBrightness = 0.55f } object Update { + val avatarJustin = ComposeColor(0xFF7AB8FF) val heading = TextStyle(fontSize = 18.0.sp, fontWeight = FontWeight(800), lineHeight = 20.160000000000004.sp, letterSpacing = -0.02.em) const val settingsVisibleRows = 6.0f val headerTop = 75.0.dp diff --git a/wolfskin-design/tokens.css b/wolfskin-design/tokens.css index ef0f40c6..fdc284bd 100644 --- a/wolfskin-design/tokens.css +++ b/wolfskin-design/tokens.css @@ -516,6 +516,7 @@ --discover-stageGap: 10.0px; --discover-ambientInset: 80.0px; --discover-ambientBlur: 60.0px; + --update-avatarJustin: #7ab8ff; --update-heading: 36.0px; --update-headerTop: 150.0px; --update-headerHeight: 84.0px; diff --git a/wolfskin-design/tokens.sha256 b/wolfskin-design/tokens.sha256 index ef997d43..ae81afd2 100644 --- a/wolfskin-design/tokens.sha256 +++ b/wolfskin-design/tokens.sha256 @@ -2,8 +2,8 @@ b3f096b692c395534177cc82ac68f5b801870bc7e28f0dd5cd7c17e2103920a3 discover-token 993d933950e17fb72d0902ffc440e1e4ed4dc088c96a8d12974431c77dc31634 generate_tokens.py 25985087986a46f00e2819917d0421ff2b437ec21c54f6ce6412b84c0eb05302 home-tokens.json be3af0ddcb0d67abef0805d19d7a33cdb55428bcace656b50f0a38935f212028 shell-tokens.json -18c08c6f4d77ee0690e63ec2e1ce14854eced539746c41c948942460b37a38b2 src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt +289935944db0aa2f43224d7532331ad9ac5c666dbe40f1c44c9b7ee725f972ad src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt e0a2f5df2f249bce3f6e13f4a0320c7b18b85777eaf04a27a428d6683a6aad28 title-tokens.json -eed527dac5e09b209619cd67990556a5f9223d05e7e2516f300cb4760a198ca7 tokens.css +5fa37cdbb5ab1bf0dbb0f83b8facabcd601ffb22492ad54f8aa9fc86cb729ee4 tokens.css 2e5e0476341adec1ec05331af38bc67cd02ba013a0988aea40a70eff21513264 tokens.json -824cb58d1507bc05212323ecfa66664f8b2bde23438940eed8717266161cac55 update-tokens.json +20c17f4e980baccce555b0cff8f76a2cfbc0db4e88d5a11f43abf73f0d936eb2 update-tokens.json diff --git a/wolfskin-design/update-tokens.json b/wolfskin-design/update-tokens.json index 35dc1a1f..b5e59c6f 100644 --- a/wolfskin-design/update-tokens.json +++ b/wolfskin-design/update-tokens.json @@ -1,5 +1,6 @@ { "update": { + "avatarJustin": {"$type": "color", "$value": "#7ab8ff"}, "heading": { "$type": "typography", "$value": {"fontWeight": 800},